| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.myfaces.webapp; |
| |
| import java.net.URL; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.faces.application.ResourceDependencies; |
| import javax.faces.application.ResourceDependency; |
| import javax.faces.component.FacesComponent; |
| import javax.faces.component.UIComponent; |
| import javax.faces.component.behavior.FacesBehavior; |
| import javax.faces.context.ExternalContext; |
| import javax.faces.convert.Converter; |
| import javax.faces.convert.FacesConverter; |
| import javax.faces.event.ListenerFor; |
| import javax.faces.event.ListenersFor; |
| import javax.faces.event.NamedEvent; |
| import javax.faces.model.FacesDataModel; |
| import javax.faces.render.FacesBehaviorRenderer; |
| import javax.faces.render.FacesRenderer; |
| import javax.faces.render.Renderer; |
| import javax.faces.validator.FacesValidator; |
| import javax.faces.validator.Validator; |
| import javax.faces.webapp.FacesServlet; |
| import javax.servlet.Servlet; |
| import javax.servlet.ServletContainerInitializer; |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletRegistration; |
| import javax.servlet.annotation.HandlesTypes; |
| import org.apache.myfaces.config.MyfacesConfig; |
| |
| import org.apache.myfaces.context.servlet.StartupServletExternalContextImpl; |
| import org.apache.myfaces.spi.FacesConfigResourceProvider; |
| import org.apache.myfaces.spi.FacesConfigResourceProviderFactory; |
| import org.apache.myfaces.util.lang.ClassUtils; |
| |
| /** |
| * This class is called by any Java EE 6 complaint container at startup. |
| * It checks if the current webapp is a JSF-webapp by checking if some of |
| * the JSF related annotations are specified in the webapp classpath or if |
| * the faces-config.xml file is present. If so, the listener checks if |
| * the FacesServlet has already been defined in web.xml and if not, it adds |
| * the FacesServlet with the mappings (/faces/*, *.jsf, *.faces) dynamically. |
| * |
| * @author Jakob Korherr (latest modification by $Author$) |
| * @version $Revision$ $Date$ |
| */ |
| @HandlesTypes({ |
| FacesBehavior.class, |
| FacesBehaviorRenderer.class, |
| FacesComponent.class, |
| FacesConverter.class, |
| FacesRenderer.class, |
| FacesValidator.class, |
| FacesDataModel.class, |
| ListenerFor.class, |
| ListenersFor.class, |
| NamedEvent.class, |
| ResourceDependencies.class, |
| ResourceDependency.class, |
| UIComponent.class, |
| Converter.class, |
| Renderer.class, |
| Validator.class |
| }) |
| public class MyFacesContainerInitializer implements ServletContainerInitializer |
| { |
| |
| /** |
| * If the servlet mapping for the FacesServlet is added dynamically, Boolean.TRUE |
| * is stored under this key in the ServletContext. |
| */ |
| public static final String FACES_SERVLET_ADDED_ATTRIBUTE = "org.apache.myfaces.DYNAMICALLY_ADDED_FACES_SERVLET"; |
| |
| /** |
| * If the servlet mapping for the FacesServlet is found on the ServletContext, Boolean.TRUE |
| * is stored under this key in the ServletContext. |
| */ |
| public static final String FACES_SERVLET_FOUND = "org.apache.myfaces.FACES_SERVLET_FOUND"; |
| |
| private static final String FACES_CONFIG_RESOURCE = "/WEB-INF/faces-config.xml"; |
| private static final Logger log = Logger.getLogger(MyFacesContainerInitializer.class.getName()); |
| private static final String[] FACES_SERVLET_MAPPINGS = { "/faces/*", "*.jsf", "*.faces" }; |
| private static final String[] FACES_SERVLET_FULL_MAPPINGS = { "/faces/*", "*.jsf", "*.faces", "*.xhtml" }; |
| private static final String FACES_SERVLET_NAME = "FacesServlet"; |
| private static final Class<? extends Servlet> FACES_SERVLET_CLASS = FacesServlet.class; |
| private static final Class<?> DELEGATED_FACES_SERVLET_CLASS = DelegatedFacesServlet.class; |
| |
| @Override |
| public void onStartup(Set<Class<?>> clazzes, ServletContext servletContext) throws ServletException |
| { |
| log.log(Level.INFO, "Using " + this.getClass().getName()); |
| |
| |
| MyFacesHttpSessionListener httpSessionListener = new MyFacesHttpSessionListener(); |
| servletContext.addListener(httpSessionListener); |
| // Publishes the MyFacesHttpSessionListener instance into the servletContext. |
| // This allows the FacesConfigurator to access the instance and to set the |
| // correct ManagedBeanDestroyer instance on it. |
| servletContext.setAttribute(MyFacesHttpSessionListener.APPLICATION_MAP_KEY, httpSessionListener); |
| |
| |
| boolean startDireclty = shouldStartupRegardless(servletContext); |
| if (startDireclty) |
| { |
| // if the INITIALIZE_ALWAYS_STANDALONE param was set to true, |
| // we do not want to have the FacesServlet being added, we simply |
| // do no extra configuration in here. |
| return; |
| } |
| |
| // Check for one or more of this conditions: |
| // 1. A faces-config.xml file is found in WEB-INF |
| // 2. A faces-config.xml file is found in the META-INF directory of a jar in the application's classpath. |
| // 3. A filename ending in .faces-config.xml is found in the META-INF directory of a jar in the |
| // application's classpath. |
| // 4. The javax.faces.CONFIG_FILES context param is declared in web.xml or web-fragment.xml. |
| // 5. The Set of classes passed to the onStartup() method of the ServletContainerInitializer |
| // implementation is not empty. |
| if ((clazzes != null && !clazzes.isEmpty()) || isFacesConfigPresent(servletContext)) |
| { |
| // look for the FacesServlet |
| Map<String, ? extends ServletRegistration> servlets = servletContext.getServletRegistrations(); |
| for (Map.Entry<String, ? extends ServletRegistration> servletEntry : servlets.entrySet()) |
| { |
| String className = servletEntry.getValue().getClassName(); |
| if (FACES_SERVLET_CLASS.getName().equals(className) || isDelegatedFacesServlet(className)) |
| { |
| // we found a FacesServlet; set an attribute for use during initialization |
| servletContext.setAttribute(FACES_SERVLET_FOUND, Boolean.TRUE); |
| return; |
| } |
| } |
| |
| // the FacesServlet is not installed yet - install it |
| ServletRegistration.Dynamic servlet = servletContext.addServlet(FACES_SERVLET_NAME, FACES_SERVLET_CLASS); |
| |
| //try to add typical JSF mappings |
| String[] mappings = isAutomaticXhtmlMappingDisabled(servletContext) ? |
| FACES_SERVLET_MAPPINGS : FACES_SERVLET_FULL_MAPPINGS; |
| Set<String> conflictMappings = servlet.addMapping(mappings); |
| if (conflictMappings != null && !conflictMappings.isEmpty()) |
| { |
| //at least one of the attempted mappings is in use, remove and try again |
| Set<String> newMappings = new HashSet<>(Arrays.asList(mappings)); |
| newMappings.removeAll(conflictMappings); |
| mappings = newMappings.toArray(new String[newMappings.size()]); |
| servlet.addMapping(mappings); |
| } |
| |
| if (mappings != null && mappings.length > 0) |
| { |
| // at least one mapping was added |
| // now we have to set a field in the ServletContext to indicate that we have |
| // added the mapping dynamically, because MyFaces just parsed the web.xml to |
| // find mappings and thus it would abort initializing |
| servletContext.setAttribute(FACES_SERVLET_ADDED_ATTRIBUTE, Boolean.TRUE); |
| |
| // add a log message |
| log.log(Level.INFO, "Added FacesServlet with mappings=" + Arrays.toString(mappings)); |
| } |
| } |
| } |
| |
| /** |
| * Checks if the <code>INITIALIZE_ALWAYS_STANDALONE</code> flag is ture in <code>web.xml</code>. |
| * If the flag is true, this means we should not add the FacesServlet, instead we want to |
| * init MyFaces regardless... |
| */ |
| private boolean shouldStartupRegardless(ServletContext servletContext) |
| { |
| try |
| { |
| String standaloneStartup = servletContext.getInitParameter(MyfacesConfig.INITIALIZE_ALWAYS_STANDALONE); |
| |
| return "true".equalsIgnoreCase(standaloneStartup); |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| } |
| |
| /** |
| * Checks if the <code>INITIALIZE_SCAN_JARS_FOR_FACES_CONFIG</code> flag is true in <code>web.xml</code>. |
| * If the flag is true, this means we should scan app jars for *.faces-config.xml before adding |
| * any FacesServlet; in false, we skip that scan for performance. |
| */ |
| private boolean shouldSkipJarFacesConfigScan(ServletContext servletContext) |
| { |
| try |
| { |
| String skipJarScan = servletContext.getInitParameter(MyfacesConfig.INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN); |
| |
| if (skipJarScan == null) |
| { |
| skipJarScan = System.getProperty(MyfacesConfig.INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN); |
| } |
| return "true".equalsIgnoreCase(skipJarScan); |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| } |
| |
| private boolean isAutomaticXhtmlMappingDisabled(ServletContext servletContext) |
| { |
| try |
| { |
| String xhtmlMappingDisabled = servletContext.getInitParameter( |
| FacesServlet.DISABLE_FACESSERVLET_TO_XHTML_PARAM_NAME); |
| |
| if (xhtmlMappingDisabled == null) |
| { |
| xhtmlMappingDisabled = "false"; |
| } |
| return "true".equalsIgnoreCase(xhtmlMappingDisabled); |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| } |
| |
| /** |
| * Checks if /WEB-INF/faces-config.xml is present. |
| * @return |
| */ |
| private boolean isFacesConfigPresent(ServletContext servletContext) |
| { |
| try |
| { |
| // 1. A faces-config.xml file is found in WEB-INF |
| if (servletContext.getResource(FACES_CONFIG_RESOURCE) != null) |
| { |
| return true; |
| } |
| |
| // 4. The javax.faces.CONFIG_FILES context param is declared in web.xml or web-fragment.xml. |
| // check for alternate faces-config files specified by javax.faces.CONFIG_FILES |
| String configFilesAttrValue = servletContext.getInitParameter(FacesServlet.CONFIG_FILES_ATTR); |
| if (configFilesAttrValue != null) |
| { |
| String[] configFiles = configFilesAttrValue.split(","); |
| for (String file : configFiles) |
| { |
| if (servletContext.getResource(file.trim()) != null) |
| { |
| return true; |
| } |
| } |
| } |
| |
| // Skip this scan - for performance - if INITIALIZE_SKIP_JAR_FACES_CONFIG_SCAN is set to true |
| // 2. A faces-config.xml file is found in the META-INF directory of a jar in the |
| // application's classpath. |
| // 3. A filename ending in .faces-config.xml is found in the META-INF directory of a jar in |
| // the application's classpath. |
| // To do this properly it is necessary to use some SPI interfaces MyFaces already has, to |
| // deal with OSGi and other |
| // environments properly. |
| if (!shouldSkipJarFacesConfigScan(servletContext)) |
| { |
| ExternalContext externalContext = new StartupServletExternalContextImpl(servletContext, true); |
| FacesConfigResourceProviderFactory factory = FacesConfigResourceProviderFactory. |
| getFacesConfigResourceProviderFactory(externalContext); |
| FacesConfigResourceProvider provider = factory.createFacesConfigResourceProvider(externalContext); |
| Collection<URL> metaInfFacesConfigUrls = provider.getMetaInfConfigurationResources(externalContext); |
| |
| if (metaInfFacesConfigUrls != null && !metaInfFacesConfigUrls.isEmpty()) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| } |
| |
| /** |
| * Checks if the class represented by className implements DelegatedFacesServlet. |
| * @param className |
| * @return |
| */ |
| private boolean isDelegatedFacesServlet(String className) |
| { |
| if (className == null) |
| { |
| // The class name can be null if this is e.g., a JSP mapped to |
| // a servlet. |
| |
| return false; |
| } |
| try |
| { |
| Class<?> clazz = ClassUtils.classForName(className); |
| return DELEGATED_FACES_SERVLET_CLASS.isAssignableFrom(clazz); |
| } |
| catch (ClassNotFoundException cnfe) |
| { |
| return false; |
| } |
| } |
| } |