/** * Parse all relevant JavaServer Faces configuration resources, and configure the Reference * Implementation runtime environment. * * <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(); } 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); InitFacesContext initContext = new InitFacesContext(context); ReflectionUtils.initCache(Thread.currentThread().getContextClassLoader()); Throwable caughtThrowable = null; try { if (LOGGER.isLoggable(Level.INFO)) { LOGGER.log(Level.INFO, "jsf.config.listener.version", getServletContextIdentifier(context)); } // see if we need to disable our TLValidator Util.setHtmlTLVActive(webConfig.isOptionEnabled(EnableHtmlTagLibraryValidator)); 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); } Application app = initContext.getApplication(); app.subscribeToEvent(PostConstructViewMapEvent.class, UIViewRoot.class, webAppListener); app.subscribeToEvent(PreDestroyViewMapEvent.class, UIViewRoot.class, webAppListener); 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); initContext.release(); 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; try { initContext = new InitFacesContext(context); if (webAppListener != null) { webAppListener.contextDestroyed(sce); webAppListener = null; } if (webResourcePool != null) { webResourcePool.shutdownNow(); } if (!ConfigManager.getInstance().hasBeenInitialized(context)) { 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); Util.setNonFacesContextApplicationMap(null); } catch (Exception e) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log( Level.SEVERE, "Unexpected exception when attempting to tear down the Mojarra runtime", e); } } finally { ApplicationAssociate.clearInstance(initContext.getExternalContext()); ApplicationAssociate.setCurrentInstance(null); com.sun.faces.application.ApplicationImpl.clearInstance(initContext.getExternalContext()); com.sun.faces.application.InjectionApplicationFactory.clearInstance( initContext.getExternalContext()); // Release the initialization mark on this web application ConfigManager.getInstance().destory(context); if (initContext != null) { initContext.release(); } ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader()); WebConfiguration.clear(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); } } // ----------------------------------------- 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().destory(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; } } // ----------------------------------------------------------- Inner classes /** * Processes a web application's deployment descriptor looking for a reference to <code> * javax.faces.webapp.FacesServlet</code>. */ 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; /** * 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. * * @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>. */ 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; } /** * 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) { // ignored; } } } } 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 /** * Return a <code>SAXParserFactory</code> instance that is non-validating and is namespace * aware. * * @return configured <code>SAXParserFactory</code> */ private SAXParserFactory getConfiguredFactory() { SAXParserFactory factory = SAXParserFactory.newInstance(); 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); } } /** * A simple SAX handler to process the elements of interested within a web application's * deployment descriptor. */ 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 }
/** Create and configure DocumentBuilderFactory instances. */ public class DbfFactory { private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); private static final String AS_INSTALL_ROOT = "com.sun.aas.installRoot"; private static final String AS_SCHEMA_DIR = System.getProperty(AS_INSTALL_ROOT) + File.separatorChar + "lib" + File.separatorChar + "schemas" + File.separatorChar; private static final String AS_DTD_DIR = System.getProperty(AS_INSTALL_ROOT) + File.separatorChar + "lib" + File.separatorChar + "dtds" + File.separatorChar; /** Location of the facelet-taglib 2.0 Schema */ private static final String FACELET_TAGLIB_2_0_XSD = "/com/sun/faces/web-facelettaglibrary_2_0.xsd"; /** Location of the Faces 2.0 Schema */ private static final String FACES_2_0_XSD = "/com/sun/faces/web-facesconfig_2_0.xsd"; /** Location of the Faces 2.0 Schema */ private static final String FACES_2_1_XSD = "/com/sun/faces/web-facesconfig_2_1.xsd"; /** Location of the Faces 1.2 Schema */ private static final String FACES_1_2_XSD = "/com/sun/faces/web-facesconfig_1_2.xsd"; /** Location of the Faces private 1.1 Schema */ private static final String FACES_1_1_XSD = "/com/sun/faces/web-facesconfig_1_1.xsd"; /** Location of the facelet taglib xsd within GlassFish. */ private static final String FACELET_TAGLIB_2_0_XSD_FILE = AS_SCHEMA_DIR + "web-facelettaglibrary_2_0.xsd"; /** Location of the facelet taglib xsd within GlassFish. */ private static final String FACELET_TAGLIB_2_2_XSD_FILE = AS_SCHEMA_DIR + "web-facelettaglibrary_2_2.xsd"; /** Location of the faces 2.0 xsd within GlassFish. */ private static final String FACES_2_0_XSD_FILE = AS_SCHEMA_DIR + "web-facesconfig_2_0.xsd"; /** Location of the faces 2.1 xsd within GlassFish. */ private static final String FACES_2_1_XSD_FILE = AS_SCHEMA_DIR + "web-facesconfig_2_0.xsd"; /** Location of the faces 1.2 xsd within GlassFish. */ private static final String FACES_1_2_XSD_FILE = AS_SCHEMA_DIR + "web-facesconfig_1_2.xsd"; /** Our cached 2.0 facelet-taglib Schema object for validation */ private static Schema FACELET_TAGLIB_20_SCHEMA; /** Our cached 2.2 facelet-taglib Schema object for validation */ private static Schema FACELET_TAGLIB_22_SCHEMA; /** Our cached 2.0 Schema object for validation */ private static Schema FACES_20_SCHEMA; /** Our cached 2.1 Schema object for validation */ private static Schema FACES_21_SCHEMA; /** Our cached 1.2 Schema object for validation */ private static Schema FACES_12_SCHEMA; /** Our cached 1.1 Schema object for validation */ private static Schema FACES_11_SCHEMA; /** EntityResolver */ public static final EntityResolver FACES_ENTITY_RESOLVER = new FacesEntityResolver(); public enum FacesSchema { FACES_20(FACES_20_SCHEMA), FACES_21(FACES_21_SCHEMA), FACES_12(FACES_12_SCHEMA), FACES_11(FACES_11_SCHEMA), FACELET_TAGLIB_20(FACELET_TAGLIB_20_SCHEMA), FACELET_TAGLIB_22(FACELET_TAGLIB_22_SCHEMA); private Schema schema; FacesSchema(Schema schema) { this.schema = schema; } public Schema getSchema() { return schema; } } /** ErrorHandler */ public static final FacesErrorHandler FACES_ERROR_HANDLER = new FacesErrorHandler(); static { initStatics(); } // ---------------------------------------------------------- Public Methods public static DocumentBuilderFactory getFactory() { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); return factory; } /** Init our cache objects. */ private static void initStatics() { // First, cache the various files // PENDING_RELEASE (rlubke) clean this up try { URL url = DbfFactory.class.getResource(FACES_1_2_XSD); if (url == null) { // try to load from the file File f = new File(FACES_1_2_XSD_FILE); if (!f.exists()) { throw new IllegalStateException("Unable to find web-facesconfig_1_2.xsd"); } url = f.toURI().toURL(); } URLConnection conn = url.openConnection(); conn.setUseCaches(false); InputStream in = conn.getInputStream(); SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER); FACES_12_SCHEMA = factory.newSchema(new StreamSource(in)); url = DbfFactory.class.getResource(FACES_1_1_XSD); conn = url.openConnection(); conn.setUseCaches(false); in = conn.getInputStream(); factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER); FACES_11_SCHEMA = factory.newSchema(new StreamSource(in)); url = DbfFactory.class.getResource(FACES_2_1_XSD); if (url == null) { // try to load from the file File f = new File(FACES_2_1_XSD_FILE); if (!f.exists()) { throw new IllegalStateException("Unable to find web-facesconfig_2_1.xsd"); } url = f.toURI().toURL(); } conn = url.openConnection(); conn.setUseCaches(false); in = conn.getInputStream(); factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER); FACES_21_SCHEMA = factory.newSchema(new StreamSource(in)); url = DbfFactory.class.getResource(FACES_2_0_XSD); if (url == null) { // try to load from the file File f = new File(FACES_2_0_XSD_FILE); if (!f.exists()) { throw new IllegalStateException("Unable to find web-facesconfig_2_0.xsd"); } url = f.toURI().toURL(); } conn = url.openConnection(); conn.setUseCaches(false); in = conn.getInputStream(); factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER); FACES_20_SCHEMA = factory.newSchema(new StreamSource(in)); url = DbfFactory.class.getResource(FACELET_TAGLIB_2_0_XSD); if (url == null) { // try to load from the file File f = new File(FACELET_TAGLIB_2_0_XSD_FILE); if (!f.exists()) { throw new IllegalStateException("Unable to find web-facelettaglibrary_2_0.xsd"); } url = f.toURI().toURL(); } conn = url.openConnection(); conn.setUseCaches(false); in = conn.getInputStream(); factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER); FACELET_TAGLIB_20_SCHEMA = factory.newSchema(new StreamSource(in)); } catch (Exception e) { throw new ConfigurationException(e); } } // ----------------------------------------------------------- Inner Classes private static class FacesEntityResolver extends DefaultHandler implements LSResourceResolver { /** Contains associations between grammar name and the physical resource. */ private static final String[][] DTD_SCHEMA_INFO = { { "web-facesconfig_1_0.dtd", "/com/sun/faces/web-facesconfig_1_0.dtd", AS_DTD_DIR + "web-facesconfig_1_0.dtd" }, { "web-facesconfig_1_1.dtd", "/com/sun/faces/web-facesconfig_1_1.dtd", AS_DTD_DIR + "web-facesconfig_1_1.dtd" }, {"web-facesconfig_2_0.xsd", FACES_2_0_XSD, FACES_2_0_XSD_FILE}, {"web-facesconfig_2_1.xsd", FACES_2_1_XSD, FACES_2_1_XSD_FILE}, {"facelet-taglib_1_0.dtd", "/com/sun/faces/facelet-taglib_1_0.dtd", null}, {"web-facelettaglibrary_2_0.xsd", FACELET_TAGLIB_2_0_XSD, FACELET_TAGLIB_2_0_XSD_FILE}, {"web-facesconfig_1_2.xsd", FACES_1_2_XSD, FACES_1_2_XSD_FILE}, {"web-facesconfig_1_1.xsd", FACES_1_1_XSD, null}, {"javaee_5.xsd", "/com/sun/faces/javaee_5.xsd", AS_SCHEMA_DIR + "javaee_5.xsd"}, { "javaee_web_services_client_1_2.xsd", "/com/sun/faces/javaee_web_services_client_1_2.xsd", AS_SCHEMA_DIR + "javaee_web_services_client_1_2.xsd" }, {"xml.xsd", "/com/sun/faces/xml.xsd", AS_SCHEMA_DIR + "xml.xsd"}, {"datatypes.dtd", "/com/sun/faces/datatypes.dtd", AS_SCHEMA_DIR + "datatypes.dtd"}, {"XMLSchema.dtd", "/com/sun/faces/XMLSchema.dtd", AS_SCHEMA_DIR + "XMLSchema.dtd"} }; /** Contains mapping between grammar name and the local URL to the physical resource. */ private HashMap<String, String> entities = new HashMap<String, String>(12, 1.0f); // -------------------------------------------------------- Constructors public FacesEntityResolver() { // Add mappings between last segment of system ID and // the expected local physical resource. If the resource // cannot be found, then rely on default entity resolution // and hope a firewall isn't in the way or a proxy has // been configured for (String[] aDTD_SCHEMA_INFO : DTD_SCHEMA_INFO) { URL url = this.getClass().getResource(aDTD_SCHEMA_INFO[1]); if (url == null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, "jsf.config.cannot_resolve_entities", new Object[] {aDTD_SCHEMA_INFO[1], aDTD_SCHEMA_INFO[0]}); } // the resource isn't available on the classpath, so // assume that we're running within a GF environment String path = aDTD_SCHEMA_INFO[2]; if (path != null) { File f = new File(path); if (f.exists()) { try { url = f.toURI().toURL(); } catch (MalformedURLException mue) { if (LOGGER.isLoggable(Level.SEVERE)) { LOGGER.log(Level.SEVERE, mue.toString(), mue); } } if (url == null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, "jsf.config.cannot_resolve_entities", new Object[] {aDTD_SCHEMA_INFO[1], aDTD_SCHEMA_INFO[2]}); } } else { entities.put(aDTD_SCHEMA_INFO[0], url.toString()); } } } } else { entities.put(aDTD_SCHEMA_INFO[0], url.toString()); } } } // END JsfEntityResolver // ----------------------------------------- Methods from DefaultHandler /** * Resolves the physical resource using the last segment of the <code>systemId</code> (e.g. * http://java.sun.com/dtds/web-facesconfig_1_1.dtd, the last segment would be * web-facesconfig_1_1.dtd). If a mapping cannot be found for the segment, then defer to the * <code>DefaultHandler</code> for resolution. */ public InputSource resolveEntity(String publicId, String systemId) throws SAXException { // publicId is ignored. Resolution performed using // the systemId. // If no system ID, defer to superclass if (systemId == null) { InputSource result; try { result = super.resolveEntity(publicId, null); } catch (Exception e) { throw new SAXException(e); } return result; } String grammarName = systemId.substring(systemId.lastIndexOf('/') + 1); String entityURL = entities.get(grammarName); InputSource source; if (entityURL == null) { // we don't have a registered mapping, so defer to our // superclass for resolution if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Unknown entity, deferring to superclass."); } try { source = super.resolveEntity(publicId, systemId); } catch (Exception e) { throw new SAXException(e); } } else { try { source = new InputSource(new URL(entityURL).openStream()); } catch (Exception e) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, "jsf.config.cannot_create_inputsource", entityURL); } source = null; } } // Set the System ID of the InputSource with the URL of the local // resource - necessary to prevent parsing errors if (source != null) { source.setSystemId(entityURL); if (publicId != null) { source.setPublicId(publicId); } } return source; } // END resolveEntity public LSInput resolveResource( String type, String namespaceURI, String publicId, String systemId, String baseURI) { try { InputSource source = resolveEntity(publicId, systemId); if (source != null) { return new Input(source.getByteStream()); } } catch (Exception e) { throw new ConfigurationException(e); } return null; } } // END FacesEntityResolver private static class FacesErrorHandler implements ErrorHandler { public void warning(SAXParseException exception) throws SAXException { // do nothing } public void error(SAXParseException exception) throws SAXException { throw exception; } public void fatalError(SAXParseException exception) throws SAXException { throw exception; } } // END FacesErrorHandler private static final class Input implements LSInput { InputStream in; public Input(InputStream in) { this.in = in; } public Reader getCharacterStream() { return null; } public void setCharacterStream(Reader characterStream) {} public InputStream getByteStream() { return in; } public void setByteStream(InputStream byteStream) {} public String getStringData() { return null; } public void setStringData(String stringData) {} public String getSystemId() { return null; } public void setSystemId(String systemId) {} public String getPublicId() { return null; } public void setPublicId(String publicId) {} public String getBaseURI() { return null; } public void setBaseURI(String baseURI) {} public String getEncoding() { return null; } public void setEncoding(String encoding) {} public boolean getCertifiedText() { return false; } public void setCertifiedText(boolean certifiedText) {} } }
/** * This <code>ConfigProcessor</code> handles all elements defined under <code>/faces-config/valiator * </code>. */ public class ValidatorConfigProcessor extends AbstractConfigProcessor { private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); /** /faces-config/validator */ private static final String VALIDATOR = "validator"; /** /faces-config/component/validator-id */ private static final String VALIDATOR_ID = "validator-id"; /** /faces-config/component/validator-class */ private static final String VALIDATOR_CLASS = "validator-class"; // -------------------------------------------- Methods from ConfigProcessor /** * @see ConfigProcessor#process(javax.servlet.ServletContext,com.sun.faces.config.DocumentInfo[]) */ public void process(ServletContext sc, DocumentInfo[] documentInfos) throws Exception { // process annotated Validators first as Validators configured // via config files take precedence processAnnotations(FacesValidator.class); for (int i = 0; i < documentInfos.length; i++) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Processing validator elements for document: ''{0}''", documentInfos[i].getSourceURI())); } Document document = documentInfos[i].getDocument(); String namespace = document.getDocumentElement().getNamespaceURI(); NodeList validators = document.getDocumentElement().getElementsByTagNameNS(namespace, VALIDATOR); if (validators != null && validators.getLength() > 0) { addValidators(validators, namespace); } } processDefaultValidatorIds(); invokeNext(sc, documentInfos); } // --------------------------------------------------------- Private Methods private void processDefaultValidatorIds() { Application app = getApplication(); Map<String, String> defaultValidatorInfo = app.getDefaultValidatorInfo(); for (Map.Entry<String, String> info : defaultValidatorInfo.entrySet()) { String defaultValidatorId = info.getKey(); boolean found = false; for (Iterator<String> registered = app.getValidatorIds(); registered.hasNext(); ) { if (defaultValidatorId.equals(registered.next())) { found = true; break; } } if (!found) { String msg = MessageFormat.format( "Default validator ''{0}'' does not reference a registered validator.", defaultValidatorId); throw new ConfigurationException(msg); } } } private void addValidators(NodeList validators, String namespace) throws XPathExpressionException { Application app = getApplication(); Verifier verifier = Verifier.getCurrentInstance(); for (int i = 0, size = validators.getLength(); i < size; i++) { Node validator = validators.item(i); NodeList children = ((Element) validator).getElementsByTagNameNS(namespace, "*"); String validatorId = null; String validatorClass = null; for (int c = 0, csize = children.getLength(); c < csize; c++) { Node n = children.item(c); if (n.getNodeType() == Node.ELEMENT_NODE) { if (VALIDATOR_ID.equals(n.getLocalName())) { validatorId = getNodeText(n); } else if (VALIDATOR_CLASS.equals(n.getLocalName())) { validatorClass = getNodeText(n); } } } if (validatorId != null && validatorClass != null) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Calling Application.addValidator({0},{1})", validatorId, validatorClass)); } boolean doAdd = true; if (validatorId.equals(BeanValidator.VALIDATOR_ID)) { doAdd = ApplicationConfigProcessor.isBeanValidatorAvailable(); } if (doAdd) { if (verifier != null) { verifier.validateObject(Verifier.ObjectType.VALIDATOR, validatorClass, Validator.class); } app.addValidator(validatorId, validatorClass); } } } } }
/** Class Documentation */ public class WebConfiguration { // Log instance for this class private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); // A Simple regular expression of allowable boolean values private static final Pattern ALLOWABLE_BOOLEANS = Pattern.compile("true|false"); // Key under which we store our WebConfiguration instance. private static final String WEB_CONFIG_KEY = "com.sun.faces.config.WebConfiguration"; // Logging level. Defaults to FINE private Level loggingLevel = Level.FINE; private Map<BooleanWebContextInitParameter, Boolean> booleanContextParameters = new EnumMap<BooleanWebContextInitParameter, Boolean>(BooleanWebContextInitParameter.class); private Map<WebContextInitParameter, String> contextParameters = new EnumMap<WebContextInitParameter, String>(WebContextInitParameter.class); private Map<WebContextInitParameter, Map<String, String>> facesConfigParameters = new EnumMap<WebContextInitParameter, Map<String, String>>(WebContextInitParameter.class); private Map<WebEnvironmentEntry, String> envEntries = new EnumMap<WebEnvironmentEntry, String>(WebEnvironmentEntry.class); private Map<WebContextInitParameter, String[]> cachedListParams; private Set<String> setParams = new HashSet<String>(); private ServletContext servletContext; private ArrayList<DeferredLoggingAction> deferredLoggingActions; private FaceletsConfiguration faceletsConfig; // ------------------------------------------------------------ Constructors private WebConfiguration(ServletContext servletContext) { this.servletContext = servletContext; String contextName = getServletContextName(); initSetList(servletContext); processBooleanParameters(servletContext, contextName); processInitParameters(servletContext, contextName); if (canProcessJndiEntries()) { processJndiEntries(contextName); } // build the cache of list type params cachedListParams = new HashMap<WebContextInitParameter, String[]>(3); getOptionValue(WebContextInitParameter.ResourceExcludes, " "); getOptionValue(WebContextInitParameter.DefaultSuffix, " "); getOptionValue(WebContextInitParameter.FaceletsViewMappings, ";"); } // ---------------------------------------------------------- Public Methods /** * Return the WebConfiguration instance for this application passing the result of * FacesContext.getCurrentInstance().getExternalContext() to {@link * #getInstance(javax.faces.context.ExternalContext)}. * * @return the WebConfiguration for this application or <code>null</code> if no FacesContext is * available. */ public static WebConfiguration getInstance() { FacesContext facesContext = FacesContext.getCurrentInstance(); return getInstance(facesContext.getExternalContext()); } /** * Return the WebConfiguration instance for this application. * * @param extContext the ExternalContext for this request * @return the WebConfiguration for this application */ public static WebConfiguration getInstance(ExternalContext extContext) { WebConfiguration config = (WebConfiguration) extContext.getApplicationMap().get(WEB_CONFIG_KEY); if (config == null) { return getInstance((ServletContext) extContext.getContext()); } else { return config; } } /** * Return the WebConfiguration instance for this application. * * @param servletContext the ServletContext * @return the WebConfiguration for this application or <code>null</code> if no WebConfiguration * could be located */ public static WebConfiguration getInstance(ServletContext servletContext) { WebConfiguration webConfig = (WebConfiguration) servletContext.getAttribute(WEB_CONFIG_KEY); if (webConfig == null) { webConfig = new WebConfiguration(servletContext); servletContext.setAttribute(WEB_CONFIG_KEY, webConfig); } return webConfig; } /** * @return The <code>ServletContext</code> originally used to construct this WebConfiguration * instance */ public ServletContext getServletContext() { return servletContext; } /** * Obtain the value of the specified boolean parameter * * @param param the parameter of interest * @return the value of the specified boolean parameter */ public boolean isOptionEnabled(BooleanWebContextInitParameter param) { if (booleanContextParameters.get(param) != null) { return booleanContextParameters.get(param); } else { return param.getDefaultValue(); } } /** * Obtain the value of the specified parameter * * @param param the parameter of interest * @return the value of the specified parameter */ public String getOptionValue(WebContextInitParameter param) { String result = contextParameters.get(param); if (null == result) { WebContextInitParameter alternate = param.getAlternate(); if (null != alternate) { result = contextParameters.get(alternate); } } return result; } public FaceletsConfiguration getFaceletsConfiguration() { if (null == faceletsConfig) { faceletsConfig = new FaceletsConfiguration(this); } return faceletsConfig; } public Map<String, String> getFacesConfigOptionValue( WebContextInitParameter param, boolean create) { Map<String, String> result = null; assert (null != facesConfigParameters); result = facesConfigParameters.get(param); if (null == result) { if (create) { result = new ConcurrentHashMap<String, String>(3); facesConfigParameters.put(param, result); } else { result = Collections.emptyMap(); } } return result; } public Map<String, String> getFacesConfigOptionValue(WebContextInitParameter param) { return getFacesConfigOptionValue(param, false); } public String[] getOptionValue(WebContextInitParameter param, String sep) { String[] result; assert (null != cachedListParams); if (null == (result = cachedListParams.get(param))) { String value = getOptionValue(param); if (null == value) { result = new String[0]; } else { result = Util.split(value, sep); } cachedListParams.put(param, result); } return result; } /** * Obtain the value of the specified env-entry * * @param entry the env-entry of interest * @return the value of the specified env-entry */ public String getEnvironmentEntry(WebEnvironmentEntry entry) { return envEntries.get(entry); } /** * @param param the init parameter of interest * @return <code>true</code> if the parameter was explicitly set, otherwise, <code>false</code> */ public boolean isSet(WebContextInitParameter param) { return isSet(param.getQualifiedName()); } /** * @param param the init parameter of interest * @return <code>true</code> if the parameter was explicitly set, otherwise, <code>false</code> */ public boolean isSet(BooleanWebContextInitParameter param) { return isSet(param.getQualifiedName()); } /** @return the name of this application */ public String getServletContextName() { if (servletContext.getMajorVersion() == 2 && servletContext.getMinorVersion() <= 4) { return servletContext.getServletContextName(); } else { return servletContext.getContextPath(); } } public void overrideContextInitParameter(BooleanWebContextInitParameter param, boolean value) { if (param == null) { return; } boolean oldVal = booleanContextParameters.put(param, value); if (LOGGER.isLoggable(Level.FINE) && oldVal != value) { LOGGER.log( Level.FINE, "Overriding init parameter {0}. Changing from {1} to {2}.", new Object[] {param.getQualifiedName(), oldVal, value}); } } public void overrideContextInitParameter(WebContextInitParameter param, String value) { if (param == null || value == null || value.length() == 0) { return; } value = value.trim(); String oldVal = contextParameters.put(param, value); cachedListParams.remove(param); if (oldVal != null) { if (LOGGER.isLoggable(Level.FINE) && !(oldVal.equals(value))) { LOGGER.log( Level.FINE, "Overriding init parameter {0}. Changing from {1} to {2}.", new Object[] {param.getQualifiedName(), oldVal, value}); } } } public void doPostBringupActions() { if (deferredLoggingActions != null) { for (DeferredLoggingAction loggingAction : deferredLoggingActions) { loggingAction.log(); } } } // ------------------------------------------------- Package Private Methods static void clear(ServletContext servletContext) { servletContext.removeAttribute(WEB_CONFIG_KEY); } // --------------------------------------------------------- Private Methods /** * Is the configured value valid against the default boolean pattern. * * @param param the boolean parameter * @param value the configured value * @return <code>true</code> if the value is valid, otherwise <code>false</code> */ private boolean isValueValid(BooleanWebContextInitParameter param, String value) { if (!ALLOWABLE_BOOLEANS.matcher(value).matches()) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log( Level.WARNING, "jsf.config.webconfig.boolconfig.invalidvalue", new Object[] {value, param.getQualifiedName(), "true|false"}); } return false; } return true; } /** * Process all boolean context initialization parameters. * * @param servletContext the ServletContext of interest * @param contextName the context name */ private void processBooleanParameters(ServletContext servletContext, String contextName) { // process boolean contxt parameters for (BooleanWebContextInitParameter param : BooleanWebContextInitParameter.values()) { String strValue = servletContext.getInitParameter(param.getQualifiedName()); boolean value; if (strValue != null && strValue.length() > 0 && param.isDeprecated()) { BooleanWebContextInitParameter alternate = param.getAlternate(); if (LOGGER.isLoggable(Level.WARNING)) { if (alternate != null) { queueLoggingAction( new DeferredBooleanParameterLoggingAction( param, Level.WARNING, "jsf.config.webconfig.param.deprecated", new Object[] { contextName, param.getQualifiedName(), alternate.getQualifiedName() })); } else { queueLoggingAction( new DeferredBooleanParameterLoggingAction( param, Level.WARNING, "jsf.config.webconfig.param.deprecated.no_replacement", new Object[] {contextName, param.getQualifiedName()})); } } if (alternate != null) { if (isValueValid(param, strValue)) { value = Boolean.valueOf(strValue); } else { value = param.getDefaultValue(); } if (LOGGER.isLoggable(Level.INFO) && alternate != null) { queueLoggingAction( new DeferredBooleanParameterLoggingAction( param, Level.INFO, ((value) ? "jsf.config.webconfig.configinfo.reset.enabled" : "jsf.config.webconfig.configinfo.reset.disabled"), new Object[] {contextName, alternate.getQualifiedName()})); } booleanContextParameters.put(alternate, value); } continue; } if (!param.isDeprecated()) { if (strValue == null) { value = param.getDefaultValue(); } else { if (isValueValid(param, strValue)) { value = Boolean.valueOf(strValue); } else { value = param.getDefaultValue(); } } // first param processed should be // com.sun.faces.displayConfiguration if (BooleanWebContextInitParameter.DisplayConfiguration.equals(param) && value) { loggingLevel = Level.INFO; } if (LOGGER.isLoggable(loggingLevel)) { LOGGER.log( loggingLevel, ((value) ? "jsf.config.webconfig.boolconfiginfo.enabled" : "jsf.config.webconfig.boolconfiginfo.disabled"), new Object[] {contextName, param.getQualifiedName()}); } booleanContextParameters.put(param, value); } } } /** * Adds all com.sun.faces init parameter names to a list. This allows callers to determine if a * parameter was explicitly set. * * @param servletContext the ServletContext of interest */ private void initSetList(ServletContext servletContext) { for (Enumeration e = servletContext.getInitParameterNames(); e.hasMoreElements(); ) { String name = e.nextElement().toString(); if (name.startsWith("com.sun.faces") || name.startsWith("javax.faces")) { setParams.add(name); } } } /** * @param name the param name * @return <code>true</code> if the name was explicitly specified */ private boolean isSet(String name) { return setParams.contains(name); } /** * Process all non-boolean context initialization parameters. * * @param servletContext the ServletContext of interest * @param contextName the context name */ private void processInitParameters(ServletContext servletContext, String contextName) { for (WebContextInitParameter param : WebContextInitParameter.values()) { String value = servletContext.getInitParameter(param.getQualifiedName()); if (value != null && value.length() > 0 && param.isDeprecated()) { WebContextInitParameter alternate = param.getAlternate(); DeprecationLoggingStrategy strategy = param.getDeprecationLoggingStrategy(); if (strategy == null || strategy.shouldBeLogged(this)) { if (LOGGER.isLoggable(Level.WARNING)) { if (alternate != null) { queueLoggingAction( new DeferredParameterLoggingAction( param, Level.WARNING, "jsf.config.webconfig.param.deprecated", new Object[] { contextName, param.getQualifiedName(), alternate.getQualifiedName() })); } else { queueLoggingAction( new DeferredParameterLoggingAction( param, Level.WARNING, "jsf.config.webconfig.param.deprecated.no_replacement", new Object[] {contextName, param.getQualifiedName()})); } } } if (alternate != null) { queueLoggingAction( new DeferredParameterLoggingAction( param, Level.INFO, "jsf.config.webconfig.configinfo.reset", new Object[] {contextName, alternate.getQualifiedName(), value})); contextParameters.put(alternate, value); } continue; } if ((value == null || value.length() == 0) && !param.isDeprecated()) { value = param.getDefaultValue(); } if (value == null || value.length() == 0) { continue; } if (value.length() > 0) { if (LOGGER.isLoggable(loggingLevel)) { LOGGER.log( loggingLevel, "jsf.config.webconfig.configinfo", new Object[] {contextName, param.getQualifiedName(), value}); } contextParameters.put(param, value); } else { if (LOGGER.isLoggable(loggingLevel)) { LOGGER.log( loggingLevel, "jsf.config.webconfig.option.notconfigured", new Object[] {contextName, param.getQualifiedName()}); } } } } /** * Process all JNDI entries. * * @param contextName the context name */ private void processJndiEntries(String contextName) { Context initialContext = null; try { initialContext = new InitialContext(); } catch (NamingException ne) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log(Level.WARNING, ne.toString(), ne); } } if (initialContext != null) { // process environment entries for (WebEnvironmentEntry entry : WebEnvironmentEntry.values()) { String entryName = entry.getQualifiedName(); String value = null; try { value = (String) initialContext.lookup(entryName); } catch (NamingException root) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine(root.toString()); } } if (value != null) { if (LOGGER.isLoggable(Level.INFO)) { // special logic for ClientStateSavingPassword if (!entry.equals(WebEnvironmentEntry.ClientStateSavingPassword)) { if (LOGGER.isLoggable(loggingLevel)) { LOGGER.log( loggingLevel, "jsf.config.webconfig.enventryinfo", new Object[] {contextName, entryName, value}); } } else { if (LOGGER.isLoggable(loggingLevel)) { LOGGER.log( loggingLevel, "jsf.config.webconfig.enventry.clientencrypt", contextName); } } } envEntries.put(entry, value); } } } } private boolean canProcessJndiEntries() { try { Util.getCurrentLoader(this).loadClass("javax.naming.InitialContext"); } catch (Exception e) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine( "javax.naming is unavailable. JNDI entries related to Mojarra configuration will not be processed."); } return false; } return true; } private void queueLoggingAction(DeferredLoggingAction loggingAction) { if (deferredLoggingActions == null) { deferredLoggingActions = new ArrayList<DeferredLoggingAction>(); } deferredLoggingActions.add(loggingAction); } // ------------------------------------------------------------------- Enums /** * An <code>enum</code> of all non-boolean context initalization parameters recognized by the * implementation. */ public enum WebContextInitParameter { // implementation note: // if a parameter is to be deprecated, // then the <name>Deprecated enum element // *must* appear after the one that is taking // its place. The reporting logic depends on this ManagedBeanFactoryDecorator("com.sun.faces.managedBeanFactoryDecoratorClass", ""), StateSavingMethod("javax.faces.STATE_SAVING_METHOD", "server"), FaceletsSuffix(ViewHandler.FACELETS_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_FACELETS_SUFFIX), DefaultSuffix(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME, ViewHandler.DEFAULT_SUFFIX), JavaxFacesConfigFiles("javax.faces.CONFIG_FILES", ""), JavaxFacesProjectStage("javax.faces.PROJECT_STAGE", "Production"), AlternateLifecycleId("javax.faces.LIFECYCLE_ID", ""), ResourceExcludes( ResourceHandler.RESOURCE_EXCLUDES_PARAM_NAME, ResourceHandler.RESOURCE_EXCLUDES_DEFAULT_VALUE + " .groovy"), NumberOfViews("com.sun.faces.numberOfViewsInSession", "15"), NumberOfViewsDeprecated("com.sun.faces.NUMBER_OF_VIEWS_IN_SESSION", "15", true, NumberOfViews), NumberOfLogicalViews("com.sun.faces.numberOfLogicalViews", "15"), NumberOfLogicalViewsDeprecated( "com.sun.faces.NUMBER_OF_VIEWS_IN_LOGICAL_VIEW_IN_SESSION", "15", true, NumberOfLogicalViews), NumberOfConcurrentFlashUsers("com.sun.faces.numberOfConcerrentFlashUsers", "5000"), NumberOfFlashesBetweenFlashReapings( "com.sun.faces.numberOfFlashesBetweenFlashReapings", "5000"), InjectionProviderClass("com.sun.faces.injectionProvider", ""), SerializationProviderClass("com.sun.faces.serializationProvider", ""), ResponseBufferSize("com.sun.faces.responseBufferSize", "1024"), FaceletsBufferSize("javax.faces.FACELETS_BUFFER_SIZE", "1024"), FaceletsBufferSizeDeprecated( "facelets.BUFFER_SIZE", "1024", true, FaceletsBufferSize, new FaceletsConfigParamLoggingStrategy()), ClientStateWriteBufferSize("com.sun.faces.clientStateWriteBufferSize", "8192"), ResourceBufferSize("com.sun.faces.resourceBufferSize", "2048"), ExpressionFactory("com.sun.faces.expressionFactory", "com.sun.el.ExpressionFactoryImpl"), ClientStateTimeout("com.sun.faces.clientStateTimeout", ""), DefaultResourceMaxAge( "com.sun.faces.defaultResourceMaxAge", "604800000" // 7 days ), ResourceUpdateCheckPeriod( "com.sun.faces.resourceUpdateCheckPeriod", "5" // in minutes ), CompressableMimeTypes("com.sun.faces.compressableMimeTypes", ""), DisableUnicodeEscaping("com.sun.faces.disableUnicodeEscaping", "auto"), FaceletsDefaultRefreshPeriod("javax.faces.FACELETS_REFRESH_PERIOD", "2"), FaceletsDefaultRefreshPeriodDeprecated( "facelets.REFRESH_PERIOD", "2", true, FaceletsDefaultRefreshPeriod, new FaceletsConfigParamLoggingStrategy()), FaceletsResourceResolver(ResourceResolver.FACELETS_RESOURCE_RESOLVER_PARAM_NAME, ""), FaceletsResourceResolverDeprecated( "facelets.RESOURCE_RESOLVER", "", true, FaceletsResourceResolver, new FaceletsConfigParamLoggingStrategy()), FaceletsViewMappings(ViewHandler.FACELETS_VIEW_MAPPINGS_PARAM_NAME, ""), FaceletsViewMappingsDeprecated( "facelets.VIEW_MAPPINGS", "", true, FaceletsViewMappings, new FaceletsConfigParamLoggingStrategy()), FaceletsLibraries("javax.faces.FACELETS_LIBRARIES", ""), FaceletsLibrariesDeprecated( "facelets.LIBRARIES", "", true, FaceletsLibraries, new FaceletsConfigParamLoggingStrategy()), FaceletsDecorators("javax.faces.FACELETS_DECORATORS", ""), FaceletsDecoratorsDeprecated( "facelets.DECORATORS", "", true, FaceletsDecorators, new FaceletsConfigParamLoggingStrategy()), DuplicateJARPattern("com.sun.faces.duplicateJARPattern", ""), ValidateEmptyFields(UIInput.VALIDATE_EMPTY_FIELDS_PARAM_NAME, "auto"), FullStateSavingViewIds(StateManager.FULL_STATE_SAVING_VIEW_IDS_PARAM_NAME, ""), AnnotationScanPackages("com.sun.faces.annotationScanPackages", ""), FaceletFactory("com.sun.faces.faceletFactory", ""), FaceletCache("com.sun.faces.faceletCache", ""), FaceletsProcessingFileExtensionProcessAs("", ""); private String defaultValue; private String qualifiedName; private WebContextInitParameter alternate; private boolean deprecated; private DeprecationLoggingStrategy loggingStrategy; // ---------------------------------------------------------- Public Methods public String getDefaultValue() { return defaultValue; } public String getQualifiedName() { return qualifiedName; } DeprecationLoggingStrategy getDeprecationLoggingStrategy() { return loggingStrategy; } // ------------------------------------------------- Package Private Methods WebContextInitParameter(String qualifiedName, String defaultValue) { this(qualifiedName, defaultValue, false, null); } WebContextInitParameter( String qualifiedName, String defaultValue, boolean deprecated, WebContextInitParameter alternate) { this.qualifiedName = qualifiedName; this.defaultValue = defaultValue; this.deprecated = deprecated; this.alternate = alternate; } WebContextInitParameter( String qualifiedName, String defaultValue, boolean deprecated, WebContextInitParameter alternate, DeprecationLoggingStrategy loggingStrategy) { this(qualifiedName, defaultValue, deprecated, alternate); this.loggingStrategy = loggingStrategy; } // --------------------------------------------------------- Private Methods private WebContextInitParameter getAlternate() { return alternate; } private boolean isDeprecated() { return deprecated; } } /** * An <code>enum</code> of all boolean context initalization parameters recognized by the * implementation. */ public enum BooleanWebContextInitParameter { // implementation note: // if a parameter is to be deprecated, // then the <name>Deprecated enum element // *must* appear after the one that is taking // its place. The reporting logic depends on this DisplayConfiguration("com.sun.faces.displayConfiguration", false), ValidateFacesConfigFiles("com.sun.faces.validateXml", false), VerifyFacesConfigObjects("com.sun.faces.verifyObjects", false), ForceLoadFacesConfigFiles("com.sun.faces.forceLoadConfiguration", false), DisableArtifactVersioning("com.sun.faces.disableVersionTracking", false, true, null), EnableHtmlTagLibraryValidator("com.sun.faces.enableHtmlTagLibValidator", false), EnableCoreTagLibraryValidator("com.sun.faces.enableCoreTagLibValidator", false), PreferXHTMLContentType("com.sun.faces.preferXHTML", false), PreferXHTMLContextTypeDeprecated( "com.sun.faces.PreferXHTML", false, true, PreferXHTMLContentType), CompressViewState("com.sun.faces.compressViewState", true), CompressViewStateDeprecated("com.sun.faces.COMPRESS_STATE", true, true, CompressViewState), CompressJavaScript("com.sun.faces.compressJavaScript", true), ExternalizeJavaScriptDeprecated("com.sun.faces.externalizeJavaScript", true, true, null), SendPoweredByHeader("com.sun.faces.sendPoweredByHeader", true), EnableJSStyleHiding("com.sun.faces.enableJSStyleHiding", false), EnableScriptInAttributeValue("com.sun.faces.enableScriptsInAttributeValues", true), WriteStateAtFormEnd("com.sun.faces.writeStateAtFormEnd", true), EnableLazyBeanValidation("com.sun.faces.enableLazyBeanValidation", true), EnableLoadBundle11Compatibility("com.sun.faces.enabledLoadBundle11Compatibility", false), EnableRestoreView11Compatibility("com.sun.faces.enableRestoreView11Compatibility", false), SerializeServerState("com.sun.faces.serializeServerState", false), EnableViewStateIdRendering("com.sun.faces.enableViewStateIdRendering", true), RegisterConverterPropertyEditors("com.sun.faces.registerConverterPropertyEditors", false), EnableGroovyScripting("com.sun.faces.enableGroovyScripting", false), DisableFaceletJSFViewHandler("javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER", false), DisableDefaultBeanValidator(BeanValidator.DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME, false), DateTimeConverterUsesSystemTimezone( "javax.faces.DATETIMECONVERTER_DEFAULT_TIMEZONE_IS_SYSTEM_TIMEZONE", false), FaceletsSkipComments("javax.faces.FACELETS_SKIP_COMMENTS", false), FaceletsSkipCommentsDeprecated( "facelets.SKIP_COMMENTS", false, true, FaceletsSkipComments, new FaceletsConfigParamLoggingStrategy()), PartialStateSaving(StateManager.PARTIAL_STATE_SAVING_PARAM_NAME, true), GenerateUniqueServerStateIds("com.sun.faces.generateUniqueServerStateIds", true), AutoCompleteOffOnViewState("com.sun.faces.autoCompleteOffOnViewState", true), EnableThreading("com.sun.faces.enableThreading", false), AllowTextChildren("com.sun.faces.allowTextChildren", false), CacheResourceModificationTimestamp("com.sun.faces.cacheResourceModificationTimestamp", false), EnableAgressiveSessionDirtying("com.sun.faces.enableAgressiveSessionDirtying", false), EnableMissingResourceLibraryDetection( "com.sun.faces.enableMissingResourceLibraryDetection", false); private BooleanWebContextInitParameter alternate; private String qualifiedName; private boolean defaultValue; private boolean deprecated; private DeprecationLoggingStrategy loggingStrategy; // ---------------------------------------------------------- Public Methods public boolean getDefaultValue() { return defaultValue; } public String getQualifiedName() { return qualifiedName; } DeprecationLoggingStrategy getDeprecationLoggingStrategy() { return loggingStrategy; } // ------------------------------------------------- Package Private Methods BooleanWebContextInitParameter(String qualifiedName, boolean defaultValue) { this(qualifiedName, defaultValue, false, null); } BooleanWebContextInitParameter( String qualifiedName, boolean defaultValue, boolean deprecated, BooleanWebContextInitParameter alternate) { this.qualifiedName = qualifiedName; this.defaultValue = defaultValue; this.deprecated = deprecated; this.alternate = alternate; } BooleanWebContextInitParameter( String qualifiedName, boolean defaultValue, boolean deprecated, BooleanWebContextInitParameter alternate, DeprecationLoggingStrategy loggingStrategy) { this(qualifiedName, defaultValue, deprecated, alternate); this.loggingStrategy = loggingStrategy; } // --------------------------------------------------------- Private Methods private BooleanWebContextInitParameter getAlternate() { return alternate; } private boolean isDeprecated() { return deprecated; } } /** * An <code>enum</code> of all environment entries (specified in the web.xml) recognized by the * implemenetation. */ public enum WebEnvironmentEntry { ClientStateSavingPassword("ClientStateSavingPassword"), ProjectStage(javax.faces.application.ProjectStage.PROJECT_STAGE_JNDI_NAME); private static final String JNDI_PREFIX = "java:comp/env/"; private String qualifiedName; // ---------------------------------------------------------- Public Methods public String getQualifiedName() { return qualifiedName; } // ------------------------------------------------- Package Private Methods WebEnvironmentEntry(String qualifiedName) { if (qualifiedName.startsWith(JNDI_PREFIX)) { this.qualifiedName = qualifiedName; } else { this.qualifiedName = JNDI_PREFIX + qualifiedName; } } } /** * An <code>enum</code> of all possible values for the <code>disableUnicodeEscaping</code> * configuration parameter. */ public enum DisableUnicodeEscaping { True("true"), False("false"), Auto("auto"); private final String value; DisableUnicodeEscaping(String value) { this.value = value; } public static DisableUnicodeEscaping getByValue(String value) { for (DisableUnicodeEscaping disableUnicodeEscaping : DisableUnicodeEscaping.values()) { if (disableUnicodeEscaping.value.equals(value)) { return disableUnicodeEscaping; } } return null; } } // ----------------------------------------------------------- Inner Classes private interface DeprecationLoggingStrategy { boolean shouldBeLogged(WebConfiguration configuration); } private static class FaceletsConfigParamLoggingStrategy implements DeprecationLoggingStrategy { public boolean shouldBeLogged(WebConfiguration configuration) { return !configuration.isOptionEnabled( BooleanWebContextInitParameter.DisableFaceletJSFViewHandler); } } // END FaceletsConfigParamLoggingStrategy private interface DeferredLoggingAction { void log(); } // END DeferredLogginAction private class DeferredParameterLoggingAction implements DeferredLoggingAction { private WebContextInitParameter parameter; private Level loggingLevel; private String logKey; private Object[] params; DeferredParameterLoggingAction( WebContextInitParameter parameter, Level loggingLevel, String logKey, Object[] params) { this.parameter = parameter; this.loggingLevel = loggingLevel; this.logKey = logKey; this.params = params; } public void log() { if (WebConfiguration.LOGGER.isLoggable(loggingLevel)) { DeprecationLoggingStrategy strategy = parameter.getDeprecationLoggingStrategy(); if (strategy != null && strategy.shouldBeLogged(WebConfiguration.this)) { WebConfiguration.LOGGER.log(loggingLevel, logKey, params); } } } } // END DeferredParameterLogginAction private class DeferredBooleanParameterLoggingAction implements DeferredLoggingAction { private BooleanWebContextInitParameter parameter; private Level loggingLevel; private String logKey; private Object[] params; DeferredBooleanParameterLoggingAction( BooleanWebContextInitParameter parameter, Level loggingLevel, String logKey, Object[] params) { this.parameter = parameter; this.loggingLevel = loggingLevel; this.logKey = logKey; this.params = params; } public void log() { if (WebConfiguration.LOGGER.isLoggable(loggingLevel)) { DeprecationLoggingStrategy strategy = parameter.getDeprecationLoggingStrategy(); if (strategy != null && strategy.shouldBeLogged(WebConfiguration.this)) { WebConfiguration.LOGGER.log(loggingLevel, logKey, params); } } } } // END DeferredBooleanParameterLoggingAction } // END WebConfiguration
/** 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 static final List<ConfigurationResourceProvider> FACES_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 static final List<ConfigurationResourceProvider> FACELET_TAGLIBRARY_RESOURCE_PROVIDERS; /** * 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. */ private static final int NUMBER_OF_TASK_THREADS = 5; /** * 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 {@link ParseTask} to indiciate 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"; /** * Contains each <code>ServletContext</code> that we've initialized. The <code>ServletContext * </code> will be removed when the application is destroyed. */ @SuppressWarnings({"CollectionWithoutInitialCapacity"}) private List<ServletContext> initializedContexts = new CopyOnWriteArrayList<ServletContext>(); /** The chain of {@link ConfigProcessor} instances to processing of faces-config documents. */ private static final ConfigProcessor FACES_CONFIG_PROCESSOR_CHAIN; /** The chain of {@link ConfigProcessor} instances to processing of facelet-taglib documents. */ 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; } /** * This method bootstraps JSF based on the parsed configuration resources. * * @param sc the <code>ServletContext</code> for the application that requires initialization */ public void initialize(ServletContext sc) { if (!hasBeenInitialized(sc)) { initializedContexts.add(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 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); } } } /** * This method will remove any information about the application. * * @param sc the <code>ServletContext</code> for the application that needs to be removed */ public void destroy(ServletContext sc) { releaseFactories(); initializedContexts.remove(sc); } /** * @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) { return (initializedContexts.contains(sc)); } /** @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); } } /** * 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. * * @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; } /** * 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>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. * * @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); } /** * Obtains an array of <code>Document</code>s to be processed by {@link * ConfigManager#FACES_CONFIG_PROCESSOR_CHAIN}. * * @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); int i = 0, j = 0; 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(); } j++; } } catch (InterruptedException ignored) { } catch (Exception e) { throw new ConfigurationException(e); } i++; } 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 /** * 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. */ 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 /** * Constructs a new ParseTask instance * * @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.factory = DbfFactory.getFactory(); 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 ("2.2".equals(versionStr)) { if ("facelet-taglib".equals(documentElement.getLocalName())) { schema = DbfFactory.FacesSchema.FACELET_TAGLIB_22; } 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)).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 = TransformerFactory.newInstance(); 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 { 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 /** * 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. */ 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 }
/** * This <code>ConfigProcessor</code> handles all elements defined under <code> * /faces-config/managed-bean</code>. */ public class ManagedBeanConfigProcessor extends AbstractConfigProcessor { private static final Logger LOGGER = FacesLogger.CONFIG.getLogger(); /** /faces-config/managed-bean */ private static final String MANAGED_BEAN = "managed-bean"; /** /faces-config/managed-bean/description */ private static final String DESCRIPTION = "description"; /** /faces-config/mananged-bean/managed-bean-name */ private static final String MGBEAN_NAME = "managed-bean-name"; /** /faces-config/managed-bean/mananged-bean-class */ private static final String MGBEAN_CLASS = "managed-bean-class"; /** /faces-config/managed-bean/managed-bean-scope */ private static final String MGBEAN_SCOPE = "managed-bean-scope"; /** /faces-config/managed-bean/managed-property */ private static final String MG_PROPERTY = "managed-property"; /** /faces-config/managed-bean/managed-property/property-name */ private static final String MG_PROPERTY_NAME = "property-name"; /** /faces-config/managed-bean/managed-property/property-class */ private static final String MG_PROPERTY_TYPE = "property-class"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries/map-entry/null-value * <li>/faces-config/managed-bean/managed-property/null-value * <li>/faces-config/managed-bean/managed-property/map-entries/map-entry/null-value * <li>/faces-config/managed-bean/list-entries/null-value * <li>/faces-config/managed-bean/managed-property/list-entries/null-value * </ul> */ private static final String NULL_VALUE = "null-value"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries/map-entry/value * <li>/faces-config/managed-bean/managed-property/value * <li>/faces-config/managed-bean/managed-property/map-entries/map-entry/value * <li>/faces-config/managed-bean/list-entries/value * <li>/faces-config/managed-bean/managed-property/list-entries/value * </ul> */ private static final String VALUE = "value"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/managed-property/map-entries/map-entry/key * </ul> */ private static final String KEY = "key"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries/key-class * <li>/faces-config/managed-bean/managed-property/map-entries/key-class * </ul> */ private static final String MAP_KEY_CLASS = "key-class"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries/value-class * <li>/faces-config/managed-bean/managed-property/map-entries/value-class * <li>/faces-config/managed-bean/list-entries/value-class * <li>/faces-config/managed-bean/managed-property/list-entries/value-class * </ul> */ private static final String VALUE_CLASS = "value-class"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries/map-entry * <li>/faces-config/managed-bean/managed-property/map-entries/map-entry * </ul> */ private static final String MAP_ENTRY = "map-entry"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/map-entries * <li>/faces-config/managed-bean/managed-property/map-entries * </ul> */ private static final String MAP_ENTRIES = "map-entries"; /** * Handles: * * <ul> * <li>/faces-config/managed-bean/list-entries * <li>/faces-config/managed-bean/managed-property/list-entries * </ul> */ private static final String LIST_ENTRIES = "list-entries"; /** <code>eager</code> attribute defined in the managed-bean element. */ private static final String EAGER_ATTRIBUTE = "eager"; private static final String DEFAULT_SCOPE = "request"; // -------------------------------------------- Methods from ConfigProcessor /** * @see ConfigProcessor#process(javax.servlet.ServletContext,com.sun.faces.config.DocumentInfo[]) */ public void process(ServletContext sc, DocumentInfo[] documentInfos) throws Exception { // process annotated managed beans first as managed beans configured // via config files take precedence processAnnotations(ManagedBean.class); BeanManager beanManager = ApplicationAssociate.getInstance(sc).getBeanManager(); for (int i = 0; i < documentInfos.length; i++) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Processing managed-bean elements for document: ''{0}''", documentInfos[i].getSourceURL())); } Document document = documentInfos[i].getDocument(); String namespace = document.getDocumentElement().getNamespaceURI(); NodeList managedBeans = document.getDocumentElement().getElementsByTagNameNS(namespace, MANAGED_BEAN); if (managedBeans != null && managedBeans.getLength() > 0) { for (int m = 0, size = managedBeans.getLength(); m < size; m++) { addManagedBean(beanManager, managedBeans.item(m)); } } } beanManager.preProcessesBeans(); invokeNext(sc, documentInfos); } // --------------------------------------------------------- Private Methods private void addManagedBean(BeanManager beanManager, Node managedBean) { NodeList children = managedBean.getChildNodes(); String beanName = null; String beanClass = null; String beanScope = null; ManagedBeanInfo.ListEntry listEntry = null; ManagedBeanInfo.MapEntry mapEntry = null; List<Node> managedProperties = null; List<Node> descriptions = null; for (int i = 0, size = children.getLength(); i < size; i++) { Node n = children.item(i); if (n.getNodeType() == Node.ELEMENT_NODE) { if (MGBEAN_NAME.equals(n.getLocalName())) { beanName = getNodeText(n); } else if (MGBEAN_CLASS.equals(n.getLocalName())) { beanClass = getNodeText(n); } else if (MGBEAN_SCOPE.equals(n.getLocalName())) { beanScope = getNodeText(n); if (beanScope == null) { beanScope = DEFAULT_SCOPE; } } else if (LIST_ENTRIES.equals(n.getLocalName())) { listEntry = buildListEntry(n); } else if (MAP_ENTRIES.equals(n.getLocalName())) { mapEntry = buildMapEntry(n); } else if (MG_PROPERTY.equals(n.getLocalName())) { if (managedProperties == null) { managedProperties = new ArrayList<Node>(size); } managedProperties.add(n); } else if (DESCRIPTION.equals(n.getLocalName())) { if (descriptions == null) { descriptions = new ArrayList<Node>(4); } descriptions.add(n); } } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Begin processing managed bean ''{0}''", beanName); } List<ManagedBeanInfo.ManagedProperty> properties = null; if (managedProperties != null && !managedProperties.isEmpty()) { properties = new ArrayList<ManagedBeanInfo.ManagedProperty>(managedProperties.size()); for (Node managedProperty : managedProperties) { properties.add(buildManagedProperty(managedProperty)); } } beanManager.register( new ManagedBeanInfo( beanName, beanClass, beanScope, isEager(managedBean, beanName, beanScope), mapEntry, listEntry, properties, getTextMap(descriptions))); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Completed processing bean ''{0}''", beanName); } } private ManagedBeanInfo.ListEntry buildListEntry(Node listEntry) { if (listEntry != null) { String valueClass = "java.lang.String"; List<String> values = null; NodeList children = listEntry.getChildNodes(); for (int i = 0, size = children.getLength(); i < size; i++) { Node child = children.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { if (VALUE_CLASS.equals(child.getLocalName())) { valueClass = getNodeText(child); } else if (VALUE.equals(child.getLocalName())) { if (values == null) { values = new ArrayList<String>(size); } values.add(getNodeText(child)); } else if (NULL_VALUE.equals(child.getLocalName())) { if (values == null) { values = new ArrayList<String>(size); } values.add(ManagedBeanInfo.NULL_VALUE); } } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Created ListEntry valueClass={1}, values={3}", valueClass, (values != null && !values.isEmpty()) ? values.toString() : "none")); } return (new ManagedBeanInfo.ListEntry( valueClass, (values == null) ? TypedCollections.dynamicallyCastList(Collections.emptyList(), String.class) : values)); } return null; } private ManagedBeanInfo.MapEntry buildMapEntry(Node mapEntry) { if (mapEntry != null) { String valueClass = "java.lang.String"; String keyClass = "java.lang.String"; Map<String, String> entries = null; NodeList children = mapEntry.getChildNodes(); for (int i = 0, size = children.getLength(); i < size; i++) { Node child = children.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { if (VALUE_CLASS.equals(child.getLocalName())) { valueClass = getNodeText(child); } else if (MAP_KEY_CLASS.equals(child.getLocalName())) { keyClass = getNodeText(child); } else if (MAP_ENTRY.equals(child.getLocalName())) { if (entries == null) { entries = new LinkedHashMap<String, String>(8, 1.0f); } NodeList c = child.getChildNodes(); String key = null; String value = null; for (int j = 0, jsize = c.getLength(); j < jsize; j++) { Node node = c.item(j); if (node.getNodeType() == Node.ELEMENT_NODE) { if (KEY.equals(node.getLocalName())) { key = getNodeText(node); } else if (VALUE.equals(node.getLocalName())) { value = getNodeText(node); } else if (NULL_VALUE.equals(node.getLocalName())) { value = ManagedBeanInfo.NULL_VALUE; } } } entries.put(key, value); } } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Created MapEntry keyClass={0}, valueClass={1}, entries={3}", keyClass, valueClass, (entries != null) ? entries.toString() : "none")); } return (new ManagedBeanInfo.MapEntry(keyClass, valueClass, entries)); } return null; } private ManagedBeanInfo.ManagedProperty buildManagedProperty(Node managedProperty) { if (managedProperty != null) { String propertyName = null; String propertyClass = null; String value = null; ManagedBeanInfo.MapEntry mapEntry = null; ManagedBeanInfo.ListEntry listEntry = null; NodeList children = managedProperty.getChildNodes(); for (int i = 0, size = children.getLength(); i < size; i++) { Node child = children.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { if (MG_PROPERTY_NAME.equals(child.getLocalName())) { propertyName = getNodeText(child); } else if (MG_PROPERTY_TYPE.equals(child.getLocalName())) { propertyClass = getNodeText(child); } else if (VALUE.equals(child.getLocalName())) { value = getNodeText(child); } else if (NULL_VALUE.equals(child.getLocalName())) { value = ManagedBeanInfo.NULL_VALUE; } else if (LIST_ENTRIES.equals(child.getLocalName())) { listEntry = buildListEntry(child); } else if (MAP_ENTRIES.equals(child.getLocalName())) { mapEntry = buildMapEntry(child); } } } if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log( Level.FINE, MessageFormat.format( "Adding ManagedProperty propertyName={0}, propertyClass={1}, propertyValue={2}, hasMapEntry={3}, hasListEntry={4}", propertyName, ((propertyClass != null) ? propertyClass : "inferred"), value, (mapEntry != null), (listEntry != null))); } return new ManagedBeanInfo.ManagedProperty( propertyName, propertyClass, value, mapEntry, listEntry); } return null; } private boolean isEager(Node managedBean, String beanName, String scope) { NamedNodeMap attributes = managedBean.getAttributes(); Node eagerNode = attributes.getNamedItem(EAGER_ATTRIBUTE); boolean eager = false; if (eagerNode != null) { eager = Boolean.valueOf(getNodeText(eagerNode)); if (eager && (scope == null || !ELUtils.Scope.APPLICATION.toString().equals(scope))) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.log( Level.WARNING, "jsf.configuration.illegal.eager.bean", new Object[] {beanName, scope}); } eager = false; } } return eager; } }