2008-11-24 3 views
16

Je construis une application web JSF + Facelets, dont une partie est une méthode qui scanne un répertoire de temps en temps et indexe les changements. Cette méthode fait partie d'un bean qui est dans la portée de l'application. J'ai construit une sous-classe de TimerTask pour appeler la méthode toutes les X millisecondes. Mon problème est d'initialiser le bean. Je peux référencer le bean sur une page, et quand je vais à la page, le bean est initialisé, et fonctionne comme indiqué; ce que je voudrais à la place, c'est que le bean soit initialisé lorsque le contexte web est initialisé, afin qu'il ne nécessite pas de visite de page pour démarrer la méthode d'indexation. Google a montré quelques personnes qui veulent cette fonctionnalité, mais pas de vraies solutions en dehors de l'intégration avec Spring, que je ne veux vraiment pas faire juste pour obtenir cette fonctionnalité. J'ai essayé de jouer avec les servlets qui ont un ensemble "load-on-startup", et un ServletContextListener pour faire avancer les choses, et je n'ai pas réussi à obtenir le réglage correct, soit parce qu'il n'y a pas 't un FacesContext disponible, ou parce que je ne peux pas référencer le bean de l'environnement JSF.JSF initialise le bean application-portée lorsque le contexte est initialisé

Est-il possible d'initialiser un bean JSF au démarrage de l'application Web?

Répondre

14

Si le code appelle FacesContext, il ne fonctionnera pas en dehors d'un fil associé à un cycle de vie de la demande JSF. Un objet FacesContext est créé pour chaque requête et placé à la fin de la requête. La raison pour laquelle vous pouvez le récupérer via un static call est qu'il est défini sur ThreadLocal au début de la demande. Le cycle de vie d'un FacesContext n'a aucun rapport avec celui d'un ServletContext.

Peut-être que cela ne suffit pas (il semble que vous ayez déjà parcouru cette route), mais vous devriez pouvoir utiliser un ServletContextListener pour faire ce que vous voulez. Assurez-vous simplement que tous les appels au FacesContext sont conservés dans le thread de demande du JSP.

web.xml:

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

Mise en œuvre:

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

} 

Vous pouvez adresser cet objet via le champ d'application JSF (ou tout simplement directement si aucune autre variable existe avec le même nom):

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

Si vous souhaitez récupérer l'objet dans un bean géré JSF, vous pouvez l'obtenir auprès de la société ExternalContext:

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

Cela a fini par s'arranger - il me manquait le bit "setAttribute" pour le rendre accessible dans le code JSF. Merci! –

0

Utilisation des écouteurs ou le chargement au démarrage, essayez ceci: http://www.thoughtsabout.net/blog/archives/000033.html

+0

J'ai déjà utilisé cette astuce, mais cela ne fonctionne pas avec ServletContextListener car vous n'avez pas de requête/réponse pour facesContext = contextFactory.getFacesContext (servletContext, requête, réponse, cycle de vie); Il échoue si un paramètre est nul. Même problème avec load-on-startup/init. –

+0

Je pourrais être meilleur/plus facile de simplement créer votre classe au démarrage et de le stocker. Ensuite, lorsque le bean jsf est accédé pour la première fois, il vérifie cette classe et l'utilise ou copie les données de l'un à l'autre. – Loki

0

Dans JSF 2+ vous pouvez utiliser un SystemEventListener pour le manipuler. Vous devez le définir pour agir sur le PostConstructApplicationEvent pour l'initialiser.

<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 mise en œuvre ressemblerait à quelque chose comme:

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

Cela vous permettra de faire beaucoup plus que simplement passer une valeur.

+1

Notez que l'OP utilise JSF 1.x (JSF 2.x n'existait pas lorsque la question a été posée). Pour JSF 2.x il y a d'ailleurs un moyen beaucoup plus simple: juste un '@ManagedBean (eager = true) @ ApplicationScoped'. Pas besoin de XML ou d'un désordre d'interface. Voir aussi http://stackoverflow.com/questions/3600534/jsf-how-do-i-force-an-application-scoped-bean-to-instantiate-at-application-st/3600740#3600740 – BalusC

+0

Je suis retourné et lisez la question, et votre réponse résout la dernière partie de sa question sur le chargement avide de JSF 2.x. Bonne prise. –