2008-11-24 10 views
16

Estoy construyendo una aplicación web JSF + Facelets, una de las cuales es un método que escanea un directorio de vez en cuando e indexa los cambios. Este método es parte de un bean que está en el alcance de la aplicación. Creé una subclase de TimerTask para llamar al método cada X milisegundos. Mi problema es inicializar el frijol. Puedo hacer referencia al bean en una página, y cuando voy a la página, el bean se inicializa y funciona como se indica; lo que me gustaría en cambio es que el bean se inicialice cuando se inicializa el contexto web, de modo que no se requiera una visita a la página para iniciar el método de indexación. Google ha mostrado a algunas personas que quieren esta funcionalidad, pero no tiene soluciones reales aparte de la integración con Spring, lo que realmente no quiero hacer solo para obtener esta funcionalidad.JSF inicializa el bean scope-scope cuando se inicializa el contexto

He intentado juguetear tanto con los servlets que tienen el conjunto "cargar al inicio" como con un ServletContextListener para poner en marcha las cosas, y no han podido obtener la configuración correcta, ya sea porque no hay 't FacesContext disponible, o porque no puedo hacer referencia al bean desde el entorno JSF.

¿Hay alguna manera de obtener un bean JSF inicializado en el inicio de la aplicación web?

Respuesta

14

Si el código llama FacesContext, no va a funcionar fuera de un subproceso asociado con una petición JSF ciclo de vida. Se crea un objeto FacesContext para cada solicitud y se elimina al final de la solicitud. La razón por la que puede recuperarlo a través de static call es porque está configurado en ThreadLocal al inicio de la solicitud. El ciclo de vida de un FacesContext no guarda relación con el de un ServletContext.

Quizás esto no sea suficiente (parece que ya ha pasado por esta ruta), pero debería poder usar un ServletContextListener para hacer lo que quiera. Solo asegúrese de que las llamadas a FacesContext se mantengan en el hilo de solicitud de JSP.

web.xml:

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

Implementación:

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(); 
     } 
    } 

} 

Puede abordar este objeto a través del ámbito de aplicación JSF (directamente o simplemente si no existe otra variable con el mismo nombre):

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

Si desea recuperar el objeto en un frijol administrado JSF, puede obtenerlo desde el ExternalContext:

FacesContext.getCurrentInstance() 
      .getExternalContext().getApplicationMap().get("foo"); 
+0

Esto terminó funcionando, me faltaba el bit "setAttribute" para que fuera accesible en el código JSF. ¡Gracias! –

0

Utilización de detectores o load-on-startup, intente esto: http://www.thoughtsabout.net/blog/archives/000033.html

+0

He usado ese truco antes, pero no funciona con ServletContextListener porque no tiene una solicitud/respuesta para facesContext = contextFactory.getFacesContext (servletContext, request, response, lifecycle); No funciona si algún parámetro es nulo. Mismo problema con load-on-startup/init. –

+0

Podría ser el mejor/más fácil crear tu clase al inicio y almacenarla. Luego, cuando se accede al jsf bean por primera vez, buscará esa clase y la usará o copiará los datos de uno a otro. – Loki

0

En JSF 2 + puede utilizar un SystemEventListener para manejarlo. Debería configurarlo para que tome medidas en el PostConstructApplicationEvent para inicializarlo.

<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> 

La implementación sería algo como:

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

Esto le permitirá hacer mucho más que simplemente pasando un valor.

+1

Tenga en cuenta que el OP está utilizando JSF 1.x (JSF 2.x no existía cuando se formuló la pregunta). Para JSF 2.x, hay un modo mucho más sencillo: simplemente un '@ManagedBean (eager = true) @ ApplicationScoped'. No hay necesidad de XML o lío de interfaz. Ver también http://stackoverflow.com/questions/3600534/jsf-how-do-i-force-an-application-scoped-bean-to-instantiate-at-application-st/3600740#3600740 – BalusC

+0

Volví y lea la pregunta y su respuesta resuelve la última parte de su pregunta sobre la carga ansiosa para JSF 2.x. Buena atrapada. –

Cuestiones relacionadas