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
Related
I have a CDI Bean which is injected to another CDI bean,
Bean1Controller:
#ViewScoped
public class bean1Controller
{
#Inject
Bean2Controller bean2;
// + setter and getter
// here I initialise the injected Bean2
#PostConstruct
public void init()
{
bean2 = new Bean2Controller();
}
public void changeFlagBoolean()
{
bean2.setFlag(true);
}
}
Bean2Controller:
#ViewScoped
public class bean2Controller
{
boolean flag=false;
// + getters+setters
}
XHTML sample:
<h:commandLink style="font-size: 10px"
value="link"
action="#{bean1Controller.changeFlagBoolean()}"
target="content" />
I have a link in my XHTML Page when I click It I run the method changeFlagBoolean() of bean1Controller to set the property flag to true of the injected bean2Controller. I proceed like That but unfortunately it doesn't work.
Any suggestion or something needs to be change in the code.
Thank you
Updated Answear,
I got the solution after such research,
The problem was from the #viewScoped scope, I couldn't change the value of the flag property in the injected bean and it still always false after doing bean2.setFlag(true);
the problem if I go from view1.xhtml to view2.xhtml I have a new instance of the bean1Controller thats why I have always false as a value because it is a view Scope based.
The olution was with the Flash https://memorynotfound.com/passing-variables-in-jsf-flash-scope/ . I keep the value in a flash and set its value as ' true' and I called it in the bean2Controller so that it is available in the bean1Controller and get it by callling flag = (boolean)flash.get("flag");
Im am using bean validation with JSF2.0. I have a validation group which I specify depending on a few conditions and link to a attribute in the managed bean. The attribute is assigned when the page first loads and works correctly (i.e. when the form is submitted the correct groups are validated). However if I change this property the validation groups are not updated and whatever the original value was set to will be used.
for example:
JSF fragment:
<h:selectOneMenu id="unitOfMerchandise" value="#itemManager[targetBean].unitOfMerchandise}">
<f:selectItem itemLabel="-- select --" itemValue="" />
<f:selectItems value="#{itemManager.unitsOfMerchandise}" />
<f:validateBean validationGroups="#{itemManager.validatorClass}" />
</h:selectOneMenu>
Method:
#ManagedBean
#ViewScoped
public class ItemManager implements Serializable {
private String validatorClass = "com.rcs.itemmngr.model.validation.RegularItem"
private OpenItemRequest openItemRequest
private void onItemTypeSelected() {
validatorClass = itemManagerModel.getValidatorItemRequestClass(openItemRequest).getName();
}
///getters setters
}
Any ideas on how to get this to work? I have also looked for a way to change the validation groups programmatically in the managed bean but with no joy.
f:validateBean gruops are evaluated once: when the component tree is built. There seem to be no simple way to update them.
You can either update them manually per component:
//bind you component here
EditableValueHolder input;
//call this to update groups
public void setValidationGroups(String validationGroups) {
for (Validator validator : input.getValidators()) {
if (!(validator instanceof BeanValidator)) {
continue;
}
BeanValidator beanValidator = (BeanValidator) validator;
beanValidator.setValidationGroups(validationGroups);
}
}
Or you can use approach described in this article:
Delete the components holding unwanted state
The idea is to remove components with f:validateBean from tree, so
they will be reinitialized on rendering with new groups:
parentComponent.getChildren().clear();
e.g. if you are executing and rendering a section, you can call
somethig like this in actionListner:
public void resetContactsValidationGroups() {
FacesContext ctx = FacesContext.getCurrentInstance();
Iterator<String> ids = ctx.getPartialViewContext().getExecuteIds().iterator();
while (ids.hasNext()) {
ctx.getViewRoot().findComponent(ids.next()).getChildren().clear();
}
}
What is the difference between using value and binding with JavaServer Faces, and when would you use one as opposed to the other? To make it clearer what my question is, a couple of simple examples are given here.
Normally with JSF in the XHTML code you would use "value" as here:
<h:form>
<h:inputText value="#{hello.inputText}"/>
<h:commandButton value="Click Me!" action="#{hello.action}"/>
<h:outputText value="#{hello.outputText}"/>
</h:form>
Then the bean is:
// Imports
#ManagedBean(name="hello")
#RequestScoped
public class Hello implements Serializable {
private String inputText;
private String outputText;
public void setInputText(String inputText) {
this.inputText = inputText;
}
public String getInputText() {
return inputText;
}
// Other getters and setters etc.
// Other methods etc.
public String action() {
// Do other things
return "success";
}
}
However, when using "binding", the XHTML code is:
<h:form>
<h:inputText binding="#{backing_hello.inputText}"/>
<h:commandButton value="Click Me!" action="#{backing_hello.action}"/>
<h:outputText value="Hello!" binding="#{backing_hello.outputText}"/>
</h:form>
and the correspondibg bean is called a backing bean, and is here:
// Imports
#ManagedBean(name="backing_hello")
#RequestScoped
public class Hello implements Serializable {
private HtmlInputText inputText;
private HtmlOutputText outputText;
public void setInputText(HtmlInputText inputText) {
this.inputText = inputText;
}
public HtmlInputText getInputText() {
return inputText;
}
// Other getters and setters etc.
// Other methods etc.
public String action() {
// Do other things
return "success";
}
}
What practical differences are there between the two systems, and when would you use a backing bean rather than a regular bean? Is it possible to use both?
I have been confused about this for some time, and would most appreciate having this cleared up.
value attribute represents the value of the component. It is the text that you see inside your text box when you open the page in browser.
binding attribute is used to bind your component to a bean property. For an example in your code your inputText component is bound to the bean like this.
#{backing_hello.inputText}`
It means that you can access the whole component and all its properties in your code as a UIComponent object. You can do lot of works with the component because now it is available in your java code.
For an example you can change its style like this.
public HtmlInputText getInputText() {
inputText.setStyle("color:red");
return inputText;
}
Or simply to disable the component according to a bean property
if(someBoolean) {
inputText.setDisabled(true);
}
and so on....
Sometimes we don't really need to apply the value of UIComponent to a bean property. For example you might need to access the UIComponent and work with it without applying its value to the model property. In such cases it's good to use a backing bean rather than a regular bean. On the other hand in some situations we might need to work with the values of the UIComponent without any need of programmatic access to them. In this case you can just go with the regular beans.
So, the rule is that use a backing bean only when you need programmatic access to the components declared in the view. In other cases use the regular beans.
I am using a session scoped managed bean for handling login in a Java EE application. After I authenticate the user, the user object is saved in this session bean. However, after I refresh the page, the session bean values are gone.
I was debugging the code and it results that the constructor of the session scoped managed bean is called again on page refresh, therefore initializing the user object with a new user. I guess this is not a normal behavior since it should be preserved on the session shouldn't it?
I am posting some parts of the login managed bean including the parameters and the login method. Basically the enteredEmail and enteredPassword stand for the entered data on the login form. If the authentication succeeds, the loggedIn boolean is turned to true and the logged in user object is stored in the checkedUser variable.
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
#ManagedBean
#SessionScoped
public class LoginController implements Serializable {
#EJB
private LoginSessionBean loginSessionBean;
#EJB
private LecturerFacade lecturerFacade;
private Lecturer checkedUser;
private String enteredEmail;
private String enteredPassword;
private boolean loggedIn;
/** Creates a new instance of loginController */
public LoginController() {
loggedIn = false;
checkedUser = new Lecturer();
}
public String login(){
RequestContext context = RequestContext.getCurrentInstance();
FacesMessage msg = null;
this.setCheckedUser(lecturerFacade.findLecturerByEmail(enteredEmail));
if(loginSessionBean.checkPassword(checkedUser, enteredPassword))
{
loggedIn = true;
msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "Welcome", checkedUser.getFirstName()+ " " + checkedUser.getLastName());
FacesContext.getCurrentInstance().addMessage(null, msg);
context.addCallbackParam("loggedIn", loggedIn);
}
return "Index";
I am also posting the two EJBs that the above managed bean uses. The lecturerFacade retrieves the user object with the entered email, while the loginSessionBean checks the password.
#Stateless
public class LecturerFacade extends AbstractFacade<Lecturer> {
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
Logger logger = Logger.getLogger("MyLog");
FileHandler fh;
protected EntityManager getEntityManager() {
return em;
}
public LecturerFacade() {
super(Lecturer.class);
}
public Lecturer findLecturerByEmail(String email) {
try {
return (Lecturer) this.getEntityManager().createQuery("SELECT l FROM Lecturer l WHERE l.email = :email").setParameter("email", email).getSingleResult();
} catch (NoResultException e) {
System.err.println("Caught NOResultException: "+ e.getMessage());
return null;
} catch (NonUniqueResultException e) {
System.err.println("Caught NonUniqueResultException: "+ e.getMessage());
return null;
} catch (IllegalStateException e) {
System.err.println("Caught IllegalStateException: "+ e.getMessage());
return null;
}
}
_
#Stateless
public class LoginSessionBean {
// Add business logic below. (Right-click in editor and choose
// "Insert Code > Add Business Method")
#PersistenceContext(unitName = "EffectinetWebPU")
private EntityManager em;
protected EntityManager getEntityManager() {
return em;
}
public void setEntityManager(EntityManager em) {
this.em = em;
}
public boolean checkPassword(Lecturer user, final String enteredPassword) {
if (user.getPassword().equals(enteredPassword)) {
return true;
} else {
return false;
}
}
}
Please if someone has any suggestion of what is going wrong, please tell me
Im using glassfish 3.1 as application server and Primefaces as JSF library. Also, I have checked and the imported the sessionScoped annotation from the right package and not from javax.enterprise...
Your problem is thus here:
<p:menuitem value="Logout" ... onclick="#{loginController.logout()}"/>
The onclick attribute should represent a JavaScript handler function which is to be executed in the webbrowser when the enduser clicks the element. Something like
onclick="alert('You have clicked this element!')"
The onclick attribute also accepts a ValueExpression, so you can even let JSF/EL autogenerate its value accordingly:
onclick="#{bean.onclickFunction}"
with
public String getOnclickFunction() {
return "alert('You have clicked this element!')";
}
All the EL is thus evaluated when the page is rendered. In your particular case, the logout() method is called everytime the EL is evaluated and thus you're invalidating the session everytime the page is rendered!
You need to bind it to an attribute which takes a MethodExpression like <h:commandLink action>, <h:commandButton action> and in this particular case <p:menuitem action>.
<p:menuitem value="Logout" ... action="#{loginController.logout()}"/>
This can be understood by understanding basic HTML and JavaScript concepts and keeping in mind that JSF ultimately produces HTML/CSS/JS. Open the JSF page in webbrowser, rightclick and View Source to realize it.
Well I managed to solve it today. This was the problem, although I cannot explain why:
I was using Primefaces 3.2 as JSF library so this was the main menu of the index page.
<h:form>
<p:menubar >
<p:menuitem id="registerLink" value="Register" rendered="#{!loginController.loggedIn}" onclick="registerDialog.show()" />
<p:menuitem id="loginLink" value="Login" rendered="#{!loginController.loggedIn}" onclick="loginDialog.show()" />
<p:submenu label="Units" rendered="true">
<p:menuitem id="addNew" value="Add New" onclick="createUnitDialog.show()" />
<p:menuitem id="myUnits" value="My Units" onclick="" />
</p:submenu>
<p:menuitem id="results" value="Results/Statistics" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="profile" value="My Profile" rendered="#{loginController.loggedIn}" onclick=""/>
<p:menuitem id="logout" value="Logout" rendered="#{loginController.loggedIn}" onclick="#{loginController.logout()}"/>
</p:menubar>
</h:form>
After setting breakpoints to the whole code I discovered that the logout() method, which is supposed to destroy the managed bean, was called on every page refresh. I don't know why this happened as it should be called when the logout menuitem was clicked.
However, after changing the onclick="#{loginController.logout()} with action="#{loginController.logout()} the problem was solved.
I checked the documentation of Primefaces but nowhere this behavior was explained
Simple question from a beginner at JSF:
I have very simple JSF form:
<h:form>
<p>#{messages.loginTextfieldUsername}</p>
<h:inputText value="#{userServiceImpl.user.name}" />
<p>#{messages.loginTextfieldPassword}</p>
<h:inputSecret value="#{userServiceImpl.user.password}" />
<h:commandButton value="#{messages.loginButtonLogin}" action="#{userServiceImpl.authenticateUser}" />
</h:form>
The userServiceImpl class is:
#Named
#RequestScoped
public class UserServiceImpl implements UserService {
private UserSession userSession;
private User user;
#Inject
public UserServiceImpl(UserSession userSession) {
this.userSession = userSession;
}
#PostConstruct
public void prepareService() {
user = new User();
}
#Override
public View authenticateUser() {
userSession.setLoggedUser(user);
return View.MAIN;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
My goal is pretty simple: when the user hits the login button, I want to authenticate the user.
The problem is:
When the authenticate method is called, the User attributes are null. I debugged the application and the getUser method is called and the values are properly set, but at some point (which I did not find [yet]) before the authenticateUser is called the User attributes are set to null...
I'm aware that this is a pretty basic question... but are you able to point out where my mistake is?
Thanks.
Based on your previous question, you seem to have experimented with <managed-bean-scope> of none in faces-config.xml for some reason. The problem symptoms matches exactly when using #ManagedBean #NoneScoped. You seem to have configured this bean in faces-config.xml as well on a none scope which totally explains this problem. With the none scope, a brand new bean instance is been created everytime when #{userServiceImpl} is been evaluated in EL. Your form submit has thus effectively created 3 beans: one where the user name is set, another one where user password is set and another one where action is invoked.
You need to remove the managed bean configuration from faces-config.xml. You should not use it when you intend to use #Inject (or #ManagedBean). The faces-config.xml way of configuring beans is a leftover from old JSF 1.x ages when annotations weren't available. As of JSF 2.x it would only override any bean management annotations.