001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: https://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: https://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.main;
029
030import org.opencms.util.A_CmsModeStringEnumeration;
031import org.opencms.util.CmsFileUtil;
032import org.opencms.util.CmsStringUtil;
033
034import java.io.File;
035
036import javax.servlet.ServletContext;
037
038import org.apache.commons.logging.Log;
039
040/**
041 * Stores specific servlet container options, that might influence OpenCms behavior.<p>
042 *
043 * @since 7.0.5
044 */
045public class CmsServletContainerSettings {
046
047    /**
048     *  Enumeration class for the configuration mode.<p>
049     */
050    public static final class CmsServletContainerCfgMode extends A_CmsModeStringEnumeration {
051
052        /** Auto configuration mode. */
053        protected static final CmsServletContainerCfgMode MODE_AUTO = new CmsServletContainerCfgMode("auto");
054
055        /** Manual configuration mode. */
056        protected static final CmsServletContainerCfgMode MODE_MANUAL = new CmsServletContainerCfgMode("manual");
057
058        /** No set configuration mode. */
059        protected static final CmsServletContainerCfgMode MODE_NONE = new CmsServletContainerCfgMode("none");
060
061        /** Version id required for safe serialization. */
062        private static final long serialVersionUID = -8191582624108081577L;
063
064        /**
065         * Private constructor.<p>
066         *
067         * @param mode the remote command execution return type integer representation
068         */
069        private CmsServletContainerCfgMode(String mode) {
070
071            super(mode);
072        }
073
074        /**
075         * Returns the parsed mode object if the string representation matches, or <code>null</code> if not.<p>
076         *
077         * @param mode the string representation to parse
078         *
079         * @return the parsed mode object
080         */
081        public static CmsServletContainerCfgMode valueOf(String mode) {
082
083            if (mode == null) {
084                return null;
085            }
086            if (mode.equalsIgnoreCase(MODE_NONE.getMode())) {
087                return MODE_NONE;
088            }
089            if (mode.equalsIgnoreCase(MODE_MANUAL.getMode())) {
090                return MODE_MANUAL;
091            }
092            if (mode.equalsIgnoreCase(MODE_AUTO.getMode())) {
093                return MODE_AUTO;
094            }
095            return null;
096        }
097
098        /**
099         * Checks if this is the auto mode.<p>
100         *
101         * @return <code>true</code> if this is the auto mode
102         */
103        public boolean isAuto() {
104
105            return this == MODE_AUTO;
106        }
107
108        /**
109         * Checks if this is the manual mode.<p>
110         *
111         * @return <code>true</code> if this is the manual mode
112         */
113        public boolean isManual() {
114
115            return this == MODE_MANUAL;
116        }
117
118        /**
119         * Checks if this is the none mode.<p>
120         *
121         * @return <code>true</code> if this is the none mode
122         */
123        public boolean isNone() {
124
125            return this == MODE_NONE;
126        }
127    }
128
129    /** String remote command execution return type. */
130    public static final CmsServletContainerCfgMode CFG_MODE_AUTO = CmsServletContainerCfgMode.MODE_AUTO;
131
132    /** Map remote command execution return type. */
133    public static final CmsServletContainerCfgMode CFG_MODE_MANUAL = CmsServletContainerCfgMode.MODE_MANUAL;
134
135    /** List remote command execution return type. */
136    public static final CmsServletContainerCfgMode CFG_MODE_NONE = CmsServletContainerCfgMode.MODE_NONE;
137
138    /** The static log object for this class. */
139    private static final Log LOG = CmsLog.getLog(CmsServletContainerSettings.class);
140
141    /** If the servlet can throw an exception if initialization fails, for instance, Weblogic and Resin have problems with the exception. */
142    private static boolean m_servletThrowsException = true;
143
144    /**
145     * The replacement request attribute for the {@link javax.servlet.http.HttpServletRequest#getPathInfo()} method,
146     * which is needed because this method is not properly implemented in BEA WLS 9.x.<p>
147     */
148    private static final String REQUEST_ERROR_PAGE_ATTRIBUTE_WEBLOGIC = "weblogic.servlet.errorPage";
149
150    /** Constant name to identify GlassFish servers. */
151    // 2.1: "Sun GlassFish Enterprise Server v2.1"
152    private static final String SERVLET_CONTAINER_GLASSFISH = "GlassFish";
153
154    /** Constant name to identify Resin servers. */
155    private static final String SERVLET_CONTAINER_RESIN = "Resin";
156
157    /** Constant name to identify BEA WebLogic servers. */
158    private static final String SERVLET_CONTAINER_WEBLOGIC = "WebLogic Server";
159
160    /** Constant name to identify IBM Websphere servers. */
161    private static final String SERVLET_CONTAINER_WEBSPHERE = "IBM WebSphere Application Server";
162
163    /** The context path of the web application. */
164    private String m_contextPath;
165
166    /** The default web application (usually "ROOT"). */
167    private String m_defaultWebApplicationName;
168
169    /** The configuration mode. */
170    private CmsServletContainerCfgMode m_mode = CFG_MODE_NONE;
171
172    /** The OpenCms context and servlet path, e.g. <code>/opencms/opencms</code>. */
173    private String m_openCmsContext;
174
175    /** 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. */
176    private boolean m_preventResponseFlush;
177
178    /** If the tags need to be released after ending, this has to be prevented when running with Resin, for example. */
179    private boolean m_releaseTagsAfterEnd = true;
180
181    /**
182       * The request error page attribute to use if {@link javax.servlet.http.HttpServletRequest#getPathInfo()}
183       * is not working properly, like in BEA WLS 9.x.
184       */
185    private String m_requestErrorPageAttribute;
186
187    /** The name of the servlet container running OpenCms. */
188    private String m_servletContainerName;
189
190    /** The servlet path for the OpenCms servlet. */
191    private String m_servletPath;
192
193    /** The web application name. */
194    private String m_webApplicationName;
195
196    /** The OpenCms web application servlet container folder path (in the "real" file system). */
197    private String m_webApplicationRfsPath;
198
199    /** The OpenCms web application "WEB-INF" path (in the "real" file system). */
200    private String m_webInfRfsPath;
201
202    /**
203     * Creates a new object.<p>
204     *
205     * @param context used to find out specifics of the servlet container
206     */
207    public CmsServletContainerSettings(ServletContext context) {
208
209        // CmsSystemInfo<init> has to call this with null (for setup)
210        if (context != null) {
211            // check for OpenCms home (base) directory path
212            String webInfRfsPath = context.getInitParameter(OpenCmsServlet.SERVLET_PARAM_OPEN_CMS_HOME);
213            if (CmsStringUtil.isEmpty(webInfRfsPath)) {
214                webInfRfsPath = CmsFileUtil.searchWebInfFolder(context.getRealPath("/"));
215                if (CmsStringUtil.isEmpty(webInfRfsPath)) {
216                    throw new CmsInitException(Messages.get().container(Messages.ERR_CRITICAL_INIT_FOLDER_0));
217                }
218            }
219
220            // set the default web application name
221            // read the the default name from the servlet context parameters
222            String defaultWebApplication = context.getInitParameter("DefaultWebApplication");
223
224            // read the the OpenCms servlet mapping from the servlet context parameters
225            String servletMapping = context.getInitParameter(OpenCmsServlet.SERVLET_PARAM_OPEN_CMS_SERVLET);
226            if (servletMapping == null) {
227                throw new CmsInitException(Messages.get().container(Messages.ERR_CRITICAL_INIT_SERVLET_0));
228            }
229
230            // read the servlet container name
231            String servletContainerName = context.getServerInfo();
232
233            // web application context:
234            // read it from the servlet context parameters
235            //      this is needed in case an application server specific deployment descriptor is used to changed the webapp context
236            String webApplicationContextFromConfig = context.getInitParameter(
237                OpenCmsServlet.SERVLET_PARAM_WEB_APPLICATION_CONTEXT);
238            String contextPath = "";
239            if (CmsStringUtil.isEmptyOrWhitespaceOnly(webApplicationContextFromConfig)) {
240                contextPath = context.getContextPath();
241            } else {
242                if (defaultWebApplication.equals(webApplicationContextFromConfig)) {
243                    contextPath = "";
244                } else {
245                    contextPath = "/" + webApplicationContextFromConfig;
246                }
247            }
248            // init values:
249            init(webInfRfsPath, defaultWebApplication, servletMapping, servletContainerName, contextPath);
250            // finally care for the speciality of different servlet containers:
251            initContainerSpecifics(context);
252        }
253    }
254
255    /**
256     * Creates an instance based on the given values.<p>
257     *
258     * This is intended for <code>{@link CmsShell}</code> access.<p>
259     *
260     * @param webInfRfsPath the OpenCms web application "WEB-INF" path in the "real" file system) to set
261     * @param servletMapping the OpenCms servlet mapping  (e.g. "/opencms/*")
262     * @param webApplicationContext the name/path of the OpenCms web application context (optional, will be calculated form the path if null)
263     * @param defaultWebApplication the default web application name (usually "ROOT")
264     * @param servletContainerName the name of the servlet container running OpenCms
265     */
266    protected CmsServletContainerSettings(
267        String webInfRfsPath,
268        String defaultWebApplication,
269        String servletMapping,
270        String servletContainerName,
271        String webApplicationContext) {
272
273        init(webInfRfsPath, defaultWebApplication, servletMapping, servletContainerName, webApplicationContext);
274    }
275
276    /**
277     * Checks if the servlet can throw an exception if initialization fails.<p>
278     *
279     * @return <code>true</code> if the servlet can throw an exception if initialization fails
280     */
281    public static boolean isServletThrowsException() {
282
283        return m_servletThrowsException;
284    }
285
286    /**
287     * Returns the web application context path, e.g. "" (empty String) if the web application
288     * is the default web application (usually "ROOT"), or "/opencms" if the web application
289     * is called "opencms".<p>
290     *
291     * <i>From the Java Servlet Specification v2.4:</i><br>
292     * <b>Context Path:</b> The path prefix associated with the ServletContext that this
293     * servlet is a part of. If this context is the "default" context rooted at the base of
294     * the web server's URL name space, this path will be an empty string. Otherwise,
295     * if the context is not rooted at the root of the server's name space, the path starts
296     * with a "/" character but does not end with a "/" character.<p>
297     *
298     * @return the web application context path
299     * @see #getWebApplicationName()
300     * @see #getServletPath()
301     * @see #getOpenCmsContext()
302     */
303    public String getContextPath() {
304
305        return m_contextPath;
306    }
307
308    /**
309     * Returns the default web application name (usually "ROOT").<p>
310     *
311     * @return the default web application name
312     */
313    public String getDefaultWebApplicationName() {
314
315        return m_defaultWebApplicationName;
316    }
317
318    /**
319     * Returns the mode.<p>
320     *
321     * @return the mode
322     */
323    public CmsServletContainerCfgMode getMode() {
324
325        return m_mode;
326    }
327
328    /**
329     * Returns the OpenCms request context, e.g. "/opencms/opencms".<p>
330     *
331     * The OpenCms context will always start with a "/" and never have a trailing "/".
332     * The OpenCms context is identical to <code>getContexPath() + getServletPath()</code>.<p>
333     *
334     * @return the OpenCms request context, e.g. "/opencms/opencms"
335     * @see #getContextPath()
336     * @see #getServletPath()
337     */
338    public String getOpenCmsContext() {
339
340        return m_openCmsContext;
341    }
342
343    /**
344     * Returns the request error page attribute.<p>
345     *
346     * @return the request error page attribute
347     */
348    public String getRequestErrorPageAttribute() {
349
350        return m_requestErrorPageAttribute;
351    }
352
353    /**
354     * Returns the name of the servlet container running OpenCms.<p>
355     *
356     * @return the name of the servlet container running OpenCms
357     */
358    public String getServletContainerName() {
359
360        return m_servletContainerName;
361    }
362
363    /**
364     * Returns the OpenCms servlet path, e.g. "/opencms".<p>
365     *
366     * <i>From the Java Servlet Specification v2.4:</i><br>
367     * <b>Servlet Path:</b> The path section that directly corresponds to the mapping
368     * which activated this request. This path starts with a?/? character except in the
369     * case where the request is matched with the ?/*? pattern, in which case it is the
370     * empty string.<p>
371     *
372     * @return the OpenCms servlet path
373     * @see #getContextPath()
374     * @see #getWebApplicationName()
375     * @see #getOpenCmsContext()
376     */
377    public String getServletPath() {
378
379        return m_servletPath;
380    }
381
382    /**
383     * Returns the OpenCms web application name, e.g. "opencms" or "ROOT" (no leading or trailing "/").<p>
384     *
385     * The web application name is stored for informational purposes only.
386     * If you want to construct an URI, use either {@link #getContextPath()} and
387     * {@link #getServletPath()}, or for links to the OpenCms VFS use {@link  #getOpenCmsContext()}.<p>
388     *
389     * @return the OpenCms web application name
390     * @see #getContextPath()
391     * @see #getServletPath()
392     * @see #getOpenCmsContext()
393     */
394    public String getWebApplicationName() {
395
396        return m_webApplicationName;
397    }
398
399    /**
400     * Returns the OpenCms web application folder in the servlet container.<p>
401     *
402     * @return the OpenCms web application folder in the servlet container
403     */
404    public String getWebApplicationRfsPath() {
405
406        return m_webApplicationRfsPath;
407    }
408
409    /**
410     * Returns the OpenCms web application "WEB-INF" directory path.<p>
411     *
412     * @return the OpenCms web application "WEB-INF" directory path
413     */
414    public String getWebInfRfsPath() {
415
416        return m_webInfRfsPath;
417    }
418
419    /**
420     * Checks if the flex response has to prevent buffer flushing.<p>
421     *
422     * @return <code>true</code> if the flex response has to prevent buffer flushing
423     */
424    public boolean isPreventResponseFlush() {
425
426        return m_preventResponseFlush;
427    }
428
429    /**
430     * Checks if the tags need to be released after ending.<p>
431     *
432     * @return <code>true</code> if the tags need to be released after ending
433     */
434    public boolean isReleaseTagsAfterEnd() {
435
436        return m_releaseTagsAfterEnd;
437    }
438
439    /**
440     * Sets the mode from the configuration.<p>
441     *
442     * @param configValue the mode to set
443     */
444    public void setMode(String configValue) {
445
446        m_mode = CmsServletContainerCfgMode.valueOf(configValue);
447    }
448
449    /**
450     * Sets if the flex response has to prevent buffer flushing.<p>
451     *
452     * @param preventResponseFlush the flag to set
453     */
454    public void setPreventResponseFlush(boolean preventResponseFlush) {
455
456        m_preventResponseFlush = preventResponseFlush;
457    }
458
459    /**
460     * Sets if the tags need to be released after ending.<p>
461     *
462     * @param releaseTagsAfterEnd the flag to set
463     */
464    public void setReleaseTagsAfterEnd(boolean releaseTagsAfterEnd) {
465
466        m_releaseTagsAfterEnd = releaseTagsAfterEnd;
467    }
468
469    /**
470     * Sets the request error page attribute.<p>
471     *
472     * @param requestErrorPageAttribute the request error page attribute to set
473     */
474    public void setRequestErrorPageAttribute(String requestErrorPageAttribute) {
475
476        m_requestErrorPageAttribute = requestErrorPageAttribute;
477    }
478
479    /**
480     * Sets if the servlet can throw an exception if initialization fails.<p>
481     *
482     * @param servletThrowsException the flag to set
483     */
484    public void setServletThrowsException(boolean servletThrowsException) {
485
486        m_servletThrowsException = servletThrowsException;
487    }
488
489    /**
490     * Initialization code common to both constructors.<p>
491     *
492     * While the "webapplication - mode" constructor obtains all values from the
493     * <code>{@link ServletContext}</code> the <code>{@link CmsShell}</code> constructor
494     * accepts them as arguments and passes them here. <p>
495     *
496     * @param webInfRfsPath the OpenCms web application "WEB-INF" path in the "real" file system) to set
497     * @param servletMapping the OpenCms servlet mapping  (e.g. "/opencms/*")
498     * @param webApplicationContext the name/path of the OpenCms web application context (optional, will be calculated form the path if null)
499     * @param defaultWebApplication the default web application name (usually "ROOT")
500     * @param servletContainerName the name of the servlet container running OpenCms
501     * @param contextPath the context path
502     */
503    private void init(
504        String webInfRfsPath,
505        String defaultWebApplication,
506        String servletMapping,
507        String servletContainerName,
508        String contextPath) {
509
510        // WEB-INF RFS path
511
512        webInfRfsPath = webInfRfsPath.replace('\\', '/');
513        if (!webInfRfsPath.endsWith("/")) {
514            webInfRfsPath = webInfRfsPath + "/";
515        }
516        m_webInfRfsPath = CmsFileUtil.normalizePath(webInfRfsPath);
517
518        // default web application
519        if (defaultWebApplication == null) {
520            defaultWebApplication = "";
521        }
522        if (defaultWebApplication.endsWith("/")) {
523            defaultWebApplication = defaultWebApplication.substring(0, defaultWebApplication.length() - 1);
524        }
525        if (defaultWebApplication.startsWith("/")) {
526            defaultWebApplication = defaultWebApplication.substring(1);
527        }
528        m_defaultWebApplicationName = defaultWebApplication;
529
530        // servlet mapping
531        if (!servletMapping.startsWith("/")) {
532            servletMapping = "/" + servletMapping;
533        }
534        if (servletMapping.endsWith("/*")) {
535            // usually a mapping must be in the form "/opencms/*", cut off all slashes
536            servletMapping = servletMapping.substring(0, servletMapping.length() - 2);
537        }
538        m_servletPath = servletMapping;
539
540        // servlet container name
541        if (servletContainerName == null) {
542            m_servletContainerName = "";
543        }
544        m_servletContainerName = servletContainerName;
545
546        // set the web application name
547        File path = new File(m_webInfRfsPath);
548        m_webApplicationName = path.getParentFile().getName();
549
550        // set the context path
551        if (contextPath == null) {
552            // this should only happen during test cases
553            m_contextPath = "/" + m_webApplicationName;
554        } else {
555            m_contextPath = contextPath;
556        }
557        // set the OpenCms context
558        m_openCmsContext = m_contextPath + m_servletPath;
559
560        // set the web application rfs path
561        m_webApplicationRfsPath = path.getParentFile().getAbsolutePath();
562        if (!m_webApplicationRfsPath.endsWith(File.separator)) {
563            m_webApplicationRfsPath += File.separator;
564        }
565
566        // fill some defaults:
567        m_releaseTagsAfterEnd = false;
568        m_requestErrorPageAttribute = null;
569        m_servletThrowsException = true;
570        m_preventResponseFlush = false;
571    }
572
573    /**
574     * Initializes these container settings with container specific settings.<p>
575     *
576     * @param context
577     *      the servlet context to find out information about the servlet container
578     */
579    private void initContainerSpecifics(ServletContext context) {
580
581        // the tags behavior
582        m_releaseTagsAfterEnd = !(m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) > -1);
583
584        // the request error page attribute
585        if (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBLOGIC) > -1) {
586            m_requestErrorPageAttribute = REQUEST_ERROR_PAGE_ATTRIBUTE_WEBLOGIC;
587        }
588
589        // the failed initialization behavior
590        m_servletThrowsException = true;
591        m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) < 0);
592        m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBLOGIC) < 0);
593        m_servletThrowsException &= (m_servletContainerName.indexOf(SERVLET_CONTAINER_GLASSFISH) < 0);
594
595        // the flush flex response behavior
596        m_preventResponseFlush = false;
597        m_preventResponseFlush |= (m_servletContainerName.indexOf(SERVLET_CONTAINER_WEBSPHERE) > -1);
598        m_preventResponseFlush |= (m_servletContainerName.indexOf(SERVLET_CONTAINER_RESIN) > -1);
599    }
600}