All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencms.main.OpenCmsServlet Maven / Gradle / Ivy

Go to download

OpenCms is an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 17.0
Show newest version
/*
 * This library is part of OpenCms -
 * the Open Source Content Management System
 *
 * Copyright (c) Alkacon Software GmbH & Co. KG (http://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: http://www.alkacon.com
 *
 * For further information about OpenCms, please see the
 * project website: http://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.ade.configuration.CmsADEConfigData;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.site.CmsSite;
import org.opencms.staticexport.CmsStaticExportData;
import org.opencms.staticexport.CmsStaticExportRequest;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsThreadLocalStack;
import org.opencms.util.CmsUUID;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;

/**
 * This the main servlet of the OpenCms system.

* * From here, all operations that are results of HTTP requests are invoked. * Any incoming request is handled in multiple steps: * *

  1. The requesting {@link org.opencms.file.CmsUser} is authenticated * and a {@link org.opencms.file.CmsObject} with this users context information * is created. This {@link org.opencms.file.CmsObject} is used to access all functions of OpenCms, limited by * the authenticated users permissions. If the user is not identified, it is set to the default user, usually named "Guest".
  2. * *
  3. The requested {@link org.opencms.file.CmsResource} is loaded into OpenCms and depending on its type * (and the users persmissions to display or modify it), * it is send to one of the OpenCms {@link org.opencms.loader.I_CmsResourceLoader} implementations * do be processed.
  4. * *
  5. * The {@link org.opencms.loader.I_CmsResourceLoader} will then decide what to do with the * contents of the requested {@link org.opencms.file.CmsResource}. * In case of a JSP resource the JSP handling mechanism is invoked with the {@link org.opencms.loader.CmsJspLoader}, * in case of an image (or another static resource) this will be returned by the {@link org.opencms.loader.CmsDumpLoader} * etc. *
* * @since 6.0.0 * * @see org.opencms.main.CmsShell * @see org.opencms.file.CmsObject * @see org.opencms.main.OpenCms */ public class OpenCmsServlet extends HttpServlet implements I_CmsRequestHandler { /** * Context class for storing request-dependent caches etc. */ public static class RequestCache { /** Cache for sitemap configurations. */ private Map m_configCache = new HashMap<>(); /** Map of attributes. */ private Map m_attributes = new HashMap<>(); /** Buffer for log messages to write at the end of the request. */ private CmsDuplicateRemovingLogBuffer m_logBuffer = new CmsDuplicateRemovingLogBuffer(); /** * Adds a log message to the log buffer. * * @param channel the channel * @param level the log level * @param message the message */ public void addLog(String channel, String level, String message) { m_logBuffer.add(channel, level, message); } /** * Called at the end of the request. */ public void close() { m_logBuffer.flush(); } /** * Gets the attribute for the given key. * * @param key the key * @return the attribute */ public Object getAttribute(String key) { return m_attributes.get(key); } /** * Gets the stored attribute for the given key, or lazily initializes it with the given provider function if it doesn't exist yet. * * @param key the key * @param provider the function to lazily initialize the entry * @return the attribute */ public Object getAttribute(String key, Function provider) { return m_attributes.computeIfAbsent(key, provider); } /** * Gets the cached sitemap configuration data. * * @param key the key * @return the sitemap config from the cache */ public CmsADEConfigData getCachedConfig(String key) { return m_configCache.get(key); } /** * Sets the attribute for the given key. * * @param key the key * @param value the attribute value */ public void setAttribute(String key, Object value) { m_attributes.put(key, value); } /** * Sets the cached sitemap configuration data. * * @param key the key * @param config the sitemap configuration to cache */ public void setCachedConfig(String key, CmsADEConfigData config) { m_configCache.put(key, config); } } /** * Debugging information about currently running requests. */ public static class RequestInfo { /** Start time of the request. */ private long m_startTime; /** URI of the request. */ private String m_uri; /** Thread id of the thread handling the request. */ private long m_threadId; /** * Creates a new instance. * * @param uri the URI * @param startTime the start time */ public RequestInfo(String uri, long startTime) { m_startTime = startTime; m_uri = uri; m_threadId = Thread.currentThread().getId(); } /** * Gets the request start time. * * @return the start time */ public long getStartTime() { return m_startTime; } /** * Gets the thread id * * @return the thread id */ public long getThreadId() { return m_threadId; } /** * Gets the URI of the request. * * @return the URI of the request */ public String getUri() { return m_uri; } } /** The current request in a threadlocal. */ public static final CmsThreadLocalStack currentRequestStack = new CmsThreadLocalStack(); /** Map containing beans with information about currently running requests. */ public static final ConcurrentHashMap activeRequests = new ConcurrentHashMap<>(); /** The current thread context for the request. */ private static final CmsThreadLocalStack requestCacheStack = new CmsThreadLocalStack(); /** GWT RPC services suffix. */ public static final String HANDLE_GWT = ".gwt"; /** Handler prefix. */ public static final String HANDLE_PATH = "/handle"; /** Name of the DefaultWebApplication parameter in the web.xml OpenCms servlet configuration. */ public static final String SERVLET_PARAM_DEFAULT_WEB_APPLICATION = "DefaultWebApplication"; /** Name of the OpenCmsHome parameter in the web.xml OpenCms servlet configuration. */ public static final String SERVLET_PARAM_OPEN_CMS_HOME = "OpenCmsHome"; /** Name of the OpenCmsServlet parameter in the web.xml OpenCms servlet configuration. */ public static final String SERVLET_PARAM_OPEN_CMS_SERVLET = "OpenCmsServlet"; /** Name of the WebApplicationContext parameter in the web.xml OpenCms servlet configuration. */ public static final String SERVLET_PARAM_WEB_APPLICATION_CONTEXT = "WebApplicationContext"; /** Path to handler "error page" files in the VFS. */ private static final String HANDLE_VFS_PATH = "/system/handler" + HANDLE_PATH; /** Handler "error page" file suffix. */ private static final String HANDLE_VFS_SUFFIX = ".html"; /** Handler implementation names. */ private static final String[] HANDLER_NAMES = {"404"}; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(OpenCmsServlet.class); /** Serial version UID required for safe serialization. */ private static final long serialVersionUID = 4729951599966070050L; /** URL prefix for the built-in service handler. */ public static final String HANDLE_BUILTIN_SERVICE = "/handleBuiltinService/"; /** * Gets a thread-local, request-specific context object for requests made to the OpenCms servlet. * * @return the thread context */ public static RequestCache getRequestCache() { RequestCache result = requestCacheStack.top(); return result; } /** * Helper method used to conveniently access the request cache, does nothing if the request cache isn't set. * * @param handler the handler to pass the request cache to */ public static void withRequestCache(Consumer handler) { RequestCache cache = getRequestCache(); if (cache != null) { handler.accept(cache); } } /** * OpenCms servlet main request handling method.

* * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { // we are using stacks for these because the doGet method may be called reentrantly, e.g. when using servlet forwarding. currentRequestStack.push(req); requestCacheStack.push(new RequestCache()); final CmsUUID requestId = new CmsUUID(); RequestInfo reqInfo = new RequestInfo( req.getRequestURL().toString() + (req.getQueryString() != null ? "?" + req.getQueryString() : ""), System.currentTimeMillis()); activeRequests.put(requestId, reqInfo); try { // check to OpenCms runlevel int runlevel = OpenCmsCore.getInstance().getRunLevel(); // write OpenCms server identification in the response header res.setHeader(CmsRequestUtil.HEADER_SERVER, OpenCmsCore.getInstance().getSystemInfo().getVersion()); if (runlevel != OpenCms.RUNLEVEL_4_SERVLET_ACCESS) { // not the "normal" servlet runlevel if (runlevel == OpenCms.RUNLEVEL_3_SHELL_ACCESS) { // we have shell runlevel only, upgrade to servlet runlevel (required after setup wizard) init(getServletConfig()); } else { // illegal runlevel, we can't process requests // sending status code 403, indicating the server understood the request but refused to fulfill it res.sendError(HttpServletResponse.SC_FORBIDDEN); // goodbye return; } } String path = OpenCmsCore.getPathInfo(req); if (path.startsWith(HANDLE_BUILTIN_SERVICE)) { // built-in services are for small AJAX-related functionality in the core that doesn't need to be configurable String remainder = path.substring(HANDLE_BUILTIN_SERVICE.length() - 1); // we want a leading slash in remainder OpenCmsCore.getInstance().invokeBuiltinService(remainder, req, res); } else if (path.startsWith(HANDLE_PATH)) { // this is a request to an OpenCms handler URI invokeHandler(req, res); } else if (path.endsWith(HANDLE_GWT)) { // handle GWT rpc services String serviceName = CmsResource.getName(path); serviceName = serviceName.substring(0, serviceName.length() - HANDLE_GWT.length()); OpenCmsCore.getInstance().invokeGwtService(serviceName, req, res, getServletConfig()); } else { // standard request to a URI in the OpenCms VFS OpenCmsCore.getInstance().showResource(req, res); } } finally { currentRequestStack.pop(); requestCacheStack.pop().close(); activeRequests.remove(requestId); } } /** * OpenCms servlet POST request handling method, * will just call {@link #doGet(HttpServletRequest, HttpServletResponse)}.

* * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @Override public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { doGet(req, res); } /** * @see org.opencms.main.I_CmsRequestHandler#getHandlerNames() */ public String[] getHandlerNames() { return HANDLER_NAMES; } /** * @see org.opencms.main.I_CmsRequestHandler#handle(HttpServletRequest, HttpServletResponse, String) */ public void handle(HttpServletRequest req, HttpServletResponse res, String name) throws IOException, ServletException { int errorCode; try { errorCode = Integer.valueOf(name).intValue(); } catch (NumberFormatException nf) { res.sendError(HttpServletResponse.SC_FORBIDDEN); LOG.debug("Error parsing handler name.", nf); return; } switch (errorCode) { case 404: CmsObject cms = null; CmsStaticExportData exportData = null; try { // this will be set in the root site cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport()); exportData = OpenCms.getStaticExportManager().getExportData(req, cms); } catch (CmsException e) { // unlikely to happen if (LOG.isWarnEnabled()) { LOG.warn( Messages.get().getBundle().key( Messages.LOG_INIT_CMSOBJECT_IN_HANDLER_2, name, OpenCmsCore.getPathInfo(req)), e); } } if (exportData != null) { try { // generate a static export request wrapper CmsStaticExportRequest exportReq = new CmsStaticExportRequest(req, exportData); // export the resource and set the response status according to the result res.setStatus(OpenCms.getStaticExportManager().export(exportReq, res, cms, exportData)); } catch (Throwable t) { if (LOG.isWarnEnabled()) { LOG.warn(Messages.get().getBundle().key(Messages.LOG_ERROR_EXPORT_1, exportData), t); } openErrorHandler(req, res, errorCode); } } else { openErrorHandler(req, res, errorCode); } break; default: openErrorHandler(req, res, errorCode); } } /** * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig) */ @Override public synchronized void init(ServletConfig config) throws ServletException { super.init(config); try { // upgrade the runlevel // usually this should have already been done by the context listener // however, after a fresh install / setup this will be done from here OpenCmsCore.getInstance().upgradeRunlevel(config.getServletContext()); // finalize OpenCms initialization OpenCmsCore.getInstance().initServlet(this); } catch (CmsInitException e) { if (Messages.ERR_CRITICAL_INIT_WIZARD_0.equals(e.getMessageContainer().getKey())) { // if wizard is still enabled - allow retry of initialization (required for setup wizard) // this means the servlet init() call must be terminated by an exception if (CmsServletContainerSettings.isServletThrowsException()) { throw new ServletException(e.getMessage()); } else { // this is needed since some servlet containers does not like the servlet to throw exceptions, // like BEA WLS 9.x and Resin LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), e); } } } catch (Throwable t) { LOG.error(Messages.get().getBundle().key(Messages.LOG_ERROR_GENERIC_0), t); } } /** * Manages requests to internal OpenCms request handlers.

* * @param req the current request * @param res the current response * @throws ServletException in case an error occurs * @throws ServletException in case an error occurs * @throws IOException in case an error occurs */ protected void invokeHandler(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { String pathInfo = OpenCmsCore.getPathInfo(req); String name = pathInfo.substring(HANDLE_PATH.length()); I_CmsRequestHandler handler = OpenCmsCore.getInstance().getRequestHandler(name); if ((handler == null) && name.contains("/")) { // if the name contains a '/', also check for handlers matching the first path fragment only name = name.substring(0, name.indexOf("/")); handler = OpenCmsCore.getInstance().getRequestHandler(name); } if (handler != null) { handler.handle(req, res, name); } else { LOG.warn("Invalid request handler call: " + pathInfo); openErrorHandler(req, res, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } /** * Displays an error code handler loaded from the OpenCms VFS, * or if such a page does not exist, * displays the default servlet container error code.

* * @param req the current request * @param res the current response * @param errorCode the error code to display * @throws IOException if something goes wrong * @throws ServletException if something goes wrong */ protected void openErrorHandler(HttpServletRequest req, HttpServletResponse res, int errorCode) throws IOException, ServletException { String handlerUri = (new StringBuffer(64)).append(HANDLE_VFS_PATH).append(errorCode).append( HANDLE_VFS_SUFFIX).toString(); // provide the original error code in a request attribute req.setAttribute(CmsRequestUtil.ATTRIBUTE_ERRORCODE, new Integer(errorCode)); CmsObject cms; CmsFile file; try { // create OpenCms context, this will be set in the root site cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest()); cms.getRequestContext().setSecureRequest(OpenCms.getSiteManager().usesSecureSite(req)); } catch (CmsException e) { // unlikely to happen as the OpenCms "Guest" context can always be initialized CmsMessageContainer container = Messages.get().container( Messages.LOG_INIT_CMSOBJECT_IN_HANDLER_2, new Integer(errorCode), handlerUri); if (LOG.isWarnEnabled()) { LOG.warn(org.opencms.jsp.Messages.getLocalizedMessage(container, req), e); } // however, if it _does_ happen, then we really can't continue here if (!res.isCommitted()) { // since the handler file is not accessible, display the default error page res.sendError(errorCode, e.getLocalizedMessage()); } return; } try { if (!tryCustomErrorPage(cms, req, res, errorCode)) { cms.getRequestContext().setUri(handlerUri); cms.getRequestContext().setSecureRequest(OpenCms.getSiteManager().usesSecureSite(req)); // read the error handler file file = cms.readFile(handlerUri, CmsResourceFilter.IGNORE_EXPIRATION); OpenCms.getResourceManager().loadResource(cms, file, req, res); } } catch (CmsException e) { // unable to load error page handler VFS resource CmsMessageContainer container = Messages.get().container( Messages.ERR_SHOW_ERR_HANDLER_RESOURCE_2, new Integer(errorCode), handlerUri); throw new ServletException(org.opencms.jsp.Messages.getLocalizedMessage(container, req), e); } } /** * Tries to load the custom error page at the given rootPath. * @param cms {@link CmsObject} used for reading the resource (site root and uri get adjusted!) * @param req the current request * @param res the current response * @param rootPath the VFS root path to the error page resource * @return a flag, indicating if the error page could be loaded */ private boolean loadCustomErrorPage( CmsObject cms, HttpServletRequest req, HttpServletResponse res, String rootPath) { try { // get the site of the error page resource CmsSite errorSite = OpenCms.getSiteManager().getSiteForRootPath(rootPath); cms.getRequestContext().setSiteRoot(errorSite.getSiteRoot()); String relPath = cms.getRequestContext().removeSiteRoot(rootPath); if (cms.existsResource(relPath)) { cms.getRequestContext().setUri(relPath); OpenCms.getResourceManager().loadResource(cms, cms.readResource(relPath), req, res); return true; } else { return false; } } catch (Throwable e) { // something went wrong log the exception and return false LOG.error(e.getMessage(), e); return false; } } /** * Tries to load a site specific error page. If * @param cms {@link CmsObject} used for reading the resource (site root and uri get adjusted!) * @param req the current request * @param res the current response * @param errorCode the error code to display * @return a flag, indicating if the custom error page could be loaded. */ private boolean tryCustomErrorPage(CmsObject cms, HttpServletRequest req, HttpServletResponse res, int errorCode) { String siteRoot = OpenCms.getSiteManager().matchRequest(req).getSiteRoot(); CmsSite site = OpenCms.getSiteManager().getSiteForSiteRoot(siteRoot); if (site != null) { // store current site root and URI String currentSiteRoot = cms.getRequestContext().getSiteRoot(); String currentUri = cms.getRequestContext().getUri(); try { if (site.getErrorPage() != null) { String rootPath = site.getErrorPage(); if (loadCustomErrorPage(cms, req, res, rootPath)) { return true; } } String rootPath = CmsStringUtil.joinPaths(siteRoot, "/.errorpages/handle" + errorCode + ".html"); if (loadCustomErrorPage(cms, req, res, rootPath)) { return true; } } finally { cms.getRequestContext().setSiteRoot(currentSiteRoot); cms.getRequestContext().setUri(currentUri); } } return false; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy