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

org.opencms.loader.CmsJspLoader Maven / Gradle / Ivy

/*
 * 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.loader;

import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.file.CmsFile;
import org.opencms.file.CmsObject;
import org.opencms.file.CmsPropertyDefinition;
import org.opencms.file.CmsRequestContext;
import org.opencms.file.CmsResource;
import org.opencms.file.CmsResourceFilter;
import org.opencms.file.CmsVfsResourceNotFoundException;
import org.opencms.file.history.CmsHistoryResourceHandler;
import org.opencms.flex.CmsFlexCache;
import org.opencms.flex.CmsFlexController;
import org.opencms.flex.CmsFlexRequest;
import org.opencms.flex.CmsFlexResponse;
import org.opencms.gwt.shared.CmsGwtConstants;
import org.opencms.i18n.CmsEncoder;
import org.opencms.i18n.CmsMessageContainer;
import org.opencms.jsp.CmsJspTagEnableAde;
import org.opencms.jsp.jsonpart.CmsJsonPartFilter;
import org.opencms.jsp.util.CmsJspLinkMacroResolver;
import org.opencms.jsp.util.CmsJspStandardContextBean;
import org.opencms.main.CmsEvent;
import org.opencms.main.CmsException;
import org.opencms.main.CmsLog;
import org.opencms.main.I_CmsEventListener;
import org.opencms.main.OpenCms;
import org.opencms.monitor.CmsMemoryMonitor;
import org.opencms.relations.CmsRelation;
import org.opencms.relations.CmsRelationFilter;
import org.opencms.relations.CmsRelationType;
import org.opencms.staticexport.CmsLinkManager;
import org.opencms.util.CmsFileUtil;
import org.opencms.util.CmsRequestUtil;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.I_CmsRegexSubstitution;
import org.opencms.workplace.CmsWorkplaceManager;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.SocketException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;

import com.google.common.base.Splitter;

/**
 * The JSP loader which enables the execution of JSP in OpenCms.

* * Parameters supported by this loader:

* *
jsp.repository
* (Optional) This is the root directory in the "real" file system where generated JSPs are stored. * The default is the web application path, e.g. in Tomcat if your web application is * names "opencms" it would be ${TOMCAT_HOME}/webapps/opencms/. * The jsp.folder (see below) is added to this path. * Usually the jsp.repository is not changed. *
* *
jsp.folder
* (Optional) A path relative to the jsp.repository path where the * JSPs generated by OpenCms are stored. The default is to store the generated JSP in * /WEB-INF/jsp/. * This works well in Tomcat 4, and the JSPs are * not accessible directly from the outside this way, only through the OpenCms servlet. * Please note: Some servlet environments (e.g. BEA Weblogic) do not permit * JSPs to be stored under /WEB-INF. For environments like these, * set the path to some place where JSPs can be accessed, e.g. /jsp/ only. *
* *
jsp.errorpage.committed
* (Optional) This parameter controls behavior of JSP error pages * i.e. <% page errorPage="..." %>. If you find that these don't work * in your servlet environment, you should try to change the value here. * The default true has been tested with Tomcat 4.1 and 5.0. * Older versions of Tomcat like 4.0 require a setting of false.
*
* * @since 6.0.0 * * @see I_CmsResourceLoader */ public class CmsJspLoader implements I_CmsResourceLoader, I_CmsFlexCacheEnabledLoader, I_CmsEventListener { /** Property value for "cache" that indicates that the FlexCache should be bypassed. */ public static final String CACHE_PROPERTY_BYPASS = "bypass"; /** Property value for "cache" that indicates that the output should be streamed. */ public static final String CACHE_PROPERTY_STREAM = "stream"; /** Default jsp folder constant. */ public static final String DEFAULT_JSP_FOLDER = "/WEB-INF/jsp/"; /** Special JSP directive tag start (%>). */ public static final String DIRECTIVE_END = "%>"; /** Special JSP directive tag start (<%(). */ public static final String DIRECTIVE_START = "<%@"; /** Extension for JSP managed by OpenCms (.jsp). */ public static final String JSP_EXTENSION = ".jsp"; /** Cache max age parameter name. */ public static final String PARAM_CLIENT_CACHE_MAXAGE = "client.cache.maxage"; /** Jsp cache size parameter name. */ public static final String PARAM_JSP_CACHE_SIZE = "jsp.cache.size"; /** Error page committed parameter name. */ public static final String PARAM_JSP_ERRORPAGE_COMMITTED = "jsp.errorpage.committed"; /** Jsp folder parameter name. */ public static final String PARAM_JSP_FOLDER = "jsp.folder"; /** Jsp repository parameter name. */ public static final String PARAM_JSP_REPOSITORY = "jsp.repository"; /** The id of this loader. */ public static final int RESOURCE_LOADER_ID = 6; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsJspLoader.class); /** The maximum age for delivered contents in the clients cache. */ private static long m_clientCacheMaxAge; /** Read write locks for jsp files. */ private static Map m_fileLocks = CmsMemoryMonitor.createLRUCacheMap(10000); /** The directory to store the generated JSP pages in (absolute path). */ private static String m_jspRepository; /** The directory to store the generated JSP pages in (relative path in web application). */ private static String m_jspWebAppRepository; /** The CmsFlexCache used to store generated cache entries in. */ private CmsFlexCache m_cache; /** The resource loader configuration. */ private CmsParameterConfiguration m_configuration; /** Flag to indicate if error pages are marked as "committed". */ private boolean m_errorPagesAreNotCommitted; /** The offline JSPs. */ private Map m_offlineJsps; /** The online JSPs. */ private Map m_onlineJsps; /** A map from taglib names to their URIs. */ private Map m_taglibs = new HashMap(); /** Lock used to prevent JSP repository from being accessed while it is purged. The read lock is needed for accessing the JSP repository, the write lock is needed for purging it. */ private ReentrantReadWriteLock m_purgeLock = new ReentrantReadWriteLock(true); /** * The constructor of the class is empty, the initial instance will be * created by the resource manager upon startup of OpenCms.

* * @see org.opencms.loader.CmsResourceManager */ public CmsJspLoader() { m_configuration = new CmsParameterConfiguration(); OpenCms.addCmsEventListener( this, new int[] {EVENT_CLEAR_CACHES, EVENT_CLEAR_OFFLINE_CACHES, EVENT_CLEAR_ONLINE_CACHES}); m_fileLocks = CmsMemoryMonitor.createLRUCacheMap(10000); initCaches(1000); } /** * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#addConfigurationParameter(java.lang.String, java.lang.String) */ public void addConfigurationParameter(String paramName, String paramValue) { m_configuration.add(paramName, paramValue); if (paramName.startsWith("taglib.")) { m_taglibs.put(paramName.replaceFirst("^taglib\\.", ""), paramValue.trim()); } } /** * @see org.opencms.main.I_CmsEventListener#cmsEvent(org.opencms.main.CmsEvent) */ public void cmsEvent(CmsEvent event) { switch (event.getType()) { case EVENT_CLEAR_CACHES: m_offlineJsps.clear(); m_onlineJsps.clear(); return; case EVENT_CLEAR_OFFLINE_CACHES: m_offlineJsps.clear(); return; case EVENT_CLEAR_ONLINE_CACHES: m_onlineJsps.clear(); return; default: // do nothing } } /** * Destroy this ResourceLoder, this is a NOOP so far. */ public void destroy() { // NOOP } /** * @see org.opencms.loader.I_CmsResourceLoader#dump(org.opencms.file.CmsObject, org.opencms.file.CmsResource, java.lang.String, java.util.Locale, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public byte[] dump( CmsObject cms, CmsResource file, String element, Locale locale, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // get the current Flex controller CmsFlexController controller = CmsFlexController.getController(req); CmsFlexController oldController = null; if (controller != null) { // for dumping we must create an new "top level" controller, save the old one to be restored later oldController = controller; } byte[] result = null; try { // now create a new, temporary Flex controller controller = getController(cms, file, req, res, false, false); if (element != null) { // add the element parameter to the included request String[] value = new String[] {element}; Map parameters = Collections.singletonMap( I_CmsResourceLoader.PARAMETER_ELEMENT, value); controller.getCurrentRequest().addParameterMap(parameters); } controller.getCurrentRequest().addAttributeMap(CmsRequestUtil.getAtrributeMap(req)); // dispatch to the JSP result = dispatchJsp(controller); // remove temporary controller CmsFlexController.removeController(req); } finally { if ((oldController != null) && (controller != null)) { // update "date last modified" oldController.updateDates(controller.getDateLastModified(), controller.getDateExpires()); // reset saved controller CmsFlexController.setController(req, oldController); } } return result; } /** * @see org.opencms.loader.I_CmsResourceLoader#export(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public byte[] export(CmsObject cms, CmsResource resource, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // get the Flex controller CmsFlexController controller = getController(cms, resource, req, res, false, true); // dispatch to the JSP byte[] result = dispatchJsp(controller); // remove the controller from the request CmsFlexController.removeController(req); // return the contents return result; } /** * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#getConfiguration() */ public CmsParameterConfiguration getConfiguration() { // return the configuration in an immutable form return m_configuration; } /** * Returns the absolute path in the "real" file system for the JSP repository * toplevel directory.

* * @return The full path to the JSP repository */ public String getJspRepository() { return m_jspRepository; } /** * @see org.opencms.loader.I_CmsResourceLoader#getLoaderId() */ public int getLoaderId() { return RESOURCE_LOADER_ID; } /** * Returns a set of root paths of files that are including the given resource using the 'link.strong' macro.

* * @param cms the current cms context * @param resource the resource to check * @param referencingPaths the set of already referencing paths, also return parameter * * @throws CmsException if something goes wrong */ public void getReferencingStrongLinks(CmsObject cms, CmsResource resource, Set referencingPaths) throws CmsException { CmsRelationFilter filter = CmsRelationFilter.SOURCES.filterType(CmsRelationType.JSP_STRONG); Iterator it = cms.getRelationsForResource(resource, filter).iterator(); while (it.hasNext()) { CmsRelation relation = it.next(); try { CmsResource source = relation.getSource(cms, CmsResourceFilter.DEFAULT); // check if file was already included if (referencingPaths.contains(source.getRootPath())) { // no need to include this file more than once continue; } referencingPaths.add(source.getRootPath()); getReferencingStrongLinks(cms, source, referencingPaths); } catch (CmsException e) { if (LOG.isErrorEnabled()) { LOG.error(e.getLocalizedMessage(), e); } } } } /** * Return a String describing the ResourceLoader, * which is (localized to the system default locale) * "The OpenCms default resource loader for JSP".

* * @return a describing String for the ResourceLoader */ public String getResourceLoaderInfo() { return Messages.get().getBundle().key(Messages.GUI_LOADER_JSP_DEFAULT_DESC_0); } /** * @see org.opencms.configuration.I_CmsConfigurationParameterHandler#initConfiguration() */ public void initConfiguration() { m_jspRepository = m_configuration.get(PARAM_JSP_REPOSITORY); if (m_jspRepository == null) { m_jspRepository = OpenCms.getSystemInfo().getWebApplicationRfsPath(); } m_jspWebAppRepository = m_configuration.getString(PARAM_JSP_FOLDER, DEFAULT_JSP_FOLDER); if (!m_jspWebAppRepository.endsWith("/")) { m_jspWebAppRepository += "/"; } m_jspRepository = CmsFileUtil.normalizePath(m_jspRepository + m_jspWebAppRepository); String maxAge = m_configuration.get(PARAM_CLIENT_CACHE_MAXAGE); if (maxAge == null) { m_clientCacheMaxAge = -1; } else { m_clientCacheMaxAge = Long.parseLong(maxAge); } // get the "error pages are committed or not" flag from the configuration m_errorPagesAreNotCommitted = m_configuration.getBoolean(PARAM_JSP_ERRORPAGE_COMMITTED, true); int cacheSize = m_configuration.getInteger(PARAM_JSP_CACHE_SIZE, -1); if (cacheSize > 0) { initCaches(cacheSize); } // output setup information if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_JSP_REPOSITORY_ABS_PATH_1, m_jspRepository)); CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_WEBAPP_PATH_1, m_jspWebAppRepository)); CmsLog.INIT.info( Messages.get().getBundle().key( Messages.INIT_JSP_REPOSITORY_ERR_PAGE_COMMOTED_1, Boolean.valueOf(m_errorPagesAreNotCommitted))); if (m_clientCacheMaxAge > 0) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_CLIENT_CACHE_MAX_AGE_1, maxAge)); } if (cacheSize > 0) { CmsLog.INIT.info( Messages.get().getBundle().key(Messages.INIT_JSP_CACHE_SIZE_1, String.valueOf(cacheSize))); } CmsLog.INIT.info( Messages.get().getBundle().key(Messages.INIT_LOADER_INITIALIZED_1, this.getClass().getName())); } } /** * @see org.opencms.loader.I_CmsResourceLoader#isStaticExportEnabled() */ public boolean isStaticExportEnabled() { return true; } /** * @see org.opencms.loader.I_CmsResourceLoader#isStaticExportProcessable() */ public boolean isStaticExportProcessable() { return true; } /** * @see org.opencms.loader.I_CmsResourceLoader#isUsableForTemplates() */ public boolean isUsableForTemplates() { return true; } /** * @see org.opencms.loader.I_CmsResourceLoader#isUsingUriWhenLoadingTemplate() */ public boolean isUsingUriWhenLoadingTemplate() { return false; } /** * @see org.opencms.loader.I_CmsResourceLoader#load(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ public void load(CmsObject cms, CmsResource file, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException, CmsException { CmsRequestContext context = cms.getRequestContext(); // If we load template jsp or template-element jsp (xml contents or xml pages) don't show source (2nd test) if ((CmsHistoryResourceHandler.isHistoryRequest(req)) && (context.getUri().equals(context.removeSiteRoot(file.getRootPath())))) { showSource(cms, file, req, res); } else { // load and process the JSP boolean streaming = false; boolean bypass = false; // read "cache" property for requested VFS resource to check for special "stream" and "bypass" values String cacheProperty = cms.readPropertyObject(file, CmsPropertyDefinition.PROPERTY_CACHE, true).getValue(); if (cacheProperty != null) { cacheProperty = cacheProperty.trim(); if (CACHE_PROPERTY_STREAM.equals(cacheProperty)) { streaming = true; } else if (CACHE_PROPERTY_BYPASS.equals(cacheProperty)) { streaming = true; bypass = true; } } // For now, disable flex caching when the __json parameter is used if (CmsJsonPartFilter.isJsonRequest(req)) { streaming = true; bypass = true; } // get the Flex controller CmsFlexController controller = getController(cms, file, req, res, streaming, true); Lock lock = m_purgeLock.readLock(); try { lock.lock(); if (bypass || controller.isForwardMode()) { // initialize the standard contex bean to be available for all requests CmsJspStandardContextBean.getInstance(controller.getCurrentRequest()); // once in forward mode, always in forward mode (for this request) controller.setForwardMode(true); // bypass Flex cache for this page, update the JSP first if necessary String target = updateJsp(file, controller, new HashSet()); // dispatch to external JSP req.getRequestDispatcher(target).forward(controller.getCurrentRequest(), res); } else { // Flex cache not bypassed, dispatch to internal JSP dispatchJsp(controller); } } finally { lock.unlock(); } // remove the controller from the request if not forwarding if (!controller.isForwardMode()) { CmsFlexController.removeController(req); } } } /** * Replaces taglib attributes in page directives with taglib directives.

* * @param content the JSP source text * * @return the transformed JSP text */ @Deprecated public String processTaglibAttributes(String content) { // matches a whole page directive final Pattern directivePattern = Pattern.compile("(?sm)<%@\\s*page.*?%>"); // matches a taglibs attribute and captures its values final Pattern taglibPattern = Pattern.compile("(?sm)taglibs\\s*=\\s*\"(.*?)\""); final Pattern commaPattern = Pattern.compile("(?sm)\\s*,\\s*"); final Set taglibs = new LinkedHashSet(); // we insert the marker after the first page directive final String marker = ":::TAGLIBS:::"; I_CmsRegexSubstitution directiveSub = new I_CmsRegexSubstitution() { private boolean m_first = true; public String substituteMatch(String string, Matcher matcher) { String match = string.substring(matcher.start(), matcher.end()); I_CmsRegexSubstitution taglibSub = new I_CmsRegexSubstitution() { public String substituteMatch(String string1, Matcher matcher1) { // values of the taglibs attribute String match1 = string1.substring(matcher1.start(1), matcher1.end(1)); for (String taglibKey : Splitter.on(commaPattern).split(match1)) { taglibs.add(taglibKey); } return ""; } }; String result = CmsStringUtil.substitute(taglibPattern, match, taglibSub); if (m_first) { result += marker; m_first = false; } return result; } }; String substituted = CmsStringUtil.substitute(directivePattern, content, directiveSub); // insert taglib inclusion substituted = substituted.replaceAll(marker, generateTaglibInclusions(taglibs)); // remove empty page directives substituted = substituted.replaceAll("(?sm)<%@\\s*page\\s*%>", ""); return substituted; } /** * Removes the given resources from the cache.

* * @param rootPaths the set of root paths to remove * @param online if online or offline */ public void removeFromCache(Set rootPaths, boolean online) { Map cache; if (online) { cache = m_onlineJsps; } else { cache = m_offlineJsps; } Iterator itRemove = rootPaths.iterator(); while (itRemove.hasNext()) { String rootPath = itRemove.next(); cache.remove(rootPath); } } /** * Removes a JSP from an offline project from the RFS.

* * @param resource the offline JSP resource to remove from the RFS * * @throws CmsLoaderException if accessing the loader fails */ public void removeOfflineJspFromRepository(CmsResource resource) throws CmsLoaderException { String jspName = getJspRfsPath(resource, false); Set pathSet = new HashSet(); pathSet.add(resource.getRootPath()); ReentrantReadWriteLock lock = getFileLock(jspName); lock.writeLock().lock(); try { removeFromCache(pathSet, false); File jspFile = new File(jspName); jspFile.delete(); } finally { lock.writeLock().unlock(); } } /** * @see org.opencms.loader.I_CmsResourceLoader#service(org.opencms.file.CmsObject, org.opencms.file.CmsResource, javax.servlet.ServletRequest, javax.servlet.ServletResponse) */ public void service(CmsObject cms, CmsResource resource, ServletRequest req, ServletResponse res) throws ServletException, IOException, CmsLoaderException { Lock lock = m_purgeLock.readLock(); try { lock.lock(); CmsFlexController controller = CmsFlexController.getController(req); // get JSP target name on "real" file system String target = updateJsp(resource, controller, new HashSet(8)); // important: Indicate that all output must be buffered controller.getCurrentResponse().setOnlyBuffering(true); // initialize the standard contex bean to be available for all requests CmsJspStandardContextBean.getInstance(controller.getCurrentRequest()); // dispatch to external file controller.getCurrentRequest().getRequestDispatcherToExternal(cms.getSitePath(resource), target).include( req, res); } finally { lock.unlock(); } } /** * @see org.opencms.loader.I_CmsFlexCacheEnabledLoader#setFlexCache(org.opencms.flex.CmsFlexCache) */ public void setFlexCache(CmsFlexCache cache) { m_cache = cache; // output setup information if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ADD_FLEX_CACHE_0)); } } /** * Triggers an asynchronous purge of the JSP repository.

* * @param afterPurgeAction the action to execute after purging */ public void triggerPurge(final Runnable afterPurgeAction) { OpenCms.getExecutor().execute(new Runnable() { @SuppressWarnings("synthetic-access") public void run() { WriteLock lock = m_purgeLock.writeLock(); try { lock.lock(); doPurge(afterPurgeAction); } catch (Exception e) { LOG.error("Error while purging jsp repository: " + e.getLocalizedMessage(), e); } finally { lock.unlock(); } } }); } /** * Updates a JSP page in the "real" file system in case the VFS resource has changed.

* * Also processes the <%@ cms %> tags before the JSP is written to the real FS. * Also recursively updates all files that are referenced by a <%@ cms %> tag * on this page to make sure the file actually exists in the real FS. * All <%@ include %> tags are parsed and the name in the tag is translated * from the OpenCms VFS path to the path in the real FS. * The same is done for filenames in <%@ page errorPage=... %> tags.

* * @param resource the requested JSP file resource in the VFS * @param controller the controller for the JSP integration * @param updatedFiles a Set containing all JSP pages that have been already updated * * @return the file name of the updated JSP in the "real" FS * * @throws ServletException might be thrown in the process of including the JSP * @throws IOException might be thrown in the process of including the JSP * @throws CmsLoaderException if the resource type can not be read */ public String updateJsp(CmsResource resource, CmsFlexController controller, Set updatedFiles) throws IOException, ServletException, CmsLoaderException { String jspVfsName = resource.getRootPath(); String extension; boolean isHardInclude; int loaderId = OpenCms.getResourceManager().getResourceType(resource.getTypeId()).getLoaderId(); if ((loaderId == CmsJspLoader.RESOURCE_LOADER_ID) && (!jspVfsName.endsWith(JSP_EXTENSION))) { // this is a true JSP resource that does not end with ".jsp" extension = JSP_EXTENSION; isHardInclude = false; } else { // not a JSP resource or already ends with ".jsp" extension = ""; // if this is a JSP we don't treat it as hard include isHardInclude = (loaderId != CmsJspLoader.RESOURCE_LOADER_ID); } String jspTargetName = CmsFileUtil.getRepositoryName( m_jspWebAppRepository, jspVfsName + extension, controller.getCurrentRequest().isOnline()); // check if page was already updated if (updatedFiles.contains(jspTargetName)) { // no need to write the already included file to the real FS more then once return jspTargetName; } String jspPath = CmsFileUtil.getRepositoryName( m_jspRepository, jspVfsName + extension, controller.getCurrentRequest().isOnline()); File d = new File(jspPath).getParentFile(); if ((d == null) || (d.exists() && !(d.isDirectory() && d.canRead()))) { CmsMessageContainer message = Messages.get().container(Messages.LOG_ACCESS_DENIED_1, jspPath); LOG.error(message.key()); // can not continue throw new ServletException(message.key()); } if (!d.exists()) { // create directory structure d.mkdirs(); } ReentrantReadWriteLock readWriteLock = getFileLock(jspVfsName); try { // get a read lock for this jsp readWriteLock.readLock().lock(); File jspFile = new File(jspPath); // check if the JSP must be updated boolean mustUpdate = false; long jspModificationDate = 0; if (!jspFile.exists()) { // file does not exist in real FS mustUpdate = true; // make sure the parent folder exists File folder = jspFile.getParentFile(); if (!folder.exists()) { boolean success = folder.mkdirs(); if (!success) { LOG.error( org.opencms.db.Messages.get().getBundle().key( org.opencms.db.Messages.LOG_CREATE_FOLDER_FAILED_1, folder.getAbsolutePath())); } } } else { jspModificationDate = jspFile.lastModified(); if (jspModificationDate < resource.getDateLastModified()) { // file in real FS is older then file in VFS mustUpdate = true; } else if (controller.getCurrentRequest().isDoRecompile()) { // recompile is forced with parameter mustUpdate = true; } else { // check if update is needed if (controller.getCurrentRequest().isOnline()) { mustUpdate = !m_onlineJsps.containsKey(jspVfsName); } else { mustUpdate = !m_offlineJsps.containsKey(jspVfsName); } // check strong links only if update is needed if (mustUpdate) { // update strong link dependencies mustUpdate = updateStrongLinks(resource, controller, updatedFiles); } } } if (mustUpdate) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_WRITING_JSP_1, jspTargetName)); } // jsp needs updating, acquire a write lock readWriteLock.readLock().unlock(); readWriteLock.writeLock().lock(); try { // check again if updating is still necessary as this might have happened while waiting for the write lock if (!jspFile.exists() || (jspModificationDate == jspFile.lastModified())) { updatedFiles.add(jspTargetName); byte[] contents; String encoding; try { CmsObject cms = controller.getCmsObject(); contents = cms.readFile(resource).getContents(); // check the "content-encoding" property for the JSP, use system default if not found on path encoding = cms.readPropertyObject( resource, CmsPropertyDefinition.PROPERTY_CONTENT_ENCODING, true).getValue(); if (encoding == null) { encoding = OpenCms.getSystemInfo().getDefaultEncoding(); } else { encoding = CmsEncoder.lookupEncoding(encoding.trim(), encoding); } } catch (CmsException e) { controller.setThrowable(e, jspVfsName); throw new ServletException( Messages.get().getBundle().key(Messages.ERR_LOADER_JSP_ACCESS_1, jspVfsName), e); } try { // parse the JSP and modify OpenCms critical directives contents = parseJsp(contents, encoding, controller, updatedFiles, isHardInclude); if (LOG.isInfoEnabled()) { // check for existing file and display some debug info LOG.info( Messages.get().getBundle().key( Messages.LOG_JSP_PERMCHECK_4, new Object[] { jspFile.getAbsolutePath(), Boolean.valueOf(jspFile.exists()), Boolean.valueOf(jspFile.isFile()), Boolean.valueOf(jspFile.canWrite())})); } // write the parsed JSP content to the real FS synchronized (CmsJspLoader.class) { // this must be done only one file at a time FileOutputStream fs = new FileOutputStream(jspFile); fs.write(contents); fs.close(); // we set the modification date to (approximately) that of the VFS resource. This is needed because in the Online project, the old version of a JSP // may be generated in the RFS JSP repository *after* the JSP has been changed, but *before* it has been published, which would lead // to it not being updated after the changed JSP is published. // Note: the RFS may only support second precision for the last modification date jspFile.setLastModified((1 + (resource.getDateLastModified() / 1000)) * 1000); } if (controller.getCurrentRequest().isOnline()) { m_onlineJsps.put(jspVfsName, Boolean.TRUE); } else { m_offlineJsps.put(jspVfsName, Boolean.TRUE); } if (LOG.isInfoEnabled()) { LOG.info( Messages.get().getBundle().key( Messages.LOG_UPDATED_JSP_2, jspTargetName, jspVfsName)); } } catch (FileNotFoundException e) { throw new ServletException( Messages.get().getBundle().key(Messages.ERR_LOADER_JSP_WRITE_1, jspFile.getName()), e); } } } finally { readWriteLock.readLock().lock(); readWriteLock.writeLock().unlock(); } } // update "last modified" and "expires" date on controller controller.updateDates(jspFile.lastModified(), CmsResource.DATE_EXPIRED_DEFAULT); } finally { //m_processingFiles.remove(jspVfsName); readWriteLock.readLock().unlock(); } return jspTargetName; } /** * Updates the internal jsp repository when the servlet container * tries to compile a jsp file that may not exist.

* * @param servletPath the servlet path, just to avoid unneeded recursive calls * @param request the current request */ public void updateJspFromRequest(String servletPath, CmsFlexRequest request) { // assemble the RFS name of the requested jsp String jspUri = servletPath; String pathInfo = request.getPathInfo(); if (pathInfo != null) { jspUri += pathInfo; } // check the file name if ((jspUri == null) || !jspUri.startsWith(m_jspWebAppRepository)) { // nothing to do, this kind of request are handled by the CmsJspLoader#service method return; } // remove prefixes jspUri = jspUri.substring(m_jspWebAppRepository.length()); if (jspUri.startsWith(CmsFlexCache.REPOSITORY_ONLINE)) { jspUri = jspUri.substring(CmsFlexCache.REPOSITORY_ONLINE.length()); } else if (jspUri.startsWith(CmsFlexCache.REPOSITORY_OFFLINE)) { jspUri = jspUri.substring(CmsFlexCache.REPOSITORY_OFFLINE.length()); } else { // this is not an OpenCms jsp file return; } // read the resource from OpenCms CmsFlexController controller = CmsFlexController.getController(request); try { CmsResource includeResource; try { // first try to read the resource assuming no additional jsp extension was needed includeResource = readJspResource(controller, jspUri); } catch (CmsVfsResourceNotFoundException e) { // try removing the additional jsp extension if (jspUri.endsWith(JSP_EXTENSION)) { jspUri = jspUri.substring(0, jspUri.length() - JSP_EXTENSION.length()); } includeResource = readJspResource(controller, jspUri); } // make sure the jsp referenced file is generated updateJsp(includeResource, controller, new HashSet(8)); } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug(e.getLocalizedMessage(), e); } } } /** * Dispatches the current request to the OpenCms internal JSP.

* * @param controller the current controller * * @return the content of the processed JSP * * @throws ServletException if inclusion does not work * @throws IOException if inclusion does not work */ protected byte[] dispatchJsp(CmsFlexController controller) throws ServletException, IOException { // get request / response wrappers CmsFlexRequest f_req = controller.getCurrentRequest(); CmsFlexResponse f_res = controller.getCurrentResponse(); try { f_req.getRequestDispatcher(controller.getCmsObject().getSitePath(controller.getCmsResource())).include( f_req, f_res); } catch (SocketException e) { // uncritical, might happen if client (browser) does not wait until end of page delivery LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e); } byte[] result = null; HttpServletResponse res = controller.getTopResponse(); if (!controller.isStreaming() && !f_res.isSuspended()) { try { // if a JSP error page was triggered the response will be already committed here if (!res.isCommitted() || m_errorPagesAreNotCommitted) { // check if the current request was done by a workplace user boolean isWorkplaceUser = CmsWorkplaceManager.isWorkplaceUser(f_req); // check if the content was modified since the last request if (controller.isTop() && !isWorkplaceUser && CmsFlexController.isNotModifiedSince(f_req, controller.getDateLastModified())) { if (f_req.getParameterMap().size() == 0) { // only use "expires" header on pages that have no parameters, // otherwise some browsers (e.g. IE 6) will not even try to request // updated versions of the page CmsFlexController.setDateExpiresHeader( res, controller.getDateExpires(), m_clientCacheMaxAge); } res.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return null; } // get the result byte array result = f_res.getWriterBytes(); HttpServletRequest req = controller.getTopRequest(); if (req.getHeader(CmsRequestUtil.HEADER_OPENCMS_EXPORT) != null) { // this is a non "on-demand" static export request, don't write to the response stream req.setAttribute( CmsRequestUtil.HEADER_OPENCMS_EXPORT, new Long(controller.getDateLastModified())); } else if (controller.isTop()) { // process headers and write output if this is the "top" request/response res.setContentLength(result.length); // check for preset error code Integer errorCode = (Integer)req.getAttribute(CmsRequestUtil.ATTRIBUTE_ERRORCODE); if (errorCode == null) { // set last modified / no cache headers only if this is not an error page if (isWorkplaceUser) { res.setDateHeader(CmsRequestUtil.HEADER_LAST_MODIFIED, System.currentTimeMillis()); CmsRequestUtil.setNoCacheHeaders(res); } else { // set date last modified header CmsFlexController.setDateLastModifiedHeader(res, controller.getDateLastModified()); if ((f_req.getParameterMap().size() == 0) && (controller.getDateLastModified() > -1)) { // only use "expires" header on pages that have no parameters // and that are cachable (i.e. 'date last modified' is set) // otherwise some browsers (e.g. IE 6) will not even try to request // updated versions of the page CmsFlexController.setDateExpiresHeader( res, controller.getDateExpires(), m_clientCacheMaxAge); } } // set response status to "200 - OK" (required for static export "on-demand") res.setStatus(HttpServletResponse.SC_OK); } else { // set previously saved error code res.setStatus(errorCode.intValue()); } // process the headers CmsFlexResponse.processHeaders(f_res.getHeaders(), res); res.getOutputStream().write(result); res.getOutputStream().flush(); } } } catch (IllegalStateException e) { // uncritical, might happen if JSP error page was used LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e); } catch (SocketException e) { // uncritical, might happen if client (browser) does not wait until end of page delivery LOG.debug(Messages.get().getBundle().key(Messages.LOG_IGNORING_EXC_1, e.getClass().getName()), e); } } return result; } /** * Purges the JSP repository. * * @param taglibs the taglib identifiers * * @return a string containing taglib directives */ protected String generateTaglibInclusions(Collection taglibs) { StringBuffer buffer = new StringBuffer(); for (String taglib : taglibs) { String uri = m_taglibs.get(taglib); if (uri != null) { buffer.append("<%@ taglib prefix=\"" + taglib + "\" uri=\"" + uri + "\" %>"); } } return buffer.toString(); } /** * Delivers a Flex controller, either by creating a new one, or by re-using an existing one.

* * @param cms the initial CmsObject to wrap in the controller * @param resource the resource requested * @param req the current request * @param res the current response * @param streaming indicates if the response is streaming * @param top indicates if the response is the top response * * @return a Flex controller */ protected CmsFlexController getController( CmsObject cms, CmsResource resource, HttpServletRequest req, HttpServletResponse res, boolean streaming, boolean top) { CmsFlexController controller = null; if (top) { // only check for existing controller if this is the "top" request/response controller = CmsFlexController.getController(req); } if (controller == null) { // create new request / response wrappers if (!cms.getRequestContext().getCurrentProject().isOnlineProject() && (CmsHistoryResourceHandler.isHistoryRequest(req) || CmsJspTagEnableAde.isDirectEditDisabled(req))) { cms.getRequestContext().setAttribute(CmsGwtConstants.PARAM_DISABLE_DIRECT_EDIT, Boolean.TRUE); } controller = new CmsFlexController(cms, resource, m_cache, req, res, streaming, top); CmsFlexController.setController(req, controller); CmsFlexRequest f_req = new CmsFlexRequest(req, controller); CmsFlexResponse f_res = new CmsFlexResponse(res, controller, streaming, true); controller.push(f_req, f_res); } else if (controller.isForwardMode()) { // reset CmsObject (because of URI) if in forward mode controller = new CmsFlexController(cms, controller); CmsFlexController.setController(req, controller); } return controller; } /** * Initializes the caches.

* * @param cacheSize the cache size */ protected void initCaches(int cacheSize) { m_offlineJsps = CmsMemoryMonitor.createLRUCacheMap(cacheSize); m_onlineJsps = CmsMemoryMonitor.createLRUCacheMap(cacheSize); } /** * Parses the JSP and modifies OpenCms critical directive information.

* * @param byteContent the original JSP content * @param encoding the encoding to use for the JSP * @param controller the controller for the JSP integration * @param updatedFiles a Set containing all JSP pages that have been already updated * @param isHardInclude indicated if this page is actually a "hard" include with <%@ include file="..." > * * @return the modified JSP content */ protected byte[] parseJsp( byte[] byteContent, String encoding, CmsFlexController controller, Set updatedFiles, boolean isHardInclude) { String content; // make sure encoding is set correctly try { content = new String(byteContent, encoding); } catch (UnsupportedEncodingException e) { // encoding property is not set correctly LOG.error( Messages.get().getBundle().key( Messages.LOG_UNSUPPORTED_ENC_1, controller.getCurrentRequest().getElementUri()), e); try { encoding = OpenCms.getSystemInfo().getDefaultEncoding(); content = new String(byteContent, encoding); } catch (UnsupportedEncodingException e2) { // should not happen since default encoding is always a valid encoding (checked during system startup) content = new String(byteContent); } } // parse for special %(link:...) macros content = parseJspLinkMacros(content, controller); // parse for special <%@cms file="..." %> tag content = parseJspCmsTag(content, controller, updatedFiles); // parse for included files in tags content = parseJspIncludes(content, controller, updatedFiles); // parse for <%@page pageEncoding="..." %> tag content = parseJspEncoding(content, encoding, isHardInclude); // Processes magic taglib attributes in page directives content = processTaglibAttributes(content); // convert the result to bytes and return it try { return content.getBytes(encoding); } catch (UnsupportedEncodingException e) { // should not happen since encoding was already checked return content.getBytes(); } } /** * Parses the JSP content for the special <%cms file="..." %> tag.

* * @param content the JSP content to parse * @param controller the current JSP controller * @param updatedFiles a set of already updated jsp files * * @return the parsed JSP content */ protected String parseJspCmsTag(String content, CmsFlexController controller, Set updatedFiles) { // check if a JSP directive occurs in the file int i1 = content.indexOf(DIRECTIVE_START); if (i1 < 0) { // no directive occurs return content; } StringBuffer buf = new StringBuffer(content.length()); int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length(), elen = DIRECTIVE_END.length(); while (i1 >= 0) { // parse the file and replace JSP filename references i2 = content.indexOf(DIRECTIVE_END, i1 + slen); if (i2 < 0) { // wrong syntax (missing end directive) - let the JSP compiler produce the error message return content; } else if (i2 > i1) { String directive = content.substring(i1 + slen, i2); if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_DETECTED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0; while (directive.charAt(t1) == ' ') { t1++; } String argument = null; if (directive.startsWith("cms", t1)) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "cms")); } t2 = directive.indexOf("file", t1 + 3); t5 = 4; } if (t2 > 0) { String sub = directive.substring(t2 + t5); char c1 = sub.charAt(t3); while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) { c1 = sub.charAt(++t3); } t4 = t3; while (c1 != '"') { c1 = sub.charAt(++t4); } if (t4 > t3) { argument = sub.substring(t3, t4); } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument)); } } if (argument != null) { // try to update the referenced file String jspname = updateJsp(argument, controller, updatedFiles); if (jspname != null) { directive = jspname; if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_CHANGED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } } // cms directive was found buf.append(content.substring(p0, i1)); buf.append(directive); p0 = i2 + elen; i1 = content.indexOf(DIRECTIVE_START, p0); } else { // cms directive was not found buf.append(content.substring(p0, i1 + slen)); buf.append(directive); p0 = i2; i1 = content.indexOf(DIRECTIVE_START, p0); } } } if (i2 > 0) { // the content of the JSP was changed buf.append(content.substring(p0, content.length())); content = buf.toString(); } return content; } /** * Parses the JSP content for the <%page pageEncoding="..." %> tag * and ensures that the JSP page encoding is set according to the OpenCms * "content-encoding" property value of the JSP.

* * @param content the JSP content to parse * @param encoding the encoding to use for the JSP * @param isHardInclude indicated if this page is actually a "hard" include with <%@ include file="..." > * * @return the parsed JSP content */ protected String parseJspEncoding(String content, String encoding, boolean isHardInclude) { // check if a JSP directive occurs in the file int i1 = content.indexOf(DIRECTIVE_START); if (i1 < 0) { // no directive occurs if (isHardInclude) { return content; } } StringBuffer buf = new StringBuffer(content.length() + 64); int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length(); boolean found = false; if (i1 < 0) { // no directive found at all, append content to buffer buf.append(content); } while (i1 >= 0) { // parse the file and set/replace page encoding i2 = content.indexOf(DIRECTIVE_END, i1 + slen); if (i2 < 0) { // wrong syntax (missing end directive) - let the JSP compiler produce the error message return content; } else if (i2 > i1) { String directive = content.substring(i1 + slen, i2); if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_DETECTED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0; while (directive.charAt(t1) == ' ') { t1++; } String argument = null; if (directive.startsWith("page", t1)) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "page")); } t2 = directive.indexOf("pageEncoding", t1 + 4); t5 = 12; if (t2 > 0) { found = true; } } if (t2 > 0) { String sub = directive.substring(t2 + t5); char c1 = sub.charAt(t3); while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) { c1 = sub.charAt(++t3); } t4 = t3; while (c1 != '"') { c1 = sub.charAt(++t4); } if (t4 > t3) { argument = sub.substring(t3, t4); } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument)); } } if (argument != null) { // a pageEncoding setting was found, changes have to be made String pre = directive.substring(0, t2 + t3 + t5); String suf = directive.substring(t2 + t3 + t5 + argument.length()); // change the encoding directive = pre + encoding + suf; if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_CHANGED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } } buf.append(content.substring(p0, i1 + slen)); buf.append(directive); p0 = i2; i1 = content.indexOf(DIRECTIVE_START, p0); } } if (i2 > 0) { // the content of the JSP was changed buf.append(content.substring(p0, content.length())); } if (found) { content = buf.toString(); } else if (!isHardInclude) { // encoding setting was not found // if this is not a "hard" include then add the encoding to the top of the page // checking for the hard include is important to prevent errors with // multiple page encoding settings if a template is composed from several hard included elements // this is an issue in Tomcat 4.x but not 5.x StringBuffer buf2 = new StringBuffer(buf.length() + 32); buf2.append("<%@ page pageEncoding=\""); buf2.append(encoding); buf2.append("\" %>"); buf2.append(buf); content = buf2.toString(); } return content; } /** * Parses the JSP content for includes and replaces all OpenCms VFS * path information with information for the real FS.

* * @param content the JSP content to parse * @param controller the current JSP controller * @param updatedFiles a set of already updated files * * @return the parsed JSP content */ protected String parseJspIncludes(String content, CmsFlexController controller, Set updatedFiles) { // check if a JSP directive occurs in the file int i1 = content.indexOf(DIRECTIVE_START); if (i1 < 0) { // no directive occurs return content; } StringBuffer buf = new StringBuffer(content.length()); int p0 = 0, i2 = 0, slen = DIRECTIVE_START.length(); while (i1 >= 0) { // parse the file and replace JSP filename references i2 = content.indexOf(DIRECTIVE_END, i1 + slen); if (i2 < 0) { // wrong syntax (missing end directive) - let the JSP compiler produce the error message return content; } else if (i2 > i1) { String directive = content.substring(i1 + slen, i2); if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_DETECTED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } int t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0; while (directive.charAt(t1) == ' ') { t1++; } String argument = null; if (directive.startsWith("include", t1)) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "include")); } t2 = directive.indexOf("file", t1 + 7); t5 = 6; } else if (directive.startsWith("page", t1)) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_X_DIRECTIVE_DETECTED_1, "page")); } t2 = directive.indexOf("errorPage", t1 + 4); t5 = 11; } if (t2 > 0) { String sub = directive.substring(t2 + t5); char c1 = sub.charAt(t3); while ((c1 == ' ') || (c1 == '=') || (c1 == '"')) { c1 = sub.charAt(++t3); } t4 = t3; while (c1 != '"') { c1 = sub.charAt(++t4); } if (t4 > t3) { argument = sub.substring(t3, t4); } if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_DIRECTIVE_ARG_1, argument)); } } if (argument != null) { // a file was found, changes have to be made String pre = directive.substring(0, t2 + t3 + t5); String suf = directive.substring(t2 + t3 + t5 + argument.length()); // now try to update the referenced file String jspname = updateJsp(argument, controller, updatedFiles); if (jspname != null) { // only change something in case no error had occurred directive = pre + jspname + suf; if (LOG.isDebugEnabled()) { LOG.debug( Messages.get().getBundle().key( Messages.LOG_DIRECTIVE_CHANGED_3, DIRECTIVE_START, directive, DIRECTIVE_END)); } } } buf.append(content.substring(p0, i1 + slen)); buf.append(directive); p0 = i2; i1 = content.indexOf(DIRECTIVE_START, p0); } } if (i2 > 0) { // the content of the JSP was changed buf.append(content.substring(p0, content.length())); content = buf.toString(); } return content; } /** * Parses all jsp link macros, and replace them by the right target path.

* * @param content the content to parse * @param controller the request controller * * @return the parsed content */ protected String parseJspLinkMacros(String content, CmsFlexController controller) { CmsJspLinkMacroResolver macroResolver = new CmsJspLinkMacroResolver(controller.getCmsObject(), null, true); return macroResolver.resolveMacros(content); } /** * Returns the jsp resource identified by the given name, using the controllers cms context.

* * @param controller the flex controller * @param jspName the name of the jsp * * @return an OpenCms resource * * @throws CmsException if something goes wrong */ protected CmsResource readJspResource(CmsFlexController controller, String jspName) throws CmsException { // create an OpenCms user context that operates in the root site CmsObject cms = OpenCms.initCmsObject(controller.getCmsObject()); // we only need to change the site, but not the project, // since the request has already the right project set cms.getRequestContext().setSiteRoot(""); // try to read the resource return cms.readResource(jspName); } /** * Delivers the plain uninterpreted resource with escaped XML.

* * This is intended for viewing historical versions.

* * @param cms the initialized CmsObject which provides user permissions * @param file the requested OpenCms VFS resource * @param req the servlet request * @param res the servlet response * * @throws IOException might be thrown by the servlet environment * @throws CmsException in case of errors accessing OpenCms functions */ protected void showSource(CmsObject cms, CmsResource file, HttpServletRequest req, HttpServletResponse res) throws CmsException, IOException { CmsResource historyResource = (CmsResource)CmsHistoryResourceHandler.getHistoryResource(req); if (historyResource == null) { historyResource = file; } CmsFile historyFile = cms.readFile(historyResource); String content = new String(historyFile.getContents()); // change the content-type header so that browsers show plain text res.setContentLength(content.length()); res.setContentType("text/plain"); Writer out = res.getWriter(); out.write(content); out.close(); } /** * Updates a JSP page in the "real" file system in case the VFS resource has changed based on the resource name.

* * Generates a resource based on the provided name and calls {@link #updateJsp(CmsResource, CmsFlexController, Set)}.

* * @param vfsName the name of the JSP file resource in the VFS * @param controller the controller for the JSP integration * @param updatedFiles a Set containing all JSP pages that have been already updated * * @return the file name of the updated JSP in the "real" FS */ protected String updateJsp(String vfsName, CmsFlexController controller, Set updatedFiles) { String jspVfsName = CmsLinkManager.getAbsoluteUri(vfsName, controller.getCurrentRequest().getElementRootPath()); if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_UPDATE_JSP_1, jspVfsName)); } String jspRfsName; try { CmsResource includeResource; try { // first try a root path includeResource = readJspResource(controller, jspVfsName); } catch (CmsVfsResourceNotFoundException e) { // if fails, try a site relative path includeResource = readJspResource( controller, controller.getCmsObject().getRequestContext().addSiteRoot(jspVfsName)); } // make sure the jsp referenced file is generated jspRfsName = updateJsp(includeResource, controller, updatedFiles); if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_NAME_REAL_FS_1, jspRfsName)); } } catch (Exception e) { jspRfsName = null; if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_ERR_UPDATE_1, jspVfsName), e); } } return jspRfsName; } /** * Updates all jsp files that include the given jsp file using the 'link.strong' macro.

* * @param resource the current updated jsp file * @param controller the controller for the jsp integration * @param updatedFiles the already updated files * * @return true if the given JSP file should be updated due to dirty included files * * @throws ServletException might be thrown in the process of including the JSP * @throws IOException might be thrown in the process of including the JSP * @throws CmsLoaderException if the resource type can not be read */ protected boolean updateStrongLinks(CmsResource resource, CmsFlexController controller, Set updatedFiles) throws CmsLoaderException, IOException, ServletException { int numberOfUpdates = updatedFiles.size(); CmsObject cms = controller.getCmsObject(); CmsRelationFilter filter = CmsRelationFilter.TARGETS.filterType(CmsRelationType.JSP_STRONG); Iterator it; try { it = cms.getRelationsForResource(resource, filter).iterator(); } catch (CmsException e) { // should never happen if (LOG.isErrorEnabled()) { LOG.error(e.getLocalizedMessage(), e); } return false; } while (it.hasNext()) { CmsRelation relation = it.next(); CmsResource target = null; try { target = relation.getTarget(cms, CmsResourceFilter.DEFAULT); } catch (CmsException e) { // should never happen if (LOG.isErrorEnabled()) { LOG.error(e.getLocalizedMessage(), e); } continue; } // prevent recursive update when including the same file if (resource.equals(target)) { continue; } // update the target updateJsp(target, controller, updatedFiles); } // the current jsp file should be updated only if one of the included jsp has been updated return numberOfUpdates < updatedFiles.size(); } /** * Returns the read-write-lock for the given jsp vfs name.

* * @param jspVfsName the jsp vfs name * * @return the read-write-lock */ private ReentrantReadWriteLock getFileLock(String jspVfsName) { ReentrantReadWriteLock lock = m_fileLocks.get(jspVfsName); if (lock == null) { synchronized (m_fileLocks) { if (!m_fileLocks.containsKey(jspVfsName)) { m_fileLocks.put(jspVfsName, new ReentrantReadWriteLock(true)); } lock = m_fileLocks.get(jspVfsName); } } return lock; } /** * Returns the RFS path for a JSP resource.

* * This does not check whether there actually exists a file at the returned path. * * @param resource the JSP resource * @param online true if the path for the online project should be returned * * @return the RFS path for the JSP * * @throws CmsLoaderException if accessing the resource loader fails */ private String getJspRfsPath(CmsResource resource, boolean online) throws CmsLoaderException { String jspVfsName = resource.getRootPath(); String extension; int loaderId = OpenCms.getResourceManager().getResourceType(resource.getTypeId()).getLoaderId(); if ((loaderId == CmsJspLoader.RESOURCE_LOADER_ID) && (!jspVfsName.endsWith(JSP_EXTENSION))) { // this is a true JSP resource that does not end with ".jsp" extension = JSP_EXTENSION; } else { // not a JSP resource or already ends with ".jsp" extension = ""; } String jspPath = CmsFileUtil.getRepositoryName(m_jspRepository, jspVfsName + extension, online); return jspPath; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy