Package com.liferay.faces.bridge.servlet

Source Code of com.liferay.faces.bridge.servlet.BridgeSessionListener

/**
* Copyright (c) 2000-2014 Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.faces.bridge.servlet;

import java.util.Enumeration;

import javax.portlet.faces.BridgeException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import com.liferay.faces.bridge.bean.BeanManager;
import com.liferay.faces.bridge.bean.BeanManagerFactory;
import com.liferay.faces.bridge.scope.BridgeRequestScopeManager;
import com.liferay.faces.bridge.scope.BridgeRequestScopeManagerFactory;
import com.liferay.faces.util.config.ApplicationConfig;
import com.liferay.faces.util.factory.FactoryExtensionFinder;
import com.liferay.faces.util.logging.Logger;
import com.liferay.faces.util.logging.LoggerFactory;
import com.liferay.faces.util.product.Product;
import com.liferay.faces.util.product.ProductConstants;
import com.liferay.faces.util.product.ProductMap;


/**
* This class provides the ability to cleanup session-scoped and view-scoped managed-beans upon session expiration.
*
* @author  Neil Griffin
*/
public class BridgeSessionListener implements HttpSessionListener, ServletContextListener {

  // Logger
  private static final Logger logger = LoggerFactory.getLogger(BridgeSessionListener.class);

  // Private Constants
  private static final String MOJARRA_ACTIVE_VIEW_MAPS = "com.sun.faces.application.view.activeViewMaps";
  private static final String MOJARRA_PACKAGE_PREFIX = "com.sun.faces";
  private static final String MOJARRA_VIEW_SCOPE_MANAGER = "com.sun.faces.application.view.viewScopeManager";

  // Private Data Members
  private boolean firstInstance;

