#SessionScoped public class User {
... //settings, attributes, etc
}
#ViewScoped public class FooController {
#ManagedProperty(value="#{user}")
private User user;
...
}
#RequestScoped public class LoginController {
#ManagedProperty(value="#{user}")
private User user;
public String login() {
//handle Servlet 3.0 based authenticate()... if success, make new User object and slap it into context
request.getSession().setAttribute("user", user);
return "?faces-redirect=true";
}
...
}
xhtml pages include the login controls on pretty much every page. The ideal would be that they are able to login, and the page will refresh and the existing FooController would have the reference to the currently logged in user, which conditionally renders buttons / elements. The behavior is that the login occurs, but the FooController view is still "valid", so the managed bean never is attempted to be injected again. If I navigate off the page, and back onto it [reconstructing the view scoped bean] the user bean is reinjected nicely... but I'd prefer not to have that interim step. Any ideas?
I've tried various forms of FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("user"); in hopes that it will re-pull it from session but no avail. I do not want to tightly couple code in my LoginController to reference specifically invalidating a FooController or BarController or any other that refs the user bean.
Why are you trying to remove user (a #SessionBean), from the viewMap (a map of #ViewScoped objects in the current session)?
You should be removing FooController, the #ViewScoped, bean from the viewMap, i.e.
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("fooController");.
This is what will get rid of the viewscoped bean and force a new one to be created. Then of course, you'll need to refresh the page anyway.
If you're intent on removing the session bean however, you should be accessing the session directly:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().remove("user");.
That gets rid of the user object
Ok, I figured this out on the drive home, it was a lifecycle problem. I was trying to get JSF to do something it shouldn't be doing to managed beans.
Instead of new'ing up a User object and reassigning it to the managed instance of user in LoginController, i changed the login method to look like this:
public String login() {
//handle Servlet 3.0 based authenticate()... if success...
User loadedFromDB = someDao.load(principal.getName());
user.setDefaultPage(loadedFromDB.getDefaultPage()); // NOTE: The user object IS THE MANAGED BEAN
user.setDefaultScheme(loadedFromDB.getDefaultScheme()); // This is the same object the view scoped bean has a ref on, so directly setting that object's fields proliferates that to any other bean that has the user in scope.
... //etc... not calling NEW, not reassigning object ref to user
loadedFromDB = null;
return "?faces-redirect=true";
}
This accomplishes what was needed. Who knew, if you stop fighting the framework for a minute and just use it, it helps you out :)
Related
All of my pages are backed their own ViewScoped bean, but I'm finding that there are a lot of similar methods used on these pages. For example, a user may want to view dates in their preferred time zone so each time a page is loaded, the DB is queried for what their preferred time zone is.
So my initial thought was to create a ViewScoped bean to manage this. The timeZone value would be only be "good" for the lifetime of the page and they would be lazy-loaded to avoid unnecessary database hits:
#Named
#ViewScoped
public class Preference implements Serializable {
#Inject
private SessionManager sessionManager;
#EJB(name = "PreferencesReadFacade")
private PreferencesReadFacadeRemote prefReadFacade;
private HashMap<String, Object> cache = new HashMap<>();
/**
* #return the user's TimeZone preference
*/
public String getTimeZone() {
if(cache.get("TimeZone") == null) {
cache.put("TimeZone", prefReadFacade.getUserPreference(sessionManager.getUserId(), "TimeZone").toString());
}
return cache.get("TimeZone").toString();
}
}
Usage:
<h:outputText value="#{preference.timeZone}"/>
Is there anything wrong with this type of methodology? Is there a better way of doing something like this?
EDIT: Would like to add that I'm using ICEfaces and Omnifaces so if there are resources in these libraries at my disposal, I'm certainly open to using those.
Your approach is bsolutely correct - you may reuse the same bean in multiple pages regardless of its scope. If those pages are in the same scope, a bean would be reused, otherwise a new bean would be created with an empty cache. If the scope is ViewScoped, the bean would be recreated for every page, hence DB would be accessed first when the data is needed on after a page loads.
You may also make your common bean a base bean of other viewscoped beans, which are constructed for a particular page (they must remain viewscoped).
Or, you may inject your Preference bean into any other Named bean, which is used in your pages. In this way, you may inject it to a bean with any scope, but CDI will always give you the same bean for a view (within viewscope), but different when you redirect to a new page.
But your solution is equally correct, if not even better.
You can create one #SessionScoped bean and hold there user preferences like time zone. Then #Inject it to your #ViewScoped beans and get time zone from #SessionScoped. As long as http session lives, the DB query will be done only once in the user session if you do it in #PostConstruct and assign to variables.
I am trying to save a Cookie upon user request, and to later use that cookie to fill in a text field. I am using Java CDI and a login bean. I am new to all three. For online resources all I can find is
#Inject #CookieParam
private String username;
and
#Inject #CookieParam("username")
private Instance<String> usernameResolver;
...
String username = usernameResolver.get();
For the first one the error message says " Unsatisfied dependencies for type [String] with qualifiers [#Default]"
For the second one the only error I get says "Failed to start context"
How should I fix this problem?
Thanks
As the #CookieParam package name hints, this is specific to JAX-RS, Java EE's other framefork for RESTful web services. This would only work in a JAX-RS managed resource as annotated by #Path. This won't work in a JSF or CDI managed bean as annotated by #ManagedBean or #Named.
If you're using JSF's #ManagedBean to manage the bean, then it's available by EL-evaluating #{cookie.username} as #ManagedProperty.
#ManagedBean
public class Bean {
#ManagedProperty("#{cookie.username}")
private String username;
// ...
}
If you're using CDI's #Named to manage the bean, then you've resort to either a custom annotation, or grabbing it as Cookie from the current FacesContext. As the former is not trivial (but actually a nice idea for OmniFaces though), I'll show only the latter:
#Named
public class Bean {
private String username;
#PostConstruct
public void init() {
Cookie cookie = (Cookie) FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap().get("username");
if (cookie != null) {
username = cookie.getValue();
}
}
// ...
}
Then, in order to save it, the only way is using ExternalContext#addResponseCookie().
FacesContext.getCurrentInstance().getExternalContext().addResponseCookie("username", username, properties);
Don't forget to take into account that a cookie value is very restrictive as to allowed characters. You might want to URL-encode and -decode it upon save and retrieval. JSF utility library OmniFaces offers helper methods which do that implicitly.
username = Faces.getRequestCookie("username");
Faces.addResponseCookie("username", username, -1);
Unrelated to the concrete problem, storing something sensitive like "user name" as a cookie is scary. Are you aware that cookies are manipulatable by the enduser? That the person who's visiting your webpage can easily edit a cookie value representing an "user name" to that of someone else?
I`m kind of noob to JSF and I'm trying to figure out which would be the most elegant solution for the following scenario:
Let's say that I have a user managed bean called UserMB:
#ManagedBean
public class UserMB {
private User user;
private List<User> users;
// getters and setters here
public void addUser(User user){
// do add user logic here
}
public List<User> listAllUsers(){
// do list All users logic here
}
#PostConstruct
private void init(){
// populate List<user> users - for the listAllUsers scenario
}
}
Let`s assume that i do not have a form to submit directly to listAllUsers() method, but instead i want to see all users when I open the page list-all-users.xhtml.
When I hit the managed bean from addUser.xhtml a query will be performed to DB to load all users because the bean will not know if i want to use listAllUsers() method or addUser() method.
Should i split this functionality in 2 managed beans ?
Because if so I would have to create several managed beans to deal with "User" business (ie. in Struts2 i would have only one Action that would take care of all user interactions).
P.S. I know that there is the solution to populate List in getter method but I read one article of BalusC that advise us not to do this...
Should i split this functionality in 2 managed beans ?
Yes. Use one bean per view/form. Keep the backing bean class as slick as possible. Don't give it too much responsibilities.
After I commit some data into the database I want my session beans to automatically refresh themselves to reflect the recently committed data. How do I achieve this when using managed session beans in JSF 2.0?
Currently I have to restart the web server in order for the sessions to clear and load anew again.
2 ways:
Put them in the view scope instead. Storing view-specific data sessionwide is a waste. If you have a performance concern, you should concentrate on implementing connection pooling, DB-level pagination and/or caching in the persistence layer (JPA2 for example supports second level caching).
#ManagedBean
#ViewScoped
public class FooBean {
// ...
}
Add a public load() method so that it can be invoked from the action method (if necessary, from another bean).
#ManagedBean
#SessionScoped
public class FooBean {
private List<Foo> foos;
#EJB
private FooService fooService;
#PostConstruct
public void load() {
foos = fooService.list();
}
// ...
}
which can be invoked in action method inside the same bean (if you submit the form to the very same managed bean of course):
public void submit() {
fooService.save(foos);
load();
}
or from an action method in another bean (for the case that your managed bean design is a bit off from usual):
#ManagedProperty("#{fooBean}")
private FooBean fooBean;
public void submit() {
fooService.save(foos);
fooBean.load();
}
This of course only affects the current session. If you'd like to affect other sessions as well, you should really consider putting them in the view scope instead, as suggested in the 1st way.
See also:
How to choose the right bean scope?
How do I simulate the #postConstruct behaviour in JSF 1.1 like as in JSF 1.2 and newer?
Actually, I want to call a bean method automatically during page loading?
I am using IceFaces 1.8 on JSF 1.1.
The point of #PostConstruct is to provide a hook to execute some code after all managed properties (as in <managed-property> or #ManagedProperty) are been set and all dependency injections (e.g. #EJB, #Resource, #Inject and so on) have taken place.
If you don't have any of them, just use the bean's constructor.
public class Bean {
public Bean() {
// Just do your job here. Don't do it the hard way.
}
// ...
}
Or if you actually want to execute it when a specific property has been set, then do the job in the setter while null-checking the current property value.
public class Bean {
private SomeObject someManagedProperty;
public void setSomeManagedProperty(someManagedProperty) {
if (this.someManagedProperty == null && someManagedProperty != null) {
// First-time set, now you can do your job here.
}
this.someManagedProperty = someManagedProperty;
}
// ...
}
Update as per the comments:
I meant to execute the method every time the page is loaded
The #PostConstruct doesn't do that. However, if the bean is request scoped, then you will see the same effect. You seem to be using a session or application scoped managed bean to manage request scoped data. This is in essence wrong. You should convert it to a request scoped bean. Any real session scoped data can be split into a session scoped bean which you then inject by <managed-property>.