Is there any in JSF2 a callback for activation / deactivation of a session bean? [duplicate] - jsf

Is it possible to do something like this: When a user session starts I read a certain integral attribute from the database. As the user performs certain activities in this session, I update that variable(stored in session) & when the session ends, then I finally store that value to the DB.
My question is how do I identify using the JSF framework if the user session has ended & I should then store the value back to DB?

Apart from the HttpSessionListener, you can use a session scoped managed bean for this. You use #PostConstruct (or just the bean's constructor) and #PreDestroy annotations to hook on session creation and destroy
#ManagedBean
#SessionScoped
public class SessionManager {
#PostConstruct
public void sessionInitialized() {
// ...
}
#PreDestroy
public void sessionDestroyed() {
// ...
}
}
The only requirement is that this bean is referenced in a JSF page or as #ManagedProperty of any request scoped bean. Otherwise it won't get created. But in your case this should be no problem as you're apparently already using a session scoped managed bean, just adding a #PreDestroy method ought to be sufficient.

My question is how do I identify using
the JSF framework if the user session
has ended & I should then store the
value back to DB?
The JSF framework does not have a separate concept of a session; it uses the underlying session management features of the Servlet specification.
You would have to create a HttpSessionListener that provides hooks for you to capture the session creation and destruction events, where you can read the value and store it back into the DB.

HttpSessionListener, or if you need Dependency Injection for that save, you might use #PostConstruct & #PreDestroy. Remember that the session is destroyed when you call invalidate() or after session timeout, not when the user closes the browser. Why do you use Session Scope anyway, Conversation Scope might fit you better.

Related

Removing JSF managed beans on session timeout

I'm having trouble working out how to correctly handle automatic destruction of a session in JSF. Of course, at this time, the session gets invalidated by the container, resulting in #PreDestroy methods being called on the session scoped beans as well.
At PreDestroy of some session scoped beans, we're unregistering some listeners, like below:
#PreDestroy
public void destroy() {
getWS().removeLanguageChangeListener(this);
}
However, the getWS() method actually attempts to get a reference to another session scoped bean, but that fails, as FacesContext.getCurrentInstance() returns null.
The latter appears to be normal JSF behaviour, according to Ryan Lubke:
We're true to the specification here. I'm not sure it's safe to assume
that the FacesContext will be available in all #PreDestroy cases.
Consider session scoped beans. The session could be timed out by the
container due to inactivity. The FacesContext cannot be available at
that time.
Fine by me, but how should one then make sure all objects are correctly cleared? Is it bad practice to remove self as listener in PreDestroy?
Or would we only have to do this for request/view scoped beans, as they live less long than the session scope of WS (from getWS() ) ?
Note that I get this behaviour on Tomcat7, but I expect this problem happens on every container.
I think session beans are cleaned in a dedicated thread on a servlet container and thus are outside of FacesContext (which is associated with a JSF Request). You could use HttpSessionListener to overcome the problem and cleanup session resources. Something like:
#WebListener
public class LifetimeHttpSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(final HttpSessionEvent e) {
// create some instance here and save it in HttpSession map
HttpSession session = e.getSession();
session.setAttribute("some_key", someInstance);
// or elsewhere in JSF context:
// FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("some_key", someInstance);
}
#Override
public void sessionDestroyed(final HttpSessionEvent e) {
// get resources and cleanup them here
HttpSession session = e.getSession();
Object someInstance = session.getAttribute("some_key");
}
}
Hope this can be helpful for you

SessionScope and Scheduled threads

In my application i have a service that performs heavy loading (parsing of different files) up on creation. The data is metadata, so wont change during runtime (localized strings, key/value mappings, etc.) Therefore I decided to make this Service SessionScoped, so I don't need to parse the values with every request. Not ApplicationScoped to make sure the data is refreshed, when the user logs in again.
this works pretty well, but now i need to access that service inside a thread, that is run with the #Schedule Annotation. Of course Weld does not like that and says: org.jboss.weld.context.ContextNotActiveException: WELD-001303 No active contexts for scope type javax.enterprise.context.SessionScoped
#Singleton
public class DailyMails {
#Inject
MailService mailService; //just Named
#Inject
GroupDataService groupDataService; //Stateless
#Inject
LocalizationService localizationService; //SessionScoped
#Schedule(hour = "2", minute = "0", second = "0", dayOfWeek="Mon,Tue,Wed,Thu,Fri", persistent = false)
public void run() {
//do work
}
}
Can I manually create a Session at this point, so that I can use the SessionScoped service?
Edit: I know, that a Service should not ne SessionScoped nor should it hold any Data(-Collections). However in this Situation it seems legit to me to avoid multiple File-System accesses.
I thought about making the Service to a unscoped service and "cache" the data in a session scoped bean. However then I would need to inject the session bean to that Service, which will
again make the service kind of "session scoped".
Shouldn't this work:
#Inject #New
LocalizationService localizationService;
At least, that's how I interpret the specification.

