Removing JSF managed beans on session timeout - jsf

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

Related

How to inject an #Normal (#ApplicationScoped) bean into a #Dependent scope if the bean does not have a no-arg constructor

This post is related to an older SO Post of mine, wherein I was trying to understand the requirements of a no-args constructor by WELD.
Right now, I'm trying to figure out if there is a way in CDI to inject an #ApplicationScoped bean (#Normal) into a #Dependent scope. From what I've read from WELD, the requirements are to have a non-private no-arg constructor to be proxyable. However, I do not have control over the bean definition as it is provided by a library. My code is doing the following:
#Produces
#ApplicationScoped
#Named("keycloakAdmin")
public Keycloak getKeycloakAdminClient(#Named("keycloakDeployment") final KeycloakDeployment deployment) {
String clientId = deployment.getResourceName();
Map<String, Object> clientCredentials = deployment.getResourceCredentials();
// need to set the resteasy client connection pool size > 0 to ensure thread safety (https://access.redhat.com/solutions/2192911)
ResteasyClient client = new ResteasyClientBuilder().connectionPoolSize(CONNECTION_POOL_SIZE).maxPooledPerRoute(CONNECTION_POOL_SIZE)
.defaultProxy("localhost",8888)
.build();
KeycloakBuilder builder = KeycloakBuilder.builder()
.clientId(clientId)
.clientSecret((String) clientCredentials.get(CredentialRepresentation.SECRET))
.realm(deployment.getRealm())
.serverUrl(deployment.getAuthServerBaseUrl())
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.resteasyClient(client);
return builder.build();
}
// error thrown here that cannot inject #Normal scoped bean as it is not proxyable because it has no no-args constructor
#Produces
#Dependent
#Named("keycloakRealm")
public RealmRepresentation getKeycloakRealm( #Named("keycloakAdmin") final Keycloak adminClient ){
// error thrown here that cannot inject #Normal scoped bean as it is not proxyable because it has no no-arg
return adminClient.realm(resolveKeycloakDeployment().getRealm()).toRepresentation();
}
The problem is that I do not control the Keycloak bean; it is provided by the library. Consequently, I have no way of providing a no-argument constructor to the bean.
Does this mean it is impossible to do? Are there any workarounds that one can use? This would seem like a significant limitation by WELD, particularly when it comes to #Produceing 3rd party beans.
My goal is to have a single Keycloak bean for the application as it is thread-safe and only needs to be initialized once. However, I want to be able to inject it into non-application-scoped beans.
There is a #Singleton scope which may address my issue, but if #Singleton works for this case, what is the purpose of the 2 different scopes? Under what circumstances would one want a non-proxied singleton (#Singleton) vs a proxied one (#ApplicationScoped)? Or is #Singleton for the entire container, whereas #ApplicationScoped for the application (WAR) only instead? How does it apply to an EAR or multiple ears?

Refresh #ApplicationScoped bean gives nullpointer

First I'd like to clear out that I know what a NullPointerException is and how to handle that. My question is about how to access the #ApplicationScoped bean that is created on server startup, through the scheduler that I would like to run once a day. All I get now is a new bean, which is null at the beginning and not the already created bean.
The scheduler does the initiation of the bean so I end up with two beans, one that is updated by the scheduler (all the time) and one that is used when navigating in the web application.
So...
I have a web application with a #ApplicationScoped bean. This one is loaded from database on Tomcat startup.
This means that if something is changed in database in some other way than through the web application the changes won't be viewable until the Tomcat has been restarted.
So I thought I could make a Schedule to reload this #ApplicationScoped bean on a specific time during the day. The trouble is that I get a whole new #ApplicationScoped bean.
So how should I do to get the current bean updated and not a new one?
I've tried the solution in this tread: Refresh/Reload Application scope managed bean without any luck. My bean is still null.
I've also looked at the following threads without really solving my problem:
application scoped bean's view is not updated
Access ApplicationScoped bean through ServletContext
My ApplicationBean.java
import javax.enterprise.context.ApplicationScoped;
#Named(value = "applicationBean")
#ApplicationScoped
public class ApplicationBean {
#PostConstruct
public void init() {
//load variables from db
}
}
My ScheduleTaskConfig.java
#WebListener
public class ScheduleTaskConfig implements ServletContextListener {
private ScheduledExecutorService scheduler;
#Override
public void contextInitialized(ServletContextEvent event) {
Reloader reloader = new Reloader(event.getServletContext());
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(reloader, 2, 3, TimeUnit.MINUTES);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
scheduler.shutdownNow();
}
}
My Reloader.java
public class Reloader implements Runnable {
private ServletContext context;
public Reloader(ServletContext context) {
this.context = context;
}
#Override
public void run() {
ApplicationBean applicationBean = (ApplicationBean) context.getAttribute("applicationBean");
if (applicationBean != null) {
applicationBean.init();
}
}
}
I just don't know what is wrong. I don't understand how to solve this.
I know I've written that the schedule should run every 3 minutes but when I get this to work the schedule should run once a day.
Is there a better way to do this or have I just forgotten something? I've googled the whole day without getting forward so I really need help.
Thank you.
Edited:
So my servlet context is up and my listener is notified before my application context is up?
What if I use the webpage then my application context is up (?) but my scheduler still doesn't work, the bean in the scheduler is still a new one and not the one I've been using when navigating in the web application. Or am I just confused over this? :)
Thank you for your help.
Edited:
I'm using Primefaces 6.0 and I suppose this bean is a JSF bean. Other beans used are Spring beans imported from a model class. Sorry for the confusion I have removed the Spring notation and added Primefaces instead.
I hope this clears things out. Thank you

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

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.

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