How to show h:message based on condition? - jsf

I am making multi-language website where I am using Validator for one field.
After validation, I receive response as err002, err003 and based on this error I would be showing the respective error in message format. So what I was planning is something like below.
What I have is <h:message for="password">
What I wanted to do is as below.
if (message is err002) {
show message of err002 from the properties file.
#{msg['err002']}
}
if (message is err003) {
show message of err003 from the properties file.
#{msg['err003']}
}
Any idea how to get this done?
Actually what I want to do is display error message in both language. What I have is language code in session bean, but I can't check language code in validators.
Any idea/ suggestion how this can be done would be greatful.
Edit 1
faces-config.xml
<application>
<locale-config>
<default-locale>zh_CN</default-locale>
</locale-config>
<resource-bundle>
<base-name>resources.welcome</base-name>
<var>msg</var>
</resource-bundle>
</application>
LanguageBean.java
#ManagedBean(name = "language")
#SessionScoped
public class LanguageBean implements Serializable {
Properties files that I have are
welcome.properties and welcome_zh_CN.properties

You can easily achive it in a validator method. Use it like
#FacesValidator("passwordValidator")
public class PasswordValidator implements Validator {
String err1, err2, err3;
public PasswordValidator() {
ResourceBundle bundle = ResourceBundle.getBundle("msg", FacesContext.getCurrentInstance().getViewRoot().getLocale());
err1 = bundle.getString("err1");
err2 = bundle.getString("err2");
err3 = bundle.getString("err3");
}
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
String pass = (String) value;
FacesMessage msg;
if(/*some condition*/) {
msg = new FacesMessage(err1);
} else if(/*other condition*/) {
msg = new FacesMessage(err2);
} else {
msg = new FacesMessage(err3);
}
if(msg != null) {
throw new ValidatorException(msg);
}
}
}
And use it in view with
<h:inputText id="password" validator="passwordValidator" .../>
<h:message for=password .../>

Related

form with enctype="multipart/form-data" is not getting submit by command button

I am building a test application with java6, JSF2.0 on WebSphere Portal 8.0.0.3 to achieve the file upload functionality.
FileUploadView.xhtml
<h:form enctype="multipart/form-data">
<input type="file" name="fileItem1" />
<h:commandButton value="submit" action="#{bean.submit}" />
</h:form>
FileUploadPortlet.java
class FileUploadPortlet extends com.ibm.faces20.portlet.FaceletPortlet{
public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException {
boolean isMultipart = PortletFileUpload.isMultipartContent(request);
try {
if (isMultipart) {
FileItemFactory factory = new DiskFileItemFactory();
PortletFileUpload upload = new PortletFileUpload(factory);
List /* FileItem */ items = upload.parseRequest(request);
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
String name = item.getFieldName();
String value = item.getString();
} else {
String fileName = item.getName();
long sizeInBytes = item.getSize();
System.out.println("FileName is: "+fileName+" Size is: "+sizeInBytes);
}
}
}
} catch ( Exception e) {
System.out.println("SampleFileUploadPortlet.processAction() Error occured");
e.printStackTrace();
}
}
}
portlet.xml
<portlet>
<portlet-name>FileUploadView</portlet-name>
<portlet-class>com.code.FileUploadPortlet</portlet.class>
........
</portlet>
FileIploadManagedBean.java
#ManagedBean(name = "bean")
#SessionScoped
public class FileIploadManagedBean{
public void submit(){
System.out.println("Hello i am in managed bean!!");
}
}
When I am trying to submit the form, I can get the file in processAction method of FileUploadPortlet.java. But my submit method of managed bean is not getting called.
But when I remove enctype="multipart/form-data" from my form. Submit method of managed bean is getting called before processAction method of FileUploadPortlet and file is not visible in this processAction method.

How to get the values from multiple dynaforms?

