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.FacesContext;
27  
28  import javax.servlet.ServletContext;
29  import javax.servlet.ServletContextEvent;
30  import javax.servlet.ServletContextListener;
31  import java.lang.reflect.InvocationTargetException;
32  import java.lang.reflect.Method;
33  
34  import java.util.ArrayList;
35  import java.util.Iterator;
36  import java.util.LinkedList;
37  import java.util.List;
38  import java.util.logging.Level;
39  import java.util.logging.Logger;
40  
41  /**
42   * Initialise the MyFaces system.
43   * <p>
44   * This context listener is registered by the JSP TLD file for the standard JSF "f" components. Normally, servlet
45   * containers will automatically load and process .tld files at startup time, and therefore register and run this class
46   * automatically.
47   * </p><p>
48   * Some very old servlet containers do not do this correctly, so in those cases this listener may be registered manually
49   * in web.xml. Registering it twice (ie in both .tld and web.xml) will result in a harmless warning message being
50   * generated. Very old versions of MyFaces Core do not register the listener in the .tld file, so those also need a
51   * manual entry in web.xml. However all versions since at least 1.1.2 have this entry in the tld.
52   * </p><p>
53   * This listener also delegates all session, request and context events to ManagedBeanDestroyer.
54   * Because of that we only need to register one listener in the tld.
55   * </p>
56   *
57   * @author Manfred Geiler (latest modification by $Author$)
58   * @version $Revision$ $Date$
59   */
60  public class StartupServletContextListener implements ServletContextListener
61  {
62      static final String FACES_INIT_DONE = "org.apache.myfaces.webapp.StartupServletContextListener.FACES_INIT_DONE";
63  
64      /**
65       * comma delimited list of plugin classes which can be hooked into myfaces
66       */
67      @JSFWebConfigParam(since = "2.0")
68      static final String FACES_INIT_PLUGINS = "org.apache.myfaces.FACES_INIT_PLUGINS";
69  
70      private static final byte FACES_INIT_PHASE_PREINIT = 0;
71      private static final byte FACES_INIT_PHASE_POSTINIT = 1;
72      private static final byte FACES_INIT_PHASE_PREDESTROY = 2;
73      private static final byte FACES_INIT_PHASE_POSTDESTROY = 3;
74  
75      private static final Logger log = Logger.getLogger(StartupServletContextListener.class.getName());
76  
77      private FacesInitializer _facesInitializer;
78      private ServletContext _servletContext;
79  
80      @Override
81      public void contextInitialized(ServletContextEvent event)
82      {
83          if (_servletContext != null)
84          {
85              throw new IllegalStateException("context is already initialized");
86          }
87          _servletContext = event.getServletContext();
88  
89          Boolean b = (Boolean) _servletContext.getAttribute(FACES_INIT_DONE);
90          if (b == null || b.booleanValue() == false)
91          {
92              long start = System.currentTimeMillis();
93  
94              if (_facesInitializer == null)
95              {
96                  _facesInitializer = FacesInitializerFactory.getFacesInitializer(_servletContext);
97              }
98  
99              // Create startup FacesContext before initializing
100             FacesContext facesContext = _facesInitializer.initStartupFacesContext(_servletContext);
101 
102             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREINIT);
103             _facesInitializer.initFaces(_servletContext);
104             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTINIT);
105             _servletContext.setAttribute(FACES_INIT_DONE, Boolean.TRUE);
106 
107             //Destroy startup FacesContext
108             _facesInitializer.destroyStartupFacesContext(facesContext);
109 
110             log.log(Level.INFO, "MyFaces Core has started, it took ["
111                     + (System.currentTimeMillis() - start)
112                     + "] ms.");
113         }
114         else
115         {
116             log.info("MyFaces already initialized");
117         }
118     }
119 
120     @Override
121     public void contextDestroyed(ServletContextEvent event)
122     {
123         if (_facesInitializer != null && _servletContext != null)
124         {
125             // Create startup FacesContext before start undeploy
126             FacesContext facesContext = _facesInitializer.initShutdownFacesContext(_servletContext);
127 
128             dispatchInitializationEvent(event, FACES_INIT_PHASE_PREDESTROY);
129 
130             _facesInitializer.destroyFaces(_servletContext);
131 
132             LifecycleProviderFactory.getLifecycleProviderFactory().release();
133 
134             // Destroy startup FacesContext, but note we do before publish postdestroy event on
135             // plugins and before release factories.
136             if (facesContext != null)
137             {
138                 _facesInitializer.destroyShutdownFacesContext(facesContext);
139             }
140 
141             FactoryFinder.releaseFactories();
142 
143             //DiscoverSingleton.release(); //clears EnvironmentCache and prevents leaking classloader references
144             dispatchInitializationEvent(event, FACES_INIT_PHASE_POSTDESTROY);
145         }
146 
147         _servletContext = null;
148     }
149 
150     /**
151      * configure the faces initializer
152      *
153      * @param facesInitializer
154      */
155     public void setFacesInitializer(FacesInitializer facesInitializer) // TODO who uses this method?
156     {
157         if (_facesInitializer != null && _facesInitializer != facesInitializer && _servletContext != null)
158         {
159             _facesInitializer.destroyFaces(_servletContext);
160         }
161         _facesInitializer = facesInitializer;
162         if (_servletContext != null)
163         {
164             facesInitializer.initFaces(_servletContext);
165         }
166     }
167 
168     /**
169      * loads the faces init plugins per reflection and Service loader
170      * in a jdk6 environment
171      *
172      * @return false in case of a failed attempt or no listeners found
173      *         which then will cause the jdk5 context.xml code to trigger
174      */
175     private boolean loadFacesInitPluginsViaServiceLoader()
176     {
177         try
178         {
179             Class serviceLoader = ClassUtils.getContextClassLoader().loadClass("java.util.ServiceLoader");
180             Method m = serviceLoader.getDeclaredMethod("load", Class.class, ClassLoader.class);
181             
182             Object loader = m.invoke(serviceLoader, StartupListener.class, ClassUtils.getContextClassLoader());
183             m = loader.getClass().getDeclaredMethod("iterator");
184             
185             Iterator<StartupListener> it = (Iterator<StartupListener>) m.invoke(loader);
186             List<StartupListener> listeners = new LinkedList<StartupListener>();
187             if (!it.hasNext())
188             {
189                 return false;
190             }
191             while (it.hasNext())
192             {
193                 listeners.add(it.next());
194             }
195 
196             _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
197             return true;
198         }
199         catch (ClassNotFoundException e)
200         {
201 
202         }
203         catch (NoSuchMethodException e)
204         {
205             log.log(Level.SEVERE, e.getMessage(), e);
206         }
207         catch (InvocationTargetException e)
208         {
209             log.log(Level.SEVERE, e.getMessage(), e);
210         }
211         catch (IllegalAccessException e)
212         {
213             log.log(Level.SEVERE, e.getMessage(), e);
214         }
215         return false;
216     }
217 
218     /**
219      * loads the faces init plugins per reflection from the context param.
220      */
221     private void loadFacesInitViaContextParam()
222     {
223         String plugins = (String) _servletContext.getInitParameter(FACES_INIT_PLUGINS);
224         if (plugins == null)
225         {
226             return;
227         }
228         log.info("MyFaces Plugins found");
229         
230         String[] pluginEntries = plugins.split(",");
231         List<StartupListener> listeners = new ArrayList<StartupListener>(pluginEntries.length);
232         for (String pluginEntry : pluginEntries)
233         {
234             try
235             {
236                 Class pluginClass = null;
237                 pluginClass = ClassUtils.getContextClassLoader().loadClass(pluginEntry);
238                 if (pluginClass == null)
239                 {
240                     pluginClass = this.getClass().getClassLoader().loadClass(pluginEntry);
241                 }
242                 listeners.add((StartupListener) pluginClass.newInstance());
243             }
244             catch (ClassNotFoundException e)
245             {
246                 log.log(Level.SEVERE, e.getMessage(), e);
247             }
248             catch (InstantiationException e)
249             {
250                 log.log(Level.SEVERE, e.getMessage(), e);
251             }
252             catch (IllegalAccessException e)
253             {
254                 log.log(Level.SEVERE, e.getMessage(), e);
255             }
256         }
257 
258         _servletContext.setAttribute(FACES_INIT_PLUGINS, listeners);
259 
260     }
261 
262     /**
263      * the central initialisation event dispatcher which calls
264      * our listeners
265      *
266      * @param event
267      * @param operation
268      */
269     private void dispatchInitializationEvent(ServletContextEvent event, int operation)
270     {
271         if (operation == FACES_INIT_PHASE_PREINIT)
272         {
273             if (!loadFacesInitPluginsViaServiceLoader())
274             {
275                 loadFacesInitViaContextParam();
276             }
277         }
278 
279         List<StartupListener> pluginEntries = (List<StartupListener>) _servletContext.getAttribute(FACES_INIT_PLUGINS);
280         if (pluginEntries == null)
281         {
282             return;
283         }
284 
285         //now we process the plugins
286         for (StartupListener initializer : pluginEntries)
287         {
288             log.info("Processing plugin");
289 
290             //for now the initializers have to be stateless to
291             //so that we do not have to enforce that the initializer
292             //must be serializable
293             switch (operation)
294             {
295                 case FACES_INIT_PHASE_PREINIT:
296                     initializer.preInit(event);
297                     break;
298                 case FACES_INIT_PHASE_POSTINIT:
299                     initializer.postInit(event);
300                     break;
301                 case FACES_INIT_PHASE_PREDESTROY:
302                     initializer.preDestroy(event);
303                     break;
304                 default:
305                     initializer.postDestroy(event);
306                     break;
307             }
308         }
309         log.info("Processing MyFaces plugins done");
310     }
311 }