View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.webapp;
20  
21  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
22  import org.apache.myfaces.config.annotation.LifecycleProviderFactory;
23  import org.apache.myfaces.shared.util.ClassUtils;
24  
25  import javax.faces.FactoryFinder;
26  import javax.faces.context.ExternalContext;
27  import javax.faces.context.FacesContext;
28  import javax.servlet.ServletContext;
29  import javax.servlet.ServletContextAttributeEvent;
30  import javax.servlet.ServletContextAttributeListener;
31  import javax.servlet.ServletContextEvent;
32  import javax.servlet.ServletContextListener;
33  import javax.servlet.ServletRequestAttributeEvent;
34  import javax.servlet.ServletRequestAttributeListener;
35  import javax.servlet.ServletRequestEvent;
36  import javax.servlet.ServletRequestListener;
37  import javax.servlet.http.HttpSessionAttributeListener;
38  import javax.servlet.http.HttpSessionBindingEvent;
39  import javax.servlet.http.HttpSessionEvent;
40  import javax.servlet.http.HttpSessionListener;
41  import java.lang.reflect.InvocationTargetException;
42  import java.lang.reflect.Method;
43  import java.util.ArrayList;
44  import java.util.Iterator;
45  import java.util.LinkedList;
46  import java.util.List;
47  import java.util.Map;
48  import java.util.logging.Level;
49  import java.util.logging.Logger;
50  
51  /**
52   * Initialise the MyFaces system.
53   * <p>
54   * This context listener is registered by the JSP TLD file for the standard JSF "f" components. Normally, servlet
55   * containers will automatically load and process .tld files at startup time, and therefore register and run this class
56   * automatically.
57   * <p>
58   * Some very old servlet containers do not do this correctly, so in those cases this listener may be registered manually
59   * in web.xml. Registering it twice (ie in both .tld and web.xml) will result in a harmless warning message being
60   * generated. Very old versions of MyFaces Core do not register the listener in the .tld file, so those also need a
61   * manual entry in web.xml. However all versions since at least 1.1.2 have this entry in the tld.
62   * 
63   * This listener also delegates all session, request and context events to ManagedBeanDestroyer. 
64   * Because of that we only need to register one listener in the tld.
65   * 
66   * @author Manfred Geiler (latest modification by $Author: werpu $)
67   * @version $Revision: 1304306 $ $Date: 2012-03-23 07:18:53 -0500 (Fri, 23 Mar 2012) $
68   */
69  public class StartupServletContextListener implements ServletContextListener,
70          HttpSessionAttributeListener, HttpSessionListener,
71          ServletRequestListener, ServletRequestAttributeListener,
72          ServletContextAttributeListener
73  {
74      static final String FACES_INIT_DONE = "org.apache.myfaces.webapp.StartupServletContextListener.FACES_INIT_DONE";
75  
76      /**
77       * comma delimited list of plugin classes which can be hooked into myfaces 
78       */
79      @JSFWebConfigParam(since="2.0")
80      static final String FACES_INIT_PLUGINS = "org.apache.myfaces.FACES_INIT_PLUGINS";
81  
82      private static final byte FACES_INIT_PHASE_PREINIT = 0;
83      private static final byte FACES_INIT_PHASE_POSTINIT = 1;
84      private static final byte FACES_INIT_PHASE_PREDESTROY = 2;
85      private static final byte FACES_INIT_PHASE_POSTDESTROY = 3;
86  
87      //private static final Log log = LogFactory.getLog(StartupServletContextListener.class);
88      private static final Logger log = Logger.getLogger(StartupServletContextListener.class.getName());
89  
90      private FacesInitializer _facesInitializer;
91      private ServletContext _servletContext;
92      private ManagedBeanDestroyerListener _detroyerListener = new ManagedBeanDestroyerListener();
93      
94      public void contextInitialized(ServletContextEvent event)
95      {
96          if (_servletContext != null)
97          {
98              throw new IllegalStateException("context is already initialized");
99          }
100         _servletContext = event.getServletContext();
101         
102         Boolean b = (Boolean) _servletContext.getAttribute(FACES_INIT_DONE);
103         if (b == null || b.booleanValue() == false)
104         {
105             if (_facesInitializer == null)
106             {
107                 _facesInitializer = FacesInitializerFactory.getFacesInitializer(_servletContext);
108             }
109 
110             // Create startup FacesContext before initializing
111             FacesContext facesContext = _facesInitializer.initStartupFacesContext(_servletContext);
112 
113             // publish the ManagedBeanDestroyerListener instance in the application map
114             _publishManagedBeanDestroyerListener(facesContext);
115             
116             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREINIT);
117             _facesInitializer.initFaces(_servletContext);
118             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTINIT);
119             _servletContext.setAttribute(FACES_INIT_DONE, Boolean.TRUE);
120 
121             // call contextInitialized on ManagedBeanDestroyerListener
122             _detroyerListener.contextInitialized(event);
123             
124             //Destroy startup FacesContext
125             _facesInitializer.destroyStartupFacesContext(facesContext);
126         }
127         else
128         {
129             log.info("MyFaces already initialized");
130         }
131     }
132 
133     /**
134      * Publishes the ManagedBeanDestroyerListener instance in the application map.
135      * This allows the FacesConfigurator to access the instance and to set the
136      * correct ManagedBeanDestroyer instance on it.
137      *
138      * @param facesContext
139      */
140     private void _publishManagedBeanDestroyerListener(FacesContext facesContext)
141     {
142         ExternalContext externalContext = facesContext.getExternalContext();
143         Map<String, Object> applicationMap = externalContext.getApplicationMap();
144 
145         applicationMap.put(ManagedBeanDestroyerListener.APPLICATION_MAP_KEY, _detroyerListener);
146     }
147     
148     public void contextDestroyed(ServletContextEvent event)
149     {
150         if (_facesInitializer != null && _servletContext != null)
151         {
152             // Create startup FacesContext before start undeploy
153             FacesContext facesContext = _facesInitializer.initShutdownFacesContext(_servletContext);
154             
155             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
156             // call contextDestroyed on ManagedBeanDestroyerListener to destroy the attributes
157             _detroyerListener.contextDestroyed(event);
158 
159             _facesInitializer.destroyFaces(_servletContext);
160             
161             LifecycleProviderFactory.getLifecycleProviderFactory().release();
162 
163             // Destroy startup FacesContext, but note we do before publish postdestroy event on
164             // plugins and before release factories.
165             if (facesContext != null)
166             {
167                 _facesInitializer.destroyShutdownFacesContext(facesContext);
168             }
169             
170             FactoryFinder.releaseFactories();
171 
172             //DiscoverSingleton.release(); //clears EnvironmentCache and prevents leaking classloader references
173             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTDESTROY);
174         }
175         
176         _servletContext = null;
177     }
178     
179     /**
180      * configure the faces initializer
181      * 
182      * @param facesInitializer
183      */
184     public void setFacesInitializer(FacesInitializer facesInitializer) // TODO who uses this method?
185     {
186         if (_facesInitializer != null && _facesInitializer != facesInitializer && _servletContext != null)
187         {
188             _facesInitializer.destroyFaces(_servletContext);
189         }
190         _facesInitializer = facesInitializer;
191         if (_servletContext != null)
192         {
193             facesInitializer.initFaces(_servletContext);
194         }
195     }
196 
197     /**
198      * loads the faces init plugins per reflection and Service loader
199      * in a jdk6 environment
200      *
201      * @return false in case of a failed attempt or no listeners found
202      *         which then will cause the jdk5 context.xml code to trigger
203      */
204     private boolean loadFacesInitPluginsJDK6()
205     {
206         String[] pluginEntries = null;
207         try
208         {
209             Class serviceLoader = ClassUtils.getContextClassLoader().loadClass("java.util.ServiceLoader");
210             Method m = serviceLoader.getDeclaredMethod("load", Class.class, ClassLoader.class);
211             Object loader = m.invoke(serviceLoader, StartupListener.class, ClassUtils.getContextClassLoader());
212             m = loader.getClass().getDeclaredMethod("iterator");
213             Iterator<StartupListener> it = (Iterator<StartupListener>) m.invoke(loader);
214             List<StartupListener> listeners = new LinkedList<StartupListener>();
215             if (!it.hasNext())
216             {
217                 return false;
218             }
219             while (it.hasNext())
220             {
221                 listeners.add(it.next());
222             }
223             //StartupListener[] listeners1 = listeners.toArray(new StartupListener[listeners.size()]);
224             _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
225             return true;
226         }
227         catch (ClassNotFoundException e)
228         {
229 
230         }
231         catch (NoSuchMethodException e)
232         {
233             log.log(Level.SEVERE, e.getMessage(), e);
234         }
235         catch (InvocationTargetException e)
236         {
237             log.log(Level.SEVERE, e.getMessage(), e);
238         }
239         catch (IllegalAccessException e)
240         {
241             log.log(Level.SEVERE, e.getMessage(), e);
242         }
243         return false;
244     }
245 
246     /**
247      * loads the faces init plugins per reflection and Service loader
248      * in a jdk6 environment
249      */
250     private void loadFacesInitPluginsJDK5()
251     {
252 
253         String plugins = (String) _servletContext.getInitParameter(FACES_INIT_PLUGINS);
254         if (plugins == null)
255         {
256             return;
257         }
258         log.info("MyFaces Plugins found");
259         String[] pluginEntries = plugins.split(",");
260         List<StartupListener> listeners = new ArrayList<StartupListener>(pluginEntries.length);
261         for (String pluginEntry : pluginEntries)
262         {
263             try
264             {
265                 Class pluginClass = null;
266                 pluginClass = ClassUtils.getContextClassLoader().loadClass(pluginEntry);
267                 if (pluginClass == null)
268                 {
269                     pluginClass = this.getClass().getClassLoader().loadClass(pluginEntry);
270                 }
271                 listeners.add((StartupListener) pluginClass.newInstance());
272             }
273             catch (ClassNotFoundException e)
274             {
275                 log.log(Level.SEVERE, e.getMessage(), e);
276             }
277             catch (InstantiationException e)
278             {
279                 log.log(Level.SEVERE, e.getMessage(), e);
280             }
281             catch (IllegalAccessException e)
282             {
283                 log.log(Level.SEVERE, e.getMessage(), e);
284             }
285         }
286         // StartupListener[] listeners1 = listeners.toArray(new StartupListener[listeners.size()]);
287         _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
288 
289     }
290 
291 
292     /**
293      * the central initialisation event dispatcher which calls
294      * our listeners
295      *
296      * @param event
297      * @param operation
298      */
299     private void dispatchInitializationEvent(ServletContextEvent event, int operation)
300     {
301 
302         if (operation == FACES_INIT_PHASE_PREINIT)
303         {
304             if (!loadFacesInitPluginsJDK6())
305             {
306                 loadFacesInitPluginsJDK5();
307             }
308         }
309 
310         List<StartupListener> pluginEntries = (List<StartupListener>) _servletContext.getAttribute(FACES_INIT_PLUGINS);
311         if (pluginEntries == null)
312         {
313             return;
314         }
315 
316         //now we process the plugins
317         for (StartupListener initializer : pluginEntries)
318         {
319             log.info("Processing plugin");
320 
321             //for now the initializers have to be stateless to
322             //so that we do not have to enforce that the initializer
323             //must be serializable
324             switch (operation)
325             {
326                 case FACES_INIT_PHASE_PREINIT:
327                     initializer.preInit(event);
328                     break;
329                 case FACES_INIT_PHASE_POSTINIT:
330                     initializer.postInit(event);
331                     break;
332                 case FACES_INIT_PHASE_PREDESTROY:
333                     initializer.preDestroy(event);
334                     break;
335                 default:
336                     initializer.postDestroy(event);
337                     break;
338             }
339         }
340         log.info("Processing MyFaces plugins done");
341     }
342     
343     /* the following methods are needed to serve ManagedBeanDestroyerListener */
344     /* Session related methods ***********************************************/
345     
346     public void attributeAdded(HttpSessionBindingEvent event)
347     {
348         _detroyerListener.attributeAdded(event);
349     }
350 
351     public void attributeRemoved(HttpSessionBindingEvent event)
352     {
353         _detroyerListener.attributeRemoved(event);
354     }
355 
356     public void attributeReplaced(HttpSessionBindingEvent event)
357     {
358         _detroyerListener.attributeReplaced(event);
359     }
360 
361     public void sessionCreated(HttpSessionEvent event)
362     {
363         _detroyerListener.sessionCreated(event);
364     }
365 
366     public void sessionDestroyed(HttpSessionEvent event)
367     {
368         _detroyerListener.sessionDestroyed(event);
369     }
370     
371     /* Context related methods ***********************************************/
372     
373     public void attributeAdded(ServletContextAttributeEvent event)
374     {
375         _detroyerListener.attributeAdded(event);
376     }
377 
378     public void attributeRemoved(ServletContextAttributeEvent event)
379     {
380         _detroyerListener.attributeRemoved(event);
381     }
382 
383     public void attributeReplaced(ServletContextAttributeEvent event)
384     {
385         _detroyerListener.attributeReplaced(event);
386     }
387     
388     /* Request related methods ***********************************************/
389     
390     public void attributeAdded(ServletRequestAttributeEvent event)
391     {
392         _detroyerListener.attributeAdded(event);
393     }
394 
395     public void attributeRemoved(ServletRequestAttributeEvent event)
396     {
397         _detroyerListener.attributeRemoved(event);
398     }
399 
400     public void attributeReplaced(ServletRequestAttributeEvent event)
401     {
402         _detroyerListener.attributeReplaced(event);
403     }
404 
405     public void requestInitialized(ServletRequestEvent event)
406     {
407         _detroyerListener.requestInitialized(event);
408     }
409     
410     public void requestDestroyed(ServletRequestEvent event)
411     {        
412         _detroyerListener.requestDestroyed(event);
413     }
414 
415 }