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: werpu $)
69   * @version $Revision: 1304305 $ $Date: 2012-03-23 07:17:45 -0500 (Fri, 23 Mar 2012) $
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             if (_facesInitializer == null)
108             {
109                 _facesInitializer = FacesInitializerFactory.getFacesInitializer(_servletContext);
110             }
111 
112             // Create startup FacesContext before initializing
113             FacesContext facesContext = _facesInitializer.initStartupFacesContext(_servletContext);
114 
115             // publish the ManagedBeanDestroyerListener instance in the application map
116             _publishManagedBeanDestroyerListener(facesContext);
117 
118             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREINIT);
119             _facesInitializer.initFaces(_servletContext);
120             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTINIT);
121             _servletContext.setAttribute(FACES_INIT_DONE, Boolean.TRUE);
122 
123             // call contextInitialized on ManagedBeanDestroyerListener
124             _detroyerListener.contextInitialized(event);
125 
126             //Destroy startup FacesContext
127             _facesInitializer.destroyStartupFacesContext(facesContext);
128         }
129         else
130         {
131             log.info("MyFaces already initialized");
132         }
133     }
134 
135     /**
136      * Publishes the ManagedBeanDestroyerListener instance in the application map.
137      * This allows the FacesConfigurator to access the instance and to set the
138      * correct ManagedBeanDestroyer instance on it.
139      *
140      * @param facesContext
141      */
142     private void _publishManagedBeanDestroyerListener(FacesContext facesContext)
143     {
144         ExternalContext externalContext = facesContext.getExternalContext();
145         Map<String, Object> applicationMap = externalContext.getApplicationMap();
146 
147         applicationMap.put(ManagedBeanDestroyerListener.APPLICATION_MAP_KEY, _detroyerListener);
148     }
149 
150     public void contextDestroyed(ServletContextEvent event)
151     {
152         if (_facesInitializer != null && _servletContext != null)
153         {
154             // Create startup FacesContext before start undeploy
155             FacesContext facesContext = _facesInitializer.initShutdownFacesContext(_servletContext);
156 
157             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
158             // call contextDestroyed on ManagedBeanDestroyerListener to destroy the attributes
159             _detroyerListener.contextDestroyed(event);
160 
161             _facesInitializer.destroyFaces(_servletContext);
162 
163             LifecycleProviderFactory.getLifecycleProviderFactory().release();
164 
165             // Destroy startup FacesContext, but note we do before publish postdestroy event on
166             // plugins and before release factories.
167             if (facesContext != null)
168             {
169                 _facesInitializer.destroyShutdownFacesContext(facesContext);
170             }
171 
172             FactoryFinder.releaseFactories();
173 
174             //DiscoverSingleton.release(); //clears EnvironmentCache and prevents leaking classloader references
175             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTDESTROY);
176         }
177 
178         _servletContext = null;
179     }
180 
181     /**
182      * configure the faces initializer
183      *
184      * @param facesInitializer
185      */
186     public void setFacesInitializer(FacesInitializer facesInitializer) // TODO who uses this method?
187     {
188         if (_facesInitializer != null && _facesInitializer != facesInitializer && _servletContext != null)
189         {
190             _facesInitializer.destroyFaces(_servletContext);
191         }
192         _facesInitializer = facesInitializer;
193         if (_servletContext != null)
194         {
195             facesInitializer.initFaces(_servletContext);
196         }
197     }
198 
199     /**
200      * loads the faces init plugins per reflection and Service loader
201      * in a jdk6 environment
202      *
203      * @return false in case of a failed attempt or no listeners found
204      *         which then will cause the jdk5 context.xml code to trigger
205      */
206     private boolean loadFacesInitPluginsJDK6()
207     {
208         String[] pluginEntries = null;
209         try
210         {
211             Class serviceLoader = ClassUtils.getContextClassLoader().loadClass("java.util.ServiceLoader");
212             Method m = serviceLoader.getDeclaredMethod("load", Class.class, ClassLoader.class);
213             Object loader = m.invoke(serviceLoader, StartupListener.class, ClassUtils.getContextClassLoader());
214             m = loader.getClass().getDeclaredMethod("iterator");
215             Iterator<StartupListener> it = (Iterator<StartupListener>) m.invoke(loader);
216             List<StartupListener> listeners = new LinkedList<StartupListener>();
217             if (!it.hasNext())
218             {
219                 return false;
220             }
221             while (it.hasNext())
222             {
223                 listeners.add(it.next());
224             }
225             //StartupListener[] listeners1 = listeners.toArray(new StartupListener[listeners.size()]);
226             _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
227             return true;
228         }
229         catch (ClassNotFoundException e)
230         {
231 
232         }
233         catch (NoSuchMethodException e)
234         {
235             log.log(Level.SEVERE, e.getMessage(), e);
236         }
237         catch (InvocationTargetException e)
238         {
239             log.log(Level.SEVERE, e.getMessage(), e);
240         }
241         catch (IllegalAccessException e)
242         {
243             log.log(Level.SEVERE, e.getMessage(), e);
244         }
245         return false;
246     }
247 
248     /**
249      * loads the faces init plugins per reflection and Service loader
250      * in a jdk6 environment
251      */
252     private void loadFacesInitPluginsJDK5()
253     {
254 
255         String plugins = (String) _servletContext.getInitParameter(FACES_INIT_PLUGINS);
256         if (plugins == null)
257         {
258             return;
259         }
260         log.info("MyFaces Plugins found");
261         String[] pluginEntries = plugins.split(",");
262         List<StartupListener> listeners = new ArrayList<StartupListener>(pluginEntries.length);
263         for (String pluginEntry : pluginEntries)
264         {
265             try
266             {
267                 Class pluginClass = null;
268                 pluginClass = ClassUtils.getContextClassLoader().loadClass(pluginEntry);
269                 if (pluginClass == null)
270                 {
271                     pluginClass = this.getClass().getClassLoader().loadClass(pluginEntry);
272                 }
273                 listeners.add((StartupListener) pluginClass.newInstance());
274             }
275             catch (ClassNotFoundException e)
276             {
277                 log.log(Level.SEVERE, e.getMessage(), e);
278             }
279             catch (InstantiationException e)
280             {
281                 log.log(Level.SEVERE, e.getMessage(), e);
282             }
283             catch (IllegalAccessException e)
284             {
285                 log.log(Level.SEVERE, e.getMessage(), e);
286             }
287         }
288         // StartupListener[] listeners1 = listeners.toArray(new StartupListener[listeners.size()]);
289         _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
290 
291     }
292 
293     /**
294      * the central initialisation event dispatcher which calls
295      * our listeners
296      *
297      * @param event
298      * @param operation
299      */
300     private void dispatchInitializationEvent(ServletContextEvent event, int operation)
301     {
302 
303         if (operation == FACES_INIT_PHASE_PREINIT)
304         {
305             if (!loadFacesInitPluginsJDK6())
306             {
307                 loadFacesInitPluginsJDK5();
308             }
309         }
310 
311         List<StartupListener> pluginEntries = (List<StartupListener>) _servletContext.getAttribute(FACES_INIT_PLUGINS);
312         if (pluginEntries == null)
313         {
314             return;
315         }
316 
317         //now we process the plugins
318         for (StartupListener initializer : pluginEntries)
319         {
320             log.info("Processing plugin");
321 
322             //for now the initializers have to be stateless to
323             //so that we do not have to enforce that the initializer
324             //must be serializable
325             switch (operation)
326             {
327                 case FACES_INIT_PHASE_PREINIT:
328                     initializer.preInit(event);
329                     break;
330                 case FACES_INIT_PHASE_POSTINIT:
331                     initializer.postInit(event);
332                     break;
333                 case FACES_INIT_PHASE_PREDESTROY:
334                     initializer.preDestroy(event);
335                     break;
336                 default:
337                     initializer.postDestroy(event);
338                     break;
339             }
340         }
341         log.info("Processing MyFaces plugins done");
342     }
343 
344     /* the following methods are needed to serve ManagedBeanDestroyerListener */
345     /* Session related methods ***********************************************/
346 
347     public void attributeAdded(HttpSessionBindingEvent event)
348     {
349         _detroyerListener.attributeAdded(event);
350     }
351 
352     public void attributeRemoved(HttpSessionBindingEvent event)
353     {
354         _detroyerListener.attributeRemoved(event);
355     }
356 
357     public void attributeReplaced(HttpSessionBindingEvent event)
358     {
359         _detroyerListener.attributeReplaced(event);
360     }
361 
362     public void sessionCreated(HttpSessionEvent event)
363     {
364         _detroyerListener.sessionCreated(event);
365     }
366 
367     public void sessionDestroyed(HttpSessionEvent event)
368     {
369         _detroyerListener.sessionDestroyed(event);
370     }
371 
372     /* Context related methods ***********************************************/
373 
374     public void attributeAdded(ServletContextAttributeEvent event)
375     {
376         _detroyerListener.attributeAdded(event);
377     }
378 
379     public void attributeRemoved(ServletContextAttributeEvent event)
380     {
381         _detroyerListener.attributeRemoved(event);
382     }
383 
384     public void attributeReplaced(ServletContextAttributeEvent event)
385     {
386         _detroyerListener.attributeReplaced(event);
387     }
388 
389     /* Request related methods ***********************************************/
390 
391     public void attributeAdded(ServletRequestAttributeEvent event)
392     {
393         _detroyerListener.attributeAdded(event);
394     }
395 
396     public void attributeRemoved(ServletRequestAttributeEvent event)
397     {
398         _detroyerListener.attributeRemoved(event);
399     }
400 
401     public void attributeReplaced(ServletRequestAttributeEvent event)
402     {
403         _detroyerListener.attributeReplaced(event);
404     }
405 
406     public void requestInitialized(ServletRequestEvent event)
407     {
408         _detroyerListener.requestInitialized(event);
409     }
410 
411     public void requestDestroyed(ServletRequestEvent event)
412     {
413         _detroyerListener.requestDestroyed(event);
414     }
415 
416 }