Matt McMinn
Matt McMinn

Reputation: 16291

JSF initialize application-scope bean when context initialized

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?

Upvotes: 16

Views: 39317

Answers (3)

John Yeary
John Yeary

Reputation: 1113

In JSF 2+ you can use a SystemEventListener to handle it. You would set it to take action on the PostConstructApplicationEvent to initialize it.

<system-event-listener>
    <system-event-listener-class>
     listeners.SystemEventListenerImpl
    </system-event-listener-class>
    <system-event-class>
     javax.faces.event.PostConstructApplicationEvent
    </system-event-class>                       
</system-event-listener>

The implementation would look something like :

public class SystemEventListenerImpl implements SystemEventListener {

  @Override
  public void processEvent(SystemEvent event) throws AbortProcessingException {
    Application application = (Application) event.getSource();
   //TODO
  }

  @Override
  public boolean isListenerForSource(Object source) {
    return (source instanceof Application);
  }
}

This will allow you to do a lot more than just simply passing a value.

Upvotes: 0

McDowell
McDowell

Reputation: 108859

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");

Upvotes: 15

Loki
Loki

Reputation: 30920

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

Upvotes: 0

Related Questions