Coverage Report - org.apache.myfaces.ee6.MyFacesContainerInitializer
 
Classes in this File Line Coverage Branch Coverage Complexity
MyFacesContainerInitializer
0%
0/62
0%
0/40
7.8
 
 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.ee6;
 20  
 
 21  
 import java.net.URL;
 22  
 import java.util.Arrays;
 23  
 import java.util.Collection;
 24  
 import java.util.HashSet;
 25  
 import java.util.Map;
 26  
 import java.util.Set;
 27  
 import java.util.logging.Level;
 28  
 import java.util.logging.Logger;
 29  
 
 30  
 import javax.faces.application.ResourceDependencies;
 31  
 import javax.faces.application.ResourceDependency;
 32  
 import javax.faces.bean.ApplicationScoped;
 33  
 import javax.faces.bean.CustomScoped;
 34  
 import javax.faces.bean.ManagedBean;
 35  
 import javax.faces.bean.ManagedProperty;
 36  
 import javax.faces.bean.NoneScoped;
 37  
 import javax.faces.bean.ReferencedBean;
 38  
 import javax.faces.bean.RequestScoped;
 39  
 import javax.faces.bean.SessionScoped;
 40  
 import javax.faces.bean.ViewScoped;
 41  
 import javax.faces.component.FacesComponent;
 42  
 import javax.faces.component.UIComponent;
 43  
 import javax.faces.component.behavior.FacesBehavior;
 44  
 import javax.faces.context.ExternalContext;
 45  
 import javax.faces.convert.Converter;
 46  
 import javax.faces.convert.FacesConverter;
 47  
 import javax.faces.event.ListenerFor;
 48  
 import javax.faces.event.ListenersFor;
 49  
 import javax.faces.event.NamedEvent;
 50  
 import javax.faces.render.FacesBehaviorRenderer;
 51  
 import javax.faces.render.FacesRenderer;
 52  
 import javax.faces.render.Renderer;
 53  
 import javax.faces.validator.FacesValidator;
 54  
 import javax.faces.validator.Validator;
 55  
 import javax.faces.webapp.FacesServlet;
 56  
 import javax.servlet.Servlet;
 57  
 import javax.servlet.ServletContainerInitializer;
 58  
 import javax.servlet.ServletContext;
 59  
 import javax.servlet.ServletException;
 60  
 import javax.servlet.ServletRegistration;
 61  
 import javax.servlet.annotation.HandlesTypes;
 62  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 63  
 
 64  
 import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl;
 65  
 import org.apache.myfaces.shared_impl.webapp.webxml.DelegatedFacesServlet;
 66  
 import org.apache.myfaces.spi.FacesConfigResourceProvider;
 67  
 import org.apache.myfaces.spi.FacesConfigResourceProviderFactory;
 68  
 
 69  
 /**
 70  
  * This class is called by any Java EE 6 complaint container at startup.
 71  
  * It checks if the current webapp is a JSF-webapp by checking if some of 
 72  
  * the JSF related annotations are specified in the webapp classpath or if
 73  
  * the faces-config.xml file is present. If so, the listener checks if 
 74  
  * the FacesServlet has already been defined in web.xml and if not, it adds
 75  
  * the FacesServlet with the mappings (/faces/*, *.jsf, *.faces) dynamically.
 76  
  * 
 77  
  * @author Jakob Korherr (latest modification by $Author$)
 78  
  * @version $Revision$ $Date$
 79  
  */
 80  
 @HandlesTypes({
 81  
         ApplicationScoped.class,
 82  
         CustomScoped.class,
 83  
         FacesBehavior.class,
 84  
         FacesBehaviorRenderer.class,
 85  
         FacesComponent.class,
 86  
         FacesConverter.class,
 87  
         FacesRenderer.class,
 88  
         FacesValidator.class,
 89  
         ListenerFor.class,
 90  
         ListenersFor.class,
 91  
         ManagedBean.class,
 92  
         ManagedProperty.class,
 93  
         NamedEvent.class,
 94  
         NoneScoped.class,
 95  
         ReferencedBean.class,
 96  
         RequestScoped.class,
 97  
         ResourceDependencies.class,
 98  
         ResourceDependency.class,
 99  
         SessionScoped.class,
 100  
         ViewScoped.class,
 101  
         UIComponent.class,
 102  
         Converter.class,
 103  
         Renderer.class,
 104  
         Validator.class
 105  
     })
 106  0
 public class MyFacesContainerInitializer implements ServletContainerInitializer
 107  
 {
 108  
 
 109  
     /**
 110  
      * If the servlet mapping for the FacesServlet is added dynamically, Boolean.TRUE 
 111  
      * is stored under this key in the ServletContext.
 112  
      * ATTENTION: this constant is duplicate in AbstractFacesInitializer.
 113  
      */
 114  
     private static final String FACES_SERVLET_ADDED_ATTRIBUTE = "org.apache.myfaces.DYNAMICALLY_ADDED_FACES_SERVLET";
 115  
     
 116  
     /**
 117  
      * If the servlet mapping for the FacesServlet is found on the ServletContext, Boolean.TRUE 
 118  
      * is stored under this key in the ServletContext.
 119  
      * ATTENTION: this constant is duplicate in AbstractFacesInitializer.
 120  
      */
 121  
     private static final String FACES_SERVLET_FOUND = "org.apache.myfaces.FACES_SERVLET_FOUND"; 
 122  
 
 123  
     private static final String INITIALIZE_ALWAYS_STANDALONE = "org.apache.myfaces.INITIALIZE_ALWAYS_STANDALONE";
 124  
     
 125  
     /**
 126  
      * If the flag is true, the algoritm skip jar scanning for faces-config files to check if the current
 127  
      * application requires FacesServlet to be added dynamically (servlet spec 3). This param can be set using 
 128  
      * a system property with the same name too.
 129  
      */
 130  
     @JSFWebConfigParam(since="2.2.10", expectedValues = "true, false", defaultValue = "false", 
 131  
             tags = "performance")
 132  
     private static final String INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN = 
 133  
             "org.apache.myfaces.INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN";
 134  
     
 135  
     private static final String FACES_CONFIG_RESOURCE = "/WEB-INF/faces-config.xml";
 136  0
     private static final Logger log = Logger.getLogger(MyFacesContainerInitializer.class.getName());
 137  0
     private static final String[] FACES_SERVLET_MAPPINGS = { "/faces/*", "*.jsf", "*.faces" };
 138  
     private static final String FACES_SERVLET_NAME = "FacesServlet";
 139  0
     private static final Class<? extends Servlet> FACES_SERVLET_CLASS = FacesServlet.class;
 140  0
     private static final Class<?> DELEGATED_FACES_SERVLET_CLASS = DelegatedFacesServlet.class;
 141  
 
 142  
     public void onStartup(Set<Class<?>> clazzes, ServletContext servletContext) throws ServletException
 143  
     {
 144  0
         boolean startDireclty = shouldStartupRegardless(servletContext);
 145  
 
 146  0
         if (startDireclty)
 147  
         {
 148  
             // if the INITIALIZE_ALWAYS_STANDALONE param was set to true,
 149  
             // we do not want to have the FacesServlet being added, we simply 
 150  
             // do no extra configuration in here.
 151  0
             return;
 152  
         }
 153  
 
 154  
         // Check for one or more of this conditions:
 155  
         // 1. A faces-config.xml file is found in WEB-INF
 156  
         // 2. A faces-config.xml file is found in the META-INF directory of a jar in the application's classpath.
 157  
         // 3. A filename ending in .faces-config.xml is found in the META-INF directory of a jar in the 
 158  
         //    application's classpath.
 159  
         // 4. The javax.faces.CONFIG_FILES context param is declared in web.xml or web-fragment.xml.
 160  
         // 5. The Set of classes passed to the onStartup() method of the ServletContainerInitializer 
 161  
         //    implementation is not empty.
 162  0
         if ((clazzes != null && !clazzes.isEmpty()) || isFacesConfigPresent(servletContext))
 163  
         {
 164  
             // look for the FacesServlet
 165  0
             Map<String, ? extends ServletRegistration> servlets = servletContext.getServletRegistrations();
 166  0
             for (Map.Entry<String, ? extends ServletRegistration> servletEntry : servlets.entrySet())
 167  
             {
 168  0
                 String className = servletEntry.getValue().getClassName();
 169  0
                 if (FACES_SERVLET_CLASS.getName().equals(className)
 170  
                         || isDelegatedFacesServlet(className))
 171  
                 {
 172  
                     // we found a FacesServlet; set an attribute for use during initialization
 173  0
                     servletContext.setAttribute(FACES_SERVLET_FOUND, Boolean.TRUE);                    
 174  0
                     return;
 175  
                 }
 176  0
             }
 177  
 
 178  
             // the FacesServlet is not installed yet - install it
 179  0
             ServletRegistration.Dynamic servlet = servletContext.addServlet(FACES_SERVLET_NAME, FACES_SERVLET_CLASS);
 180  
 
 181  
             //try to add typical JSF mappings
 182  0
             String[] mappings = FACES_SERVLET_MAPPINGS;
 183  0
             Set<String> conflictMappings = servlet.addMapping(mappings);
 184  0
             if (conflictMappings != null && !conflictMappings.isEmpty())
 185  
             {
 186  
                 //at least one of the attempted mappings is in use, remove and try again
 187  0
                 Set<String> newMappings = new HashSet<String>(Arrays.asList(mappings));
 188  0
                 newMappings.removeAll(conflictMappings);
 189  0
                 mappings = newMappings.toArray(new String[newMappings.size()]);
 190  0
                 servlet.addMapping(mappings);
 191  
             }
 192  
 
 193  0
             if (mappings != null && mappings.length > 0)
 194  
             {
 195  
                 // at least one mapping was added 
 196  
                 // now we have to set a field in the ServletContext to indicate that we have
 197  
                 // added the mapping dynamically, because MyFaces just parsed the web.xml to
 198  
                 // find mappings and thus it would abort initializing
 199  0
                 servletContext.setAttribute(FACES_SERVLET_ADDED_ATTRIBUTE, Boolean.TRUE);
 200  
 
 201  
                 // add a log message
 202  0
                 log.log(Level.INFO, "Added FacesServlet with mappings="
 203  
                         + Arrays.toString(mappings));
 204  
             }
 205  
 
 206  
         }
 207  0
     }
 208  
 
 209  
     /**
 210  
      * Checks if the <code>INITIALIZE_ALWAYS_STANDALONE</code> flag is ture in <code>web.xml</code>.
 211  
      * If the flag is true, this means we should not add the FacesServlet, instead we want to
 212  
      * init MyFaces regardless...
 213  
      */
 214  
     private boolean shouldStartupRegardless(ServletContext servletContext)
 215  
     {
 216  
         try
 217  
         {
 218  0
             String standaloneStartup = servletContext.getInitParameter(INITIALIZE_ALWAYS_STANDALONE);
 219  
 
 220  
             // "true".equalsIgnoreCase(param) is faster than Boolean.valueOf()
 221  0
             return "true".equalsIgnoreCase(standaloneStartup);
 222  
         }
 223  0
         catch (Exception e)
 224  
         {
 225  0
             return false;
 226  
         }
 227  
     }
 228  
 
 229  
     /**
 230  
      * Checks if the <code>INITIALIZE_SCAN_JARS_FOR_FACES_CONFIG</code> flag is true in <code>web.xml</code>.
 231  
      * If the flag is true, this means we should scan app jars for *.faces-config.xml before adding
 232  
      * any FacesServlet; in false, we skip that scan for performance.
 233  
      */
 234  
     private boolean shouldSkipJarFacesConfigScan(ServletContext servletContext)
 235  
     {
 236  
         try
 237  
         {
 238  0
             String skipJarScan = servletContext.getInitParameter(INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN);
 239  
 
 240  0
             if (skipJarScan == null)
 241  
             {
 242  0
                 skipJarScan = System.getProperty(INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN);
 243  
             }
 244  0
             return "true".equalsIgnoreCase(skipJarScan);
 245  
         }
 246  0
         catch (Exception e)
 247  
         {
 248  0
             return false;
 249  
         }
 250  
     }
 251  
     
 252  
     /**
 253  
      * Checks if /WEB-INF/faces-config.xml is present.
 254  
      * @return
 255  
      */
 256  
     private boolean isFacesConfigPresent(ServletContext servletContext)
 257  
     {
 258  
         try
 259  
         {
 260  
             // 1. A faces-config.xml file is found in WEB-INF
 261  0
             if (servletContext.getResource(FACES_CONFIG_RESOURCE) != null)
 262  
             {
 263  0
                 return true;
 264  
             }
 265  
 
 266  
             // 4. The javax.faces.CONFIG_FILES context param is declared in web.xml or web-fragment.xml.
 267  
             // check for alternate faces-config files specified by javax.faces.CONFIG_FILES
 268  0
             String configFilesAttrValue = servletContext.getInitParameter(FacesServlet.CONFIG_FILES_ATTR);
 269  0
             if (configFilesAttrValue != null)
 270  
             {
 271  0
                 String[] configFiles = configFilesAttrValue.split(",");
 272  0
                 for (String file : configFiles)
 273  
                 {
 274  0
                     if (servletContext.getResource(file.trim()) != null)
 275  
                     {
 276  0
                         return true;
 277  
                     }
 278  
                 }
 279  
             }
 280  
 
 281  
             // Skip this scan - for performance - if INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN is set to true 
 282  
             // 2. A faces-config.xml file is found in the META-INF directory of a jar in the 
 283  
             //    application's classpath.
 284  
             // 3. A filename ending in .faces-config.xml is found in the META-INF directory of a jar in 
 285  
             //    the application's classpath.
 286  
             // To do this properly it is necessary to use some SPI interfaces MyFaces already has, to 
 287  
             // deal with OSGi and other
 288  
             // environments properly.
 289  0
             if (!shouldSkipJarFacesConfigScan(servletContext)) 
 290  
             {
 291  0
                 ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, true);
 292  0
                 FacesConfigResourceProviderFactory factory = FacesConfigResourceProviderFactory.
 293  
                     getFacesConfigResourceProviderFactory(externalContext);
 294  0
                 FacesConfigResourceProvider provider = factory.createFacesConfigResourceProvider(externalContext);
 295  0
                 Collection<URL> metaInfFacesConfigUrls =  provider.getMetaInfConfigurationResources(externalContext);
 296  
                 
 297  0
                 if (metaInfFacesConfigUrls != null && !metaInfFacesConfigUrls.isEmpty())
 298  
                 {
 299  0
                     return true;
 300  
                 }
 301  
             }
 302  0
             return false;
 303  
         }
 304  0
         catch (Exception e)
 305  
         {
 306  0
             return false;
 307  
         }
 308  
     }
 309  
 
 310  
     /**
 311  
      * Checks if the class represented by className implements DelegatedFacesServlet.
 312  
      * @param className
 313  
      * @return
 314  
      */
 315  
     private boolean isDelegatedFacesServlet(String className)
 316  
     {
 317  0
         if (className == null)
 318  
         {
 319  
             // The class name can be null if this is e.g., a JSP mapped to
 320  
             // a servlet.
 321  
 
 322  0
             return false;
 323  
         }
 324  
         try
 325  
         {
 326  0
             Class<?> clazz = Class.forName(className);
 327  0
             return DELEGATED_FACES_SERVLET_CLASS.isAssignableFrom(clazz);
 328  
         }
 329  0
         catch (ClassNotFoundException cnfe)
 330  
         {
 331  0
             return false;
 332  
         }
 333  
     }
 334  
 }