/** * Copyright (c) 2000-2017 Liferay, Inc. All rights reserved. * * Licensed 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 com.liferay.faces.util.config.internal; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import javax.faces.application.ViewHandler; import javax.faces.webapp.FacesServlet; import javax.xml.parsers.SAXParser; import com.liferay.faces.util.config.ConfiguredServlet; import com.liferay.faces.util.config.ConfiguredServletMapping; import com.liferay.faces.util.config.FacesConfig; import com.liferay.faces.util.config.WebConfig; import com.liferay.faces.util.logging.Logger; import com.liferay.faces.util.logging.LoggerFactory; /** * @author Neil Griffin */ public class FacesConfigScannerImpl implements FacesConfigScanner { // Logger private static final Logger logger = LoggerFactory.getLogger(FacesConfigScannerImpl.class); // Private Constants private static final String FACES_CONFIG_META_INF_PATH = "META-INF/faces-config.xml"; private static final String FACES_CONFIG_WEB_INF_PATH = "/WEB-INF/faces-config.xml"; private static final String FACES_SERVLET = "Faces Servlet"; private static final String FACES_SERVLET_FQCN = FacesServlet.class.getName(); private static final String MOJARRA_CONFIG_PATH = "com/sun/faces/jsf-ri-runtime.xml"; // Private Data Members private ClassLoader classLoader; private boolean resolveEntities; private ResourceReader resourceReader; private SAXParser saxParser; private WebConfig webConfig; public FacesConfigScannerImpl(ClassLoader classLoader, ResourceReader resourceReader, SAXParser saxParser, boolean resolveEntities, WebConfig webConfig) { this.classLoader = classLoader; this.saxParser = saxParser; this.resourceReader = resourceReader; this.resolveEntities = resolveEntities; this.webConfig = webConfig; } public FacesConfig scan() throws IOException { String configuredFacesServletName = FACES_SERVLET; List<ConfiguredServletMapping> facesServletMappings = new ArrayList<ConfiguredServletMapping>(); // Determine the configured servlet-name for the FacesServlet. List<ConfiguredServlet> configuredServlets = webConfig.getConfiguredServlets(); if (configuredServlets != null) { for (ConfiguredServlet configuredServlet : configuredServlets) { if (FACES_SERVLET_FQCN.equals(configuredServlet.getServletClass())) { configuredFacesServletName = configuredServlet.getServletName(); break; } } } // Determine the configured servlet-mapping entries that are associated with the FacesServlet. List<ConfiguredServletMapping> configuredServletMappings = webConfig.getConfiguredServletMappings(); if (configuredServletMappings != null) { for (ConfiguredServletMapping configuredServletMapping : configuredServletMappings) { if (configuredFacesServletName.equals(configuredServletMapping.getServletName())) { facesServletMappings.add(configuredServletMapping); } } } // Discover the suffixes/extensions that the user has specified to be associated with JSF views. String defaultSuffixParam = webConfig.getConfiguredContextParams().get(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME); if (defaultSuffixParam == null) { defaultSuffixParam = ViewHandler.DEFAULT_SUFFIX; } List<String> configuredSuffixes = Arrays.asList(defaultSuffixParam.split(" ")); // If they don't exist explicitly in web.xml, then setup implicit servlet-mapping entries to the default // suffixes. for (String configuredSuffix : configuredSuffixes) { boolean found = false; for (ConfiguredServletMapping explicitFacesServletMapping : facesServletMappings) { if (explicitFacesServletMapping.isExtensionMapped() && explicitFacesServletMapping.getExtension().equals(configuredSuffix)) { found = true; break; } } if (!found) { String urlPattern = "*" + configuredSuffix; ConfiguredServletMapping implicitFacesServletMapping = new ConfiguredServletMappingImpl(FACES_SERVLET, urlPattern, true); facesServletMappings.add(implicitFacesServletMapping); logger.debug("Added implicit extension-mapped servlet-mapping for urlPattern=[{0}]", urlPattern); } } FacesConfig facesConfig = new FacesConfigImpl(facesServletMappings, configuredSuffixes); try { FacesConfigDescriptorParser facesConfigDescriptorParser = newFacesConfigDescriptorParser(); // Parse the WEB-INF/faces-config.xml descriptor. Gathering absolute-ordering, if any. FacesConfigDescriptor webInfFacesConfigDescriptor; InputStream inputStream = resourceReader.getResourceAsStream(FACES_CONFIG_WEB_INF_PATH); webInfFacesConfigDescriptor = facesConfigDescriptorParser.parse(inputStream, FACES_CONFIG_WEB_INF_PATH); inputStream.close(); // First, parse the Mojarra configuration found in the classpath. Enumeration<URL> mojarraConfigURLs = classLoader.getResources(MOJARRA_CONFIG_PATH); if (mojarraConfigURLs != null) { boolean processedMojarraConfig = false; while (mojarraConfigURLs.hasMoreElements()) { URL mojarraConfigURL = mojarraConfigURLs.nextElement(); if (processedMojarraConfig) { logger.debug("Skipping Mojarra config: [{0}]", mojarraConfigURL); } else { logger.debug("Processing Mojarra config: [{0}]", mojarraConfigURL); FacesConfigParser mojarraConfigParser = new FacesConfigParserImpl(saxParser, resolveEntities); inputStream = mojarraConfigURL.openStream(); try { facesConfig = mojarraConfigParser.parse(inputStream, facesConfig); } catch (IOException e) { logger.error(e); } inputStream.close(); processedMojarraConfig = true; } } } FacesConfigParser facesConfigParser = newFacesConfigParser(); // Next, parse all of the META-INF/faces-config.xml files found in the classpath. Enumeration<URL> facesConfigURLs = classLoader.getResources(FACES_CONFIG_META_INF_PATH); List<FacesConfigDescriptor> facesConfigDescriptors = new ArrayList<FacesConfigDescriptor>(); if (facesConfigURLs != null) { // Build up a semi-sorted list of faces-config.xml descriptor files, ensuring that // liferay-faces-bridge-impl.jar!META-INF/faces-config.xml is ordered first and that // liferay-faces-util.jar!META-INF/faces-config.xml is ordered second. // (Note that the JSF 2.0 <ordering> element is not yet supported.) while (facesConfigURLs.hasMoreElements()) { URL facesConfigURL = facesConfigURLs.nextElement(); logger.debug("Pre-processing faces-config: [{0}]", facesConfigURL); inputStream = facesConfigURL.openStream(); FacesConfigDescriptor facesConfigDescriptor = facesConfigDescriptorParser.parse(inputStream, facesConfigURL); facesConfigDescriptors.add(facesConfigDescriptor); inputStream.close(); } // Sort the faces configuration files in accord with // javax.faces-api-2.2-FINAL_JSF_20130320_11.4.8_Ordering_of_Artifacts List<FacesConfigDescriptor> orderedConfigs = getOrderedConfigs(facesConfigDescriptors, webInfFacesConfigDescriptor); for (FacesConfigDescriptor config : orderedConfigs) { String urlString = config.getURL(); URL url = new URL(urlString); logger.debug("Post-processing faces-config: [{0}]", url); inputStream = url.openStream(); try { facesConfig = facesConfigParser.parse(inputStream, facesConfig); } catch (IOException e) { logger.error(e); } inputStream.close(); try { saxParser.reset(); } catch (Exception e) { logger.error(e); } } } // Second, parse the WEB-INF/faces-config.xml descriptor. Any entries made here will take // precedence over those found previously. inputStream = resourceReader.getResourceAsStream(FACES_CONFIG_WEB_INF_PATH); if (inputStream != null) { logger.debug("Processing faces-config: [{0}]", FACES_CONFIG_WEB_INF_PATH); facesConfig = facesConfigParser.parse(inputStream, facesConfig); inputStream.close(); } } catch (Exception e) { logger.error(e.getMessage(), e); } return facesConfig; } protected List<FacesConfigDescriptor> getOrderedConfigs(List<FacesConfigDescriptor> facesConfigDescriptors, FacesConfigDescriptor webInfFacesConfig) throws Exception { if (facesConfigDescriptors.size() > 1) { List<String> absoluteOrdering = webInfFacesConfig.getAbsoluteOrdering(); if (absoluteOrdering == null) { logger.debug("Ordering faces-config descriptors"); facesConfigDescriptors = OrderingUtil.getOrder(facesConfigDescriptors); } else { logger.debug("Ordering faces-config descriptors: absoluteOrdering=[{0}]", absoluteOrdering); facesConfigDescriptors = OrderingUtil.getOrder(facesConfigDescriptors, absoluteOrdering); } } return facesConfigDescriptors; } protected SAXParser getSAXParser() { return saxParser; } protected FacesConfigDescriptorParser newFacesConfigDescriptorParser() { return new FacesConfigDescriptorParserImpl(saxParser, resolveEntities); } protected FacesConfigParser newFacesConfigParser() { return new FacesConfigParserImpl(saxParser, resolveEntities); } }