com.sun.faces.config.ConfigManager Maven / Gradle / Ivy
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2014 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.faces.config; import com.sun.faces.RIConstants; import static com.sun.faces.RIConstants.ANNOTATED_CLASSES; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.ValidateFacesConfigFiles; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.DisableFaceletJSFViewHandler; import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableThreading; import com.sun.faces.spi.ConfigurationResourceProvider; import com.sun.faces.spi.ConfigurationResourceProviderFactory; import com.sun.faces.spi.AnnotationProvider; import com.sun.faces.spi.AnnotationProviderFactory; import com.sun.faces.spi.HighAvailabilityEnabler; import static com.sun.faces.spi.ConfigurationResourceProviderFactory.ProviderType.*; import com.sun.faces.config.configprovider.MetaInfFacesConfigResourceProvider; import com.sun.faces.config.configprovider.MojarraFacesConfigResourceProvider; import com.sun.faces.config.configprovider.WebFacesConfigResourceProvider; import com.sun.faces.config.configprovider.MetaInfFaceletTaglibraryConfigProvider; import com.sun.faces.config.configprovider.WebAppFlowConfigResourceProvider; import com.sun.faces.config.configprovider.WebFaceletTaglibResourceProvider; import com.sun.faces.config.processor.ApplicationConfigProcessor; import com.sun.faces.config.processor.BehaviorConfigProcessor; import com.sun.faces.config.processor.ComponentConfigProcessor; import com.sun.faces.config.processor.ConfigProcessor; import com.sun.faces.config.processor.ConverterConfigProcessor; import com.sun.faces.config.processor.FactoryConfigProcessor; import com.sun.faces.config.processor.LifecycleConfigProcessor; import com.sun.faces.config.processor.ManagedBeanConfigProcessor; import com.sun.faces.config.processor.NavigationConfigProcessor; import com.sun.faces.config.processor.RenderKitConfigProcessor; import com.sun.faces.config.processor.ValidatorConfigProcessor; import com.sun.faces.config.processor.FaceletTaglibConfigProcessor; import com.sun.faces.config.processor.FacesConfigExtensionProcessor; import com.sun.faces.config.processor.FacesFlowDefinitionConfigProcessor; import com.sun.faces.config.processor.ProtectedViewsConfigProcessor; import com.sun.faces.config.processor.ResourceLibraryContractsConfigProcessor; import com.sun.faces.el.ELContextImpl; import com.sun.faces.el.ELUtils; import com.sun.faces.spi.InjectionProvider; import com.sun.faces.spi.InjectionProviderFactory; import com.sun.faces.util.FacesLogger; import com.sun.faces.util.Timer; import com.sun.faces.util.Util; import org.xml.sax.InputSource; import javax.faces.FacesException; import javax.faces.FactoryFinder; import javax.faces.event.PostConstructApplicationEvent; import javax.faces.application.Application; import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import java.lang.annotation.Annotation; import java.net.URI; import java.util.Iterator; import java.util.ServiceLoader; import javax.el.ELContext; import javax.el.ELContextEvent; import javax.el.ELContextListener; import javax.el.ExpressionFactory; import javax.faces.application.ApplicationConfigurationPopulator; import javax.faces.component.UIViewRoot; import javax.xml.validation.Schema; import org.w3c.dom.*; import org.xml.sax.SAXParseException; /** *
fase */ public boolean hasBeenInitialized(ServletContext sc) { return (initializedContexts.contains(sc)); } /** * @return the results of the annotation scan task */ public static Map* This class manages the initialization of each web application that uses * JSF. *
*/ public class ConfigManager { private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); private static final Pattern JAR_PATTERN = Pattern.compile("(.*/(\\S*\\.jar)).*(/faces-config.xml|/*.\\.faces-config.xml)"); /** ** A List of resource providers that search for faces-config documents. * By default, this contains a provider for the Mojarra, and two other * providers to satisfy the requirements of the specification. *
*/ private final ListFACES_CONFIG_RESOURCE_PROVIDERS; /** * * A List of resource providers that search for faces-config documents. * By default, this contains a provider for the Mojarra, and one other * providers to satisfy the requirements of the specification. *
*/ private final ListFACELET_TAGLIBRARY_RESOURCE_PROVIDERS; /** * * The
*/ private static final int NUMBER_OF_TASK_THREADS = 5; private static final String CONFIG_MANAGER_INSTANCE_KEY = RIConstants.FACES_PREFIX + "CONFIG_MANAGER_KEY"; /** * The application-scoped key under which the Future responsible for annotation * scanning is associated with. */ private static final String ANNOTATIONS_SCAN_TASK_KEY = ConfigManager.class.getName() + "_ANNOTATION_SCAN_TASK"; /** * The initialization time FacesContext scoped key under which the * InjectionProvider is stored. */ public static final String INJECTION_PROVIDER_KEY = ConfigManager.class.getName() + "_INJECTION_PROVIDER_TASK"; /** * Name of the attribute added by ParseTask to indicate a * {@link Document} instance as a representation of *ConfigManager
will multithread the calls to the *ConfigurationResourceProvider
s as well as any calls * to parse a resources into a DOM. By default, we'll use only 5 threads * per web application. */WEB-INF/faces-config.xml
. */ public static final String WEB_INF_MARKER = "com.sun.faces.webinf"; /** ** Contains each
*/ @SuppressWarnings({"CollectionWithoutInitialCapacity"}) private ListServletContext
that we've initialized. * TheServletContext
will be removed when the application * is destroyed. *initializedContexts = new CopyOnWriteArrayList (); /** * * The chain of {@link ConfigProcessor} instances to processing of * faces-config documents. *
*/ private final ConfigProcessor FACES_CONFIG_PROCESSOR_CHAIN; /** ** The chain of {@link ConfigProcessor} instances to processing of * facelet-taglib documents. *
*/ private final ConfigProcessor FACELET_TAGLIB_CONFIG_PROCESSOR_CHAIN; /** * Stylesheet to convert 1.0 and 1.1 based faces-config documents * to our private 1.1 schema for validation. */ private static final String FACES_TO_1_1_PRIVATE_XSL = "/com/sun/faces/jsf1_0-1_1toSchema.xsl"; /** * Stylesheet to convert 1.0 facelet-taglib documents * from 1.0 to 2.0 for schema validation purposes. */ private static final String FACELETS_TO_2_0_XSL = "/com/sun/faces/facelets1_0-2_0toSchema.xsl"; private static final String FACES_CONFIG_1_X_DEFAULT_NS = "http://java.sun.com/JSF/Configuration"; private static final String FACELETS_1_0_DEFAULT_NS = "http://java.sun.com/JSF/Facelet"; public ConfigManager() { // initialize the resource providers for faces-config documents ListfacesConfigProviders = new ArrayList (3); facesConfigProviders.add(new MojarraFacesConfigResourceProvider()); facesConfigProviders.add(new MetaInfFacesConfigResourceProvider()); facesConfigProviders.add(new WebAppFlowConfigResourceProvider()); facesConfigProviders.add(new WebFacesConfigResourceProvider()); FACES_CONFIG_RESOURCE_PROVIDERS = Collections.unmodifiableList(facesConfigProviders); // initialize the resource providers for facelet-taglib documents List faceletTaglibProviders = new ArrayList (3); faceletTaglibProviders.add(new MetaInfFaceletTaglibraryConfigProvider()); faceletTaglibProviders.add(new WebFaceletTaglibResourceProvider()); FACELET_TAGLIBRARY_RESOURCE_PROVIDERS = Collections.unmodifiableList(faceletTaglibProviders); // initialize the config processors for faces-config documents ConfigProcessor[] configProcessors = { new FactoryConfigProcessor(), new LifecycleConfigProcessor(), new ApplicationConfigProcessor(), new ComponentConfigProcessor(), new ConverterConfigProcessor(), new ValidatorConfigProcessor(), new ManagedBeanConfigProcessor(), new RenderKitConfigProcessor(), new NavigationConfigProcessor(), new BehaviorConfigProcessor(), new FacesConfigExtensionProcessor(), new ProtectedViewsConfigProcessor(), new FacesFlowDefinitionConfigProcessor(), new ResourceLibraryContractsConfigProcessor() }; for (int i = 0; i < configProcessors.length; i++) { ConfigProcessor p = configProcessors[i]; if ((i + 1) < configProcessors.length) { p.setNext(configProcessors[i + 1]); } } FACES_CONFIG_PROCESSOR_CHAIN = configProcessors[0]; // initialize the config processor for facelet-taglib documents FACELET_TAGLIB_CONFIG_PROCESSOR_CHAIN = new FaceletTaglibConfigProcessor(); } // ---------------------------------------------------------- Public Methods /** * @return a ConfigManager
instance */ public static ConfigManager getInstance(ServletContext sc) { return (ConfigManager) sc.getAttribute(CONFIG_MANAGER_INSTANCE_KEY); } public static ConfigManager createInstance(ServletContext sc) { ConfigManager result = new ConfigManager(); sc.setAttribute(CONFIG_MANAGER_INSTANCE_KEY, result); return result; } public static void removeInstance(ServletContext sc) { sc.removeAttribute(CONFIG_MANAGER_INSTANCE_KEY); } private void initializeConfigProcessers(ServletContext sc) { ConfigProcessor p = FACES_CONFIG_PROCESSOR_CHAIN; do { p.initializeClassMetadataMap(sc); } while (null != (p = p.getNext())); } /** ** This method bootstraps JSF based on the parsed configuration resources. *
* * @param sc theServletContext
for the application that * requires initialization */ public void initialize(ServletContext sc) { if (!hasBeenInitialized(sc)) { initializedContexts.add(sc); initializeConfigProcessers(sc); ExecutorService executor = null; try { WebConfiguration webConfig = WebConfiguration.getInstance(sc); boolean validating = webConfig.isOptionEnabled(ValidateFacesConfigFiles); if (useThreads(sc)) { executor = createExecutorService(); } DocumentInfo[] facesDocuments = getConfigDocuments(sc, getFacesConfigResourceProviders(), executor, validating); FacesConfigInfo lastFacesConfigInfo = new FacesConfigInfo(facesDocuments[facesDocuments.length - 1]); facesDocuments = sortDocuments(facesDocuments, lastFacesConfigInfo); InitFacesContext context = (InitFacesContext) FacesContext.getCurrentInstance(); InjectionProvider containerConnector = InjectionProviderFactory.createInstance(context.getExternalContext()); context.getAttributes().put(INJECTION_PROVIDER_KEY, containerConnector); boolean isFaceletsDisabled = false; // Don't perform the check unless lastFacesConfigInfo is indeed // *the* WEB-INF/faces-config.xml if (lastFacesConfigInfo.isWebInfFacesConfig()) { isFaceletsDisabled = isFaceletsDisabled(webConfig, lastFacesConfigInfo); } if (!lastFacesConfigInfo.isWebInfFacesConfig() || !lastFacesConfigInfo.isMetadataComplete()) { // execute the Task responsible for finding annotation classes ProvideMetadataToAnnotationScanTask taskMetadata = new ProvideMetadataToAnnotationScanTask(facesDocuments, containerConnector); Future,Set >> getAnnotatedClasses(FacesContext ctx) { Map appMap = ctx.getExternalContext().getApplicationMap(); //noinspection unchecked Future ,Set >>> scanTask = (Future ,Set >>>) appMap.get(ANNOTATIONS_SCAN_TASK_KEY); try { return ((scanTask != null) ? scanTask.get() : Collections. ,Set >>emptyMap()); } catch (Exception e) { throw new FacesException(e); } } // --------------------------------------------------------- Private Methods private static boolean useThreads(ServletContext ctx) { WebConfiguration config = WebConfiguration.getInstance(ctx); return config.isOptionEnabled(EnableThreading); } private List getFacesConfigResourceProviders() { return getConfigurationResourceProviders(FACES_CONFIG_RESOURCE_PROVIDERS, FacesConfig); } private List getFaceletConfigResourceProviders() { return getConfigurationResourceProviders(FACELET_TAGLIBRARY_RESOURCE_PROVIDERS, FaceletConfig); } private List getConfigurationResourceProviders(List defaultProviders, ConfigurationResourceProviderFactory.ProviderType providerType) { ConfigurationResourceProvider[] custom = ConfigurationResourceProviderFactory.createProviders(providerType); if (custom.length == 0) { return defaultProviders; } else { List list = new ArrayList (); list.addAll(defaultProviders); // insert the custom providers after the META-INF providers and // before those that scan /WEB-INF list.addAll((defaultProviders.size() - 1), Arrays.asList(custom)); return Collections.unmodifiableList(list); } } /** * * Sort the
* * @param facesDocuments an array of allfaces-config
documents found on the classpath * and those specified by thejavax.faces.CONFIG_FILES
context * init parameter. *faces-config
* documents * @param webInfFacesConfig FacesConfigInfo representing the WEB-INF/faces-config.xml * for this app * * @return the sorted documents */ private DocumentInfo[] sortDocuments(DocumentInfo[] facesDocuments, FacesConfigInfo webInfFacesConfig) { int len = (webInfFacesConfig.isWebInfFacesConfig() ? facesDocuments.length - 1 : facesDocuments.length); ListabsoluteOrdering = webInfFacesConfig.getAbsoluteOrdering(); if (len > 1) { List list = new ArrayList (); for (int i = 1; i < len; i++) { list.add(new DocumentOrderingWrapper(facesDocuments[i])); } DocumentOrderingWrapper[] ordering = list.toArray(new DocumentOrderingWrapper[list.size()]); if (absoluteOrdering == null) { DocumentOrderingWrapper.sort(ordering); // sorting complete, now update the appropriate locations within // the original array with the sorted documentation. for (int i = 1; i < len; i++) { facesDocuments[i] = ordering[i - 1].getDocument(); } return facesDocuments; } else { DocumentOrderingWrapper[] result = DocumentOrderingWrapper.sort(ordering, absoluteOrdering); DocumentInfo[] ret = new DocumentInfo[((webInfFacesConfig.isWebInfFacesConfig()) ? (result.length + 2) : (result.length + 1))]; for (int i = 1; i < len; i++) { ret[i] = result[i - 1].getDocument(); } // add the impl specific config file ret[0] = facesDocuments[0]; // add the WEB-INF if necessary if (webInfFacesConfig.isWebInfFacesConfig()) { ret[ret.length - 1] = facesDocuments[facesDocuments.length - 1]; } return ret; } } return facesDocuments; } /** * * Utility method to check if JSF 2.0 Facelets should be disabled. * If it's not explicitly disabled by the context init parameter, then * check the version of the WEB-INF/faces-config.xml document. If the version * is less than 2.0, then override the default value for the context init * parameter so that other parts of the system that use that config option * will know it has been disabled. *
* ** NOTE: Since this method overrides a configuration value, it should * be called before *any* document parsing is performed the configuration * value may be queried by the
* * @param webconfig configuration for this application * @param facesConfigInfo object representing WEB-INF/faces-config.xml * @returnConfigParser
s. *true
if Facelets should be disabled */ private boolean isFaceletsDisabled(WebConfiguration webconfig, FacesConfigInfo facesConfigInfo) { boolean isFaceletsDisabled = webconfig.isOptionEnabled(DisableFaceletJSFViewHandler); if (!isFaceletsDisabled) { // if not explicitly disabled, make a sanity check against // /WEB-INF/faces-config.xml isFaceletsDisabled = !facesConfigInfo.isVersionGreaterOrEqual(2.0); webconfig.overrideContextInitParameter(DisableFaceletJSFViewHandler, isFaceletsDisabled); } return isFaceletsDisabled; } /** * Push the providedFuture
to the specifiedServletContext
. */ private void pushTaskToContext(ServletContext sc, Future,Set >>> scanTask) { sc.setAttribute(ANNOTATIONS_SCAN_TASK_KEY, scanTask); } /** * Publishes a {@link javax.faces.event.PostConstructApplicationEvent} event for the current * {@link Application} instance. */ void publishPostConfigEvent() { FacesContext ctx = FacesContext.getCurrentInstance(); Application app = ctx.getApplication(); if (null == ((InitFacesContext)ctx).getELContext()) { ELContext elContext = new ELContextImpl(app.getELResolver()); elContext.putContext(FacesContext.class, ctx); ExpressionFactory exFactory = ELUtils.getDefaultExpressionFactory(ctx); if (null != exFactory) { elContext.putContext(ExpressionFactory.class, exFactory); } UIViewRoot root = ctx.getViewRoot(); if (null != root) { elContext.setLocale(root.getLocale()); } ELContextListener[] listeners = app.getELContextListeners(); if (listeners.length > 0) { ELContextEvent event = new ELContextEvent(elContext); for (ELContextListener listener: listeners) { listener.contextCreated(event); } } ((InitFacesContext)ctx).setELContext(elContext); } app.publishEvent(ctx, PostConstructApplicationEvent.class, Application.class, app); } /** * * Obtains an array of
* * @param sc theDocument
s to be processed * by {@link ConfigManager#FACES_CONFIG_PROCESSOR_CHAIN}. *ServletContext
for the application to be * processed * @param providersList
ofConfigurationResourceProvider
* instances that provide the URL of the documents to parse. * @param executor theExecutorService
used to dispatch parse * request to * @param validating flag indicating whether or not the documents * should be validated * @return an array ofDocumentInfo
s */ private static DocumentInfo[] getConfigDocuments(ServletContext sc, Listproviders, ExecutorService executor, boolean validating) { List >> urlTasks = new ArrayList >>(providers.size()); for (ConfigurationResourceProvider p : providers) { FutureTask > t = new FutureTask >(new URITask(p, sc)); urlTasks.add(t); if (executor != null) { executor.execute(t); } else { t.run(); } } List > docTasks = new ArrayList >(providers.size() << 1); for (FutureTask > t : urlTasks) { try { Collection l = t.get(); for (URI u : l) { FutureTask d = new FutureTask (new ParseTask(sc, validating, u)); docTasks.add(d); if (executor != null) { executor.execute(d); } else { d.run(); } } } catch (InterruptedException ignored) { } catch (Exception e) { throw new ConfigurationException(e); } } List docs = new ArrayList (docTasks.size()); for (FutureTask t : docTasks) { try { docs.add(t.get()); } catch (ExecutionException e) { throw new ConfigurationException(e); } catch (InterruptedException ignored) { } } return docs.toArray(new DocumentInfo[docs.size()]); } /** * Create a new ExecutorService
with * {@link #NUMBER_OF_TASK_THREADS} threads. */ private static ExecutorService createExecutorService() { int tc = Runtime.getRuntime().availableProcessors(); if (tc > NUMBER_OF_TASK_THREADS) { tc = NUMBER_OF_TASK_THREADS; } return Executors.newFixedThreadPool(tc); } // /** // * @param throwable Throwable // * @return the root cause of this error // */ // private Throwable unwind(Throwable throwable) { // // Throwable t = null; // if (throwable != null) { // t = unwind(throwable.getCause()); // if (t == null) { // t = throwable; // } // } // return t; // // } /** * Calls through to {@link javax.faces.FactoryFinder#releaseFactories()} * ignoring any exceptions. */ private void releaseFactories() { try { FactoryFinder.releaseFactories(); } catch (FacesException ignored) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Exception thrown from FactoryFinder.releaseFactories()", ignored); } } } // ----------------------------------------------------------- Inner Classes private static final class ProvideMetadataToAnnotationScanTask { DocumentInfo [] documentInfos; InjectionProvider containerConnector; Seturis = null; Set jarNames = null; private ProvideMetadataToAnnotationScanTask(DocumentInfo [] documentInfos, InjectionProvider containerConnector) { this.documentInfos = documentInfos; this.containerConnector = containerConnector; } private void initializeIvars(Set > annotatedSet) { if (null != uris || null != jarNames) { assert(null != uris && null != jarNames); return; } uris = new HashSet (documentInfos.length); jarNames = new HashSet (documentInfos.length); for (DocumentInfo docInfo : documentInfos) { URI sourceURI = docInfo.getSourceURI(); Matcher m = JAR_PATTERN.matcher(sourceURI.toString()); if (m.matches()) { String jarName = m.group(2); if (!jarNames.contains(jarName)) { FacesConfigInfo configInfo = new FacesConfigInfo(docInfo); if (!configInfo.isMetadataComplete()) { uris.add(sourceURI); jarNames.add(jarName); } else { /* * Because the container annotation scanning does not * know anything about faces-config.xml metadata-complete * the annotatedSet of classes will include classes that * are not supposed to be included. * * The code below looks at the CodeSource of the class * and determines whether or not it should be removed * from the annotatedSet because the faces-config.xml that * owns it has metadata-complete="true". */ ArrayList > toRemove = new ArrayList >(1); String sourceURIString = sourceURI.toString(); if (annotatedSet != null) { for (Class clazz : annotatedSet) { if (sourceURIString.contains(clazz.getProtectionDomain().getCodeSource().getLocation().toString())) { toRemove.add(clazz); } } annotatedSet.removeAll(toRemove); } } } } } } private Set getAnnotationScanURIs(Set > annotatedSet) { initializeIvars(annotatedSet); return uris; } private Set getJarNames(Set > annotatedSet) { initializeIvars(annotatedSet); return jarNames; } private com.sun.faces.spi.AnnotationScanner getAnnotationScanner() { com.sun.faces.spi.AnnotationScanner result = null; if (this.containerConnector instanceof com.sun.faces.spi.AnnotationScanner) { result = (com.sun.faces.spi.AnnotationScanner) this.containerConnector; } return result; } } /** * Scans the class files within a web application returning a Set
* of classes that have been annotated with a standard Faces annotation. */ private static class AnnotationScanTask implements Callable,Set >>> { private ServletContext sc; private InitFacesContext facesContext; private AnnotationProvider provider; private ProvideMetadataToAnnotationScanTask metadataGetter; private Set > annotatedSet; // -------------------------------------------------------- Constructors public AnnotationScanTask(ServletContext sc, InitFacesContext facesContext, ProvideMetadataToAnnotationScanTask metadataGetter) { this.facesContext = facesContext; this.provider = AnnotationProviderFactory.createAnnotationProvider(sc); this.metadataGetter = metadataGetter; this.annotatedSet = (Set >) sc.getAttribute(ANNOTATED_CLASSES); } // ----------------------------------------------- Methods from Callable public Map ,Set >> call() throws Exception { Timer t = Timer.getInstance(); if (t != null) { t.startTiming(); } // We are executing on a different thread. facesContext.callSetCurrentInstance(); Set scanUris = null; com.sun.faces.spi.AnnotationScanner annotationScanner = metadataGetter.getAnnotationScanner(); // This is where we discover what kind of InjectionProvider // we have. if (provider instanceof DelegatingAnnotationProvider && null != annotationScanner) { // This InjectionProvider is capable of annotation scanning *and* // injection. ((DelegatingAnnotationProvider)provider).setAnnotationScanner(annotationScanner, metadataGetter.getJarNames(annotatedSet)); scanUris = Collections.emptySet(); } else { // This InjectionProvider is capable of annotation scanning only scanUris = metadataGetter.getAnnotationScanURIs(annotatedSet); } //AnnotationScanner scanner = new AnnotationScanner(sc); Map ,Set >> annotatedClasses = provider.getAnnotatedClasses(scanUris); if (t != null) { t.stopTiming(); t.logResult("Configuration annotation scan complete."); } return annotatedClasses; } } // END AnnotationScanTask /** * * This
*/ private static class ParseTask implements CallableCallable
will be used by {@link ConfigManager#getConfigDocuments(javax.servlet.ServletContext, java.util.List, java.util.concurrent.ExecutorService, boolean)}. * It represents a single configuration resource to be parsed into a DOM. *{ private static final String JAVAEE_SCHEMA_LEGACY_DEFAULT_NS = "http://java.sun.com/xml/ns/javaee"; private static final String JAVAEE_SCHEMA_DEFAULT_NS = "http://xmlns.jcp.org/xml/ns/javaee"; private static final String EMPTY_FACES_CONFIG = "com/sun/faces/empty-faces-config.xml"; private static final String FACES_CONFIG_TAGNAME = "faces-config"; private static final String FACELET_TAGLIB_TAGNAME = "facelet-taglib"; private ServletContext servletContext; private URI documentURI; private DocumentBuilderFactory factory; private boolean validating; // -------------------------------------------------------- Constructors /** * * Constructs a new ParseTask instance *
* * @param servletContext the servlet context. * @param validating whether or not we're validating * @param documentURI a URL to the configuration resource to be parsed * @throws Exception general error */ public ParseTask(ServletContext servletContext, boolean validating, URI documentURI) throws Exception { this.servletContext = servletContext; this.documentURI = documentURI; this.validating = validating; } // ----------------------------------------------- Methods from Callable /** * @return the result of the parse operation (a DOM) * @throws Exception if an error occurs during the parsing process */ public DocumentInfo call() throws Exception { try { Timer timer = Timer.getInstance(); if (timer != null) { timer.startTiming(); } Document d = getDocument(); if (timer != null) { timer.stopTiming(); timer.logResult("Parse " + documentURI.toURL().toExternalForm()); } return new DocumentInfo(d, documentURI); } catch (Exception e) { throw new ConfigurationException(MessageFormat.format( "Unable to parse document ''{0}'': {1}", documentURI.toURL().toExternalForm(), e.getMessage()), e); } } // ----------------------------------------------------- Private Methods /** * @returnDocument
based ondocumentURI
. * @throws Exception if an error occurs during the process of building a *Document
*/ private Document getDocument() throws Exception { Document returnDoc; DocumentBuilder db = getNonValidatingBuilder(); URL documentURL = documentURI.toURL(); InputSource is = new InputSource(getInputStream(documentURL)); is.setSystemId(documentURI.toURL().toExternalForm()); Document doc = null; try { doc = db.parse(is); } catch (SAXParseException spe) { // [mojarra-1693] // Test if this is a zero length or whitespace only faces-config.xml file. // If so, just make an empty Document InputStream stream = is.getByteStream(); stream.close(); is = new InputSource(getInputStream(documentURL)); stream = is.getByteStream(); if (streamIsZeroLengthOrEmpty(stream) && documentURL.toExternalForm().endsWith("faces-config.xml")) { ClassLoader loader = this.getClass().getClassLoader(); is = new InputSource(getInputStream(loader.getResource(EMPTY_FACES_CONFIG))); doc = db.parse(is); } } String documentNS = null; if (null == doc) { if (FacesFlowDefinitionConfigProcessor.uriIsFlowDefinition(documentURI)) { documentNS = RIConstants.JAVAEE_XMLNS; doc = FacesFlowDefinitionConfigProcessor.synthesizeEmptyFlowDefinition(documentURI); } } else { Element documentElement = doc.getDocumentElement(); documentNS = documentElement.getNamespaceURI(); String rootElementTagName = documentElement.getTagName(); boolean isNonFacesConfigDocument = !FACES_CONFIG_TAGNAME.equals(rootElementTagName) && !FACELET_TAGLIB_TAGNAME.equals(rootElementTagName); if (isNonFacesConfigDocument) { ClassLoader loader = this.getClass().getClassLoader(); is = new InputSource(getInputStream(loader.getResource(EMPTY_FACES_CONFIG))); doc = db.parse(is); if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, MessageFormat.format("Config document {0} with namespace URI {1} is not a faces-config or facelet-taglib file. Ignoring.", documentURI.toURL().toExternalForm(), documentNS)); } return doc; } } if (validating && documentNS != null) { DOMSource domSource = new DOMSource(doc, documentURL.toExternalForm()); /* * If the Document in question is 1.2 (i.e. it has a namespace matching * JAVAEE_SCHEMA_DEFAULT_NS, then perform validation using the cached schema * and return. Otherwise we assume a 1.0 or 1.1 faces-config in which case * we need to transform it to reference a special 1.1 schema before validating. */ Node documentElement = ((Document) domSource.getNode()).getDocumentElement(); if (JAVAEE_SCHEMA_DEFAULT_NS.equals(documentNS)) { Attr version = (Attr) documentElement.getAttributes().getNamedItem("version"); Schema schema; if (version != null) { String versionStr = version.getValue(); if ("2.2".equals(versionStr)) { if ("facelet-taglib".equals(documentElement.getLocalName())) { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACELET_TAGLIB_22); } else { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACES_22); } } else { throw new ConfigurationException("Unknown Schema version: " + versionStr); } DocumentBuilder builder = getBuilderForSchema(schema); if (builder.isValidating()) { builder.getSchema().newValidator().validate(domSource); returnDoc = ((Document) domSource.getNode()); } else { returnDoc = ((Document) domSource.getNode()); } } else { // this shouldn't happen, but... throw new ConfigurationException("No document version available."); } } else if (JAVAEE_SCHEMA_LEGACY_DEFAULT_NS.equals(documentNS)) { Attr version = (Attr) documentElement.getAttributes().getNamedItem("version"); Schema schema; if (version != null) { String versionStr = version.getValue(); if ("2.0".equals(versionStr)) { if ("facelet-taglib".equals(documentElement.getLocalName())) { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACELET_TAGLIB_20); } else { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACES_20); } } else if ("2.1".equals(versionStr)) { if ("facelet-taglib".equals(documentElement.getLocalName())) { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACELET_TAGLIB_20); } else { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACES_21); } } else if ("1.2".equals(versionStr)) { schema = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACES_12); } else { throw new ConfigurationException("Unknown Schema version: " + versionStr); } DocumentBuilder builder = getBuilderForSchema(schema); if (builder.isValidating()) { builder.getSchema().newValidator().validate(domSource); returnDoc = ((Document) domSource.getNode()); } else { returnDoc = ((Document) domSource.getNode()); } } else { // this shouldn't happen, but... throw new ConfigurationException("No document version available."); } } else { DOMResult domResult = new DOMResult(); Transformer transformer = getTransformer(documentNS); transformer.transform(domSource, domResult); // copy the source document URI to the transformed result // so that processes that need to build URLs relative to the // document will work as expected. ((Document) domResult.getNode()) .setDocumentURI(((Document) domSource .getNode()).getDocumentURI()); Schema schemaToApply; if (FACES_CONFIG_1_X_DEFAULT_NS.equals(documentNS)) { schemaToApply = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACES_11); } else if (FACELETS_1_0_DEFAULT_NS.equals(documentNS)) { schemaToApply = DbfFactory.getSchema(servletContext, DbfFactory.FacesSchema.FACELET_TAGLIB_20); } else { throw new IllegalStateException(); } DocumentBuilder builder = getBuilderForSchema(schemaToApply); if (builder.isValidating()) { builder.getSchema().newValidator().validate(new DOMSource(domResult.getNode())); returnDoc = (Document) domResult.getNode(); } else { returnDoc = (Document) domResult.getNode(); } } } else { returnDoc = doc; } // mark this document as the parsed representation of the // WEB-INF/faces-config.xml. This is used later in the configuration // processing. if (documentURL.toExternalForm().contains("/WEB-INF/faces-config.xml")) { Attr webInf = returnDoc.createAttribute(WEB_INF_MARKER); webInf.setValue("true"); returnDoc.getDocumentElement().getAttributes().setNamedItem(webInf); } return returnDoc; } private boolean streamIsZeroLengthOrEmpty(InputStream is) throws IOException { boolean isZeroLengthOrEmpty = (0 == is.available()); final int size = 1024; byte[] b = new byte[size]; String s; while (!isZeroLengthOrEmpty && -1 != is.read(b, 0, size)) { s = (new String(b, RIConstants.CHAR_ENCODING)).trim(); isZeroLengthOrEmpty = 0 == s.length(); b[0] = 0; for (int i = 1; i < size; i += i) { System.arraycopy(b, 0, b, i, ((size - i) < i) ? (size - i) : i); } } return isZeroLengthOrEmpty; } /** * Obtain aTransformer
using the style sheet * referenced by theXSL
constant. * * @return a new Tranformer instance * @throws Exception if a Tranformer instance could not be created */ private static Transformer getTransformer(String documentNS) throws Exception { TransformerFactory factory = Util.createTransformerFactory(); String xslToApply; if (FACES_CONFIG_1_X_DEFAULT_NS.equals(documentNS)) { xslToApply = FACES_TO_1_1_PRIVATE_XSL; } else if (FACELETS_1_0_DEFAULT_NS.equals(documentNS)) { xslToApply = FACELETS_TO_2_0_XSL; } else { throw new IllegalStateException(); } return factory .newTransformer(new StreamSource(getInputStream(ConfigManager .class.getResource(xslToApply)))); } /** * @return anInputStream
to the resource referred to by *url
* @param url sourceURL
* @throws IOException if an error occurs */ private static InputStream getInputStream(URL url) throws IOException { URLConnection conn = url.openConnection(); conn.setUseCaches(false); return new BufferedInputStream(conn.getInputStream()); } private DocumentBuilder getNonValidatingBuilder() throws Exception { DocumentBuilderFactory tFactory = DbfFactory.getFactory(); tFactory.setValidating(false); DocumentBuilder tBuilder = tFactory.newDocumentBuilder(); tBuilder.setEntityResolver(DbfFactory.FACES_ENTITY_RESOLVER); tBuilder.setErrorHandler(DbfFactory.FACES_ERROR_HANDLER); return tBuilder; } private DocumentBuilder getBuilderForSchema(Schema schema) throws Exception { this.factory = DbfFactory.getFactory(); try { factory.setSchema(schema); } catch (UnsupportedOperationException upe) { return getNonValidatingBuilder(); } DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(DbfFactory.FACES_ENTITY_RESOLVER); builder.setErrorHandler(DbfFactory.FACES_ERROR_HANDLER); return builder; } } // END ParseTask /** ** This
*/ private static class URITask implements CallableCallable
will be used by {@link ConfigManager#getConfigDocuments(javax.servlet.ServletContext, java.util.List, java.util.concurrent.ExecutorService, boolean)}. * It represents one or more URLs to configuration resources that require * processing. *> { private ConfigurationResourceProvider provider; private ServletContext sc; // -------------------------------------------------------- Constructors /** * Constructs a new URITask
instance. * @param provider theConfigurationResourceProvider
from * which zero or moreURL
s will be returned * @param sc theServletContext
of the current application */ public URITask(ConfigurationResourceProvider provider, ServletContext sc) { this.provider = provider; this.sc = sc; } // ----------------------------------------------- Methods from Callable /** * @return zero or moreURL
instances * @throws Exception if an Exception is thrown by the underlying *ConfigurationResourceProvider
*/ public Collectioncall() throws Exception { Collection untypedCollection = provider.getResources(sc); Iterator untypedCollectionIterator = untypedCollection.iterator(); Collection result = Collections.emptyList(); if (untypedCollectionIterator.hasNext()) { Object cur = untypedCollectionIterator.next(); // account for older versions of the provider that return Collection . if (cur instanceof URL) { result = new ArrayList (untypedCollection.size()); result.add(new URI(((URL)cur).toExternalForm())); while (untypedCollectionIterator.hasNext()) { cur = untypedCollectionIterator.next(); result.add(new URI(((URL)cur).toExternalForm())); } } else { result = (Collection ) untypedCollection; } } return result; } } // END URITask }