tags:

views:

9090

answers:

2

I'm building a JSF+Facelets web app, one piece of which is a method that scans a directory every so often and indexes any changes. This method is part of a bean which is in application scope. I have built a subclass of TimerTask to call the method every X milliseconds. My problem is getting the bean initialized. I can reference the bean on a page, and when I go to the page, the bean is initialized, and works as directed; what I would like instead is for the bean to be initialized when the web context is initialized, so that it doesn't require a page visit to start the indexing method. Google has shown a few people that want this functionality, but no real solutions outside of integrating with Spring, which I really don't want to do just to get this piece of functionality.

I've tried playing around with both the servlets that have "load-on-startup" set, and a ServletContextListener to get things going, and haven't been able to get the set up right, either because there isn't a FacesContext available, or because I can't reference the bean from the JSF environment.

Is there any way to get a JSF bean initialized on web app startup?

A: 

Using listeners or load-on-startup, try this: http://www.thoughtsabout.net/blog/archives/000033.html

Loki
I've used that trick before, but it doesn't work with ServletContextListener because you don't have a request/response for facesContext = contextFactory.getFacesContext(servletContext, request, response, lifecycle); It fails if any param is null. Same problem with load-on-startup/init.
Matt McMinn
I tmight be best/easier to just create you class at startup and store it. Then when the jsf bean is accessed for the first time, it will check for that class and use it or copy the data from one to the other.
Loki
+2  A: 

If your code calls FacesContext, it will not work outside a thread associated with a JSF request lifecycle. A FacesContext object is created for every request and disposed at the end of the request. The reason you can fetch it via a static call is because it is set to a ThreadLocal at the start of the request. The lifecycle of a FacesContext bears no relation to that of a ServletContext.

Maybe this isn't enough (it sounds like you've already been down this route), but you should be able to use a ServletContextListener to do what you want. Just make sure that any calls to the FacesContext are kept in the JSP's request thread.

web.xml:

<listener>
    <listener-class>appobj.MyApplicationContextListener</listener-class>
</listener>

Implementation:

public class MyApplicationContextListener implements ServletContextListener {

    private static final String FOO = "foo";

    public void contextInitialized(ServletContextEvent event) {
     MyObject myObject = new MyObject();
     event.getServletContext().setAttribute(FOO, myObject);
    }

    public void contextDestroyed(ServletContextEvent event) {
     MyObject myObject = (MyObject) event.getServletContext().getAttribute(
       FOO);
     try {
      event.getServletContext().removeAttribute(FOO);
     } finally {
      myObject.dispose();
     }
    }

}

You can address this object via the JSF application scope (or just directly if no other variable exists with the same name):

<f:view>
    <h:outputText value="#{applicationScope.foo.value}" />
    <h:outputText value="#{foo.value}" />
</f:view>

If you wish to retrieve the object in a JSF managed bean, you can get it from the ExternalContext:

FacesContext.getCurrentInstance()
            .getExternalContext().getApplicationMap().get("foo");
McDowell
This ended up working out - I was missing the "setAttribute" bit to make it accessible in the JSF code. Thanks!
Matt McMinn