I have been following this tutorial
http://www.primefaces.org/showcase-ext/sections/dynaform/basicUsage.jsf
I have been able to create tree Dynaform objects and send it to the page. But I am having a hard time obtaining the values that the user entered once they clicked submit. I want to be able to get these values in the backbean.
Here is submit button
<p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
process="dynaForm" update=":mainForm:dynaFormGroup :mainForm:inputValues"
oncomplete="handleComplete(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
I know the submit calls this function
<h:outputScript id="dynaFormScript" target="body">
/* <![CDATA[ */
function handleComplete(xhr, status, args) {
if(args && args.isValid) {
PF('inputValuesWidget').show();
} else {
PF('inputValuesWidget').hide();
}
}
/* ]]> */
</h:outputScript>
Then in the bean we have:
public String submitForm() {
FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
boolean hasErrors = (sev != null && (FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0));
RequestContext requestContext = RequestContext.getCurrentInstance();
requestContext.addCallbackParam("isValid", !hasErrors);
return null;
}
How would I be able to get either the fields values from the submitted form?
I have 3 dynaforms that I would like to submit them and be able to get the values in the back bean. Can anyone explain? I tried looking up some tutorials but I didn't find any explaining this.
Thanks.
It's the same as plain JSF.
You need a variable in your bean, its getters and setters.
Then, you compare it to the DynaFormControl.
#ManagedBean
#SessionScoped
public class DynaFormController implements Serializable {
private static final long serialVersionUID = 1L;
private DynaFormModel model;
private BookProperty bookProperty;
public String getBookProperty() {
return bookProperty;
}
public void setBookProperty(BookProperty bookProperty) {
this.bookProperty = bookProperty;
}
public String submitForm() {
//your code
List<DynaFormControl> controls = model.getControls();
for (DynaFormControl control : controls) {
if(control.getData() instanceof BookProperty) {
BookProperty bp = (BookProperty) c.getData();
//use the object
}
}
return null;
}
}

How to override h:selectOneRadio renderer? Where is the renderer class in jsf-impl?

Is it possible to override renderer used by <h:selectOneRadio>? I tried to find the class from jsf-impl package of JSF 2.2 but didn't find it. The reason I want to do this is to get rid of the table it generates.
Is it possible to override renderer used by h:selectOneRadio?
Yes, surely it is. Otherwise, UI component libraries like PrimeFaces couldn't exist.
I tried to find the class from jsf-impl package but didn't find it.
The exact class depends on the JSF implementation you're using. If it's Mojarra, then it's the com.sun.faces.renderkit.html_basic.RadioRenderer class. If it's MyFaces, then it's the org.apache.myfaces.renderkit.html.HtmlRadioRenderer class.
In order to properly override it, just extend the class and override methods where necessary and register it as follows in your faces-config.xml:
<render-kit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.example.MyRadioRenderer</renderer-class>
</renderer>
</render-kit>
Keep in mind that you're this way tight-coupling the renderer to the specific JSF impl/version. Such an extended renderer is not compatible with a different JSF implementation (i.e. your app wouldn't deploy when you ever replace Mojarra by MyFaces) and may possibly break when the current JSF implementation has been updated to a newer version. If you worry about this, consider writing the renderer entirely from scratch, like PrimeFaces et.al. do.
The reason I want to do this is to get rid of the table it generates.
Consider looking at Tomahawk or PrimeFaces instead of reinventing the wheel. They have respectively a <t:selectOneRadio layout="spread"><t:radio> and <p:selectOneRadio layout="custom"><p:radioButton> which allows you positioning those things everywhere you want.
See also:
<h:selectOneRadio> renders table element, how to avoid this?
I added
<render-kit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.sial.ecommerce.configurator.ui.model.RadioRendererWithoutDataTable</renderer-class>
</renderer>
</render-kit>
to faces-config.xml.
And created a class which extends com.sun.faces.renderkit.html_basic.RadioRenderer And I did override the method encodeEnd then commented out the code which adding table elements.
public class RadioRendererWithoutDataTable extends com.sun.faces.renderkit.html_basic.RadioRenderer {
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
rendererParamsNotNull(context, component);
if (!shouldEncode(component)) {
return;
}
ResponseWriter writer = context.getResponseWriter();
assert (writer != null);
String alignStr;
Object borderObj;
boolean alignVertical = false;
int border = 0;
if (null != (alignStr = (String) component.getAttributes().get("layout"))) {
alignVertical = alignStr.equalsIgnoreCase("pageDirection");
}
if (null != (borderObj = component.getAttributes().get("border"))) {
border = (Integer) borderObj;
}
Converter converter = null;
if (component instanceof ValueHolder) {
converter = ((ValueHolder) component).getConverter();
}
// renderBeginText(component, border, alignVertical, context, true);
Iterator<SelectItem> items = RenderKitUtils.getSelectItems(context, component);
Object currentSelections = getCurrentSelectedValues(component);
Object[] submittedValues = getSubmittedSelectedValues(component);
Map<String, Object> attributes = component.getAttributes();
OptionComponentInfo optionInfo = new OptionComponentInfo((String) attributes.get("disabledClass"),
(String) attributes.get("enabledClass"), (String) attributes.get("unselectedClass"),
(String) attributes.get("selectedClass"), Util.componentIsDisabled(component), isHideNoSelection(component));
int idx = -1;
while (items.hasNext()) {
SelectItem curItem = items.next();
idx++;
// If we come across a group of options, render them as a nested
// table.
if (curItem instanceof SelectItemGroup) {
// write out the label for the group.
if (curItem.getLabel() != null) {
// if (alignVertical) {
// writer.startElement("tr", component);
// }
//writer.startElement("td", component);
writer.writeText(curItem.getLabel(), component, "label");
// writer.endElement("td");
// if (alignVertical) {
// writer.endElement("tr");
// }
}
// if (alignVertical) {
// writer.startElement("tr", component);
// }
// writer.startElement("td", component);
// writer.writeText("\n", component, null);
// renderBeginText(component, 0, alignVertical, context, false);
// render options of this group.
SelectItem[] itemsArray = ((SelectItemGroup) curItem).getSelectItems();
for (int i = 0; i < itemsArray.length; ++i) {
renderOption(context, component, converter, itemsArray[i], currentSelections, submittedValues, alignVertical, i,
optionInfo);
}
// renderEndText(component, alignVertical, context);
// writer.endElement("td");
// if (alignVertical) {
// writer.endElement("tr");
// writer.writeText("\n", component, null);
// }
} else {
renderOption(context, component, converter, curItem, currentSelections, submittedValues, alignVertical, idx, optionInfo);
}
}
//renderEndText(component, alignVertical, context);
}
Then it worked for me.
When I given
<h:selectOneRadio >
<f:selectItem itemValue="1" itemLabel="Item 1" />
<f:selectItem itemValue="2" itemLabel="Item 2" />
</h:selectOneRadio>
in my jsf page.
It converted to
<input type="radio" name="bulkForm:j_idt224" id="bulkForm:j_idt224:0" value="1"><label for="bulkForm:j_idt224:0"> Item 1</label>
<input type="radio" name="bulkForm:j_idt224" id="bulkForm:j_idt224:1" value="2"><label for="bulkForm:j_idt224:1"> Item 2</label>
which was what I need.

