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
Related
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
I am building a JSF 2.0 application. Basically this is the flow :
Perform some steps (pre conditions)
Redirect to external server based on results of preconditions
Perform some post redirection steps (update status flags etc)
The question I have is, once I do a redirection to an external server, how do I get the post redirection code to execute ? The control will be totally out of my application right ?
All the 3 steps above happen on clicking a command button , and my code is under doPost()
Assuming that you're using standard Java EE stack, just put #Asynchronous on the EJB service method you're invoking after redirect. It'll run in a separate thread and allowing the current thread to continue directly (and thus to return from action method directly without blocking).
E.g.
#ManagedBean
#RequestScoped
public class Bean {
#EJB
private SomeService someService;
public void doPost() throws IOException { // Strange name for a JSF command button action btw, confuses with a standard HttpServlet method.
// ... Preprocessing ...
externalContext.redirect(externalURL);
someService.doSomethingAsynchronously();
}
// ...
}
with
#Stateless
public class SomeService {
#Asynchronous
public void doSomethingAsynchronously() {
// ... Update stuff ...
}
// ...
}
I have a JSF application that uses JSF 1.2 and Servlets 2.3 and Seam 2.2.0. I want to run a sort of backup everytime the application closes. I have tried using the ServletContextListener like this:
public class ApplicationStartupAction implements ServletContextListener {
#In
FormActionImpl formAction;
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("Context destroyed");
formAction.migrateForms();
}
public void contextInitialized(ServletContextEvent arg0) {
//Do nothing
System.out.println("Startup");
}
}
I wanted to use the FormActionImpl class because that has access to the JPA-handlers and the database, and I need to access the database. This doesn't work (I get a nullpointerexception) for the line "forAction.migrateForms();". My best guess is that I can't use the injections, because those classes have already been destroyed at this stage of the shutdown?
Is there a way to gain access to managed beans during the shutdown? Or use JPA in some other way?
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.
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.