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