ManagedBean Inheritance & Common Name

In one of our applications we have two classes of users,
Internal Users (class InternalUser extends User)
External Users (class ExternalUser extends User)
We are using these as session-scoped managed beans (basically to inject the logged-in user's details, which has some common detail in the class User and a few specific details in each of the 2 classes stated above).
Can I have the same name for both the managed beans (here "loggedInUser")?
Faces is throwing an exception "Managed bean named 'loggedInUser' has already been registered. Replacing existing managed bean class..."
How can we manage this scenario?
One way is to just not make it a JSF managed bean. Make the bean associated with the login form a request/view scoped one and put the User instance in the session scope youself.
E.g.
User user = service.find(username, password);
if (user != null) {
externalContext.getSessionMap().put("user", user);
}
It'll be available as #{user} in EL scope (and thus also be injectable via #ManagedProperty in other beans on exactly that expression). In the find() method, you can just return either InternalUser or ExternalUser accordingly.
Perhaps a managed bean named CurrentUser with an Internal/ExternalUser as a member? In my own application, I am not sure I'd inject this required data by making the User sub-classes double as business objects and managed beans the way you are attempting to do it.

How can I initialize a Java FacesServlet

I need to run some code when the FacesServlet starts, but as FacesServlet is declared final I can not extend it and overwrite the init() method.
In particular, I want to write some data to the database during development and testing, after hibernate has dropped and created the datamodel.
Is there a way to configure Faces to run some method, e.g. in faces-config.xml?
Or is it best to create a singleton bean that does the initialization?
Use an eagerly initialized application scoped managed bean.
#ManagedBean(eager=true)
#ApplicationScoped
public class App {
#PostConstruct
public void startup() {
// ...
}
#PreDestroy
public void shutdown() {
// ...
}
}
(class and method names actually doesn't matter, it's free to your choice, it's all about the annotations)
This is guaranteed to be constructed after the startup of the FacesServlet, so the FacesContext will be available whenever necessary. This in contrary to the ServletContextListener as suggested by the other answer.
You could implement your own ServletContextListener that gets notified when the web application is started. Since it's a container managed you could inject resources there are do whatever you want to do. The other option is to create a #Singleton ejb with #Startup and do the work in it's #PostCreate method. Usually the ServletContextListener works fine, however if you have more than one web application inside an ear and they all share the same persistence context you may consider using a #Singleton bean.
Hey you may want to use some aspects here. Just set it to run before
void init(ServletConfig servletConfig)
//Acquire the factory instances we will
//this is from here
Maybe this will help you.

StackOverflow error when initializing JSF SessionScoped bean in HttpSessionListener

Continuing on my previous question, I'm trying to initialize a session-scoped JSF bean when the application's session first starts, so the bean will be available to a user, regardless of which page they access on my web application first. My custom listener:
public class MyHttpSessionListener implements HttpSessionListener {
#Override
public void sessionCreated(HttpSessionEvent se) {
if (FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.get("mySessionBean") == null) {
FacesContext.getCurrentInstance().getExternalContext().getSessionMap()
.put("mySessionBean", new MySessionBean());
}
}
}
However, this is giving me a stack overflow error. It appears that the put() method in the SessionMap class tries to create a new HttpSession, thus causing an infinite loop to occur with my listener. How can I initialize a JSF session-scoped bean when my application's session first starts, without running into this issue?
I'm using JSF 2 with Spring 3, running on WebSphere 7.
Thanks!
The session isn't been fully finished creating at that point. Only when the listener method leaves, the session is put into the context and available by request.getSession() as JSF's getSessionMap() is using under the covers.
Instead, you should be grabbing the session from the event argument and use its setAttribute() method. JSF lookups and stores session scoped managed beans just there and won't create a new one if already present.
public void sessionCreated(HttpSessionEvent event) {
event.getSession().setAttribute("mySessionBean", new MySessionBean());
}
Note that I removed the superfluous nullcheck as it's at that point impossible that the session bean is already there.
Unrelated to the concrete problem, you should actually never rely on the FacesContext being present in an implementation which isn't managed by JSF. It is quite possible that the session can be created during a non-JSF request.

Resources