Exemple #1
0
/**
 * 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>&lt;error-page&gt;
     *     </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
}
Exemple #2
0
/** 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);
        }
      }
    }
  }
}
Exemple #4
0
/** 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
Exemple #5
0
/** 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
}
Exemple #6
0
/**
 * 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;
  }
}