Change Localization In JSF Throughout The Session

I have a web application, which uses JSF with PrimeFaces, and there is a JSF which allows users to change the locale dynamically. But the locale changes back to the default after the user goes to another JSF.
I looked at the codes here, and here, but still couldn't get it to work.
In my resource directory, I have 3 property files.
Project
- src/main/resources
- <default package>
- messages_en_US.properties
- messages_zh_CN.properties
- messages_zh_TW.properties
In my faces-config.xml I have the locales defined
<application>
<locale-config>
<default-locale>en_US</default-locale>
<supported-locale>zh_TW</supported-locale> <!-- generic traditional chinese -->
<supported-locale>zh_CN</supported-locale> <!-- generic simplified chinese -->
</locale-config>
<message-bundle>
messages
</message-bundle>
<resource-bundle>
<base-name>messages</base-name>
<var>msg</var>
</resource-bundle>
</application>
In the JSF which allows users to change the locale, it is actually a user profile page. The <p:selectOneMenu /> items are read from an Ennum and the locale is changed upon clicking on the "Save" button.
<p:selectOneMenu id="defaultLanguage" value="#{userController.userLanguage}">
<f:selectItems value="#{userController.langcode}" var="lang" itemValue="#{lang.locale}"
itemLabel="#{lang.locale} - #{lang.desc}" />
</p:selectOneMenu>
...
<p:commandButton id="save" value="#{msg.save}" title="#{msg.save}" icon="ui-icon-disk"
styleClass="action-buttons" actionListener="#{userController.doSave}" update="#form" />
In the UserController ManagedBean, the code are as follows:
#ManagedBean
#ViewScoped
public class UserController extends BaseController implements Serializable {
public void doSave(ActionEvent e) {
String lang;
String country = null;
String[] selectedLanguage = userLanguage.split("_");
lang = selectedLanguage[0];
if (selectedLanguage.length > 1) {
country = selectedLanguage[1];
setUserLocale(new Locale(lang, country));
}
else {
setUserLocale(new Locale(lang));
}
LOG.debug("userLanguage: {}; lang: {}", userLanguage, lang);
FacesContext.getCurrentInstance().getViewRoot().setLocale(userLocale); // sets the locale from the selected user language
Messages.addGlobalInfo(getMessage(msgInfoUpdated));
}
private LangCode userLangCode; // getter + setter
private LangCode[] langcode = LangCode.values(); // getter + setter
#ManagedProperty(value = "#{loginController.userLocale}")
private Locale userLocale; // getter + setter
}
In the JSF I tried to add the <f:view locale="#{loginController.locale}" />. But still the same. In debug mode, when going to a new JSF page, the value of the userLocale is always the default locale and not the one which the user changed.
The LoginController code is as below. At the doLogin() method, I set the userLocale object with the locale from the Faces Context.
#ManagedBean
#SessionScoped
public class LoginController extends BaseController implements Serializable {
public String doLogin() {
String returnValue;
try {
currentUser = aduserFacade.validateUserLogin(username, password);
LOG.debug("Successful login: {}", currentUser.getUsrId());
// Set currentUser object into request session attribute
FacesContext context = FacesContext.getCurrentInstance();
HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
setUserSession();
userLocale = Faces.getLocale();
request.getSession().setAttribute("userSession", userSession);
returnValue = "main";
}
catch (Exception e) {
Messages.addGlobalError(getMessage(e.getMessage()));
LOG.error(e.toString(), e);
returnValue = null;
}
return returnValue;
}
private Locale userLocale; // getter + setter
}
The reason your locale is failing to hold is that the locale property you're injecting has not yet been updated with the new value as at the time you're injecting it. See, when you inject a managed bean property, as against the managed bean itself as in
#ManagedProperty(value = "#{loginController.userLocale}")
instead of
#ManagedProperty(value = "#{loginController}")
You're going to get a static value injection. Even after the value in the injected bean has been updated, you'll still only get a stale value that was set as soon as the injected bean was initialized. So what you need to do is inject the entire LoginController and then pull the value yourself, so you'll get the most current values.
See also
Managed property value not updated when both beans are of same scope
Unrelated to your question, you really shouldn't put member variables and injection at the bottom of your class. Makes your code inconvenient to read

