bug 466387: Add property for initialized context. Tune ConfigManager.hasBeenInitialized so that ConfigureListener.contextDestroyed will not harm the correct ServletContext.
diff --git a/hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-1.jar b/hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-2.jar
old mode 100755
new mode 100644
similarity index 76%
rename from hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-1.jar
rename to hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-2.jar
index 6e8922e..a47d741
--- a/hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-1.jar
+++ b/hotfix/plugins/org.glassfish.com.sun.faces_2.1.18.v201304210537-virgo-2.jar
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$1.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$1.class
new file mode 100644
index 0000000..3eb0e03
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$1.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$AnnotationScanTask.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$AnnotationScanTask.class
new file mode 100644
index 0000000..b7d47eb
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$AnnotationScanTask.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ParseTask.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ParseTask.class
new file mode 100644
index 0000000..ba4031c
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ParseTask.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ProvideMetadataToAnnotationScanTask.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ProvideMetadataToAnnotationScanTask.class
new file mode 100644
index 0000000..2f83f9e
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$ProvideMetadataToAnnotationScanTask.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$URITask.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$URITask.class
new file mode 100644
index 0000000..4ce5d47
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager$URITask.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.class
new file mode 100644
index 0000000..5903a13
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.java b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.java
new file mode 100644
index 0000000..1f0e966
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigManager.java
@@ -0,0 +1,1209 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 1997-2010 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.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.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.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 org.w3c.dom.*;
+import org.xml.sax.SAXParseException;
+
+/**
+ * <p>
+ * This class manages the initialization of each web application that uses
+ * JSF.
+ * </p>
+ */
+public class ConfigManager {
+
+ private static final String SERVLET_CONTEXT_INITIALISED = "com.sap.core.context.initialized";
+
+ private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();
+
+ private static final Pattern JAR_PATTERN = Pattern.compile("(.*/(\\S*\\.jar)).*(/faces-config.xml|/*.\\.faces-config.xml)");
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ */
+ private static final List<ConfigurationResourceProvider> FACES_CONFIG_RESOURCE_PROVIDERS;
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ */
+ private static final List<ConfigurationResourceProvider> FACELET_TAGLIBRARY_RESOURCE_PROVIDERS;
+
+ /**
+ * <p>
+ * The <code>ConfigManager</code> will multithread the calls to the
+ * <code>ConfigurationResourceProvider</code>s as well as any calls
+ * to parse a resources into a DOM. By default, we'll use only 5 threads
+ * per web application.
+ * </p>
+ */
+ private static final int NUMBER_OF_TASK_THREADS = 5;
+
+ /**
+ * <p>
+ * There is only once instance of <code>ConfigManager</code>.
+ * <p>
+ */
+ private static final ConfigManager CONFIG_MANAGER = new ConfigManager();
+
+
+ /**
+ * 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
+ * <code>/WEB-INF/faces-config.xml</code>.
+ */
+ public static final String WEB_INF_MARKER = "com.sun.faces.webinf";
+
+
+ /**
+ * <p>
+ * Contains each <code>ServletContext</code> that we've initialized.
+ * The <code>ServletContext</code> will be removed when the application
+ * is destroyed.
+ * </p>
+ */
+ @SuppressWarnings({"CollectionWithoutInitialCapacity"})
+ private List<ServletContext> initializedContexts =
+ new CopyOnWriteArrayList<ServletContext>();
+
+ /**
+ * <p>
+ * The chain of {@link ConfigProcessor} instances to processing of
+ * faces-config documents.
+ * </p>
+ */
+ private static final ConfigProcessor FACES_CONFIG_PROCESSOR_CHAIN;
+
+
+ /**
+ * <p>
+ * The chain of {@link ConfigProcessor} instances to processing of
+ * facelet-taglib documents.
+ * </p>
+ */
+ private static 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";
+
+
+ static {
+
+ // initialize the resource providers for faces-config documents
+ List<ConfigurationResourceProvider> facesConfigProviders =
+ new ArrayList<ConfigurationResourceProvider>(3);
+ facesConfigProviders.add(new MojarraFacesConfigResourceProvider());
+ facesConfigProviders.add(new MetaInfFacesConfigResourceProvider());
+ facesConfigProviders.add(new WebFacesConfigResourceProvider());
+ FACES_CONFIG_RESOURCE_PROVIDERS = Collections.unmodifiableList(facesConfigProviders);
+
+ // initialize the resource providers for facelet-taglib documents
+ List<ConfigurationResourceProvider> faceletTaglibProviders =
+ new ArrayList<ConfigurationResourceProvider>(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()
+ };
+ 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 <code>ConfigManager</code> instance
+ */
+ public static ConfigManager getInstance() {
+
+ return CONFIG_MANAGER;
+
+ }
+
+
+ /**
+ * <p>
+ * This method bootstraps JSF based on the parsed configuration resources.
+ * </p>
+ *
+ * @param sc the <code>ServletContext</code> for the application that
+ * requires initialization
+ */
+ public void initialize(ServletContext sc) {
+
+ if (!hasBeenInitialized(sc)) {
+ initializedContexts.add(sc);
+ sc.setAttribute(SERVLET_CONTEXT_INITIALISED, true);
+ 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 webInfFacesConfigInfo =
+ new FacesConfigInfo(facesDocuments[facesDocuments.length - 1]);
+
+ facesDocuments = sortDocuments(facesDocuments, webInfFacesConfigInfo);
+ InitFacesContext context = (InitFacesContext) FacesContext.getCurrentInstance();
+
+ InjectionProvider containerConnector =
+ InjectionProviderFactory.createInstance(context.getExternalContext());
+ context.getAttributes().put(INJECTION_PROVIDER_KEY, containerConnector);
+
+ boolean isFaceletsDisabled =
+ isFaceletsDisabled(webConfig, webInfFacesConfigInfo);
+ if (!webInfFacesConfigInfo.isWebInfFacesConfig() || !webInfFacesConfigInfo.isMetadataComplete()) {
+ // execute the Task responsible for finding annotation classes
+ ProvideMetadataToAnnotationScanTask taskMetadata = new ProvideMetadataToAnnotationScanTask(facesDocuments, containerConnector);
+ Future<Map<Class<? extends Annotation>,Set<Class<?>>>> annotationScan;
+ if (executor != null) {
+ annotationScan = executor.submit(new AnnotationScanTask(sc, context, taskMetadata));
+ pushTaskToContext(sc, annotationScan);
+ } else {
+ annotationScan =
+ new FutureTask<Map<Class<? extends Annotation>,Set<Class<?>>>>(new AnnotationScanTask(sc, context, taskMetadata));
+ ((FutureTask) annotationScan).run();
+ }
+ pushTaskToContext(sc, annotationScan);
+ }
+
+ //see if the app is running in a HA enabled env
+ if (containerConnector instanceof HighAvailabilityEnabler) {
+ ((HighAvailabilityEnabler)containerConnector).enableHighAvailability(sc);
+ }
+ // process the ordered documents
+ FACES_CONFIG_PROCESSOR_CHAIN.process(sc, facesDocuments);
+ if (!isFaceletsDisabled) {
+ FACELET_TAGLIB_CONFIG_PROCESSOR_CHAIN.process(
+ sc, getConfigDocuments(sc,
+ getFaceletConfigResourceProviders(),
+ executor,
+ validating));
+ }
+
+ publishPostConfigEvent();
+ } catch (Exception e) {
+ // clear out any configured factories
+ releaseFactories();
+ Throwable t = e;
+ if (!(e instanceof ConfigurationException)) {
+ t = new ConfigurationException("CONFIGURATION FAILED! " + t.getMessage(), t);
+ }
+ throw (ConfigurationException)t;
+ } finally {
+ if (executor != null) {
+ executor.shutdown();
+ }
+ sc.removeAttribute(ANNOTATIONS_SCAN_TASK_KEY);
+ }
+ }
+
+ }
+
+
+
+
+ /**
+ * <p>
+ * This method will remove any information about the application.
+ * </p>
+ * @param sc the <code>ServletContext</code> for the application that
+ * needs to be removed
+ */
+ public void destroy(ServletContext sc) {
+
+ releaseFactories();
+ initializedContexts.remove(sc);
+ sc.removeAttribute(SERVLET_CONTEXT_INITIALISED);
+
+ }
+
+
+ /**
+ * @param sc the <code>ServletContext</code> for the application in question
+ * @return <code>true</code> if this application has already been initialized,
+ * otherwise returns </code>fase</code>
+ */
+ public boolean hasBeenInitialized(ServletContext sc) {
+ if (sc.getAttribute(SERVLET_CONTEXT_INITIALISED) == null) {
+ return initializedContexts.contains(sc);
+ } else {
+ sc.removeAttribute(SERVLET_CONTEXT_INITIALISED);
+ return true;
+ }
+ }
+
+
+ /**
+ * @return the results of the annotation scan task
+ */
+ public static Map<Class<? extends Annotation>,Set<Class<?>>> getAnnotatedClasses(FacesContext ctx) {
+
+ Map<String, Object> appMap =
+ ctx.getExternalContext().getApplicationMap();
+ //noinspection unchecked
+ Future<Map<Class<? extends Annotation>,Set<Class<?>>>> scanTask =
+ (Future<Map<Class<? extends Annotation>,Set<Class<?>>>>) appMap.get(ANNOTATIONS_SCAN_TASK_KEY);
+ try {
+ return ((scanTask != null)
+ ? scanTask.get()
+ : Collections.<Class<? extends Annotation>,Set<Class<?>>>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<ConfigurationResourceProvider> getFacesConfigResourceProviders() {
+
+ return getConfigurationResourceProviders(FACES_CONFIG_RESOURCE_PROVIDERS,
+ FacesConfig);
+
+ }
+
+
+ private List<ConfigurationResourceProvider> getFaceletConfigResourceProviders() {
+
+ return getConfigurationResourceProviders(FACELET_TAGLIBRARY_RESOURCE_PROVIDERS,
+ FaceletConfig);
+
+ }
+
+
+ private List<ConfigurationResourceProvider> getConfigurationResourceProviders(List<ConfigurationResourceProvider> defaultProviders,
+ ConfigurationResourceProviderFactory.ProviderType providerType) {
+
+ ConfigurationResourceProvider[] custom =
+ ConfigurationResourceProviderFactory.createProviders(providerType);
+ if (custom.length == 0) {
+ return defaultProviders;
+ } else {
+ List<ConfigurationResourceProvider> list = new ArrayList<ConfigurationResourceProvider>();
+ 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);
+ }
+
+ }
+
+
+ /**
+ * <p>
+ * Sort the <code>faces-config</code> documents found on the classpath
+ * and those specified by the <code>javax.faces.CONFIG_FILES</code> context
+ * init parameter.
+ * </p>
+ *
+ * @param facesDocuments an array of <em>all</em> <code>faces-config</code>
+ * 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);
+
+ List<String> absoluteOrdering = webInfFacesConfig.getAbsoluteOrdering();
+
+ if (len > 1) {
+ List<DocumentOrderingWrapper> list =
+ new ArrayList<DocumentOrderingWrapper>();
+ 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;
+ }
+
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ *
+ * <p>
+ * 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 <code>ConfigParser</code>s.
+ * </p>
+ *
+ * @param webconfig configuration for this application
+ * @param facesConfigInfo object representing WEB-INF/faces-config.xml
+ * @return <code>true</code> 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 provided <code>Future</code> to the specified <code>ServletContext</code>.
+ */
+ private void pushTaskToContext(ServletContext sc,
+ Future<Map<Class<? extends Annotation>,Set<Class<?>>>> scanTask) {
+
+ sc.setAttribute(ANNOTATIONS_SCAN_TASK_KEY, scanTask);
+
+ }
+
+
+ /**
+ * Publishes a {@link javax.faces.event.PostConstructApplicationEvent} event for the current
+ * {@link Application} instance.
+ */
+ private void publishPostConfigEvent() {
+
+ FacesContext ctx = FacesContext.getCurrentInstance();
+ Application app = ctx.getApplication();
+ app.publishEvent(ctx,
+ PostConstructApplicationEvent.class,
+ Application.class,
+ app);
+
+ }
+
+
+ /**
+ * <p>
+ * Obtains an array of <code>Document</code>s to be processed
+ * by {@link ConfigManager#FACES_CONFIG_PROCESSOR_CHAIN}.
+ * </p>
+ *
+ * @param sc the <code>ServletContext</code> for the application to be
+ * processed
+ * @param providers <code>List</code> of <code>ConfigurationResourceProvider</code>
+ * instances that provide the URL of the documents to parse.
+ * @param executor the <code>ExecutorService</code> used to dispatch parse
+ * request to
+ * @param validating flag indicating whether or not the documents
+ * should be validated
+ * @return an array of <code>DocumentInfo</code>s
+ */
+ private static DocumentInfo[] getConfigDocuments(ServletContext sc,
+ List<ConfigurationResourceProvider> providers,
+ ExecutorService executor,
+ boolean validating) {
+
+ List<FutureTask<Collection<URI>>> urlTasks =
+ new ArrayList<FutureTask<Collection<URI>>>(providers.size());
+ for (ConfigurationResourceProvider p : providers) {
+ FutureTask<Collection<URI>> t =
+ new FutureTask<Collection<URI>>(new URITask(p, sc));
+ urlTasks.add(t);
+ if (executor != null) {
+ executor.execute(t);
+ } else {
+ t.run();
+ }
+ }
+
+ List<FutureTask<DocumentInfo>> docTasks =
+ new ArrayList<FutureTask<DocumentInfo>>(providers.size() << 1);
+
+ for (FutureTask<Collection<URI>> t : urlTasks) {
+ try {
+ Collection<URI> l = t.get();
+ for (URI u : l) {
+ FutureTask<DocumentInfo> d =
+ new FutureTask<DocumentInfo>(new ParseTask(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<DocumentInfo> docs = new ArrayList<DocumentInfo>(docTasks.size());
+ for (FutureTask<DocumentInfo> 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 <code>ExecutorService</code> 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;
+ Set<URI> uris = null;
+ Set<String> jarNames = null;
+
+ private ProvideMetadataToAnnotationScanTask(DocumentInfo [] documentInfos,
+ InjectionProvider containerConnector) {
+ this.documentInfos = documentInfos;
+ this.containerConnector = containerConnector;
+ }
+
+ private void initializeIvars() {
+ if (null != uris || null != jarNames) {
+ assert(null != uris && null != jarNames);
+ return;
+ }
+ uris = new HashSet<URI>(documentInfos.length);
+ jarNames = new HashSet<String>(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);
+ }
+ }
+ }
+ }
+ }
+
+ private Set<URI> getAnnotationScanURIs() {
+ initializeIvars();
+
+ return uris;
+
+ }
+
+ private Set<String> getJarNames() {
+ initializeIvars();
+
+ 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 <code>Set</code>
+ * of classes that have been annotated with a standard Faces annotation.
+ */
+ private static class AnnotationScanTask implements Callable<Map<Class<? extends Annotation>,Set<Class<?>>>> {
+
+ private ServletContext sc;
+ private InitFacesContext facesContext;
+ private AnnotationProvider provider;
+ private ProvideMetadataToAnnotationScanTask metadataGetter;
+
+ // -------------------------------------------------------- Constructors
+
+
+ public AnnotationScanTask(ServletContext sc, InitFacesContext facesContext, ProvideMetadataToAnnotationScanTask metadataGetter) {
+ this.facesContext = facesContext;
+ this.provider = AnnotationProviderFactory.createAnnotationProvider(sc);
+ this.metadataGetter = metadataGetter;
+
+ }
+
+
+ // ----------------------------------------------- Methods from Callable
+
+
+ public Map<Class<? extends Annotation>,Set<Class<?>>> call() throws Exception {
+
+ Timer t = Timer.getInstance();
+ if (t != null) {
+ t.startTiming();
+ }
+
+ // We are executing on a different thread.
+ facesContext.callSetCurrentInstance();
+ Set<URI> 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());
+ scanUris = Collections.emptySet();
+ } else {
+ // This InjectionProvider is capable of annotation scanning only
+ scanUris = metadataGetter.getAnnotationScanURIs();
+ }
+ //AnnotationScanner scanner = new AnnotationScanner(sc);
+ Map<Class<? extends Annotation>,Set<Class<?>>> annotatedClasses =
+ provider.getAnnotatedClasses(scanUris);
+
+ if (t != null) {
+ t.stopTiming();
+ t.logResult("Configuration annotation scan complete.");
+ }
+
+ return annotatedClasses;
+
+ }
+
+
+ } // END AnnotationScanTask
+
+
+ /**
+ * <p>
+ * This <code>Callable</code> 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.
+ * </p>
+ */
+ private static class ParseTask implements Callable<DocumentInfo> {
+ private static final String JAVAEE_SCHEMA_DEFAULT_NS =
+ "http://java.sun.com/xml/ns/javaee";
+ private static final String EMPTY_FACES_CONFIG =
+ "com/sun/faces/empty-faces-config.xml";
+ private URI documentURI;
+ private DocumentBuilderFactory factory;
+ private boolean validating;
+
+ // -------------------------------------------------------- Constructors
+
+
+ /**
+ * <p>
+ * Constructs a new ParseTask instance
+ * </p>
+ *
+ * @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(boolean validating, URI documentURI)
+ throws Exception {
+
+ 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
+
+
+ /**
+ * @return <code>Document</code> based on <code>documentURI</code>.
+ * @throws Exception if an error occurs during the process of building a
+ * <code>Document</code>
+ */
+ 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 = doc.getDocumentElement().getNamespaceURI();
+ 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");
+ DbfFactory.FacesSchema schema;
+ if (version != null) {
+ String versionStr = version.getValue();
+ if ("2.0".equals(versionStr)) {
+ if ("facelet-taglib".equals(documentElement.getLocalName())) {
+ schema = DbfFactory.FacesSchema.FACELET_TAGLIB_20;
+ } else {
+ schema = DbfFactory.FacesSchema.FACES_20;
+ }
+ } else if ("2.1".equals(versionStr)) {
+ if ("facelet-taglib".equals(documentElement.getLocalName())) {
+ schema = DbfFactory.FacesSchema.FACELET_TAGLIB_20;
+ } else {
+ schema = DbfFactory.FacesSchema.FACES_21;
+ }
+ } else if ("1.2".equals(versionStr)) {
+ schema = 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());
+ DbfFactory.FacesSchema schemaToApply;
+ if (FACES_CONFIG_1_X_DEFAULT_NS.equals(documentNS)) {
+ schemaToApply = DbfFactory.FacesSchema.FACES_11;
+ } else if (FACELETS_1_0_DEFAULT_NS.equals(documentNS)) {
+ schemaToApply = 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 a <code>Transformer</code> using the style sheet
+ * referenced by the <code>XSL</code> 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 an <code>InputStream</code> to the resource referred to by
+ * <code>url</code>
+ * @param url source <code>URL</code>
+ * @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(DbfFactory.FacesSchema schema)
+ throws Exception {
+ schema.initSchema();
+ this.factory = DbfFactory.getFactory();
+
+ try {
+ factory.setSchema(schema.getSchema());
+ } 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
+
+
+ /**
+ * <p>
+ * This <code>Callable</code> 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.
+ * </p>
+ */
+ private static class URITask implements Callable<Collection<URI>> {
+
+ private ConfigurationResourceProvider provider;
+ private ServletContext sc;
+
+
+ // -------------------------------------------------------- Constructors
+
+
+ /**
+ * Constructs a new <code>URITask</code> instance.
+ * @param provider the <code>ConfigurationResourceProvider</code> from
+ * which zero or more <code>URL</code>s will be returned
+ * @param sc the <code>ServletContext</code> of the current application
+ */
+ public URITask(ConfigurationResourceProvider provider,
+ ServletContext sc) {
+ this.provider = provider;
+ this.sc = sc;
+ }
+
+
+ // ----------------------------------------------- Methods from Callable
+
+
+ /**
+ * @return zero or more <code>URL</code> instances
+ * @throws Exception if an Exception is thrown by the underlying
+ * <code>ConfigurationResourceProvider</code>
+ */
+ public Collection<URI> call() throws Exception {
+ Collection untypedCollection = provider.getResources(sc);
+ Iterator untypedCollectionIterator = untypedCollection.iterator();
+ Collection<URI> result = Collections.emptyList();
+ if (untypedCollectionIterator.hasNext()) {
+ Object cur = untypedCollectionIterator.next();
+ // account for older versions of the provider that return Collection<URL>.
+ if (cur instanceof URL) {
+ result = new ArrayList<URI>(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<URI>) untypedCollection;
+ }
+ }
+
+ return result;
+ }
+
+ } // END URITask
+
+
+}
\ No newline at end of file
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$1.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$1.class
new file mode 100644
index 0000000..1a471f8
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$1.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor$Monitor.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor$Monitor.class
new file mode 100644
index 0000000..45f99b8
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor$Monitor.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor.class
new file mode 100644
index 0000000..2cb55c3
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebConfigResourceMonitor.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor$WebXmlHandler.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor$WebXmlHandler.class
new file mode 100644
index 0000000..1a15bb4
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor$WebXmlHandler.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor.class
new file mode 100644
index 0000000..393b9cc
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener$WebXmlProcessor.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.class b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.class
new file mode 100644
index 0000000..751cc7a
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.class
Binary files differ
diff --git a/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.java b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.java
new file mode 100644
index 0000000..f853735
--- /dev/null
+++ b/hotfix/source/org.glassfish.com.sun.faces_2.1.18/ConfigureListener.java
@@ -0,0 +1,1120 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright (c) 1997-2012 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 com.sun.faces.application.ApplicationAssociate;
+import com.sun.faces.application.WebappLifecycleListener;
+import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableGroovyScripting;
+import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableLazyBeanValidation;
+import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableThreading;
+import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.ForceLoadFacesConfigFiles;
+import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.VerifyFacesConfigObjects;
+import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.JavaxFacesProjectStage;
+import com.sun.faces.config.InitFacesContext;
+import com.sun.faces.config.WebConfiguration.WebContextInitParameter;
+import com.sun.faces.el.ChainTypeCompositeELResolver;
+import com.sun.faces.el.ELContextImpl;
+import com.sun.faces.el.ELContextListenerImpl;
+import com.sun.faces.el.ELUtils;
+import com.sun.faces.el.FacesCompositeELResolver;
+import com.sun.faces.mgbean.BeanBuilder;
+import com.sun.faces.mgbean.BeanManager;
+import com.sun.faces.scripting.groovy.GroovyHelper;
+import com.sun.faces.scripting.groovy.GroovyHelperFactory;
+import com.sun.faces.util.FacesLogger;
+import com.sun.faces.util.MessageUtils;
+import com.sun.faces.util.MojarraThreadFactory;
+import com.sun.faces.util.ReflectionUtils;
+import com.sun.faces.util.Timer;
+import com.sun.faces.util.Util;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.application.ProjectStage;
+import javax.faces.context.FacesContext;
+import javax.faces.event.PreDestroyApplicationEvent;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextAttributeEvent;
+import javax.servlet.ServletContextAttributeListener;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.HttpSessionListener;
+import javax.servlet.jsp.JspApplicationContext;
+import javax.servlet.jsp.JspFactory;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <p>Parse all relevant JavaServer Faces configuration resources, and
+ * configure the Reference Implementation runtime environment.</p>
+ * <p/>
+ */
+public class ConfigureListener implements ServletRequestListener,
+ HttpSessionListener,
+ ServletRequestAttributeListener,
+ HttpSessionAttributeListener,
+ ServletContextAttributeListener,
+ ServletContextListener {
+
+
+ private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();
+
+ private ScheduledThreadPoolExecutor webResourcePool;
+
+ protected WebappLifecycleListener webAppListener;
+ protected WebConfiguration webConfig;
+
+
+ // ------------------------------------------ ServletContextListener Methods
+
+
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext context = sce.getServletContext();
+
+ Timer timer = Timer.getInstance();
+ if (timer != null) {
+ timer.startTiming();
+ }
+
+ InitFacesContext initContext = new InitFacesContext(context);
+
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ MessageFormat.format(
+ "ConfigureListener.contextInitialized({0})",
+ getServletContextIdentifier(context)));
+ }
+
+ webConfig = WebConfiguration.getInstance(context);
+ ConfigManager configManager = ConfigManager.getInstance();
+
+ if (configManager.hasBeenInitialized(context)) {
+ return;
+ }
+
+ // Check to see if the FacesServlet is present in the
+ // web.xml. If it is, perform faces configuration as normal,
+ // otherwise, simply return.
+ Object mappingsAdded = context.getAttribute(RIConstants.FACES_INITIALIZER_MAPPINGS_ADDED);
+ if (mappingsAdded != null) {
+ context.removeAttribute(RIConstants.FACES_INITIALIZER_MAPPINGS_ADDED);
+ }
+
+ WebXmlProcessor webXmlProcessor = new WebXmlProcessor(context);
+ if (mappingsAdded == null) {
+ if (!webXmlProcessor.isFacesServletPresent()) {
+ if (!webConfig.isOptionEnabled(ForceLoadFacesConfigFiles)) {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ "No FacesServlet found in deployment descriptor - bypassing configuration");
+ }
+ WebConfiguration.clear(context);
+ return;
+ }
+ } else {
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ "FacesServlet found in deployment descriptor - processing configuration.");
+ }
+ }
+ }
+
+
+ // bootstrap of faces required
+ webAppListener = new WebappLifecycleListener(context);
+ webAppListener.contextInitialized(sce);
+ ReflectionUtils.initCache(Thread.currentThread().getContextClassLoader());
+ Throwable caughtThrowable = null;
+
+ try {
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "jsf.config.listener.version",
+ getServletContextIdentifier(context));
+ }
+
+ if (webConfig.isOptionEnabled(VerifyFacesConfigObjects)) {
+ if (LOGGER.isLoggable(Level.WARNING)) {
+ LOGGER.warning("jsf.config.verifyobjects.development_only");
+ }
+ // if we're verifying, force bean validation to occur at startup as well
+ webConfig.overrideContextInitParameter(EnableLazyBeanValidation, false);
+ Verifier.setCurrentInstance(new Verifier());
+ }
+ initScripting();
+ configManager.initialize(context);
+ if (shouldInitConfigMonitoring()) {
+ initConfigMonitoring(context);
+ }
+
+ // Step 7, verify that all the configured factories are available
+ // and optionall that configured objects can be created.
+ Verifier v = Verifier.getCurrentInstance();
+ if (v != null && !v.isApplicationValid()) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.severe("jsf.config.verifyobjects.failures_detected");
+ StringBuilder sb = new StringBuilder(128);
+ for (String m : v.getMessages()) {
+ sb.append(m).append('\n');
+ }
+ LOGGER.severe(sb.toString());
+ }
+ }
+ registerELResolverAndListenerWithJsp(context, false);
+ ELContext elctx = new ELContextImpl(initContext.getApplication().getELResolver());
+ elctx.putContext(FacesContext.class, initContext);
+ initContext.setELContext(elctx);
+ ApplicationAssociate associate =
+ ApplicationAssociate.getInstance(context);
+ if (associate != null) {
+ associate.setContextName(getServletContextIdentifier(context));
+ BeanManager manager = associate.getBeanManager();
+ List<String> eagerBeans = manager.getEagerBeanNames();
+ if (!eagerBeans.isEmpty()) {
+ for (String name : eagerBeans) {
+ manager.create(name, initContext);
+ }
+ }
+ boolean isErrorPagePresent = webXmlProcessor.isErrorPagePresent();
+ associate.setErrorPagePresent(isErrorPagePresent);
+ context.setAttribute(RIConstants.ERROR_PAGE_PRESENT_KEY_NAME,
+ isErrorPagePresent);
+
+ }
+
+ webConfig.doPostBringupActions();
+
+ } catch (Throwable t) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.log(Level.SEVERE, "Critical error during deployment: ", t);
+ }
+ caughtThrowable = t;
+
+ } finally {
+ Verifier.setCurrentInstance(null);
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ "jsf.config.listener.version.complete");
+ }
+ if (timer != null) {
+ timer.stopTiming();
+ timer.logResult("Initialization of context " +
+ getServletContextIdentifier(context));
+ }
+ if (null != caughtThrowable) {
+ throw new RuntimeException(caughtThrowable);
+ }
+ }
+ }
+
+
+ public void contextDestroyed(ServletContextEvent sce) {
+ ServletContext context = sce.getServletContext();
+ InitFacesContext initContext = null;
+ boolean initialized = true;
+ try {
+ initContext = getInitFacesContext(context);
+ if (null == initContext) {
+ initContext = new InitFacesContext(context);
+ } else {
+ InitFacesContext.getThreadInitContextMap().put(Thread.currentThread(), initContext);
+ }
+
+ if (webAppListener != null) {
+ webAppListener.contextDestroyed(sce);
+ webAppListener = null;
+ }
+ if (webResourcePool != null) {
+ webResourcePool.shutdownNow();
+ }
+ if (!ConfigManager.getInstance().hasBeenInitialized(context)) {
+ initialized = false;
+ return;
+ }
+ GroovyHelper helper = GroovyHelper.getCurrentInstance(context);
+ if (helper != null) {
+ helper.setClassLoader();
+ }
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ "ConfigureListener.contextDestroyed({0})",
+ context.getServletContextName());
+ }
+
+
+ ELContext elctx = new ELContextImpl(initContext.getApplication().getELResolver());
+ elctx.putContext(FacesContext.class, initContext);
+ initContext.setELContext(elctx);
+ Application app = initContext.getApplication();
+ app.publishEvent(initContext,
+ PreDestroyApplicationEvent.class,
+ Application.class,
+ app);
+
+ } catch (Exception e) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.log(Level.SEVERE,
+ "Unexpected exception when attempting to tear down the Mojarra runtime",
+ e);
+ }
+ } finally {
+ if (initialized) {
+ ApplicationAssociate.clearInstance(context);
+ ApplicationAssociate.setCurrentInstance(null);
+ // Release the initialization mark on this web application
+ ConfigManager.getInstance().destroy(context);
+ FactoryFinder.releaseFactories();
+ ReflectionUtils.clearCache(Thread.currentThread()
+ .getContextClassLoader());
+ WebConfiguration.clear(context);
+ InitFacesContext.cleanupInitMaps(context);
+ }
+ }
+
+ }
+
+
+ // ------------------------------------- Methods from ServletRequestListener
+
+
+ public void requestDestroyed(ServletRequestEvent event) {
+ if (webAppListener != null) {
+ webAppListener.requestDestroyed(event);
+ }
+ }
+
+
+ public void requestInitialized(ServletRequestEvent event) {
+ if (webAppListener != null) {
+ webAppListener.requestInitialized(event);
+ }
+ InitFacesContext.cleanupInitMaps(event.getServletContext());
+ }
+
+
+ // ----------------------------------------- Methods from HttpSessionListener
+
+
+ public void sessionCreated(HttpSessionEvent event) {
+ if (webAppListener != null) {
+ webAppListener.sessionCreated(event);
+ }
+ }
+
+
+ public void sessionDestroyed(HttpSessionEvent event) {
+ if (webAppListener != null) {
+ webAppListener.sessionDestroyed(event);
+ }
+ }
+
+
+ // ---------------------------- Methods from ServletRequestAttributeListener
+
+
+ public void attributeAdded(ServletRequestAttributeEvent event) {
+ // ignored
+ }
+
+
+ public void attributeRemoved(ServletRequestAttributeEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeRemoved(event);
+ }
+ }
+
+
+ public void attributeReplaced(ServletRequestAttributeEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeReplaced(event);
+ }
+ }
+
+
+ // ------------------------------- Methods from HttpSessionAttributeListener
+
+
+ public void attributeAdded(HttpSessionBindingEvent event) {
+ // ignored
+ }
+
+
+ public void attributeRemoved(HttpSessionBindingEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeRemoved(event);
+ }
+ }
+
+
+ public void attributeReplaced(HttpSessionBindingEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeReplaced(event);
+ }
+ }
+
+
+ // ---------------------------- Methods from ServletContextAttributeListener
+
+
+ public void attributeAdded(ServletContextAttributeEvent event) {
+ // ignored
+ }
+
+ public void attributeRemoved(ServletContextAttributeEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeRemoved(event);
+ }
+ }
+
+ public void attributeReplaced(ServletContextAttributeEvent event) {
+ if (webAppListener != null) {
+ webAppListener.attributeReplaced(event);
+ }
+ }
+
+
+ // --------------------------------------------------------- Private Methods
+
+ private boolean shouldInitConfigMonitoring() {
+
+ boolean development = isDevModeEnabled();
+ boolean threadingOptionSpecified = webConfig.isSet(EnableThreading);
+ if (development && !threadingOptionSpecified) {
+ return true;
+ }
+ boolean threadingOption = webConfig.isOptionEnabled(EnableThreading);
+ return (development && threadingOptionSpecified && threadingOption);
+
+ }
+
+ private void initConfigMonitoring(ServletContext context) {
+
+ //noinspection unchecked
+ Collection<URI> webURIs =
+ (Collection<URI>) context.getAttribute("com.sun.faces.webresources");
+ if (isDevModeEnabled() && webURIs != null && !webURIs.isEmpty()) {
+ webResourcePool = new ScheduledThreadPoolExecutor(1, new MojarraThreadFactory("WebResourceMonitor"));
+ webResourcePool.scheduleAtFixedRate(new WebConfigResourceMonitor(context, webURIs),
+ 2000,
+ 2000,
+ TimeUnit.MILLISECONDS);
+ }
+ context.removeAttribute("com.sun.faces.webresources");
+
+ }
+
+ private void initScripting() {
+ if (webConfig.isOptionEnabled(EnableGroovyScripting)) {
+ GroovyHelper helper = GroovyHelperFactory.createHelper();
+ if (helper != null) {
+ helper.setClassLoader();
+ }
+ }
+ }
+
+
+ private boolean isDevModeEnabled() {
+
+ // interrogate the init parameter directly vs looking up the application
+ return "Development".equals(webConfig.getOptionValue(JavaxFacesProjectStage));
+
+ }
+
+
+ /**
+ * This method will be invoked {@link WebConfigResourceMonitor} when
+ * changes to any of the faces-config.xml files included in WEB-INF
+ * are modified.
+ */
+ private void reload(ServletContext sc) {
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "Reloading JSF configuration for context {0}",
+ getServletContextIdentifier(sc));
+ }
+ GroovyHelper helper = GroovyHelper.getCurrentInstance();
+ if (helper != null) {
+ helper.setClassLoader();
+ }
+ // tear down the application
+ try {
+ // this will only be true in the automated test usage scenario
+ if (null != webAppListener) {
+ List<HttpSession> sessions = webAppListener.getActiveSessions();
+ if (sessions != null) {
+ for (HttpSession session : sessions) {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "Invalidating Session {0}",
+ session.getId());
+ }
+ session.invalidate();
+ }
+ }
+ }
+ ApplicationAssociate associate = ApplicationAssociate.getInstance(sc);
+ if (associate != null) {
+ BeanManager manager = associate.getBeanManager();
+ for (Map.Entry<String, BeanBuilder> entry : manager.getRegisteredBeans().entrySet()) {
+ String name = entry.getKey();
+ BeanBuilder bean = entry.getValue();
+ if (ELUtils.Scope.APPLICATION.toString().equals(bean.getScope())) {
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "Removing application scoped managed bean: {0}",
+ name);
+ }
+ sc.removeAttribute(name);
+ }
+
+ }
+ }
+ // Release any allocated application resources
+ FactoryFinder.releaseFactories();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ FacesContext initContext = new InitFacesContext(sc);
+ ApplicationAssociate
+ .clearInstance(initContext.getExternalContext());
+ ApplicationAssociate.setCurrentInstance(null);
+ // Release the initialization mark on this web application
+ ConfigManager.getInstance().destroy(sc);
+ initContext.release();
+ ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader());
+ WebConfiguration.clear(sc);
+ }
+
+ // bring the application back up, avoid re-registration of certain JSP
+ // artifacts. No verification will be performed either to make this
+ // light weight.
+
+ // init a new WebAppLifecycleListener so that the cached ApplicationAssociate
+ // is removed.
+ webAppListener = new WebappLifecycleListener(sc);
+
+ FacesContext initContext = new InitFacesContext(sc);
+ ReflectionUtils
+ .initCache(Thread.currentThread().getContextClassLoader());
+
+ try {
+ ConfigManager configManager = ConfigManager.getInstance();
+ configManager.initialize(sc);
+
+
+ registerELResolverAndListenerWithJsp(sc, true);
+ ApplicationAssociate associate =
+ ApplicationAssociate.getInstance(sc);
+ if (associate != null) {
+ Boolean errorPagePresent = (Boolean) sc.getAttribute(RIConstants.ERROR_PAGE_PRESENT_KEY_NAME);
+ if (null != errorPagePresent) {
+ associate.setErrorPagePresent(errorPagePresent);
+ associate.setContextName(getServletContextIdentifier(sc));
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ initContext.release();
+ }
+
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "Reload complete.",
+ getServletContextIdentifier(sc));
+ }
+
+ }
+
+
+ private static String getServletContextIdentifier(ServletContext context) {
+ if (context.getMajorVersion() == 2 && context.getMinorVersion() < 5) {
+ return context.getServletContextName();
+ } else {
+ return context.getContextPath();
+ }
+ }
+
+
+ private static boolean isJspTwoOne(ServletContext context) {
+
+ // The following try/catch is a hack to work around
+ // a bug in Tomcat 6 where JspFactory.getDefaultFactory() will
+ // return null unless JspRuntimeContext has been loaded.
+ try {
+ Class.forName("org.apache.jasper.compiler.JspRuntimeContext");
+ } catch (ClassNotFoundException ignored) {
+ // ignored
+ }
+
+ if (JspFactory.getDefaultFactory() == null) {
+ return false;
+ }
+ try {
+ JspFactory.class.getMethod("getJspApplicationContext",
+ ServletContext.class);
+ } catch (Exception e) {
+ return false;
+ }
+ try {
+ JspFactory.getDefaultFactory().getJspApplicationContext(context);
+ } catch (Throwable e) {
+ return false;
+ }
+ return true;
+
+ }
+
+ public void registerELResolverAndListenerWithJsp(ServletContext context, boolean reloaded) {
+
+ if (webConfig.isSet(WebContextInitParameter.ExpressionFactory)
+ || !isJspTwoOne(context)) {
+
+ // first try to load a factory defined in web.xml
+ if (!installExpressionFactory(context,
+ webConfig.getOptionValue(
+ WebContextInitParameter.ExpressionFactory))) {
+
+ throw new ConfigurationException(
+ MessageUtils.getExceptionMessageString(
+ MessageUtils.INCORRECT_JSP_VERSION_ID,
+ WebContextInitParameter.ExpressionFactory.getDefaultValue(),
+ WebContextInitParameter.ExpressionFactory.getQualifiedName()));
+
+ }
+
+ } else {
+
+ // JSP 2.1 specific check
+ if (JspFactory.getDefaultFactory().getJspApplicationContext(context) == null) {
+ return;
+ }
+
+ // register an empty resolver for now. It will be populated after the
+ // first request is serviced.
+ FacesCompositeELResolver compositeELResolverForJsp =
+ new ChainTypeCompositeELResolver(FacesCompositeELResolver.ELResolverChainType.JSP);
+ ApplicationAssociate associate =
+ ApplicationAssociate.getInstance(context);
+ if (associate != null) {
+ associate.setFacesELResolverForJsp(compositeELResolverForJsp);
+ }
+
+ // get JspApplicationContext.
+ JspApplicationContext jspAppContext = JspFactory.getDefaultFactory()
+ .getJspApplicationContext(context);
+
+ // cache the ExpressionFactory instance in ApplicationAssociate
+ if (associate != null) {
+ associate.setExpressionFactory(jspAppContext.getExpressionFactory());
+ }
+
+ // register compositeELResolver with JSP
+ try {
+ jspAppContext.addELResolver(compositeELResolverForJsp);
+ }
+ catch (IllegalStateException e) {
+ ApplicationFactory factory = (ApplicationFactory)
+ FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
+ Application app = factory.getApplication();
+ if (app.getProjectStage() != ProjectStage.UnitTest && !reloaded) {
+ throw e;
+ }
+ }
+
+ // register JSF ELContextListenerImpl with Jsp
+ ELContextListenerImpl elContextListener = new ELContextListenerImpl();
+ jspAppContext.addELContextListener(elContextListener);
+ }
+ }
+
+ private boolean installExpressionFactory(ServletContext sc,
+ String elFactoryType) {
+
+ if (elFactoryType == null) {
+ return false;
+ }
+ try {
+ ExpressionFactory factory = (ExpressionFactory)
+ Util.loadClass(elFactoryType, this).newInstance();
+ ApplicationAssociate associate =
+ ApplicationAssociate.getInstance(sc);
+ if (associate != null) {
+ associate.setExpressionFactory(factory);
+ }
+ return true;
+ } catch (Exception e) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.severe(MessageFormat.format("Unable to instantiate ExpressionFactory ''{0}''",
+ elFactoryType));
+ }
+ return false;
+ }
+
+ }
+
+
+ private InitFacesContext getInitFacesContext(ServletContext context) {
+ Map initContextServletContext = InitFacesContext.getInitContextServletContextMap();
+ Set entries = initContextServletContext.entrySet();
+ InitFacesContext initContext = null;
+ for (Iterator iterator1 = entries.iterator(); iterator1.hasNext();) {
+ Map.Entry entry1 = (Map.Entry)iterator1.next();
+ Object initContextKey = entry1.getKey();
+ Object value1 = entry1.getValue();
+ if (context == value1) {
+ initContext = (InitFacesContext)initContextKey;
+ break;
+ }
+ }
+ return initContext;
+ }
+
+ // ----------------------------------------------------------- Inner classes
+
+
+ /**
+ * <p>Processes a web application's deployment descriptor looking
+ * for a reference to <code>javax.faces.webapp.FacesServlet</code>.</p>
+ */
+ private static class WebXmlProcessor {
+
+ private static final String WEB_XML_PATH = "/WEB-INF/web.xml";
+ private static final String WEB_FRAGMENT_PATH = "META-INF/web-fragment.xml";
+
+ private boolean facesServletPresent;
+ private boolean errorPagePresent;
+
+
+ /**
+ * <p>When instantiated, the web.xml of the current application
+ * will be scanned looking for a references to the
+ * <code>FacesServlet</code>. <code>isFacesServletPresent()</code>
+ * will return the appropriate value based on the scan.</p>
+ *
+ * @param context the <code>ServletContext</code> for the application
+ * of interest
+ */
+ WebXmlProcessor(ServletContext context) {
+
+ if (context != null) {
+ scanForFacesServlet(context);
+ }
+
+ } // END WebXmlProcessor
+
+
+ /**
+ * @return <code>true</code> if the <code>WebXmlProcessor</code>
+ * detected a <code>FacesServlet</code> entry, otherwise return
+ * <code>false</code>.</p>
+ */
+ boolean isFacesServletPresent() {
+
+ return facesServletPresent;
+
+ } // END isFacesServletPresent
+
+
+ /**
+ * @return <code>true</code> if <code>WEB-INF/web.xml</code> contains
+ * a <code><error-page></code> element.
+ */
+ boolean isErrorPagePresent() {
+
+ return errorPagePresent;
+
+ }
+
+
+ /**
+ * <p>Parse the web.xml for the current application and scan
+ * for a FacesServlet entry, if found, set the
+ * <code>facesServletPresent</code> property to true.
+ *
+ * @param context the ServletContext instance for this application
+ */
+ private void scanForFacesServlet(ServletContext context) {
+ InputStream in = context.getResourceAsStream(WEB_XML_PATH);
+ if (in == null) {
+ if (context.getMajorVersion() < 3) {
+ throw new ConfigurationException("no web.xml present");
+ }
+ }
+ SAXParserFactory factory = getConfiguredFactory();
+ if (in != null) {
+ try {
+ SAXParser parser = factory.newSAXParser();
+ parser.parse(in, new WebXmlHandler());
+ } catch (Exception e) {
+ warnProcessingError(e, context);
+ facesServletPresent = true;
+ return;
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (Exception ioe) {
+ if (LOGGER.isLoggable(Level.FINEST)) {
+ LOGGER.log(Level.FINEST, "Closing stream", ioe);
+ }
+ }
+ }
+ }
+ }
+ if (!facesServletPresent && context.getMajorVersion() >= 3) {
+ ClassLoader cl = Util.getCurrentLoader(this);
+ Enumeration<URL> urls;
+ try {
+ urls = cl.getResources(WEB_FRAGMENT_PATH);
+ } catch (IOException ioe) {
+ throw new ConfigurationException(ioe);
+ }
+ if (urls != null) {
+ while (urls.hasMoreElements() && !facesServletPresent) {
+ InputStream fragmentStream = null;
+ try {
+ URL url = urls.nextElement();
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ fragmentStream = conn.getInputStream();
+ SAXParser parser = factory.newSAXParser();
+ parser.parse(fragmentStream, new WebXmlHandler());
+ } catch (Exception e) {
+ warnProcessingError(e, context);
+ facesServletPresent = true;
+ return;
+ } finally {
+ if (fragmentStream != null) {
+ try {
+ fragmentStream.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+ }
+ }
+ }
+ }
+
+ } // END scanForFacesServlet
+
+ /**
+ * <p>Return a <code>SAXParserFactory</code> instance that is
+ * non-validating and is namespace aware.</p>
+ *
+ * @return configured <code>SAXParserFactory</code>
+ */
+ private SAXParserFactory getConfiguredFactory() {
+
+ SAXParserFactory factory = Util.createSAXParserFactory();
+ factory.setValidating(false);
+ factory.setNamespaceAware(true);
+ return factory;
+
+ } // END getConfiguredFactory
+
+
+ private void warnProcessingError(Exception e, ServletContext sc) {
+
+ if (LOGGER.isLoggable(Level.WARNING)) {
+ LOGGER.log(Level.WARNING,
+ MessageFormat.format(
+ "jsf.configuration.web.xml.parse.failed",
+ getServletContextIdentifier(sc)),
+ e);
+ }
+
+ }
+
+
+ /**
+ * <p>A simple SAX handler to process the elements of interested
+ * within a web application's deployment descriptor.</p>
+ */
+ private class WebXmlHandler extends DefaultHandler {
+
+ private static final String ERROR_PAGE = "error-page";
+ private static final String SERVLET_CLASS = "servlet-class";
+ private static final String FACES_SERVLET =
+ "javax.faces.webapp.FacesServlet";
+
+ private boolean servletClassFound;
+ @SuppressWarnings({"StringBufferField"})
+ private StringBuffer content;
+
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException {
+
+ return new InputSource(new StringReader(""));
+
+ } // END resolveEntity
+
+
+ public void startElement(String uri, String localName,
+ String qName, Attributes attributes)
+ throws SAXException {
+
+ if (!errorPagePresent) {
+ if (ERROR_PAGE.equals(localName)) {
+ errorPagePresent = true;
+ return;
+ }
+ }
+ if (!facesServletPresent) {
+ if (SERVLET_CLASS.equals(localName)) {
+ servletClassFound = true;
+ //noinspection StringBufferWithoutInitialCapacity
+ content = new StringBuffer();
+ } else {
+ servletClassFound = false;
+ }
+ }
+
+
+ } // END startElement
+
+
+ public void characters(char[] ch, int start, int length)
+ throws SAXException {
+
+ if (servletClassFound && !facesServletPresent) {
+ content.append(ch, start, length);
+ }
+
+ } // END characters
+
+
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+
+ if (servletClassFound && !facesServletPresent) {
+ if (FACES_SERVLET.equals(content.toString().trim())) {
+ facesServletPresent = true;
+ }
+ }
+
+ } // END endElement
+
+ } // END WebXmlHandler
+
+ } // END WebXmlProcessor
+
+
+ private class WebConfigResourceMonitor implements Runnable {
+
+ private List<Monitor> monitors;
+ private ServletContext sc;
+
+ // -------------------------------------------------------- Constructors
+
+
+ public WebConfigResourceMonitor(ServletContext sc, Collection<URI> uris) {
+
+ assert (uris != null);
+ this.sc = sc;
+ for (URI uri : uris) {
+ if (monitors == null) {
+ monitors = new ArrayList<Monitor>(uris.size());
+ }
+ try {
+ Monitor m = new Monitor(uri);
+ monitors.add(m);
+ } catch (IOException ioe) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.severe("Unable to setup resource monitor for "
+ + uri.toString()
+ + ". Resource will not be monitored for changes.");
+ }
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ ioe.toString(),
+ ioe);
+ }
+ }
+ }
+
+ }
+
+
+ // ----------------------------------------------- Methods from Runnable
+
+ /**
+ * PENDING javadocs
+ */
+ public void run() {
+
+ assert (monitors != null);
+ boolean reloaded = false;
+ for (Iterator<Monitor> i = monitors.iterator(); i.hasNext(); ) {
+ Monitor m = i.next();
+ try {
+ if (m.hasBeenModified()) {
+ if (!reloaded) {
+ reloaded = true;
+ }
+ }
+ } catch (IOException ioe) {
+ if (LOGGER.isLoggable(Level.SEVERE)) {
+ LOGGER.severe("Unable to access url "
+ + m.uri.toString()
+ + ". Monitoring for this resource will no longer occur.");
+ }
+ if (LOGGER.isLoggable(Level.FINE)) {
+ LOGGER.log(Level.FINE,
+ ioe.toString(),
+ ioe);
+ }
+ i.remove();
+ }
+ }
+ if (reloaded) {
+ reload(sc);
+ }
+
+ }
+
+
+ // ------------------------------------------------------- Inner Classes
+
+
+ private class Monitor {
+
+ private URI uri;
+ private long timestamp = -1;
+
+ // ---------------------------------------------------- Constructors
+
+
+ Monitor(URI uri) throws IOException {
+
+ this.uri = uri;
+ this.timestamp = getLastModified();
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "Monitoring {0} for modifications",
+ uri.toURL().toExternalForm());
+ }
+
+ }
+
+
+ // ----------------------------------------- Package Private Methods
+
+
+ boolean hasBeenModified() throws IOException {
+ long temp = getLastModified();
+ if (timestamp < temp) {
+ timestamp = temp;
+ if (LOGGER.isLoggable(Level.INFO)) {
+ LOGGER.log(Level.INFO,
+ "{0} changed!",
+ uri.toURL().toExternalForm());
+ }
+ return true;
+ }
+ return false;
+
+ }
+
+
+ // ------------------------------------------------- Private Methods
+
+
+ private long getLastModified() throws IOException {
+
+ InputStream in = null;
+ try {
+ URLConnection conn = uri.toURL().openConnection();
+ conn.connect();
+ in = conn.getInputStream();
+ return conn.getLastModified();
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ }
+
+ } // END Monitor
+
+ } // END WebConfigResourceMonitor
+
+}
+