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.FacesConfigValidator;
23  import org.apache.myfaces.config.FacesConfigurator;
24  import org.apache.myfaces.config.ManagedBeanBuilder;
25  import org.apache.myfaces.config.RuntimeConfig;
26  import org.apache.myfaces.config.element.ManagedBean;
27  import org.apache.myfaces.context.ReleaseableExternalContext;
28  import org.apache.myfaces.context.servlet.StartupFacesContextImpl;
29  import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
30  import org.apache.myfaces.ee.MyFacesContainerInitializer;
31  import org.apache.myfaces.shared.application.FacesServletMappingUtils;
32  import org.apache.myfaces.shared.context.ExceptionHandlerImpl;
33  import org.apache.myfaces.shared.util.StateUtils;
34  import org.apache.myfaces.shared.util.WebConfigParamUtils;
35  import org.apache.myfaces.cdi.util.BeanEntry;
36  import org.apache.myfaces.spi.InjectionProvider;
37  import org.apache.myfaces.spi.InjectionProviderException;
38  import org.apache.myfaces.spi.InjectionProviderFactory;
39  import org.apache.myfaces.spi.ViewScopeProvider;
40  import org.apache.myfaces.spi.ViewScopeProviderFactory;
41  import org.apache.myfaces.spi.WebConfigProvider;
42  import org.apache.myfaces.spi.WebConfigProviderFactory;
43  import org.apache.myfaces.util.ExternalSpecifications;
44  import org.apache.myfaces.view.facelets.tag.MetaRulesetImpl;
45  
46  import javax.el.ExpressionFactory;
47  import javax.faces.application.Application;
48  import javax.faces.application.ProjectStage;
49  import javax.faces.component.UIViewRoot;
50  import javax.faces.context.ExceptionHandler;
51  import javax.faces.context.ExternalContext;
52  import javax.faces.context.FacesContext;
53  import javax.faces.event.PostConstructApplicationEvent;
54  import javax.faces.event.PreDestroyApplicationEvent;
55  import javax.faces.event.SystemEvent;
56  import javax.servlet.ServletContext;
57  import java.lang.reflect.InvocationTargetException;
58  import java.lang.reflect.Method;
59  import java.util.ArrayList;
60  import java.util.List;
61  import java.util.Locale;
62  import java.util.Map;
63  import java.util.logging.Level;
64  import java.util.logging.Logger;
65  import javax.faces.application.ViewVisitOption;
66  import javax.faces.push.PushContext;
67  import javax.servlet.ServletRegistration;
68  import javax.websocket.DeploymentException;
69  import javax.websocket.server.ServerContainer;
70  import javax.websocket.server.ServerEndpointConfig;
71  import org.apache.myfaces.push.EndpointImpl;
72  import org.apache.myfaces.push.WebsocketConfigurator;
73  import org.apache.myfaces.push.WebsocketFacesInit;
74  import org.apache.myfaces.shared.util.ClassUtils;
75  import org.apache.myfaces.spi.ServiceProviderFinder;
76  import org.apache.myfaces.spi.ServiceProviderFinderFactory;
77  import org.apache.myfaces.view.facelets.ViewPoolProcessor;
78  
79  /**
80   * Performs common initialization tasks.
81   */
82  public abstract class AbstractFacesInitializer implements FacesInitializer
83  {
84      private static final Logger log = Logger.getLogger(AbstractFacesInitializer.class.getName());
85  
86      /**
87       * This parameter specifies the ExpressionFactory implementation to use.
88       */
89      @JSFWebConfigParam(since="1.2.7", group="EL")
90      protected static final String EXPRESSION_FACTORY = "org.apache.myfaces.EXPRESSION_FACTORY";
91      
92      /**
93       * If this param is set to true, the check for faces servlet mapping is not done 
94       */
95      @JSFWebConfigParam(since="2.0.3", defaultValue="false")
96      protected static final String INITIALIZE_ALWAYS_STANDALONE = "org.apache.myfaces.INITIALIZE_ALWAYS_STANDALONE";
97      
98      /**
99       * Indicate if log all web config params should be done before initialize the webapp. 
100      * <p>
101      * If is set in "auto" mode, web config params are only logged on "Development" and "Production" project stages.
102      * </p> 
103      */
104     @JSFWebConfigParam(expectedValues="true, auto, false", defaultValue="auto")
105     public static final String INIT_PARAM_LOG_WEB_CONTEXT_PARAMS = "org.apache.myfaces.LOG_WEB_CONTEXT_PARAMS";
106     public static final String INIT_PARAM_LOG_WEB_CONTEXT_PARAMS_DEFAULT ="auto";
107     
108     /**
109      * This parameter enables automatic extensionless mapping for all JSF views.
110      */
111     @JSFWebConfigParam(since="2.3", expectedValues = "true, false", defaultValue = "false")
112     public static final String INIT_PARAM_AUTOMATIC_EXTENSIONLESS_MAPPING = 
113             "org.apache.myfaces.AUTOMATIC_EXTENSIONLESS_MAPPING";
114     public static final boolean INIT_PARAM_AUTOMATIC_EXTENSIONLESS_MAPPING_DEFAULT = false;
115     
116     public static final String CDI_BEAN_MANAGER_INSTANCE = "oam.cdi.BEAN_MANAGER_INSTANCE";
117     
118     private static final String CDI_SERVLET_CONTEXT_BEAN_MANAGER_ATTRIBUTE = 
119         "javax.enterprise.inject.spi.BeanManager";
120 
121     private static final String INJECTED_BEAN_STORAGE_KEY = "org.apache.myfaces.spi.BEAN_ENTRY_STORAGE";
122 
123     /**
124      * Performs all necessary initialization tasks like configuring this JSF
125      * application.
126      */
127     @Override
128     public void initFaces(ServletContext servletContext)
129     {
130         try
131         {
132             if (log.isLoggable(Level.FINEST))
133             {
134                 log.finest("Initializing MyFaces");
135             }
136 
137             // Some parts of the following configuration tasks have been implemented 
138             // by using an ExternalContext. However, that's no problem as long as no 
139             // one tries to call methods depending on either the ServletRequest or 
140             // the ServletResponse.
141             // JSF 2.0: FacesInitializer now has some new methods to
142             // use proper startup FacesContext and ExternalContext instances.
143             FacesContext facesContext = FacesContext.getCurrentInstance();
144             ExternalContext externalContext = facesContext.getExternalContext();
145 
146             // Setup ServiceProviderFinder
147             ServiceProviderFinder spf = ServiceProviderFinderFactory.getServiceProviderFinder(
148                 externalContext);
149             Map<String, List<String>> spfConfig = spf.calculateKnownServiceProviderMapInfo(
150                 externalContext, ServiceProviderFinder.KNOWN_SERVICES);
151             if (spfConfig != null)
152             {
153                 spf.initKnownServiceProviderMapInfo(externalContext, spfConfig);
154             }
155             
156             // Parse and validate the web.xml configuration file
157             
158             if (!WebConfigParamUtils.getBooleanInitParameter(externalContext, INITIALIZE_ALWAYS_STANDALONE, false))
159             {
160                 WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
161                         facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
162 
163                 if (webConfigProvider.getFacesServletMappings(facesContext.getExternalContext()).isEmpty())
164                 {
165                     // check to see if the FacesServlet was found by MyFacesContainerInitializer
166                     Boolean mappingAdded = (Boolean) servletContext.getAttribute(
167                         MyFacesContainerInitializer.FACES_SERVLET_FOUND);
168 
169                     if (mappingAdded == null || !mappingAdded)
170                     {
171                         // check if the FacesServlet has been added dynamically
172                         // in a Servlet 3.0 environment by MyFacesContainerInitializer
173                         mappingAdded = (Boolean) servletContext.getAttribute(
174                             MyFacesContainerInitializer.FACES_SERVLET_ADDED_ATTRIBUTE);
175 
176                         if (mappingAdded == null || !mappingAdded)
177                         {
178                             if (log.isLoggable(Level.WARNING))
179                             {
180                                 log.warning("No mappings of FacesServlet found. Abort initializing MyFaces.");
181                             }
182                             return;
183                         }
184                     }
185                 }
186             }
187 
188             initCDIIntegration(servletContext, externalContext);
189             
190             initContainerIntegration(servletContext, externalContext);
191             
192             ViewScopeProviderFactory factory = ViewScopeProviderFactory.getViewScopeHandlerFactory(
193                 externalContext);
194             
195             ViewScopeProvider viewScopeHandler = factory.getViewScopeHandler(
196                 externalContext);
197             
198             ManagedBeanDestroyerListener listener = (ManagedBeanDestroyerListener)
199                 externalContext.getApplicationMap().get(
200                     ManagedBeanDestroyerListener.APPLICATION_MAP_KEY);
201             if (listener != null)
202             {
203                 listener.setViewScopeHandler(viewScopeHandler);
204             }
205 
206             String useEncryption = servletContext.getInitParameter(StateUtils.USE_ENCRYPTION);
207             if ("false".equals(useEncryption))
208             {
209                 log.warning(StateUtils.USE_ENCRYPTION + " is set to false. " 
210                         + "This is unsecure and should only be used for local or intranet applications!");
211             }
212             else
213             {
214                 StateUtils.initSecret(servletContext);
215             }
216 
217             // initialize eager managed beans
218             _createEagerBeans(facesContext);
219 
220             _dispatchApplicationEvent(servletContext, PostConstructApplicationEvent.class);
221             
222             initWebsocketIntegration(servletContext, externalContext);
223 
224             if ( (facesContext.isProjectStage(ProjectStage.Development) || 
225                   facesContext.isProjectStage(ProjectStage.Production)) &&
226                  log.isLoggable(Level.INFO))
227             {
228                 log.info("ServletContext initialized.");
229             }
230 
231             WebConfigParamsLogger.logWebContextParams(facesContext);
232             
233             checkForDeprecatedContextParams(facesContext);
234             
235             //Force output EL message
236             ExternalSpecifications.isBeanValidationAvailable();
237             
238             //Start ViewPoolProcessor if necessary
239             ViewPoolProcessor.initialize(facesContext);
240             
241             Boolean automaticExtensionlessMapping = WebConfigParamUtils.getBooleanInitParameter(
242                     externalContext, INIT_PARAM_AUTOMATIC_EXTENSIONLESS_MAPPING, 
243                     INIT_PARAM_AUTOMATIC_EXTENSIONLESS_MAPPING_DEFAULT);
244             if (Boolean.TRUE.equals(automaticExtensionlessMapping))
245             {
246                 initAutomaticExtensionlessMapping(facesContext, servletContext);
247             }
248 
249             // print out a very prominent log message if the project stage is != Production
250             if (!facesContext.isProjectStage(ProjectStage.Production) &&
251                 !facesContext.isProjectStage(ProjectStage.UnitTest))
252             {
253                 ProjectStage projectStage = facesContext.getApplication().getProjectStage();
254                 StringBuilder message = new StringBuilder("\n\n");
255                 message.append("*******************************************************************\n");
256                 message.append("*** WARNING: Apache MyFaces-2 is running in ");
257                 message.append(projectStage.name().toUpperCase());        
258                 message.append(" mode.");
259                 int length = projectStage.name().length();
260                 for (int i = 0; i < 11 - length; i++)
261                 {
262                     message.append(" ");
263                 }
264                 message.append("   ***\n");
265                 message.append("***                                         ");
266                 for (int i = 0; i < length; i++)
267                 {
268                     message.append("^");
269                 }
270                 for (int i = 0; i < 20 - length; i++)
271                 {
272                     message.append(" ");
273                 }
274                 message.append("***\n");
275                 message.append("*** Do NOT deploy to your live server(s) without changing this. ***\n");
276                 message.append("*** See Application#getProjectStage() for more information.     ***\n");
277                 message.append("*******************************************************************\n");
278                 log.log(Level.WARNING, message.toString());
279             }
280 
281         }
282         catch (Exception ex)
283         {
284             log.log(Level.SEVERE, "An error occured while initializing MyFaces: "
285                       + ex.getMessage(), ex);
286         }
287     }
288     
289     /**
290      * Checks for application scoped managed-beans with eager=true,
291      * creates them and stores them in the application map.
292      * @param facesContext
293      */
294     private void _createEagerBeans(FacesContext facesContext)
295     {
296         ExternalContext externalContext = facesContext.getExternalContext();
297         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
298         List<ManagedBean> eagerBeans = new ArrayList<ManagedBean>();
299         
300         // check all registered managed-beans
301         for (ManagedBean bean : runtimeConfig.getManagedBeans().values())
302         {
303             String eager = bean.getEager();
304             if (eager != null && "true".equals(eager))
305             {
306                 // eager beans are only allowed for application scope
307                 if (ManagedBeanBuilder.APPLICATION.equals(bean.getManagedBeanScope()))
308                 {
309                     // add to eager beans
310                     eagerBeans.add(bean);
311                 }
312                 else
313                 {
314                     // log warning and continue (the bean will be lazy loaded)
315                     log.log(Level.WARNING, "The managed-bean with name "
316                             + bean.getManagedBeanName()
317                             + " must be application scoped to support eager=true.");
318                 }
319             }
320         }
321         
322         // check if there are any eager beans
323         if (!eagerBeans.isEmpty())
324         {
325             ManagedBeanBuilder managedBeanBuilder = new ManagedBeanBuilder();
326             Map<String, Object> applicationMap = externalContext.getApplicationMap();
327             
328             for (ManagedBean bean : eagerBeans)
329             {
330                 // check application scope for bean instance
331                 if (applicationMap.containsKey(bean.getManagedBeanName()))
332                 {
333                     // do not build bean, because it already exists
334                     // (e.g. @ManagedProperty from previous managed bean already created it)
335                     continue;
336                 }
337 
338                 // create instance
339                 Object beanInstance = managedBeanBuilder.buildManagedBean(facesContext, bean);
340                 
341                 // put in application scope
342                 applicationMap.put(bean.getManagedBeanName(), beanInstance);
343             }
344         }
345     }
346 
347     /**
348      * Eventually we can use our plugin infrastructure for this as well
349      * it would be a cleaner interception point than the base class
350      * but for now this position is valid as well
351      * <p/>
352      * Note we add it for now here because the application factory object
353      * leaves no possibility to have a destroy interceptor
354      * and applications are per web application singletons
355      * Note if this does not work out
356      * move the event handler into the application factory
357      *
358      * @param servletContext the servlet context to be passed down
359      * @param eventClass     the class to be passed down into the dispatching
360      *                       code
361      */
362     private void _dispatchApplicationEvent(ServletContext servletContext, Class<? extends SystemEvent> eventClass)
363     {
364         FacesContext facesContext = FacesContext.getCurrentInstance();
365         Application application = facesContext.getApplication();
366         application.publishEvent(facesContext, eventClass, Application.class, application);
367     }
368     
369     /**
370      * Cleans up all remaining resources (well, theoretically).
371      */
372     @Override
373     public void destroyFaces(ServletContext servletContext)
374     {
375 
376         FacesContext facesContext = FacesContext.getCurrentInstance();
377 
378         if (!WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
379                                                          INITIALIZE_ALWAYS_STANDALONE, false))
380         {
381             //We need to check if the current application was initialized by myfaces
382             WebConfigProvider webConfigProvider = WebConfigProviderFactory.getWebConfigProviderFactory(
383                     facesContext.getExternalContext()).getWebConfigProvider(facesContext.getExternalContext());
384 
385             if (webConfigProvider.getFacesServletMappings(facesContext.getExternalContext()).isEmpty())
386             {
387                 // check to see if the FacesServlet was found by MyFacesContainerInitializer
388                 Boolean mappingAdded = (Boolean) servletContext.getAttribute(
389                     MyFacesContainerInitializer.FACES_SERVLET_FOUND);
390 
391                 if (mappingAdded == null || !mappingAdded)
392                 {
393                     // check if the FacesServlet has been added dynamically
394                     // in a Servlet 3.0 environment by MyFacesContainerInitializer
395                     mappingAdded = (Boolean) servletContext.getAttribute(
396                         MyFacesContainerInitializer.FACES_SERVLET_ADDED_ATTRIBUTE);
397 
398                     if (mappingAdded == null || !mappingAdded)
399                     {
400                         if (log.isLoggable(Level.WARNING))
401                         {
402                             log.warning("No mappings of FacesServlet found. Abort destroy MyFaces.");
403                         }
404                         return;
405                     }
406                 }
407             }
408         }
409 
410         _dispatchApplicationEvent(servletContext, PreDestroyApplicationEvent.class);
411 
412         _callPreDestroyOnInjectedJSFArtifacts(facesContext);
413         
414         // clear the cache of MetaRulesetImpl in order to prevent a memory leak
415         MetaRulesetImpl.clearMetadataTargetCache();
416         
417         if (facesContext.getExternalContext().getApplicationMap().containsKey("org.apache.myfaces.push"))
418         {
419             WebsocketFacesInit.clearWebsocketSessionLRUCache(facesContext.getExternalContext());
420         }
421         
422         // clear UIViewParameter default renderer map
423         try
424         {
425             Class<?> c = Class.forName("javax.faces.component.UIViewParameter");
426             Method m = c.getDeclaredMethod("releaseRenderer");
427             m.setAccessible(true);
428             m.invoke(null);
429         }
430         catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e)
431         {
432             log.log(Level.SEVERE, e.getMessage(), e);
433         }
434 
435 
436         // TODO is it possible to make a real cleanup?
437     }
438 
439     /**
440      * Configures this JSF application. It's required that every
441      * FacesInitializer (i.e. every subclass) calls this method during
442      * initialization.
443      *
444      * @param servletContext    the current ServletContext
445      * @param externalContext   the current ExternalContext
446      * @param expressionFactory the ExpressionFactory to use
447      * @return the current runtime configuration
448      */
449     protected RuntimeConfig buildConfiguration(ServletContext servletContext,
450                                                ExternalContext externalContext, ExpressionFactory expressionFactory)
451     {
452         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
453         runtimeConfig.setExpressionFactory(expressionFactory);
454 
455         // And configure everything
456         new FacesConfigurator(externalContext).configure();
457 
458         validateFacesConfig(servletContext, externalContext);
459 
460         return runtimeConfig;
461     }
462 
463     protected void validateFacesConfig(ServletContext servletContext, ExternalContext externalContext)
464     {
465         String validate = servletContext.getInitParameter(FacesConfigValidator.VALIDATE_CONTEXT_PARAM);
466         if ("true".equals(validate) && log.isLoggable(Level.WARNING))
467         { // the default value is false
468             List<String> warnings = FacesConfigValidator.validate(
469                     externalContext);
470 
471             for (String warning : warnings)
472             {
473                 log.warning(warning);
474             }
475         }
476     }
477 
478     /**
479      * Try to load user-definied ExpressionFactory. Returns <code>null</code>,
480      * if no custom ExpressionFactory was specified.
481      *
482      * @param externalContext the current ExternalContext
483      * @return User-specified ExpressionFactory, or
484      *         <code>null</code>, if no no custom implementation was specified
485      */
486     protected static ExpressionFactory getUserDefinedExpressionFactory(ExternalContext externalContext)
487     {
488         String expressionFactoryClassName
489                 = WebConfigParamUtils.getStringInitParameter(externalContext, EXPRESSION_FACTORY);
490         if (expressionFactoryClassName != null
491                 && expressionFactoryClassName.trim().length() > 0)
492         {
493             if (log.isLoggable(Level.FINE))
494             {
495                 log.fine("Attempting to load the ExpressionFactory implementation "
496                         + "you've specified: '" + expressionFactoryClassName + "'.");
497             }
498 
499             return loadExpressionFactory(expressionFactoryClassName);
500         }
501 
502         return null;
503     }
504 
505     /**
506      * Loads and instantiates the given ExpressionFactory implementation.
507      *
508      * @param expressionFactoryClassName the class name of the ExpressionFactory implementation
509      * @return the newly created ExpressionFactory implementation, or
510      *         <code>null</code>, if an error occurred
511      */
512     protected static ExpressionFactory loadExpressionFactory(String expressionFactoryClassName)
513     {
514         return loadExpressionFactory(expressionFactoryClassName, true);
515     }
516     
517     protected static ExpressionFactory loadExpressionFactory(String expressionFactoryClassName, boolean logMissing)
518     {
519         try
520         {
521             ClassLoader cl = ClassUtils.getContextClassLoader();
522             if (cl == null)
523             {
524                 cl = AbstractFacesInitializer.class.getClassLoader();
525             }
526 
527             Class<?> expressionFactoryClass = cl.loadClass(expressionFactoryClassName);
528             return (ExpressionFactory) expressionFactoryClass.newInstance();
529         }
530         catch (Exception ex)
531         {
532             if (log.isLoggable(Level.FINE))
533             {
534                 log.log(Level.FINE, "An error occured while instantiating a new ExpressionFactory. "
535                         + "Attempted to load class '" + expressionFactoryClassName + "'.", ex);
536             }
537         }
538 
539         return null;
540     }
541 
542     @Override
543     public FacesContext initStartupFacesContext(ServletContext servletContext)
544     {
545         // We cannot use FacesContextFactory, because it is necessary to initialize 
546         // before Application and RenderKit factories, so we should use different object. 
547         return _createFacesContext(servletContext, true);
548     }
549         
550     @Override
551     public void destroyStartupFacesContext(FacesContext facesContext)
552     {
553         _releaseFacesContext(facesContext);
554     }
555     
556     @Override
557     public FacesContext initShutdownFacesContext(ServletContext servletContext)
558     {
559         return _createFacesContext(servletContext, false);
560     }
561     
562     @Override    
563     public void destroyShutdownFacesContext(FacesContext facesContext)
564     {
565         _releaseFacesContext(facesContext);
566     }
567     
568     private FacesContext _createFacesContext(ServletContext servletContext, boolean startup)
569     {
570         ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, startup);
571         ExceptionHandler exceptionHandler = new ExceptionHandlerImpl();
572         FacesContext facesContext = new StartupFacesContextImpl(externalContext, 
573                 (ReleaseableExternalContext) externalContext, exceptionHandler, startup);
574         
575         // If getViewRoot() is called during application startup or shutdown, 
576         // it should return a new UIViewRoot with its locale set to Locale.getDefault().
577         UIViewRoot startupViewRoot = new UIViewRoot();
578         startupViewRoot.setLocale(Locale.getDefault());
579         facesContext.setViewRoot(startupViewRoot);
580         
581         return facesContext;
582     }
583     
584     private void _releaseFacesContext(FacesContext facesContext)
585     {        
586         // make sure that the facesContext gets released.
587         // This is important in an OSGi environment 
588         if (facesContext != null)
589         {
590             facesContext.release();
591         }        
592     }
593     
594     /**
595      * Performs initialization tasks depending on the current environment.
596      *
597      * @param servletContext  the current ServletContext
598      * @param externalContext the current ExternalContext
599      */
600     protected abstract void initContainerIntegration(
601             ServletContext servletContext, ExternalContext externalContext);
602 
603     /**
604      * The intention of this method is provide a point where CDI integration is done.
605      * Faces Flow and javax.faces.view.ViewScope requires CDI in order to work, so
606      * this method should set a BeanManager instance on application map under
607      * the key "oam.cdi.BEAN_MANAGER_INSTANCE". The default implementation look on
608      * ServletContext first and then use JNDI.
609      * 
610      * @param servletContext
611      * @param externalContext 
612      */
613     protected void initCDIIntegration(
614             ServletContext servletContext, ExternalContext externalContext)
615     {
616         // Lookup bean manager and put it into an application scope attribute to 
617         // access it later. Remember the trick here is do not call any CDI api 
618         // directly, so if no CDI api is on the classpath no exception will be thrown.
619         
620         // Try with servlet context
621         Object beanManager = servletContext.getAttribute(
622             CDI_SERVLET_CONTEXT_BEAN_MANAGER_ATTRIBUTE);
623         if (beanManager == null)
624         {
625             beanManager = lookupBeanManagerFromCDI();
626         }
627         if (beanManager == null)
628         {
629             beanManager = lookupBeanManagerFromJndi();
630         }
631         if (beanManager != null)
632         {
633             externalContext.getApplicationMap().put(CDI_BEAN_MANAGER_INSTANCE,
634                 beanManager);
635         }
636     }
637 
638     /**
639      * This method tries to use the CDI-1.1 CDI.current() method to lookup the CDI BeanManager.
640      * We do all this via reflection to not blow up if CDI-1.1 is not on the classpath.
641      * @return the BeanManager or {@code null} if either not in a CDI-1.1 environment
642      *         or the BeanManager doesn't exist yet.
643      */
644     private Object lookupBeanManagerFromCDI()
645     {
646         try
647         {
648             Class cdiClass = null;
649             Method cdiCurrentMethod = null;
650             Method cdiGetBeanManagerMethod = null;
651             cdiClass = simpleClassForNameNoException("javax.enterprise.inject.spi.CDI");
652             if (cdiClass != null)
653             {
654                 cdiCurrentMethod = cdiClass.getMethod("current");
655 
656                 Object cdiInstance = cdiCurrentMethod.invoke(null);
657 
658                 cdiGetBeanManagerMethod = cdiClass.getMethod("getBeanManager");
659                 return cdiGetBeanManagerMethod.invoke(cdiInstance);
660             }
661         }
662         catch (Exception e)
663         {
664             // ignore
665         }
666         return null;
667     }
668     
669     private static Class simpleClassForNameNoException(String type)
670     {
671         try
672         {
673             return ClassUtils.classForName(type);
674         }
675         catch (ClassNotFoundException e)
676         {
677             //log.log(Level.SEVERE, "Class " + type + " not found", e);
678             //Ignore
679             return null;
680         }
681     }
682 
683     /**
684      * Try to lookup the CDI BeanManager from JNDI.
685      * We do all this via reflection to not blow up if CDI is not available.
686      */
687     private Object lookupBeanManagerFromJndi()
688     {
689         Object beanManager = null;
690         // Use reflection to avoid restricted API in GAE
691         Class icclazz = null;
692         Method lookupMethod = null;
693         try
694         {
695             icclazz = ClassUtils.simpleClassForName("javax.naming.InitialContext");
696             if (icclazz != null)
697             {
698                 lookupMethod = icclazz.getMethod("doLookup", String.class);
699             }
700         }
701         catch (Throwable t)
702         {
703             //
704         }
705         if (lookupMethod != null)
706         {
707             // Try with JNDI
708             try
709             {
710                 // in an application server
711                 //beanManager = InitialContext.doLookup("java:comp/BeanManager");
712                 beanManager = lookupMethod.invoke(icclazz, "java:comp/BeanManager");
713             }
714             catch (Exception e)
715             {
716                 // silently ignore
717             }
718             catch (NoClassDefFoundError e)
719             {
720                 //On Google App Engine, javax.naming.Context is a restricted class.
721                 //In that case, NoClassDefFoundError is thrown. stageName needs to be configured
722                 //below by context parameter.
723             }
724 
725             if (beanManager == null)
726             {
727                 try
728                 {
729                     // in a servlet container
730                     //beanManager = InitialContext.doLookup("java:comp/env/BeanManager");
731                     beanManager = lookupMethod.invoke(icclazz, "java:comp/env/BeanManager");
732                 }
733                 catch (Exception e)
734                 {
735                     // silently ignore
736                 }
737                 catch (NoClassDefFoundError e)
738                 {
739                     //On Google App Engine, javax.naming.Context is a restricted class.
740                     //In that case, NoClassDefFoundError is thrown. stageName needs to be configured
741                     //below by context parameter.
742                 }
743             }
744         }
745 
746         return beanManager;
747     }
748 
749     public void _callPreDestroyOnInjectedJSFArtifacts(FacesContext facesContext)
750     {
751         InjectionProvider injectionProvider = InjectionProviderFactory.getInjectionProviderFactory(
752             facesContext.getExternalContext()).getInjectionProvider(facesContext.getExternalContext());
753         List<BeanEntry> injectedBeanStorage =
754                 (List<BeanEntry>)facesContext.getExternalContext().getApplicationMap().get(INJECTED_BEAN_STORAGE_KEY);
755 
756         if (injectedBeanStorage != null)
757         {
758             for (BeanEntry entry : injectedBeanStorage)
759             {
760                 try
761                 {
762                     injectionProvider.preDestroy(entry.getInstance(), entry.getCreationMetaData());
763                 }
764                 catch (InjectionProviderException ex)
765                 {
766                     log.log(Level.INFO, "Exception on PreDestroy", ex);
767                 }
768             }
769             injectedBeanStorage.clear();
770         }
771     }
772     
773     protected void initWebsocketIntegration(
774             ServletContext servletContext, ExternalContext externalContext)
775     {
776         Boolean b = WebConfigParamUtils.getBooleanInitParameter(externalContext, 
777                 PushContext.ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME);
778         
779         if (Boolean.TRUE.equals(b))
780         {
781             // According to https://tyrus.java.net/documentation/1.13/index/deployment.html section 3.2
782             // we can create a websocket programmatically, getting ServerContainer instance from this location
783             final ServerContainer serverContainer = (ServerContainer) 
784                     servletContext.getAttribute("javax.websocket.server.ServerContainer");
785 
786             if (serverContainer != null)
787             {
788                 try 
789                 {
790                     serverContainer.addEndpoint(ServerEndpointConfig.Builder
791                             .create(EndpointImpl.class, EndpointImpl.JAVAX_FACES_PUSH_PATH)
792                             .configurator(new WebsocketConfigurator(externalContext)).build());
793                     
794                     //Init LRU cache
795                     WebsocketFacesInit.initWebsocketSessionLRUCache(externalContext);
796                     
797                     externalContext.getApplicationMap().put("org.apache.myfaces.push", "true");
798                 }
799                 catch (DeploymentException e)
800                 {
801                     log.log(Level.INFO, "Exception on Initialize Websocket Endpoint: ", e);
802                 }
803             }
804             else
805             {
806                 log.log(Level.INFO, "f:websocket support enabled but cannot found websocket ServerContainer instance "+
807                         "on current context. If websocket library is available, please include a FakeEndpoint instance "
808                         + "into your code to force enable it (Tyrus users).");
809             }
810         }
811     }
812     
813     /**
814      * 
815      * @since 2.3
816      * @param facesContext 
817      */
818     protected void initAutomaticExtensionlessMapping(FacesContext facesContext, ServletContext servletContext)
819     {
820         final ServletRegistration facesServletRegistration = getFacesServletRegistration(facesContext, servletContext); 
821         if (facesServletRegistration != null)
822         {
823             facesContext.getApplication().getViewHandler().getViews(facesContext, "/", 
824                     ViewVisitOption.RETURN_AS_MINIMAL_IMPLICIT_OUTCOME).forEach(s -> {
825                         facesServletRegistration.addMapping(s);
826                     });
827         }
828     }
829     
830     private ServletRegistration getFacesServletRegistration(FacesContext facesContext, 
831             ServletContext servletContext)
832     {
833         ServletRegistration facesServletRegistration = null;
834         Map<String, ? extends ServletRegistration> map = servletContext.getServletRegistrations();
835         if (map != null)
836         {
837             for (Map.Entry<String, ? extends ServletRegistration> entry : map.entrySet())
838             {
839                 if (FacesServletMappingUtils.isFacesServlet(facesContext, entry.getValue().getClassName()))
840                 {
841                     facesServletRegistration = entry.getValue();
842                     break;
843                 }
844             }
845         }
846         return facesServletRegistration;
847     }
848     
849     protected void checkForDeprecatedContextParams(FacesContext facesContext)
850     {
851         ExternalContext externalContext = facesContext.getExternalContext();
852         
853         String value;
854         
855         value = externalContext.getInitParameter("org.apache.myfaces.CDI_MANAGED_CONVERTERS_ENABLED");
856         if (value != null && !value.isEmpty())
857         {
858             log.severe("'org.apache.myfaces.CDI_MANAGED_CONVERTERS_ENABLED' is not supported anymore since 2.3. "
859                     + "Please use @FacesConverter with managed=true.");
860         }
861         
862         value = externalContext.getInitParameter("org.apache.myfaces.CDI_MANAGED_VALIDATORS_ENABLED");
863         if (value != null && !value.isEmpty())
864         {
865             log.severe("'org.apache.myfaces.CDI_MANAGED_VALIDATORS_ENABLED' is not supported anymore since 2.3. "
866                     + "Please use @FacesValidator with managed=true.");
867         }
868         
869         value = externalContext.getInitParameter("org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS");
870         if (value != null && !value.isEmpty())
871         {
872             log.severe("'org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS' is not supported anymore since 2.3.");
873         }
874         
875         value = externalContext.getInitParameter("org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE");
876         if (value != null && !value.isEmpty())
877         {
878             log.severe("'org.apache.myfaces.CACHE_OLD_VIEWS_IN_SESSION_MODE' is not supported anymore since 2.3.");
879         }
880         
881         value = externalContext.getInitParameter("org.apache.myfaces.HANDLE_STATE_CACHING_MECHANICS");
882         if (value != null && !value.isEmpty())
883         {
884             log.severe("'org.apache.myfaces.HANDLE_STATE_CACHING_MECHANICS' is not supported anymore since 2.3.");
885         }
886         
887         value = externalContext.getInitParameter("org.apache.myfaces.ERROR_HANDLER");
888         if (value != null && !value.isEmpty())
889         {
890             log.severe("'org.apache.myfaces.ERROR_HANDLER' is not supported anymore since 2.3.");
891         }
892         
893         value = externalContext.getInitParameter("org.apache.myfaces.STRICT_JSF_2_REFRESH_TARGET_AJAX");
894         if (value != null && !value.isEmpty())
895         {
896             log.severe("'org.apache.myfaces.STRICT_JSF_2_REFRESH_TARGET_AJAX' is not supported anymore since 2.3.");
897         }
898 
899         value = externalContext.getInitParameter("org.apache.myfaces.ALLOW_JAVASCRIPT");
900         if (value != null && !value.isEmpty())
901         {
902             log.severe("'org.apache.myfaces.ALLOW_JAVASCRIPT' is not supported anymore.");
903         }
904 
905         value = externalContext.getInitParameter("org.apache.myfaces.VIEWSTATE_JAVASCRIPT");
906         if (value != null && !value.isEmpty())
907         {
908             log.severe("'org.apache.myfaces.VIEWSTATE_JAVASCRIPT' is not supported anymore.");
909         }
910 
911         value = externalContext.getInitParameter("org.apache.myfaces.PRETTY_HTML");
912         if (value != null && !value.isEmpty())
913         {
914             log.severe("'org.apache.myfaces.PRETTY_HTML' is not supported anymore.");
915         }
916 
917         value = externalContext.getInitParameter("org.apache.myfaces.RENDER_FORM_SUBMIT_SCRIPT_INLINE");
918         if (value != null && !value.isEmpty())
919         {
920             log.severe("'org.apache.myfaces.RENDER_FORM_SUBMIT_SCRIPT_INLINE' is not supported anymore.");
921         }
922     }
923 }