  @Override
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    // no-op
  }

  /**
   * This method provides the ability to discover the Mojarra InjectionProvider at startup.
   */
  public void contextInitialized(ServletContextEvent servletContextEvent) {

    ServletContext servletContext = servletContextEvent.getServletContext();

    if (servletContext.getAttribute(BridgeSessionListener.class.getName()) == null) {

      logger.info("Context initialized for contextPath=[{0}]", servletContext.getContextPath());

      // Prevent multiple-instantiation of this listener.
      servletContext.setAttribute(BridgeSessionListener.class.getName(), Boolean.TRUE);
      firstInstance = true;

    }
    else {
      logger.debug("Preventing multiple instantiation for contextPath=[{0}]", servletContext.getContextPath());
    }
  }

  public void sessionCreated(HttpSessionEvent httpSessionEvent) {
    // This method is required by the HttpSessionListener interface but is not used.
  }

  public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {

    if (firstInstance) {

      // Determine which JSF implementation is being used (Mojarra/MyFaces).
      ProductMap productMap = ProductMap.getInstance();
      Product jsf = productMap.get(ProductConstants.JSF);
      boolean mojarraAbleToCleanup = true;

      if (jsf.getTitle().equals(ProductConstants.MOJARRA) && (jsf.getMajorVersion() == 2) &&
          (jsf.getMinorVersion() == 1)) {

        if (jsf.getRevisionVersion() < 18) {
          mojarraAbleToCleanup = false;

          boolean logWarning = true;
          Product iceFaces = productMap.get(ProductConstants.ICEFACES);

          if (iceFaces.isDetected()) {

            if ((iceFaces.getMajorVersion() == 2) ||
                ((iceFaces.getMajorVersion() == 3) && (iceFaces.getMinorVersion() <= 2))) {

              // Versions of ICEfaces prior to 3.3 can only go as high as Mojarra 2.1.6 so don't bother to
              // log the warning.
              logWarning = false;
            }
          }

          if (logWarning) {
            logger.warn("Unable to cleanup ViewScoped managed-beans upon session expiration. " +
              "Please upgrade to Mojarra 2.1.18 or newer. " +
              "For more info, see: http://issues.liferay.com/browse/FACES-1470");
          }
        }
      }

      // Discover Factories
      BeanManagerFactory beanManagerFactory = null;
      BridgeRequestScopeManagerFactory bridgeRequestScopeManagerFactory = null;

      try {
        beanManagerFactory = (BeanManagerFactory) FactoryExtensionFinder.getFactory(BeanManagerFactory.class);
        bridgeRequestScopeManagerFactory = (BridgeRequestScopeManagerFactory) FactoryExtensionFinder.getFactory(
            BridgeRequestScopeManagerFactory.class);
      }
      catch (BridgeException e) {
        logger.debug("Unable to discover factories because portlet never received a RenderRequest");
      }

      if ((beanManagerFactory != null) && (bridgeRequestScopeManagerFactory != null)) {

        // Cleanup instances of BridgeRequestScope that are associated with the expiring session.
        HttpSession httpSession = httpSessionEvent.getSession();
        BridgeRequestScopeManager bridgeRequestScopeManager =
          bridgeRequestScopeManagerFactory.getBridgeRequestScopeManager();
        bridgeRequestScopeManager.removeBridgeRequestScopesBySession(httpSession);

        // For each session attribute:
        String appConfigAttrName = ApplicationConfig.class.getName();
        ServletContext servletContext = httpSession.getServletContext();
        ApplicationConfig applicationConfig = (ApplicationConfig) servletContext.getAttribute(
            appConfigAttrName);
        BeanManager beanManager = beanManagerFactory.getBeanManager(applicationConfig.getFacesConfig());

        try {

          Enumeration<String> attributeNames = (Enumeration<String>) httpSession.getAttributeNames();

          while (attributeNames.hasMoreElements()) {

            String attributeName = attributeNames.nextElement();

            // If the current session attribute name is namespaced with the standard portlet prefix, then it
            // is an attribute that was set using PortletSession.setAttribute(String, Object).
            if ((attributeName != null) && attributeName.startsWith("javax.portlet.p.")) {
              int pos = attributeName.indexOf("?");

              if (pos > 0) {
                Object attributeValue = httpSession.getAttribute(attributeName);
                httpSession.removeAttribute(attributeName);

                if (attributeValue != null) {

                  // If the current session attribute value is a JSF managed-bean, then cleanup the
                  // bean by invoking methods annotated with {@link PreDestroy}. Note that in a
                  // webapp/servlet environment, the cleanup is handled by the Mojarra
                  // WebappLifecycleListener.sessionDestroyed(HttpSessionEvent) method. But in a
                  // portlet environment, Mojarra fails to recognize the session attribute as
                  // managed-bean because the attribute name contains the standard portlet prefix. An
                  // alternative approach would be to have the bridge rename the attribute (by
                  // stripping off the standard portlet prefix) so that Mojarra could find it. But
                  // this would not a good solution, because multiple instances of the same portlet
                  // would have the same session attribute names for managed-beans, and only the last
                  // one would get cleaned-up by Mojarra.
                  if (beanManager.isManagedBean(attributeName, attributeValue)) {
                    beanManager.invokePreDestroyMethods(attributeValue, true);
                  }

                  // Otherwise,
                  else {

                    // If the current session attribute is Mojarra-vendor-specific, then
                    String fqcn = attributeValue.getClass().getName();

                    if ((fqcn != null) && (fqcn.contains(MOJARRA_PACKAGE_PREFIX))) {

                      // Rename the namespaced attribute by stripping off the standard portlet
                      // prefix. This will enable Mojarra's session expiration features to find
                      // attributes that it is expecting.
                      String nonPrefixedName = attributeName.substring(pos + 1);
                      logger.debug("Renaming Mojarra session attributeName=[{0}] -> [{1}]",
                        attributeName, nonPrefixedName);
                      httpSession.setAttribute(nonPrefixedName, attributeValue);

                      // If this is the attribute that contains all of the active view maps, then
                      if (MOJARRA_ACTIVE_VIEW_MAPS.equals(nonPrefixedName)) {

                        if (mojarraAbleToCleanup) {

                          // Invoke the Mojarra
                          // ViewScopeManager.sessionDestroyed(HttpSessionEvent) method in
                          // order to cleanup the active view maps. Rather than waiting for
                          // the servlet container to call the method during session
                          // expiration, it is important to call it directly within this
                          // while-loop for two reasons: 1) If the developer did not
                          // explicitly specify the order of the Mojarra ConfigureListener and
                          // this BridgeSessionListener in WEB-IN/web.xml descriptor
                          // (FACES-1483) then there is no guarantee that the method would get
                          // called. 2) In the case of multiple portlet instances, each
                          // instance has its own namespaced attribute in the session.
                          // Renaming each namespaced attribute to
                          // "com.sun.faces.application.view.activeViewMaps" would only enable
                          // Mojarra to cleanup the last one.
                          HttpSessionListener viewScopeManager = (HttpSessionListener)
                            servletContext.getAttribute(MOJARRA_VIEW_SCOPE_MANAGER);

                          if (viewScopeManager != null) {

                            try {
                              logger.debug(
                                "Asking Mojarra ViewScopeManager to cleanup @ViewScoped managed-beans");
                              viewScopeManager.sessionDestroyed(httpSessionEvent);
                            }
                            catch (Exception e) {
                              logger.error(e);
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
        catch (IllegalStateException e) {
          logger.warn("Server does not permit cleanup of Mojarra managed-beans during session expiration");
        }
      }
    }
  }

}
TOP

Related Classes of com.liferay.faces.bridge.servlet.BridgeSessionListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.