content: /*
   * This library is part of OpenCms -
   * the Open Source Content Management System
   *
   * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
   *
   * 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.
   *
   * For further information about Alkacon Software GmbH & Co. KG, please see the
   * company website: https://www.alkacon.com
   *
   * For further information about OpenCms, please see the
   * project website: https://www.opencms.org
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   */
  
  package org.opencms.main;
  
  import org.opencms.util.A_CmsModeStringEnumeration;
  import org.opencms.util.CmsFileUtil;
  import org.opencms.util.CmsStringUtil;
  
  import java.io.File;
  
  import javax.servlet.ServletContext;
  
  import org.apache.commons.logging.Log;
  
  /**
   * Stores specific servlet container options, that might influence OpenCms behavior.<p>
   *
   * @since 7.0.5
   */
  public class CmsServletContainerSettings {
  
      /**
       *  Enumeration class for the configuration mode.<p>
       */
      public static final class CmsServletContainerCfgMode extends A_CmsModeStringEnumeration {
  
          /** Auto configuration mode. */
          protected static final CmsServletContainerCfgMode MODE_AUTO = new CmsServletContainerCfgMode("auto");
  
          /** Manual configuration mode. */
          protected static final CmsServletContainerCfgMode MODE_MANUAL = new CmsServletContainerCfgMode("manual");
  
          /** No set configuration mode. */
          protected static final CmsServletContainerCfgMode MODE_NONE = new CmsServletContainerCfgMode("none");
  
          /** Version id required for safe serialization. */
          private static final long serialVersionUID = -8191582624108081577L;
  
          /**
           * Private constructor.<p>
           *
           * @param mode the remote command execution return type integer representation
           */
          private CmsServletContainerCfgMode(String mode) {
  
              super(mode);
          }
  
          /**
           * Returns the parsed mode object if the string representation matches, or <code>null</code> if not.<p>
           *
           * @param mode the string representation to parse
           *
           * @return the parsed mode object
           */
          public static CmsServletContainerCfgMode valueOf(String mode) {
  
              if (mode == null) {
                  return null;
              }
              if (mode.equalsIgnoreCase(MODE_NONE.getMode())) {
                  return MODE_NONE;
              }
              if (mode.equalsIgnoreCase(MODE_MANUAL.getMode())) {
                  return MODE_MANUAL;
              }
              if (mode.equalsIgnoreCase(MODE_AUTO.getMode())) {
                  return MODE_AUTO;
              }
              return null;
          }
  
          /**
           * Checks if this is the auto mode.<p>
           *
           * @return <code>true</code> if this is the auto mode
           */
          public boolean isAuto() {
  
              return this == MODE_AUTO;
          }
  
          /**
           * Checks if this is the manual mode.<p>
           *
           * @return <code>true</code> if this is the manual mode
           */
          public boolean isManual() {
  
              return this == MODE_MANUAL;
          }
  
          /**
           * Checks if this is the none mode.<p>
           *
           * @return <code>true</code> if this is the none mode
           */
          public boolean isNone() {
  
              return this == MODE_NONE;
          }
      }
  
      /** String remote command execution return type. */
      public static final CmsServletContainerCfgMode CFG_MODE_AUTO = CmsServletContainerCfgMode.MODE_AUTO;
  
      /** Map remote command execution return type. */
      public static final CmsServletContainerCfgMode CFG_MODE_MANUAL = CmsServletContainerCfgMode.MODE_MANUAL;
  
      /** List remote command execution return type. */
      public static final CmsServletContainerCfgMode CFG_MODE_NONE = CmsServletContainerCfgMode.MODE_NONE;
  
      /** The static log object for this class. */
      private static final Log LOG = CmsLog.getLog(CmsServletContainerSettings.class);
  
      /** If the servlet can throw an exception if initialization fails, for instance, Weblogic and Resin have problems with the exception. */
      private static boolean m_servletThrowsException = true;
  
      /**
       * The replacement request attribute for the {@link javax.servlet.http.HttpServletRequest#getPathInfo()} method,
       * which is needed because this method is not properly implemented in BEA WLS 9.x.<p>
       */
      private static final String REQUEST_ERROR_PAGE_ATTRIBUTE_WEBLOGIC = "weblogic.servlet.errorPage";
  
      /** Constant name to identify GlassFish servers. */
      // 2.1: "Sun GlassFish Enterprise Server v2.1"
      private static final String SERVLET_CONTAINER_GLASSFISH = "GlassFish";
  
      /** Constant name to identify Resin servers. */
      private static final String SERVLET_CONTAINER_RESIN = "Resin";
  
      /** Constant name to identify BEA WebLogic servers. */
      private static final String SERVLET_CONTAINER_WEBLOGIC = "WebLogic Server";
  
      /** Constant name to identify IBM Websphere servers. */
      private static final String SERVLET_CONTAINER_WEBSPHERE = "IBM WebSphere Application Server";
  
      /** The context path of the web application. */
      private String m_contextPath;
  
      /** The default web application (usually "ROOT"). */
      private String m_defaultWebApplicationName;
  
      /** The configuration mode. */
      private CmsServletContainerCfgMode m_mode = CFG_MODE_NONE;
  
      /** The OpenCms context and servlet path, e.g. <code>/opencms/opencms</code>. */
      private String m_openCmsContext;
  
      /** If the flex response has to prevent buffer flushing, for instance, Websphere does not allow to set headers afterwards, so we have to prevent it. */
      private boolean m_preventResponseFlush;
  
      /** If the tags need to be released after ending, this has to be prevented when running with Resin, for example. */
      private boolean m_releaseTagsAfterEnd = true;
  
      /**
         * The request error page attribute to use if {@link javax.servlet.http.HttpServletRequest#getPathInfo()}
         * is not working properly, like in BEA WLS 9.x.
         */
      private String m_requestErrorPageAttribute;
  
      /** The name of the servlet container running OpenCms. */
      private String m_servletContainerName;
  
      /** The servlet path for the OpenCms servlet. */
      private String m_servletPath;
  
      /** The web application name. */
      private String m_webApplicationName;
  
      /** The OpenCms web application servlet container folder path (in the "real" file system). */
      private String m_webApplicationRfsPath;
  
      /** The OpenCms web application "WEB-INF" path (in the "real" file system). */
      private String m_webInfRfsPath;
  
      /**
       * Creates a new object.<p>
       *
       * @param context used to find out specifics of the servlet container
       */
      public CmsServletContainerSettings(ServletContext context) {
  
          // CmsSystemInfo<init> has to call this with null (for setup)
          if (context != null) {
              // check for OpenCms home (base) directory path
              String webInfRfsPath = context.getInitParameter(OpenCmsServlet.SERVLET_PARAM_OPEN_CMS_HOME);
              if (CmsStringUtil.isEmpty(webInfRfsPath)) {
                  webInfRfsPath = CmsFileUtil.searchWebInfFolder(context.getRealPath("/"));
                  if (CmsStringUtil.isEmpty(webInfRfsPath)) {
                      throw new CmsInitException(Messages.get().container(Messages.ERR_CRITICAL_INIT_FOLDER_0));
                  }
              }
  
              // set the default web application name
              // read the the default name from the servlet context parameters
              String defaultWebApplication = context.getInitParameter("DefaultWebApplication");
  
              // read the the OpenCms servlet mapping from the servlet context parameters
              String servletMapping = context.getInitParameter(OpenCmsServlet.SERVLET_PARAM_OPEN_CMS_SERVLET);
              if (servletMapping == null) {
                  throw new CmsInitException(Messages.get().container(Messages.ERR_CRITICAL_INIT_SERVLET_0));
              }
  
              // read the servlet container name
              String servletContainerName = context.getServerInfo();
  
              // web application context:
              // read it from the servlet context parameters
              //      this is needed in case an application server specific deployment descriptor is used to changed the webapp context
              String webApplicationContextFromConfig = context.getInitParameter(
                  OpenCmsServlet.SERVLET_PARAM_WEB_APPLICATION_CONTEXT);
              String contextPath = "";
              if (CmsStringUtil.isEmptyOrWhitespaceOnly(webApplicationContextFromConfig)) {
                  contextPath = context.getContextPath();
              } else {
                  if (defaultWebApplication.equals(webApplicationContextFromConfig)) {
                      contextPath = "";
                  } else {
                      contextPath = "/" + webApplicationContextFromConfig;
                  }
              }
              // init values:
              init(webInfRfsPath, defaultWebApplication, servletMapping, servletContainerName, contextPath);
              // finally care for the speciality of different servlet containers:
              initContainerSpecifics(context);
          }
      }
  
      /**
       * Creates an instance based on the given values.<p>
       *
       * This is intended for <code>{@link CmsShell}</code> access.<p>
       *
       * @param webInfRfsPath the OpenCms web application "WEB-INF" path in the "real" file system) to set
       * @param servletMapping the OpenCms servlet mapping  (e.g. "/opencms/*")
       * @param webApplicationContext the name/path of the OpenCms web application context (optional, will be calculated form the path if null)
       * @param defaultWebApplication the default web application name (usually "ROOT")
       * @param servletContainerName the name of the servlet container running OpenCms
       */
      protected CmsServletContainerSettings(
          String webInfRfsPath,
          String defaultWebApplication,
          String servletMapping,
          String servletContainerName,
          String webApplicationContext) {
  
          init(webInfRfsPath, defaultWebApplication, servletMapping, servletContainerName, webApplicationContext);
      }
  
      /**
       * Checks if the servlet can throw an exception if initialization fails.<p>
       *
       * @return <code>true</code> if the servlet can throw an exception if initialization fails
       */
      public static boolean isServletThrowsException() {
  
          return m_servletThrowsException;
      }
  
      /**
       * Returns the web application context path, e.g. "" (empty String) if the web application
       * is the default web application (usually "ROOT"), or "/opencms" if the web application
       * is called "opencms".<p>
       *
       * <i>From the Java Servlet Specification v2.4:</i><br>
       * <b>Context Path:</b> The path prefix associated with the ServletContext that this
       * servlet is a part of. If this context is the "default" context rooted at the base of
       * the web server's URL name space, this path will be an empty string. Otherwise,
       * if the context is not rooted at the root of the server's name space, the path starts
       * with a "/" character but does not end with a "/" character.<p>
       *
       * @return the web application context path
       * @see #getWebApplicationName()
       * @see #getServletPath()
       * @see #getOpenCmsContext()
       */
      public String getContextPath() {
  
          return m_contextPath;
      }
  
      /**
       * Returns the default web application name (usually "ROOT").<p>
       *
       * @return the default web application name
       */
      public String getDefaultWebApplicationName() {
  
          return m_defaultWebApplicationName;
      }
  
      /**
       * Returns the mode.<p>
       *
       * @return the mode
       */
      public CmsServletContainerCfgMode getMode() {
  
          return m_mode;
      }
  
      /**
       * Returns the OpenCms request context, e.g. "/opencms/opencms".<p>
       *
       * The OpenCms context will always start with a "/" and never have a trailing "/".
       * The OpenCms context is identical to <code>getContexPath() + getServletPath()</code>.<p>
       *
       * @return the OpenCms request context, e.g. "/opencms/opencms"
       * @see #getContextPath()
       * @see #getServletPath()
       */
      public String getOpenCmsContext() {
  
          return m_openCmsContext;
      }
  
      /**
       * Returns the request error page attribute.<p>
       *
       * @return the request error page attribute
       */
      public String getRequestErrorPageAttribute() {
  
          return m_requestErrorPageAttribute;
      }
  
      /**
       * Returns the name of the servlet container running OpenCms.<p>
       *
       * @return the name of the servlet container running OpenCms
       */
      public String getServletContainerName() {
  
          return m_servletContainerName;
      }
  
      /**
       * Returns the OpenCms servlet path, e.g. "/opencms".<p>
       *
       * <i>From the Java Servlet Specification v2.4:</i><br>
       * <b>Servlet Path:</b> The path section that directly corresponds to the mapping
       * which activated this request. This path starts with a?/? character except in the
       * case where the request is matched with the ?/*? pattern, in which case it is the
       * empty string.<p>
       *
       * @return the OpenCms servlet path
       * @see #getContextPath()
       * @see #getWebApplicationName()
       * @see #getOpenCmsContext()
       */
      public String getServletPath() {
  
          return m_servletPath;
      }
  
      /**
       * Returns the OpenCms web application name, e.g. "opencms" or "ROOT" (no leading or trailing "/").<p>
       *
       * The web application name is stored for informational purposes only.
       * If you want to construct an URI, use either {@link #getContextPath()} and
       * {@link #getServletPath()}, or for links to the OpenCms VFS use {@link  #getOpenCmsContext()}.<p>
       *
       * @return the OpenCms web application name
       * @see #getContextPath()
       * @see #getServletPath()
       * @see #getOpenCmsContext()
       */
      public String getWebApplicationName() {
  
          return m_webApplicationName;
      }
  
      /**
       * Returns the OpenCms web application folder in the servlet container.<p>
       *
       * @return the OpenCms web application folder in the servlet container
       */
      public String getWebApplicationRfsPath() {
  
          return m_webApplicationRfsPath;
      }
  
      /**
       * Returns the OpenCms web application "WEB-INF" directory path.<p>
       *
       * @return the OpenCms web application "WEB-INF" directory path
       */
      public String getWebInfRfsPath() {
  
          return m_webInfRfsPath;
      }
  
      /**
       * Checks if the flex response has to prevent buffer flushing.<p>
       *
       * @return <code>true</code> if the flex response has to prevent buffer flushing
       */
      public boolean isPreventResponseFlush() {
  
          return m_preventResponseFlush;
      }
  
      /**
       * Checks if the tags need to be released after ending.<p>
       *
       * @return <code>true</code> if the tags need to be released after ending
       */
      public boolean isReleaseTagsAfterEnd() {
  
          return m_releaseTagsAfterEnd;
      }
  
      /**
       * Sets the mode from the configuration.<p>
       *
       * @param configValue the mode to set
       */
      public void setMode(String configValue) {
  
          m_mode = CmsServletContainerCfgMode.valueOf(configValue);
      }
  
      /**
       * Sets if the flex response has to prevent buffer flushing.<p>
       *
       * @param preventResponseFlush the flag to set
       */
      public void setPreventResponseFlush(boolean preventResponseFlush) {
  
          m_preventResponseFlush = preventResponseFlush;
      }
  
      /**
       * Sets if the tags need to be released after ending.<p>
       *
       * @param releaseTagsAfterEnd the flag to set
       */
      public void setReleaseTagsAfterEnd(boolean releaseTagsAfterEnd) {
  
          m_releaseTagsAfterEnd = releaseTagsAfterEnd;
      }
  
      /**
       * Sets the request error page attribute.<p>
       *
       * @param requestErrorPageAttribute the request error page attribute to set
       */
      public void setRequestErrorPageAttribute(String requestErrorPageAttribute) {
  
          m_requestErrorPageAttribute = requestErrorPageAttribute;
      }
  
      /**
       * Sets if the servlet can throw an exception if initialization fails.<p>
       *
       * @param servletThrowsException the flag to set
       */
      public void setServletThrowsException(boolean servletThrowsException) {
  
          m_servletThrowsException = servletThrowsException;
      }
  
      /**
       * Initialization code common to both constructors.<p>
       *
       * While the "webapplication - mode" constructor obtains all values from the
       * <code>{@link ServletContext}</code> the <code>{@link CmsShell}</code> constructor
       * accepts them as arguments and passes them here. <p>
       *
       * @param webInfRfsPath the OpenCms web application "WEB-INF" path in the "real" file system) to set
       * @param servletMapping the OpenCms servlet mapping  (e.g. "/opencms/*")
       * @param webApplicationContext the name/path of the OpenCms web application context (optional, will be calculated form the path if null)
       * @param defaultWebApplication the default web application name (usually "ROOT")
       * @param servletContainerName the name of the servlet container running OpenCms
       * @param contextPath the context path
       */
      private void init(
          String webInfRfsPath,
          String defaultWebApplication,
          String servletMapping,
          String servletContainerName,
          String contextPath) {
  
          // WEB-INF RFS path
  
          webInfRfsPath = webInfRfsPath.replace('\\', '/');
          if (!webInfRfsPath.endsWith("/")) {
              webInfRfsPath = webInfRfsPath + "/";
          }
          m_webInfRfsPath = CmsFileUtil.normalizePath(webInfRfsPath);
  
          // default web application
          if (defaultWebApplication == null) {
              defaultWebApplication = "";
          }
          if (defaultWebApplication.endsWith("/")) {
              defaultWebApplication = defaultWebApplication.substring(0, defaultWebApplication.length() - 1);
          }
          if (defaultWebApplication.startsWith("/")) {
              defaultWebApplication = defaultWebApplication.substring(1);
          }
          m_defaultWebApplicationName = defaultWebApplication;
  
          // servlet mapping
          if (!servletMapping.startsWith("/")) {
              servletMapping = "/" + servletMapping;
          }
          if (servletMapping.endsWith("/*")) {
              // usually a mapping must be in the form "/opencms/*", cut off all slashes
              servletMapping = servletMapping.substring(0, servletMapping.length() - 2);
          }
          m_servletPath = servletMapping;
  
          // servlet container name
          if (servletContainerName == null) {
              m_servletContainerName = "";
          }
          m_servletContainerName = servletContainerName;
  
          // set the web application name
          File path = new File(m_webInfRfsPath);
          m_webApplicationName = path.getParentFile().getName();
  
          // set the context path
          if (contextPath == null) {
              // this should only happen during test cases
              m_contextPath = "/" + m_webApplicationName;
          } else {
              m_contextPath = contextPath;
          }
          // set the OpenCms context
          m_openCmsContext = m_contextPath + m_servletPath;
  
          // set the web application rfs path
          m_webApplicationRfsPath = path.getParentFile().getAbsolutePath();
          if (!m_webApplicationRfsPath.endsWith(File.separator)) {
              m_webApplicationRfsPath += File.separator;
          }
  
          // fill some defaults:
          m_releaseTagsAfterEnd = false;
          m_requestErrorPageAttribute = null;
          m_servletThrowsException = true;
          m_preventResponseFlush = false;
      }
  
      /**
       * Initializes these container settings with container specific settings.<p>
       *
       * @param context
       *      the servlet context to find out information about the servlet container
       */
      private void initContainerSpecifics(ServletContext context) {
  
          // the tags behavior
          m_releaseTagsAfterEnd = !(m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) > -1);
  
          // the request error page attribute
          if (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBLOGIC) > -1) {
              m_requestErrorPageAttribute = REQUEST_ERROR_PAGE_ATTRIBUTE_WEBLOGIC;
          }
  
          // the failed initialization behavior
          m_servletThrowsException = true;
          m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) < 0);
          m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBLOGIC) < 0);
          m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_GLASSFISH) < 0);
  
          // the flush flex response behavior
          m_preventResponseFlush = false;
          m_preventResponseFlush |= (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBSPHERE) > -1);
          m_preventResponseFlush |= (m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) > -1);
      }
  }
  