I am trying to restrict the rendering of a PrimeFaces tab depending on whether a user is an admin or not.
I am using the following EL to do so:
<p:tab title="Admin" rendered="#{userSession.isAdmin}">
My UserSession class:
#ManagedBean(name="userSession")
#SessionScoped
public class UserSession {
.
.
.
public boolean isAdmin;
public UserSession() {
isAdmin = false;
}
public void addRole(String role) {
if (role.equals("ADMIN") {
this.isAdmin = true;
}
role.add(role)
}
}
The addRole() method gets called and adds the role "ADMIN". I know this isn't a good way of doing things, but I'm only using it for testing purposes at the moment.
My issue is, is that the tab is not being rendered because isAdmin is recognised as false in the constructor. If I change the constructor to true, then it does render.
How can I make it so that is will recognise isAdmin is true afterwards? i.e. after the ADMIN role has been given to the user's session?
Is it possible to 'reload' the page so it can refetch the 'isAdmin' property after it changes?
You could update any Primefaces Component using RequestContext at your ManagedBean methods. For instance in your case at UserSession.addRole(...) method.
RequestContext.getCurrentInstance().update("ID_OF_YOUR_TABVIEW")
Related
Implementation: org.glassfish 2.2.12
I have the following session-scoped validator:
#ManagedBean
#SessionScoped
public class CreateGroupNameValidator extends LengthValidator implements Serializable{
#ManagedProperty(value="#{myDao}")
private MyDao myDao;
//Validate methods
}
In spite of being session-scoped and Serializable, the validator fails to restore the value of the property myDao when postback comes. I used debugger and figuredOut that the state is saved by the class StateHolderSaver which has the following consturctor:
public StateHolderSaver(FacesContext context, Object toSave) {
className = toSave.getClass().getName();
if (toSave instanceof StateHolder) {
// do not save an attached object that is marked transient.
if (!((StateHolder) toSave).isTransient()) {
Serializable [] tuple = new Serializable[StateHolderTupleIndices.LastMember.ordinal()];
tuple[StateHolderTupleIndices.StateHolderSaverInstance.ordinal()] =
(Serializable) ((StateHolder) toSave).saveState(context);
if (toSave instanceof UIComponent) {
tuple[StateHolderTupleIndices.ComponentAddedDynamically.ordinal()] = ((UIComponent)toSave).getAttributes().containsKey(DYNAMIC_COMPONENT) ? Boolean.TRUE : Boolean.FALSE;
}
savedState = tuple;
} else {
className = null;
}
} else if (toSave instanceof Serializable) {
savedState = (Serializable) toSave;
className = null;
}
}
So, since LenghtValidator implements javax.faces.component.StateHolder it didn't save my initial Dao value. Is it a normal behavior?
This is indeed specified behavior. See also a.o. Validator javadoc:
...
Validator implementations must have a zero-arguments public constructor. In addition, if the Validator class wishes to have configuration property values saved and restored with the view, the implementation must also implement StateHolder.
...
Converters and validators can be saved in JSF state so that the JSF implementation can ensure that they have after restoring the view exactly the intented property values as they were during rendering the view of the previous request (such as minimum and maximum in case of LengthValidator, might it happen that they refer a dynamic EL expression).
Although I must admit that they did during designing the JSF 1.0 specification (on which the converter/validator is still based) not really thought about the possibility to inject business service instances in a JSF converter/validator. You of course don't want to save it in the JSF view state. In case of managed properties (and thus not EJB/CDI proxies), it would only blow up the JSF view state (and thus also the HTTP session in case of server side state saving).
If you don't need your validator being JSF state aware, use composition instead of inheritance.
public class CreateGroupNameValidator {
private LengthValidator lengthValidator;
public CreateGroupNameValidator() {
lengthValidator = new LengthValidator();
}
// ...
}
Nonetheless, putting a validator in the session scope is kind of fishy. This implies that the validator's behavior is specific to the current HTTP session. I can't think of sensible real world use cases for this as they are inherently view scoped (not the validator instances but the validator properties). Usually the session scoped data (such as logged-in user) is instead injected/resolved on a thread local basis. You'd better make it request scoped if it's stateful (i.e. validator properties may vary on a per request/view basis), or application scoped if it's stateless (i.e. validator properties are the same throughout application's lifetime).
I want to disable some menuitem on my web page:
<p:menuitem value="Edit" update=":formedit:viewDisplayEdit" icon="ui-icon-document" oncomplete=... disabled="#{bean.ask(1)}"/>
And in my backup bean:
public boolean ask(int id)
{
Ask the database here for privilege "id" and return true if logged user have privilege "id"
else return false
}
Of course I'd like to check it only once - during the rendering of the page.
In this example, regardless of the result of the method "ask" menuitem is always enabled.
I know that I can set the boolean variable xmls code (#{bean.ask1}), but I would like a universal solution.
Could you help?
Make ask a private variable in the bean with get and set methods.
private boolean ask = true;
public boolean isAsk(){
return ask;
}
public void setAsk(boolean ask){
this.ask = ask;
}
Now for whatever condition you want to enable or disable the menu item, set or reset the ask variable appropriately in a different method in the bean.
if (condition = true){
setAsk(false);
}
I still get confused about the PAGE and the CONVERSATION (temp) scope. Maybe I get some help here.
As fas as I know, variables outjected to the PAGE scope live as long as the user only postbacks the same page. The temporary CONVERSATION scope instead even survives a redirect to the next page.
Here is a little example with two effects that are confusing to me:
First, component and outjections are in CONVERSATION scope and the tempUser data is displayed in a jsf page. But in the save method called from that jsf-page, the injected tempUser is null. Why?
Second, if I do the same but change component and #In/#Outs scopes to PAGE scope, the tempUser gets correctly injected on postback - but gets not saved, for wathever reason, although even the super.update()-method on userHome gets called. Or is there a problem in using the homeEntities that ways (the idea iwa to use them only as DAO wrapper)?
#Name("userAction")
#Scope(ScopeType.CONVERSATION)
public class UserAction implements Serializable {
private static final long serialVersionUID = -4852371546895918692L;
#In(create = true)
private UserHome userHome;
#Out(scope = ScopeType.CONVERSATION)
#In(required = false,scope = ScopeType.CONVERSATION)
User tempUser;
#RequestParameter
private Long userId;
#Factory("tempUser")
public User getUser() {
if (tempUser == null) {
userHome.setUserId(userId);
tempUser = userHome.getInstance();
userHome.clearInstance();
}
return tempUser;
}
public void save() {
userHome.setInstance(tempUser);
userHome.update();
}
}
The xhtml contains a a:form with
<a:commandButton
id="update"
styleClass="button admin"
action="#{userAction.save}"
value="#{messages['user.action.update']}"/>
Thanks for replies. Sorry, if this is two problems in one.
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.
JSF view code:
<f:view>
<h:form>
<h:panelGrid>
<h:inputText id="key" value="#{myManagedBean.key}"/>
<h:selectBooleanCheckbox id="rerun" value="#{myManagedBean.rerun}" rendered="#{myManagedBean.displayRerun}"/>
<h:commandButton id="check" action="#{myManagedBean.check}"/>
</h:panelGrid>
</h:form>
<f:view>
JSF model code:
public class MyManagedBean {
private boolean displayRerun;
public void setDisplayRerun(boolean aDisplayRerun) {
this.displayRerun = aDisplayRerun }
public boolean getDisplayRerun() {
return this.displayRerun;
}
private String key;
public void setKey(String aKey) {
this.key = aKey
}
public String getKey() {
return this.key;
}
private boolean rerun;
public void setRerun(boolean arerun) {
this.rerun = arerun
}
public boolean getRerun() {
return this.rerun;
}
public String check() {
//do data validation
setDisplayRerun(true);
System.out.println(getRerun());
}
}
This always prints false regardless of whether the checkbox is checked or not.
Additional Information on my requirement:
Nick/BalusC, my managed bean is of request scope. It is indeed simplified code snippet that I presented. My page has couple of user input controls along with a command button. On submit of command button, I call action method of backing bean, in which I do data validation (in this case I lookup database and see if the inputs are already registered.) If already registered, I come back to the same page, this is when I display the singleBooleanCheckBox for the user to select and hit the command button again.
I am toggling the display of the checkbox based on a managedbean property (a boolean flag set during data validation).
When I re-submit the page with checkbox checked, I do not receive this data.
For further verification, I replace the selectBooleanCheckbox, with a command button with similar behavior (basically do not render it initially, but only show it on data validation). I mapped its #action to my managedbean's action method. To my surprise, when I hit the button, the action method is not executed. Instead, the page is refreshed like in a "immediate" scenario or a redirect.
I have been struggling on this for almost 6 hrs. Appreciate your experienced insights.
Thanks.
So, you've actually a rendered attribute on the checkbox (that was not present in your initial question) and the bean is request scoped (it would have worked when it was session scoped). The submitted checkbox value will not be gathered during apply request values phase when this attribtue evaluates false at that point.
You basically need to retain the condition responsible for the rendered attribute in the subsequent request as well. This can be done in several ways: putting bean in session scope, using Tomahawk's t:saveState or h:inputHidden with a bean binding. Each is outlined in detail in this answer.