Using kaptcha with JSF

I'm trying to use http://code.google.com/p/kaptcha/ which looks like a very easy way to include CAPTCHA. My demo app is JSF and although the instructions are simple for JSP, I don't know how to use them in JSF. How do I translate this in JSF?
In your code that manages the submit action:
String kaptchaExpected = (String)request.getSession()
.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = request.getParameter("kaptcha");
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected))
{
setError("kaptcha", "Invalid validation code.");
}
I tried putting it in my:
public String button1_action() {
// TODO: Process the action.
return "success";
}
but it doesn't understand the request object :(
This equivalent JSF action should do it:
// bind to <h:inputText value="#{thisbean.kaptchaReceived}" />
private String kaptchaReceived;
public String getKaptchaReceived() {
return kaptchaReceived;
}
public void setKaptchaReceived(String kaptcha) {
kaptchaReceived = kaptcha;
}
public String button1_action() {
if (kaptchaReceived != null) {
FacesContext context = FacesContext
.getCurrentInstance();
ExternalContext ext = context.getExternalContext();
Map<String, Object> session = ext.getSessionMap();
String kaptchaExpected = session
.get(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
if (kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
return "success";
}
}
return "problem";
}
This assumes that you want to use h:inputText and h:graphicImage in your JSF view instead of HTML elements.
Implementing validator is another easy way to validate the kaptcha.
<h:inputText id="kaptcha" autocomplete="off" required="true">
<f:validator validatorId="kaptchaValidator" />
</h:inputText>
<h:message for="kaptcha" styleClass="errorMessage"/>
--- Validator ---
public class KaptchaValidator implements Validator {
#Override
public void validate(FacesContext facesContext, UIComponent uiComponent, Object value) throws ValidatorException {
HttpSession session = (HttpSession) facesContext.getExternalContext().getSession(true);
String kaptchaExpected = (String) session.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
String kaptchaReceived = (String) value;
if (kaptchaReceived == null || !kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
FacesMessage message = new FacesMessage();
message.setDetail("Invalid Security Code.");
message.setSummary("Invalid security code.");
message.setSeverity(FacesMessage.SEVERITY_INFO);
throw new ValidatorException(message);
}
}
You can retrieve the request object from the JSF External Context, which is accessible from the FacesContext, using the following code:
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
Edit (thanks to McDowell) :
Another way is to use the FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap() method to access the request parameters...

Resources