#postConstruct in JSF 1.1 - jsf

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>.

Related

Is there anything wrong with using a CDI ViewScoped bean for caching information used in multiple pages?

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.

How to get all instances of a particular cdi session scoped bean

I have a #SessionScoped cdi bean which is used to track user session information in my web application. Is there any way to find all objects of this bean from another #ApplicationScoped bean?
You cannot do this out of the box. Java EE forbid this kind of things for security reason.
Now you can imagine a more elaborate approaches to keep track of these session beans at your application scope level. The cleanest way would be to produce them from an #ApplicationScoped bean :
#ApplicationScoped
public class Registry {
private List<SessionData> data = new ArrayList<>;
#Produces
#SessionScoped
public SessionData produceSessionData() {
SessionData ret = new SessionData();
data.add(ret);
return ret;
}
public void cleanSessionData(#Disposes SessionData toClean) {
data.remove(toClean);
}
}
Note the #Dispose method which will be called when your produced bean has ended its lifecycle. A convenient way to keep your list up to date and avoid extra memory usage.

Bean annotations like #ManagedProperty and #PostConstruct doesn't work when manually instantiating the bean from another bean

I instantiated a request bean from another request bean,
new LoginManager();
But the property which is annotated with #ManagedProperty doesn't get the value from the asked reference, only in case of instantiation through the above way. It just contains null, causing NPE later in code. Also #PostConstruct won't be invoked. Why is it so & how should I deal with this?
#ManagedBean(name = "loginManager")
#RequestScoped
public class LoginManager {
private String userid;
private String password;
#ManagedProperty(value="#{currentSession}")
private UserSessionManager userSession;
}
But userSession can't read from the session scoped bean when this bean was instantiated using: new LoginManager();
However I can read the value using FacesContext!
You should not manually instantiate (manage) beans using new operator. You should let JSF do the job of managing the beans and instead grab the JSF-managed (JSF-instantiated) instance.
Either by #ManagedProperty in the bean where you need it:
#ManagedProperty("#{loginManager}")
private LoginManager loginManager;
Or by invoking EL programmatically (which is pretty borderline in your particular case):
LoginManager loginManager = context.getApplication().evaluateExpressionGet(context, "#{loginManager}", LoginManager.class);
// ...
If you insist in instantiating and managing the bean yourself, you should do all dependency injections yourself, also invoking the #PostConstruct yourself, if any, and finally also putting the bean in the desired scope yourself. E.g.
LoginManager loginManager = new LoginManager();
loginManager.setUserSession(userSession);
// Now use reflection to find and invoke #PostConstruct method.
// Finally store in the map which conforms the bean's scope.
externalContext.getRequestMap().put("loginManager", loginManager);
This boilerplate is exactly what JSF is supposed to take away from your hands. Make use of it.

Refresh managed session bean in JSF 2.0

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 can i send a parameter to be used in the #PostConstruct method of a backing bean?

I need to preload some data to be displayed when the page loads. The initialization steps are performed on a #PostConstruct-annotated method but now i need to use a parameter in order to get the data.
What i'm trying to do:
#PostConstruct
public void init()
{
List data = getDataFromDB(parameter) /*Need to read a parameter created somewhere else*/
}
Is there a way to achieve this?
Thanks in advance
It's kind of hard to say what you mean by "a parameter set somewhere else". I will assume that "somewhere else" means "sent from browser by HTTP". In such case you should create a standard property in your managed bean and:
in JSF 2.0 you could annotate it with #ManagedProperty("#{param.nameOfParameterToRead}")
in JSF 1.2 and less - use managed-property element in your bean description (faces-config.xml).
Like this:
#ManagedBean
#RequestScoped
class MyManagedBean {
#ManagedProperty("#{param.id}")
public Integer id;
#PostConstruct
public void init(){
data = getDataFromDB(id)
}
// setters and getters (mandatory, even though annotation is on an attribute!!!)
}
Careful: injecting properties does not use JSF converters, so it is best to inject strings and take care of conversion in your own code.
how about reading from Properties file, or fetching List from DB ??

Resources