0001: /*
0002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/staticexport/CmsStaticExportManager.java,v $
0003: * Date : $Date: 2008-02-27 12:05:46 $
0004: * Version: $Revision: 1.132 $
0005: *
0006: * This library is part of OpenCms -
0007: * the Open Source Content Management System
0008: *
0009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
0010: *
0011: * This library is free software; you can redistribute it and/or
0012: * modify it under the terms of the GNU Lesser General Public
0013: * License as published by the Free Software Foundation; either
0014: * version 2.1 of the License, or (at your option) any later version.
0015: *
0016: * This library is distributed in the hope that it will be useful,
0017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0019: * Lesser General Public License for more details.
0020: *
0021: * For further information about Alkacon Software GmbH, please see the
0022: * company website: http://www.alkacon.com
0023: *
0024: * For further information about OpenCms, please see the
0025: * project website: http://www.opencms.org
0026: *
0027: * You should have received a copy of the GNU Lesser General Public
0028: * License along with this library; if not, write to the Free Software
0029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0030: */
0031:
0032: package org.opencms.staticexport;
0033:
0034: import org.opencms.file.CmsFile;
0035: import org.opencms.file.CmsObject;
0036: import org.opencms.file.CmsProperty;
0037: import org.opencms.file.CmsPropertyDefinition;
0038: import org.opencms.file.CmsResource;
0039: import org.opencms.file.CmsVfsResourceNotFoundException;
0040: import org.opencms.file.types.CmsResourceTypeJsp;
0041: import org.opencms.i18n.CmsAcceptLanguageHeaderParser;
0042: import org.opencms.i18n.CmsI18nInfo;
0043: import org.opencms.i18n.CmsLocaleManager;
0044: import org.opencms.loader.I_CmsResourceLoader;
0045: import org.opencms.main.CmsContextInfo;
0046: import org.opencms.main.CmsEvent;
0047: import org.opencms.main.CmsException;
0048: import org.opencms.main.CmsIllegalArgumentException;
0049: import org.opencms.main.CmsLog;
0050: import org.opencms.main.CmsSystemInfo;
0051: import org.opencms.main.I_CmsEventListener;
0052: import org.opencms.main.OpenCms;
0053: import org.opencms.report.CmsLogReport;
0054: import org.opencms.report.I_CmsReport;
0055: import org.opencms.security.CmsSecurityException;
0056: import org.opencms.util.CmsFileUtil;
0057: import org.opencms.util.CmsMacroResolver;
0058: import org.opencms.util.CmsRequestUtil;
0059: import org.opencms.util.CmsStringUtil;
0060: import org.opencms.util.CmsUUID;
0061: import org.opencms.workplace.CmsWorkplace;
0062:
0063: import java.io.File;
0064: import java.io.FileFilter;
0065: import java.io.FileOutputStream;
0066: import java.io.IOException;
0067: import java.net.MalformedURLException;
0068: import java.net.URL;
0069: import java.util.ArrayList;
0070: import java.util.Collections;
0071: import java.util.HashMap;
0072: import java.util.Iterator;
0073: import java.util.List;
0074: import java.util.Locale;
0075: import java.util.Map;
0076:
0077: import javax.servlet.ServletException;
0078: import javax.servlet.http.HttpServletRequest;
0079: import javax.servlet.http.HttpServletResponse;
0080:
0081: import org.apache.commons.collections.map.LRUMap;
0082: import org.apache.commons.logging.Log;
0083:
0084: /**
0085: * Provides the functionality to export resources from the OpenCms VFS
0086: * to the file system.<p>
0087: *
0088: * @author Alexander Kandzior
0089: * @author Michael Moossen
0090: *
0091: * @version $Revision: 1.132 $
0092: *
0093: * @since 6.0.0
0094: */
0095: public class CmsStaticExportManager implements I_CmsEventListener {
0096:
0097: /**
0098: * Implements the file filter used to guess the right suffix of a deleted jsp file.<p>
0099: */
0100: private static class CmsPrefixFileFilter implements FileFilter {
0101:
0102: /** The base file. */
0103: private String m_baseName;
0104:
0105: /**
0106: * Creates a new instance of this filter.<p>
0107: *
0108: * @param fileName the base file to compare with.
0109: */
0110: public CmsPrefixFileFilter(String fileName) {
0111:
0112: m_baseName = fileName + ".";
0113: }
0114:
0115: /**
0116: * Accepts the given file if its name starts with the name of of the base file (without extension)
0117: * and ends with the extension.<p>
0118: *
0119: * @see java.io.FileFilter#accept(java.io.File)
0120: */
0121: public boolean accept(File f) {
0122:
0123: return f.getName().startsWith(m_baseName)
0124: && (f.getName().length() > m_baseName.length())
0125: && (f.getName().indexOf('.', m_baseName.length()) < 0);
0126: }
0127: }
0128:
0129: /** Marker for error message attribute. */
0130: public static final String EXPORT_ATTRIBUTE_ERROR_MESSAGE = "javax.servlet.error.message";
0131:
0132: /** Marker for error request uri attribute. */
0133: public static final String EXPORT_ATTRIBUTE_ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
0134:
0135: /** Marker for error servlet name attribute. */
0136: public static final String EXPORT_ATTRIBUTE_ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
0137:
0138: /** Marker for error status code attribute. */
0139: public static final String EXPORT_ATTRIBUTE_ERROR_STATUS_CODE = "javax.servlet.error.status_code";
0140:
0141: /** Name for the backup folder default name. */
0142: public static final String EXPORT_BACKUP_FOLDER_NAME = "backup";
0143:
0144: /** Name for the default work path. */
0145: public static final Integer EXPORT_DEFAULT_BACKUPS = new Integer(0);
0146:
0147: /** Name for the folder default index file. */
0148: public static final String EXPORT_DEFAULT_FILE = "index_export.html";
0149:
0150: /** Name for the default work path. */
0151: public static final String EXPORT_DEFAULT_WORKPATH = CmsSystemInfo.FOLDER_WEBINF
0152: + "temp";
0153:
0154: /** Flag value for links without parameters. */
0155: public static final int EXPORT_LINK_WITH_PARAMETER = 2;
0156:
0157: /** Flag value for links without parameters. */
0158: public static final int EXPORT_LINK_WITHOUT_PARAMETER = 1;
0159:
0160: /** Marker for externally redirected 404 uri's. */
0161: public static final String EXPORT_MARKER = "exporturi";
0162:
0163: /** Time given (in seconds) to the static export handler to finish a publish task. */
0164: public static final int HANDLER_FINISH_TIME = 60;
0165:
0166: /** Cache value to indicate a true 404 error. */
0167: private static final String CACHEVALUE_404 = "?404";
0168:
0169: /** The log object for this class. */
0170: private static final Log LOG = CmsLog
0171: .getLog(CmsStaticExportManager.class);
0172:
0173: /** HTTP header Accept-Charset. */
0174: private String m_acceptCharsetHeader;
0175:
0176: /** HTTP header Accept-Language. */
0177: private String m_acceptLanguageHeader;
0178:
0179: /** Cache for the export links. */
0180: private Map m_cacheExportLinks;
0181:
0182: /** Cache for the export uris. */
0183: private Map m_cacheExportUris;
0184:
0185: /** Cache for the online links. */
0186: private Map m_cacheOnlineLinks;
0187:
0188: /** Cache for the secure links. */
0189: private Map m_cacheSecureLinks;
0190:
0191: /** OpenCms default charset header. */
0192: private String m_defaultAcceptCharsetHeader;
0193:
0194: /** OpenCms default locale header. */
0195: private String m_defaultAcceptLanguageHeader;
0196:
0197: /** Matcher for selecting those resources which should be part of the static export. */
0198: private CmsExportFolderMatcher m_exportFolderMatcher;
0199:
0200: /** List of export resources which should be part of the static export. */
0201: private List m_exportFolders;
0202:
0203: /** The additional http headers for the static export. */
0204: private List m_exportHeaders;
0205:
0206: /** List of all resources that have the "exportname" property set. */
0207: private Map m_exportnameResources;
0208:
0209: /** Indicates if <code>true</code> is the default value for the property "export". */
0210: private boolean m_exportPropertyDefault;
0211:
0212: /** Indicates if links in the static export should be relative. */
0213: private boolean m_exportRelativeLinks;
0214:
0215: /** List of export rules. */
0216: private List m_exportRules;
0217:
0218: /** List of export suffixes where the "export" property default is always <code>true</code>. */
0219: private List m_exportSuffixes;
0220:
0221: /** Temporary variable for reading the xml config file. */
0222: private CmsStaticExportExportRule m_exportTmpRule;
0223:
0224: /** Export url to send internal requests to. */
0225: private String m_exportUrl;
0226:
0227: /** Export url with unsubstituted context values. */
0228: private String m_exportUrlConfigured;
0229:
0230: /** Export url to send internal requests to without http://servername. */
0231: private String m_exportUrlPrefix;
0232:
0233: /** Boolean value if the export is a full static export. */
0234: private boolean m_fullStaticExport;
0235:
0236: /** Handler class for static export. */
0237: private I_CmsStaticExportHandler m_handler;
0238:
0239: /** The configured link substitution handler. */
0240: private I_CmsLinkSubstitutionHandler m_linkSubstitutionHandler;
0241:
0242: /** Lock object for write access to the {@link #cmsEvent(CmsEvent)} method. */
0243: private Object m_lockCmsEvent;
0244:
0245: /** Lock object for export folder deletion in {@link #scrubExportFolders(I_CmsReport)}. */
0246: private Object m_lockScrubExportFolders;
0247:
0248: /** Lock object for write access to the {@link #m_exportnameResources} map in {@link #setExportnames()}. */
0249: private Object m_lockSetExportnames;
0250:
0251: /** Indicates if the quick static export for plain resources is enabled. */
0252: private boolean m_quickPlainExport;
0253:
0254: /** Remote address. */
0255: private String m_remoteAddr;
0256:
0257: /** Prefix to use for exported files. */
0258: private String m_rfsPrefix;
0259:
0260: /** Prefix to use for exported files with unsubstituted context values. */
0261: private String m_rfsPrefixConfigured;
0262:
0263: /** List of configured rfs rules. */
0264: private List m_rfsRules;
0265:
0266: /** Temporary variable for reading the xml config file. */
0267: private CmsStaticExportRfsRule m_rfsTmpRule;
0268:
0269: /** The number of backups stored for the export folder. */
0270: private Integer m_staticExportBackups;
0271:
0272: /** Indicates if the static export is enabled or disabled. */
0273: private boolean m_staticExportEnabled;
0274:
0275: /** The path to where the static export will be written. */
0276: private String m_staticExportPath;
0277:
0278: /** The path to where the static export will be written without the complete rfs path. */
0279: private String m_staticExportPathConfigured;
0280:
0281: /** The path to where the static export will be written during the static export process. */
0282: private String m_staticExportWorkPath;
0283:
0284: /** The path to where the static export will be written during the static export process without the complete rfs path. */
0285: private String m_staticExportWorkPathConfigured;
0286:
0287: /** Vfs Name of a resource used to do a "static export required" test. */
0288: private String m_testResource;
0289:
0290: /** If there are several identical export paths the usage of temporary directories has to be disabled. */
0291: private boolean m_useTempDirs = true;
0292:
0293: /** Prefix to use for internal OpenCms files. */
0294: private String m_vfsPrefix;
0295:
0296: /** Prefix to use for internal OpenCms files with unsubstituted context values. */
0297: private String m_vfsPrefixConfigured;
0298:
0299: /**
0300: * Creates a new static export property object.<p>
0301: *
0302: */
0303: public CmsStaticExportManager() {
0304:
0305: m_lockCmsEvent = new Object();
0306: m_lockScrubExportFolders = new Object();
0307: m_lockSetExportnames = new Object();
0308: m_exportSuffixes = new ArrayList();
0309: m_exportFolders = new ArrayList();
0310: m_exportHeaders = new ArrayList();
0311: m_rfsRules = new ArrayList();
0312: m_exportRules = new ArrayList();
0313: m_exportTmpRule = new CmsStaticExportExportRule("", "");
0314: m_rfsTmpRule = new CmsStaticExportRfsRule("", "", "", "", "",
0315: "", null, null);
0316: m_fullStaticExport = false;
0317: }
0318:
0319: /**
0320: * Adds a new export rule to the configuration.<p>
0321: *
0322: * @param name the name of the rule
0323: * @param description the description for the rule
0324: */
0325: public void addExportRule(String name, String description) {
0326:
0327: m_exportRules.add(new CmsStaticExportExportRule(name,
0328: description, m_exportTmpRule.getModifiedResources(),
0329: m_exportTmpRule.getExportResourcePatterns()));
0330: m_exportTmpRule = new CmsStaticExportExportRule("", "");
0331: }
0332:
0333: /**
0334: * Adds a regex to the latest export rule.<p>
0335: *
0336: * @param regex the regex to add
0337: */
0338: public void addExportRuleRegex(String regex) {
0339:
0340: m_exportTmpRule.addModifiedResource(regex);
0341: }
0342:
0343: /**
0344: * Adds a export uri to the latest export rule.<p>
0345: *
0346: * @param exportUri the export uri to add
0347: */
0348: public void addExportRuleUri(String exportUri) {
0349:
0350: m_exportTmpRule.addExportResourcePattern(exportUri);
0351: }
0352:
0353: /**
0354: * Adds a new rfs rule to the configuration.<p>
0355: *
0356: * @param name the name of the rule
0357: * @param description the description for the rule
0358: * @param source the source regex
0359: * @param rfsPrefix the url prefix
0360: * @param exportPath the rfs export path
0361: * @param exportWorkPath the rfs export work path
0362: * @param exportBackups the number of backups
0363: * @param useRelativeLinks the relative links value
0364: */
0365: public void addRfsRule(String name, String description,
0366: String source, String rfsPrefix, String exportPath,
0367: String exportWorkPath, String exportBackups,
0368: String useRelativeLinks) {
0369:
0370: if ((m_staticExportPathConfigured != null)
0371: && exportPath.equals(m_staticExportPathConfigured)) {
0372: m_useTempDirs = false;
0373: }
0374: Iterator itRules = m_rfsRules.iterator();
0375: while (m_useTempDirs && itRules.hasNext()) {
0376: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) itRules
0377: .next();
0378: if (exportPath.equals(rule.getExportPathConfigured())) {
0379: m_useTempDirs = false;
0380: }
0381: }
0382: Boolean relativeLinks = (useRelativeLinks == null ? null
0383: : Boolean.valueOf(useRelativeLinks));
0384: Integer backups = (exportBackups == null ? null : Integer
0385: .valueOf(exportBackups));
0386:
0387: m_rfsRules
0388: .add(new CmsStaticExportRfsRule(name, description,
0389: source, rfsPrefix, exportPath, exportWorkPath,
0390: backups, relativeLinks, m_rfsTmpRule
0391: .getRelatedSystemResources()));
0392: m_rfsTmpRule = new CmsStaticExportRfsRule("", "", "", "", "",
0393: "", null, null);
0394: }
0395:
0396: /**
0397: * Adds a regex of related system resources to the latest rfs-rule.<p>
0398: *
0399: * @param regex the regex to add
0400: */
0401: public void addRfsRuleSystemRes(String regex) {
0402:
0403: m_rfsTmpRule.addRelatedSystemRes(regex);
0404: }
0405:
0406: /**
0407: * Caches a calculated export uri.<p>
0408: *
0409: * @param rfsName the name of the resource in the "real" file system
0410: * @param vfsName the name of the resource in the VFS
0411: * @param parameters the optional parameters for the generation of the resource
0412: */
0413: public void cacheExportUri(String rfsName, String vfsName,
0414: String parameters) {
0415:
0416: m_cacheExportUris.put(rfsName, new CmsStaticExportData(vfsName,
0417: parameters));
0418: }
0419:
0420: /**
0421: * Caches a calculated online link.<p>
0422: *
0423: * @param linkName the link
0424: * @param vfsName the name of the VFS resource
0425: */
0426: public void cacheOnlineLink(Object linkName, Object vfsName) {
0427:
0428: m_cacheOnlineLinks.put(linkName, vfsName);
0429: }
0430:
0431: /**
0432: * Implements the CmsEvent interface,
0433: * the static export properties uses the events to clear
0434: * the list of cached keys in case a project is published.<p>
0435: *
0436: * @param event CmsEvent that has occurred
0437: */
0438: public void cmsEvent(CmsEvent event) {
0439:
0440: if (!isStaticExportEnabled()) {
0441: if (LOG.isWarnEnabled()) {
0442: LOG.warn(Messages.get().getBundle().key(
0443: Messages.LOG_STATIC_EXPORT_DISABLED_0));
0444: }
0445: return;
0446: }
0447: I_CmsReport report = null;
0448: Map data = event.getData();
0449: if (data != null) {
0450: report = (I_CmsReport) data
0451: .get(I_CmsEventListener.KEY_REPORT);
0452: }
0453: if (report == null) {
0454: report = new CmsLogReport(CmsLocaleManager
0455: .getDefaultLocale(), getClass());
0456: }
0457: switch (event.getType()) {
0458: case I_CmsEventListener.EVENT_UPDATE_EXPORTS:
0459: scrubExportFolders(report);
0460: clearCaches(event);
0461: break;
0462: case I_CmsEventListener.EVENT_PUBLISH_PROJECT:
0463: if (data == null) {
0464: if (LOG.isErrorEnabled()) {
0465: LOG.error(Messages.get().getBundle().key(
0466: Messages.ERR_EMPTY_EVENT_DATA_0));
0467: }
0468: return;
0469: }
0470: // event data contains a list of the published resources
0471: CmsUUID publishHistoryId = new CmsUUID((String) data
0472: .get(I_CmsEventListener.KEY_PUBLISHID));
0473: if (LOG.isDebugEnabled()) {
0474: LOG.debug(Messages.get().getBundle().key(
0475: Messages.LOG_EVENT_PUBLISH_PROJECT_1,
0476: publishHistoryId));
0477: }
0478: synchronized (m_lockCmsEvent) {
0479: getHandler().performEventPublishProject(
0480: publishHistoryId, report);
0481: }
0482: clearCaches(event);
0483:
0484: if (LOG.isDebugEnabled()) {
0485: LOG.debug(Messages.get().getBundle().key(
0486: Messages.LOG_EVENT_PUBLISH_PROJECT_FINISHED_1,
0487: publishHistoryId));
0488: }
0489:
0490: break;
0491: case I_CmsEventListener.EVENT_CLEAR_CACHES:
0492: clearCaches(event);
0493: break;
0494: default:
0495: // no operation
0496: }
0497: }
0498:
0499: /**
0500: * Exports the requested uri and at the same time writes the uri to the response output stream
0501: * if required.<p>
0502: *
0503: * @param req the current request
0504: * @param res the current response
0505: * @param cms an initialized cms context (should be initialized with the "Guest" user only)
0506: * @param data the static export data set
0507: *
0508: * @return status code of the export operation, status codes are the same as http status codes (200,303,304)
0509: *
0510: * @throws CmsException in case of errors accessing the VFS
0511: * @throws ServletException in case of errors accessing the servlet
0512: * @throws IOException in case of errors writing to the export output stream
0513: * @throws CmsStaticExportException if static export is disabled
0514: */
0515: public int export(HttpServletRequest req, HttpServletResponse res,
0516: CmsObject cms, CmsStaticExportData data)
0517: throws CmsException, IOException, ServletException,
0518: CmsStaticExportException {
0519:
0520: String vfsName = data.getVfsName();
0521: String rfsName = data.getRfsName();
0522: CmsResource resource = data.getResource();
0523:
0524: // cut the site root from the vfsName and switch to the correct site
0525: String siteRoot = OpenCms.getSiteManager().getSiteRoot(vfsName);
0526:
0527: CmsI18nInfo i18nInfo = OpenCms.getLocaleManager().getI18nInfo(
0528: req, cms.getRequestContext().currentUser(),
0529: cms.getRequestContext().currentProject(), vfsName);
0530:
0531: String remoteAddr = m_remoteAddr;
0532: if (remoteAddr == null) {
0533: remoteAddr = CmsContextInfo.LOCALHOST;
0534: }
0535: CmsContextInfo contextInfo = new CmsContextInfo(cms
0536: .getRequestContext().currentUser(), cms
0537: .getRequestContext().currentProject(), vfsName, "/",
0538: i18nInfo.getLocale(), i18nInfo.getEncoding(),
0539: remoteAddr, CmsContextInfo.CURRENT_TIME, cms
0540: .getRequestContext().getOuFqn());
0541:
0542: cms = OpenCms.initCmsObject(null, contextInfo);
0543:
0544: if (siteRoot != null) {
0545: vfsName = vfsName.substring(siteRoot.length());
0546: } else {
0547: siteRoot = "/";
0548: }
0549:
0550: if (LOG.isDebugEnabled()) {
0551: LOG.debug(Messages.get().getBundle().key(
0552: Messages.LOG_STATIC_EXPORT_SITE_ROOT_2, siteRoot,
0553: vfsName));
0554: }
0555: cms.getRequestContext().setSiteRoot(siteRoot);
0556:
0557: String oldUri = null;
0558:
0559: // this flag signals if the export method is used for "on demand" or "after publish".
0560: // if no request and result stream are available, it was called during "export on publish"
0561: boolean exportOnDemand = ((req != null) && (res != null));
0562:
0563: CmsStaticExportResponseWrapper wrapRes = null;
0564: if (res != null) {
0565: wrapRes = new CmsStaticExportResponseWrapper(res);
0566: }
0567: if (LOG.isDebugEnabled()) {
0568: LOG.debug(Messages.get().getBundle().key(
0569: Messages.LOG_SE_RESOURCE_START_1, data));
0570: }
0571:
0572: CmsFile file;
0573: // read vfs resource
0574: if (resource.isFile()) {
0575: file = cms.readFile(vfsName);
0576: } else {
0577: file = cms.readFile(OpenCms.initResource(cms, vfsName, req,
0578: wrapRes));
0579: if (cms.existsResource(vfsName + file.getName())) {
0580: vfsName = vfsName + file.getName();
0581: }
0582: rfsName += EXPORT_DEFAULT_FILE;
0583: }
0584:
0585: // check loader id for resource
0586: I_CmsResourceLoader loader = OpenCms.getResourceManager()
0587: .getLoader(file);
0588: if ((loader == null) || (!loader.isStaticExportEnabled())) {
0589: Object[] arguments = new Object[] { vfsName,
0590: new Integer(file.getTypeId()) };
0591: throw new CmsStaticExportException(Messages.get()
0592: .container(Messages.ERR_EXPORT_NOT_SUPPORTED_2,
0593: arguments));
0594: }
0595:
0596: // ensure we have exactly the same setup as if called "the usual way"
0597: // we only have to do this in case of the static export on demand
0598: if (exportOnDemand) {
0599: String mimetype = OpenCms.getResourceManager().getMimeType(
0600: file.getName(),
0601: cms.getRequestContext().getEncoding());
0602: if (wrapRes != null) {
0603: wrapRes.setContentType(mimetype);
0604: }
0605: oldUri = cms.getRequestContext().getUri();
0606: cms.getRequestContext().setUri(vfsName);
0607: }
0608:
0609: // do the export
0610: int status = -1;
0611: // only export those resource where the export property is set
0612: if (isExportLink(cms, vfsName)) {
0613: CmsLocaleManager locManager = OpenCms.getLocaleManager();
0614: List locales = locManager.getDefaultLocales(cms, vfsName);
0615: boolean exported = false;
0616: boolean matched = false;
0617: // iterate over all rules
0618: Iterator it = getRfsRules().iterator();
0619: while (it.hasNext()) {
0620: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
0621: .next();
0622: // normal case
0623: boolean export = rule.getSource().matcher(
0624: siteRoot + vfsName).matches();
0625: matched |= export;
0626: // system folder case
0627: export |= (vfsName
0628: .startsWith(CmsWorkplace.VFS_PATH_SYSTEM) && rule
0629: .match(vfsName));
0630: if (export) {
0631: // the resource has to exported for this rule
0632: CmsObject locCms = cms;
0633: Locale locale = CmsLocaleManager.getLocale(rule
0634: .getName());
0635: if (locales.contains(locale)) {
0636: // if the locale is in the default locales for the resource
0637: // so adjust the locale to use for exporting
0638: CmsContextInfo ctxInfo = new CmsContextInfo(cms
0639: .getRequestContext());
0640: ctxInfo.setLocale(locale);
0641: locCms = OpenCms.initCmsObject(cms, ctxInfo);
0642: }
0643: // read the content in the matching locale
0644: byte[] content = loader.export(locCms, file, req,
0645: wrapRes);
0646: if (content != null) {
0647: // write to rfs
0648: exported = true;
0649: String locRfsName = rfsName;
0650: if (locales.contains(locale)) {
0651: locRfsName = rule.getLocalizedRfsName(
0652: rfsName, "/");
0653: }
0654: writeResource(req, rule.getExportPath(),
0655: locRfsName, resource, content);
0656: }
0657: }
0658: }
0659: if (!matched) {
0660: // no rule matched
0661: String exportPath = getExportPath(siteRoot + vfsName);
0662: byte[] content = loader.export(cms, file, req, wrapRes);
0663: if (content != null) {
0664: exported = true;
0665: writeResource(req, exportPath, rfsName, resource,
0666: content);
0667: }
0668: }
0669: if (exported) {
0670: // get the wrapper status that was set
0671: status = (wrapRes != null) ? wrapRes.getStatus() : -1;
0672: if (status < 0) {
0673: // the status was not set, assume everything is o.k.
0674: status = HttpServletResponse.SC_OK;
0675: }
0676: } else {
0677: // the resource was not written because it was not modified.
0678: // set the status to not modified
0679: status = HttpServletResponse.SC_NOT_MODIFIED;
0680: }
0681: } else {
0682: // the resource was not used for export, so return HttpServletResponse.SC_SEE_OTHER
0683: // as a signal for not exported resource
0684: status = HttpServletResponse.SC_SEE_OTHER;
0685: }
0686:
0687: // restore context
0688: // we only have to do this in case of the static export on demand
0689: if (exportOnDemand) {
0690: cms.getRequestContext().setUri(oldUri);
0691: }
0692:
0693: return status;
0694: }
0695:
0696: /**
0697: * Starts a complete static export of all resources.<p>
0698: *
0699: * @param purgeFirst flag to delete all resources in the export folder of the rfs
0700: * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
0701: *
0702: * @throws CmsException in case of errors accessing the VFS
0703: * @throws IOException in case of errors writing to the export output stream
0704: * @throws ServletException in case of errors accessing the servlet
0705: */
0706: public synchronized void exportFullStaticRender(boolean purgeFirst,
0707: I_CmsReport report) throws CmsException, IOException,
0708: ServletException {
0709:
0710: // set member to true to get temporary export paths for rules
0711: m_fullStaticExport = true;
0712: // save the real export path
0713: String staticExportPathStore = m_staticExportPath;
0714:
0715: if (m_useTempDirs) {
0716: // set the export path to the export work path
0717: m_staticExportPath = m_staticExportWorkPath;
0718: }
0719:
0720: // delete all old exports if the purgeFirst flag is set
0721: if (purgeFirst) {
0722: Map eventData = new HashMap();
0723: eventData.put(I_CmsEventListener.KEY_REPORT, report);
0724: CmsEvent clearCacheEvent = new CmsEvent(
0725: I_CmsEventListener.EVENT_CLEAR_CACHES, eventData);
0726: OpenCms.fireCmsEvent(clearCacheEvent);
0727:
0728: scrubExportFolders(report);
0729: CmsObject cms = OpenCms.initCmsObject(OpenCms
0730: .getDefaultUsers().getUserExport());
0731: cms
0732: .deleteAllStaticExportPublishedResources(EXPORT_LINK_WITHOUT_PARAMETER);
0733: cms
0734: .deleteAllStaticExportPublishedResources(EXPORT_LINK_WITH_PARAMETER);
0735: }
0736:
0737: // do the export
0738: CmsAfterPublishStaticExportHandler handler = new CmsAfterPublishStaticExportHandler();
0739: // export everything
0740: handler.doExportAfterPublish(null, report);
0741:
0742: // set export path to the original one
0743: m_staticExportPath = staticExportPathStore;
0744:
0745: // set member to false for further exports
0746: m_fullStaticExport = false;
0747:
0748: // check if report contents no errors
0749: if (m_useTempDirs && !report.hasError()) {
0750: // backup old export folders for default export
0751: File staticExport = new File(m_staticExportPath);
0752: createExportBackupFolders(staticExport, m_staticExportPath,
0753: getExportBackups().intValue(), null);
0754:
0755: // change the name of the used temporary export folder to the original default export path
0756: File staticExportWork = new File(m_staticExportWorkPath);
0757: staticExportWork.renameTo(new File(m_staticExportPath));
0758:
0759: // backup old export folders of rule based exports
0760: Iterator it = m_rfsRules.iterator();
0761: while (it.hasNext()) {
0762: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
0763: .next();
0764: File staticExportRule = new File(rule.getExportPath());
0765: File staticExportWorkRule = new File(rule
0766: .getExportWorkPath());
0767: // only backup if a temporary folder exists for this rule
0768: if (staticExportWorkRule.exists()) {
0769: createExportBackupFolders(staticExportRule, rule
0770: .getExportPath(), rule.getExportBackups()
0771: .intValue(), OpenCms.getResourceManager()
0772: .getFileTranslator().translateResource(
0773: rule.getName()));
0774: staticExportWorkRule.renameTo(new File(rule
0775: .getExportPath()));
0776: }
0777: }
0778: } else if (report.hasError()) {
0779: report.println(Messages.get().container(
0780: Messages.ERR_EXPORT_NOT_SUCCESSFUL_0),
0781: I_CmsReport.FORMAT_WARNING);
0782: }
0783: }
0784:
0785: /**
0786: * Returns the accept-charset header used for internal requests.<p>
0787: *
0788: * @return the accept-charset header
0789: */
0790: public String getAcceptCharsetHeader() {
0791:
0792: return m_acceptCharsetHeader;
0793: }
0794:
0795: /**
0796: * Returns the accept-language header used for internal requests.<p>
0797: *
0798: * @return the accept-language header
0799: */
0800: public String getAcceptLanguageHeader() {
0801:
0802: return m_acceptLanguageHeader;
0803: }
0804:
0805: /**
0806: * Returns a cached export data for the given rfs name.<p>
0807: *
0808: * Please note that this export data contains only the vfs name and the parameters,
0809: * not the resource itself. It can therefore not directly be used as a return
0810: * value for the static export.<p>
0811: *
0812: * @param rfsName the name of the resource in the real file system to get the cached vfs resource name for
0813: *
0814: * @return a cached vfs resource name for the given rfs name, or <code>null</code>
0815: */
0816: public CmsStaticExportData getCachedExportUri(String rfsName) {
0817:
0818: return (CmsStaticExportData) m_cacheExportUris.get(rfsName);
0819: }
0820:
0821: /**
0822: * Returns a cached link for the given vfs name.<p>
0823: *
0824: * @param vfsName the name of the vfs resource to get the cached link for
0825: *
0826: * @return a cached link for the given vfs name, or null
0827: */
0828: public String getCachedOnlineLink(String vfsName) {
0829:
0830: return (String) m_cacheOnlineLinks.get(vfsName);
0831: }
0832:
0833: /**
0834: * Returns the key for the online, export and secure cache.<p>
0835: *
0836: * @param siteRoot the site root of the resource
0837: * @param uri the URI of the resource
0838: *
0839: * @return a key for the cache
0840: */
0841: public String getCacheKey(String siteRoot, String uri) {
0842:
0843: return new StringBuffer(siteRoot).append(uri).toString();
0844: }
0845:
0846: /**
0847: * Gets the default property value as a string representation.<p>
0848: *
0849: * @return <code>"true"</code> or <code>"false"</code>
0850: */
0851: public String getDefault() {
0852:
0853: return String.valueOf(m_exportPropertyDefault);
0854: }
0855:
0856: /**
0857: * Returns the current default charset header.<p>
0858: *
0859: * @return the current default charset header
0860: */
0861: public String getDefaultAcceptCharsetHeader() {
0862:
0863: return m_defaultAcceptCharsetHeader;
0864: }
0865:
0866: /**
0867: * Returns the current default locale header.<p>
0868: *
0869: * @return the current default locale header
0870: */
0871: public String getDefaultAcceptLanguageHeader() {
0872:
0873: return m_defaultAcceptLanguageHeader;
0874: }
0875:
0876: /**
0877: * Returns the default prefix for exported links in the "real" file system.<p>
0878: *
0879: * @return the default prefix for exported links in the "real" file system
0880: */
0881: public String getDefaultRfsPrefix() {
0882:
0883: return m_rfsPrefix;
0884: }
0885:
0886: /**
0887: * Returns the number of stored backups.<p>
0888: *
0889: * @return the number of stored backups
0890: */
0891: public Integer getExportBackups() {
0892:
0893: if (m_staticExportBackups != null) {
0894: return m_staticExportBackups;
0895: }
0896: // if backups not configured set to default value
0897: return EXPORT_DEFAULT_BACKUPS;
0898: }
0899:
0900: /**
0901: * Returns the export data for the request, if null is returned no export is required.<p>
0902: *
0903: * @param request the request to check for export data
0904: * @param cms an initialized cms context (should be initialized with the "Guest" user only
0905: *
0906: * @return the export data for the request, if null is returned no export is required
0907: */
0908: public CmsStaticExportData getExportData(
0909: HttpServletRequest request, CmsObject cms) {
0910:
0911: if (!isStaticExportEnabled()) {
0912: // export is disabled
0913: return null;
0914: }
0915:
0916: // build the rfs name for the export "on demand"
0917: String rfsName = request.getParameter(EXPORT_MARKER);
0918: if ((rfsName == null)) {
0919: rfsName = (String) request
0920: .getAttribute(EXPORT_ATTRIBUTE_ERROR_REQUEST_URI);
0921: }
0922:
0923: if (request.getHeader(CmsRequestUtil.HEADER_OPENCMS_EXPORT) != null) {
0924: // this is a request created by the static export and directly send to 404 handler
0925: // so remove the leading handler identification
0926: int prefix = rfsName.startsWith(getExportUrlPrefix()) ? getExportUrlPrefix()
0927: .length()
0928: : 0;
0929: if (prefix > 0) {
0930: rfsName = rfsName.substring(prefix);
0931: } else {
0932: return null;
0933: }
0934: }
0935:
0936: if (!isValidRfsName(rfsName)) {
0937: // this is not an export request, no further processing is required
0938: return null;
0939: }
0940:
0941: return getExportData(rfsName, null, cms);
0942: }
0943:
0944: /**
0945: * Returns the export data for a requested resource, if null is returned no export is required.<p>
0946: *
0947: * @param vfsName the VFS name of the resource requested
0948: * @param cms an initialized cms context (should be initialized with the "Guest" user only
0949: *
0950: * @return the export data for the request, if null is returned no export is required
0951: */
0952: public CmsStaticExportData getExportData(String vfsName,
0953: CmsObject cms) {
0954:
0955: return getExportData(getRfsName(cms, vfsName), vfsName, cms);
0956: }
0957:
0958: /**
0959: * Gets the export enabled value as a string representation.<p>
0960: *
0961: * @return <code>"true"</code> or <code>"false"</code>
0962: */
0963: public String getExportEnabled() {
0964:
0965: return String.valueOf(m_staticExportEnabled);
0966: }
0967:
0968: /**
0969: * Returns the current folder matcher.<p>
0970: *
0971: * @return the current folder matcher
0972: */
0973: public CmsExportFolderMatcher getExportFolderMatcher() {
0974:
0975: return m_exportFolderMatcher;
0976: }
0977:
0978: /**
0979: * Returns list of resources patterns which are part of the export.<p>
0980: *
0981: * @return the of resources patterns which are part of the export.
0982: */
0983: public List getExportFolderPatterns() {
0984:
0985: return Collections.unmodifiableList(m_exportFolders);
0986: }
0987:
0988: /**
0989: * Returns specific http headers for the static export.<p>
0990: *
0991: * If the header <code>Cache-Control</code> is set, OpenCms will not use its default headers.<p>
0992: *
0993: * @return the list of http export headers
0994: */
0995: public List getExportHeaders() {
0996:
0997: return Collections.unmodifiableList(m_exportHeaders);
0998: }
0999:
1000: /**
1001: * Returns the list of all resources that have the "exportname" property set.<p>
1002: *
1003: * @return the list of all resources that have the "exportname" property set
1004: */
1005: public Map getExportnames() {
1006:
1007: return m_exportnameResources;
1008: }
1009:
1010: /**
1011: * Returns the export path for the static export, that is the folder where the
1012: * static exported resources will be written to.<p>
1013: *
1014: * The returned value will be a directory like prefix. The value is configured
1015: * in the <code>opencms-importexport.xml</code> configuration file. An optimization
1016: * of the configured value will be performed, where all relative path information is resolved
1017: * (for example <code>/export/../static</code> will be resolved to <code>/export</code>.
1018: * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
1019: * (for example <code>/export/</code> becomes <code>/export</code>.<p>
1020: *
1021: * This is resource name based, and based on the rfs-rules defined in the
1022: * <code>opencms-importexport.xml</code> configuration file.<p>
1023: *
1024: * @param vfsName the name of the resource to export
1025: *
1026: * @return the export path for the static export, that is the folder where the
1027: *
1028: * @see #getRfsPrefix(String)
1029: * @see #getVfsPrefix()
1030: */
1031: public String getExportPath(String vfsName) {
1032:
1033: if (vfsName != null) {
1034: Iterator it = m_rfsRules.iterator();
1035: while (it.hasNext()) {
1036: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
1037: .next();
1038: if (rule.getSource().matcher(vfsName).matches()) {
1039: return rule.getExportPath();
1040: }
1041: }
1042: }
1043: if (m_useTempDirs && isFullStaticExport()) {
1044: return getExportWorkPath();
1045: }
1046: return m_staticExportPath;
1047: }
1048:
1049: /**
1050: * Returns the original configured export path for the static export without the complete rfs path, to be used
1051: * when re-writing the configuration.<p>
1052: *
1053: * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1054: * This method should <b>not</b> be used otherwise. Use <code>{@link #getExportPath(String)}</code>
1055: * to obtain the export path to use when exporting.<p>
1056: *
1057: * @return the original configured export path for the static export without the complete rfs path
1058: */
1059: public String getExportPathForConfiguration() {
1060:
1061: return m_staticExportPathConfigured;
1062: }
1063:
1064: /**
1065: * Returns true if the default value for the resource property "export" is true.<p>
1066: *
1067: * @return true if the default value for the resource property "export" is true
1068: */
1069: public boolean getExportPropertyDefault() {
1070:
1071: return m_exportPropertyDefault;
1072: }
1073:
1074: /**
1075: * Returns the export Rules.<p>
1076: *
1077: * @return the export Rules
1078: */
1079: public List getExportRules() {
1080:
1081: return Collections.unmodifiableList(m_exportRules);
1082: }
1083:
1084: /**
1085: * Gets the list of resource suffixes which will be exported by default.<p>
1086: *
1087: * @return list of resource suffixes
1088: */
1089: public List getExportSuffixes() {
1090:
1091: return m_exportSuffixes;
1092: }
1093:
1094: /**
1095: * Returns the export URL used for internal requests for exporting resources that require a
1096: * request / response (like JSP).<p>
1097: *
1098: * @return the export URL used for internal requests for exporting resources like JSP
1099: */
1100: public String getExportUrl() {
1101:
1102: return m_exportUrl;
1103: }
1104:
1105: /**
1106: * Returns the export URL used for internal requests with unsubstituted context values, to be used
1107: * when re-writing the configuration.<p>
1108: *
1109: * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1110: * This method should <b>not</b> be used otherwise. Use <code>{@link #getExportUrl()}</code>
1111: * to obtain the export path to use when exporting.<p>
1112: *
1113: * @return the export URL used for internal requests with unsubstituted context values
1114: */
1115: public String getExportUrlForConfiguration() {
1116:
1117: return m_exportUrlConfigured;
1118: }
1119:
1120: /**
1121: * Returns the export URL used for internal requests for exporting resources that require a
1122: * request / response (like JSP) without http://servername.<p>
1123: *
1124: * @return the export URL used for internal requests for exporting resources like JSP without http://servername
1125: */
1126: public String getExportUrlPrefix() {
1127:
1128: return m_exportUrlPrefix;
1129: }
1130:
1131: /**
1132: * Returns the export work path for the static export, that is the folder where the
1133: * static exported resources will be written to during the export process.<p>
1134: *
1135: * @return the export work path for the static export
1136: */
1137: public String getExportWorkPath() {
1138:
1139: return m_staticExportWorkPath;
1140: }
1141:
1142: /**
1143: * Returns the original configured export work path for the static export without the complete rfs path, to be used
1144: * when re-writing the configuration.<p>
1145: *
1146: * @return the original configured export work path for the static export without the complete rfs path
1147: */
1148: public String getExportWorkPathForConfiguration() {
1149:
1150: if (m_staticExportWorkPathConfigured != null) {
1151: return m_staticExportWorkPathConfigured;
1152: }
1153: // if work path not configured set to default value
1154: return EXPORT_DEFAULT_WORKPATH;
1155: }
1156:
1157: /**
1158: * Returns the configured static export handler class.<p>
1159: *
1160: * If not set, a new <code>{@link CmsAfterPublishStaticExportHandler}</code> is created and returned.<p>
1161: *
1162: * @return the configured static export handler class
1163: */
1164: public I_CmsStaticExportHandler getHandler() {
1165:
1166: if (m_handler == null) {
1167: setHandler(CmsOnDemandStaticExportHandler.class.getName());
1168: }
1169: return m_handler;
1170: }
1171:
1172: /**
1173: * Returns the configured link substitution handler class.<p>
1174: *
1175: * If not set, a new <code>{@link CmsDefaultLinkSubstitutionHandler}</code> is created and returned.<p>
1176: *
1177: * @return the configured link substitution handler class
1178: */
1179: public I_CmsLinkSubstitutionHandler getLinkSubstitutionHandler() {
1180:
1181: if (m_linkSubstitutionHandler == null) {
1182: setLinkSubstitutionHandler(CmsDefaultLinkSubstitutionHandler.class
1183: .getName());
1184: }
1185: return m_linkSubstitutionHandler;
1186: }
1187:
1188: /**
1189: * Gets the plain export optimization value as a string representation.<p>
1190: *
1191: * @return <code>"true"</code> or <code>"false"</code>
1192: */
1193: public String getPlainExportOptimization() {
1194:
1195: return String.valueOf(m_quickPlainExport);
1196: }
1197:
1198: /**
1199: * Returns true if the quick plain export is enabled.<p>
1200: *
1201: * @return true if the quick plain export is enabled
1202: */
1203: public boolean getQuickPlainExport() {
1204:
1205: return m_quickPlainExport;
1206: }
1207:
1208: /**
1209: * Gets the relative links value as a string representation.<p>
1210: *
1211: * @return <code>"true"</code> or <code>"false"</code>
1212: */
1213: public String getRelativeLinks() {
1214:
1215: return String.valueOf(m_exportRelativeLinks);
1216: }
1217:
1218: /**
1219: * Returns the remote address used for internal requests.<p>
1220: *
1221: * @return the remote address
1222: */
1223: public String getRemoteAddr() {
1224:
1225: return m_remoteAddr;
1226: }
1227:
1228: /**
1229: * Returns the remote address.<p>
1230: *
1231: * @return the remote address
1232: */
1233: public String getRemoteAddress() {
1234:
1235: return m_remoteAddr;
1236: }
1237:
1238: /**
1239: * Returns the static export rfs name for a given vfs resource.<p>
1240: *
1241: * @param cms an initialized cms context
1242: * @param vfsName the name of the vfs resource
1243: *
1244: * @return the static export rfs name for a give vfs resource
1245: *
1246: * @see #getVfsName(CmsObject, String)
1247: * @see #getRfsName(CmsObject, String, String)
1248: */
1249: public String getRfsName(CmsObject cms, String vfsName) {
1250:
1251: return getRfsName(cms, vfsName, null);
1252: }
1253:
1254: /**
1255: * Returns the static export rfs name for a given vfs resource where the link to the
1256: * resource includes request parameters.<p>
1257: *
1258: * @param cms an initialized cms context
1259: * @param vfsName the name of the vfs resource
1260: * @param parameters the parameters of the link pointing to the resource
1261: *
1262: * @return the static export rfs name for a give vfs resource
1263: */
1264: public String getRfsName(CmsObject cms, String vfsName,
1265: String parameters) {
1266:
1267: String rfsName = vfsName;
1268: try {
1269: // check if the resource folder (or a parent folder) has the "exportname" property set
1270: CmsProperty exportNameProperty = cms.readPropertyObject(
1271: CmsResource.getFolderPath(rfsName),
1272: CmsPropertyDefinition.PROPERTY_EXPORTNAME, true);
1273:
1274: if (exportNameProperty.isNullProperty()) {
1275: // if "exportname" is not set we must add the site root
1276: rfsName = cms.getRequestContext().addSiteRoot(rfsName);
1277: } else {
1278: // "exportname" property is set
1279: String exportname = exportNameProperty.getValue();
1280: if (exportname.charAt(0) != '/') {
1281: exportname = '/' + exportname;
1282: }
1283: if (exportname.charAt(exportname.length() - 1) != '/') {
1284: exportname = exportname + '/';
1285: }
1286: String value = null;
1287: boolean cont;
1288: String resourceName = rfsName;
1289: do {
1290: // find out where the export name was set, to replace these parent folders in the RFS name
1291: try {
1292: CmsProperty prop = cms
1293: .readPropertyObject(
1294: resourceName,
1295: CmsPropertyDefinition.PROPERTY_EXPORTNAME,
1296: false);
1297: if (prop.isIdentical(exportNameProperty)) {
1298: // look for the right position in path
1299: value = prop.getValue();
1300: }
1301: cont = (value == null)
1302: && (resourceName.length() > 1);
1303: } catch (CmsVfsResourceNotFoundException e) {
1304: // this is for publishing deleted resources
1305: cont = (resourceName.length() > 1);
1306: } catch (CmsSecurityException se) {
1307: // a security exception (probably no read permission) we return the current result
1308: cont = false;
1309: }
1310: if (cont) {
1311: resourceName = CmsResource
1312: .getParentFolder(resourceName);
1313: }
1314: } while (cont);
1315: rfsName = exportname
1316: + rfsName.substring(resourceName.length());
1317: }
1318:
1319: String extension = CmsFileUtil.getExtension(rfsName);
1320: // check if the VFS resource is a JSP page with a ".jsp" ending
1321: // in this case the rfs name suffix must be build with special care,
1322: // usually it must be set to ".html"
1323: boolean isJsp = extension.equals(".jsp");
1324: if (isJsp) {
1325: String suffix = null;
1326: try {
1327: CmsResource res = cms.readResource(vfsName);
1328: isJsp = (res.getTypeId() == CmsResourceTypeJsp
1329: .getStaticTypeId());
1330: // if the resource is a plain resource then no change in suffix is required
1331: if (isJsp) {
1332: suffix = cms
1333: .readPropertyObject(
1334: vfsName,
1335: CmsPropertyDefinition.PROPERTY_EXPORTSUFFIX,
1336: true).getValue(".html");
1337: }
1338: } catch (CmsVfsResourceNotFoundException e) {
1339: // resource has been deleted, so we are not able to get the right extension from the properties
1340: // try to figure out the right extension from file system
1341: File rfsFile = new File(CmsFileUtil
1342: .normalizePath(OpenCms
1343: .getStaticExportManager()
1344: .getExportPath(vfsName)
1345: + rfsName));
1346: File parent = rfsFile.getParentFile();
1347: if (parent != null) {
1348: File[] paramVariants = parent
1349: .listFiles(new CmsPrefixFileFilter(
1350: rfsFile.getName()));
1351: if ((paramVariants != null)
1352: && (paramVariants.length > 0)) {
1353: // take the first
1354: suffix = paramVariants[0].getAbsolutePath()
1355: .substring(
1356: rfsFile.getAbsolutePath()
1357: .length());
1358: }
1359: } else {
1360: // if no luck, try the default extension
1361: suffix = ".html";
1362: }
1363: }
1364: if ((suffix != null)
1365: && !extension.equals(suffix.toLowerCase())) {
1366: rfsName += suffix;
1367: extension = suffix;
1368: }
1369: }
1370: if (parameters != null) {
1371: // build the RFS name for the link with parameters
1372: rfsName = CmsFileUtil.getRfsPath(rfsName, extension,
1373: parameters);
1374: // we have found a rfs name for a vfs resource with parameters, save it to the database
1375: try {
1376: cms.writeStaticExportPublishedResource(rfsName,
1377: EXPORT_LINK_WITH_PARAMETER, parameters,
1378: System.currentTimeMillis());
1379: } catch (CmsException e) {
1380: LOG.error(Messages.get().getBundle().key(
1381: Messages.LOG_WRITE_FAILED_1, rfsName), e);
1382: }
1383: }
1384: } catch (CmsException e) {
1385: if (LOG.isDebugEnabled()) {
1386: LOG.debug(e.getLocalizedMessage(), e);
1387: }
1388: // ignore exception, return vfsName as rfsName
1389: rfsName = vfsName;
1390: }
1391:
1392: // add export rfs prefix and return result
1393: if (!vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
1394: return getRfsPrefix(
1395: cms.getRequestContext().addSiteRoot(vfsName))
1396: .concat(rfsName);
1397: } else {
1398: // check if we are generating a link to a related resource in the same rfs rule
1399: String source = cms.getRequestContext().addSiteRoot(
1400: cms.getRequestContext().getUri());
1401: Iterator it = m_rfsRules.iterator();
1402: while (it.hasNext()) {
1403: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
1404: .next();
1405: if (rule.getSource().matcher(source).matches()
1406: && rule.match(vfsName)) {
1407: return rule.getRfsPrefix().concat(rfsName);
1408: }
1409: }
1410: // this is a link across rfs rules
1411: return getRfsPrefix(
1412: cms.getRequestContext().getSiteRoot() + "/")
1413: .concat(rfsName);
1414: }
1415: }
1416:
1417: /**
1418: * Returns the prefix for exported links in the "real" file system.<p>
1419: *
1420: * The returned value will be a directory like prefix. The value is configured
1421: * in the <code>opencms-importexport.xml</code> configuration file. An optimization
1422: * of the configured value will be performed, where all relative path information is resolved
1423: * (for example <code>/export/../static</code> will be resolved to <code>/export</code>.
1424: * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
1425: * (for example <code>/export/</code> becomes <code>/export</code>.<p>
1426: *
1427: * This is resource name based, and based on the rfs-rules defined in the
1428: * <code>opencms-importexport.xml</code> configuration file.<p>
1429: *
1430: * @param vfsName the name of the resource to export
1431: *
1432: * @return the prefix for exported links in the "real" file system
1433: *
1434: * @see #getExportPath(String)
1435: * @see #getVfsPrefix()
1436: */
1437: public String getRfsPrefix(String vfsName) {
1438:
1439: if (vfsName != null) {
1440: Iterator it = m_rfsRules.iterator();
1441: while (it.hasNext()) {
1442: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
1443: .next();
1444: if (rule.getSource().matcher(vfsName).matches()) {
1445: return rule.getRfsPrefix();
1446: }
1447: }
1448: }
1449: return m_rfsPrefix;
1450: }
1451:
1452: /**
1453: * Returns the original configured prefix for exported links in the "real" file, to be used
1454: * when re-writing the configuration.<p>
1455: *
1456: * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1457: * This method should <b>not</b> be used otherwise. Use <code>{@link #getRfsPrefix(String)}</code>
1458: * to obtain the rfs prefix to use for the exported links.<p>
1459: *
1460: * @return the original configured prefix for exported links in the "real" file
1461: */
1462: public String getRfsPrefixForConfiguration() {
1463:
1464: return m_rfsPrefixConfigured;
1465: }
1466:
1467: /**
1468: * Returns the rfs Rules.<p>
1469: *
1470: * @return the rfs Rules
1471: */
1472: public List getRfsRules() {
1473:
1474: return Collections.unmodifiableList(m_rfsRules);
1475: }
1476:
1477: /**
1478: * Returns the vfs name of the test resource.<p>
1479: *
1480: * @return the vfs name of the test resource.
1481: */
1482: public String getTestResource() {
1483:
1484: return m_testResource;
1485: }
1486:
1487: /**
1488: * Returns the VFS name for the given RFS name, being the exact reverse of <code>{@link #getRfsName(CmsObject, String)}</code>.<p>
1489: *
1490: * Returns <code>null</code> if no matching VFS resource can be found for the given RFS name.<p>
1491: *
1492: * @param cms the current users OpenCms context
1493: * @param rfsName the RFS name to get the VFS name for
1494: *
1495: * @return the VFS name for the given RFS name, or <code>null</code> if the RFS name does not match to the VFS
1496: *
1497: * @see #getRfsName(CmsObject, String)
1498: */
1499: public String getVfsName(CmsObject cms, String rfsName) {
1500:
1501: CmsStaticExportData data = getExportData(rfsName, null, cms);
1502: if (data != null) {
1503: String result = data.getVfsName();
1504: if ((result != null)
1505: && result.startsWith(cms.getRequestContext()
1506: .getSiteRoot())) {
1507: result = result.substring(cms.getRequestContext()
1508: .getSiteRoot().length());
1509: }
1510: return result;
1511: }
1512: return null;
1513: }
1514:
1515: /**
1516: * Returns the prefix for the internal in the VFS.<p>
1517: *
1518: * The returned value will be a directory like prefix. The value is configured
1519: * in the <code>opencms-importexport.xml</code> configuration file. An optimization
1520: * of the configured value will be performed, where all relative path information is resolved
1521: * (for example <code>/opencms/../mycms</code> will be resolved to <code>/mycms</code>.
1522: * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
1523: * (for example <code>/opencms/</code> becomes <code>/opencms</code>.<p>
1524: *
1525: * @return the prefix for the internal in the VFS
1526: *
1527: * @see #getExportPath(String)
1528: * @see #getRfsPrefix(String)
1529: */
1530: public String getVfsPrefix() {
1531:
1532: return m_vfsPrefix;
1533: }
1534:
1535: /**
1536: * Returns the original configured prefix for internal links in the VFS, to be used
1537: * when re-writing the configuration.<p>
1538: *
1539: * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1540: * This method should <b>not</b> be used otherwise. Use <code>{@link #getVfsPrefix()}</code>
1541: * to obtain the VFS prefix to use for the internal links.<p>
1542: *
1543: * @return the original configured prefix for internal links in the VFS
1544: */
1545: public String getVfsPrefixForConfiguration() {
1546:
1547: return m_vfsPrefixConfigured;
1548: }
1549:
1550: /**
1551: * Initializes the static export manager with the OpenCms system configuration.<p>
1552: *
1553: * @param cms an OpenCms context object
1554: */
1555: public void initialize(CmsObject cms) {
1556:
1557: // initialize static export RFS path (relative to web application)
1558: m_staticExportPath = normalizeExportPath(m_staticExportPathConfigured);
1559: m_staticExportWorkPath = normalizeExportPath(getExportWorkPathForConfiguration());
1560: if (m_staticExportPath.equals(OpenCms.getSystemInfo()
1561: .getWebApplicationRfsPath())) {
1562: throw new CmsIllegalArgumentException(Messages.get()
1563: .container(Messages.ERR_INVALID_EXPORT_PATH_0));
1564: }
1565: // initialize prefix variables
1566: m_rfsPrefix = normalizeRfsPrefix(m_rfsPrefixConfigured);
1567: Iterator itRfsRules = m_rfsRules.iterator();
1568: while (itRfsRules.hasNext()) {
1569: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) itRfsRules
1570: .next();
1571: try {
1572: rule.setExportPath(normalizeExportPath(rule
1573: .getExportPathConfigured()));
1574: } catch (CmsIllegalArgumentException e) {
1575: CmsLog.INIT.warn(e.getMessageContainer());
1576: rule.setExportPath(m_staticExportPath);
1577: }
1578: try {
1579: rule.setExportWorkPath(normalizeExportPath(rule
1580: .getExportWorkPathConfigured()));
1581: } catch (CmsIllegalArgumentException e) {
1582: CmsLog.INIT.warn(e.getMessageContainer());
1583: rule.setExportWorkPath(m_staticExportWorkPath);
1584: }
1585: rule.setRfsPrefix(normalizeRfsPrefix(rule
1586: .getRfsPrefixConfigured()));
1587: }
1588: m_vfsPrefix = insertContextStrings(m_vfsPrefixConfigured);
1589: m_vfsPrefix = CmsFileUtil.normalizePath(m_vfsPrefix, '/');
1590: if (CmsResource.isFolder(m_vfsPrefix)) {
1591: // ensure prefix does NOT end with a folder '/'
1592: m_vfsPrefix = m_vfsPrefix.substring(0,
1593: m_vfsPrefix.length() - 1);
1594: }
1595: if (CmsLog.INIT.isDebugEnabled()) {
1596: if (cms != null) {
1597: CmsLog.INIT.debug(Messages.get().getBundle().key(
1598: Messages.INIT_SE_MANAGER_CREATED_1, cms));
1599: } else {
1600: CmsLog.INIT.debug(Messages.get().getBundle().key(
1601: Messages.INIT_SE_MANAGER_CREATED_0));
1602: }
1603: }
1604:
1605: LRUMap lruMap1 = new LRUMap(2048);
1606: m_cacheOnlineLinks = Collections.synchronizedMap(lruMap1);
1607: // map must be of type "LRUMap" so that memory monitor can acecss all information
1608: OpenCms.getMemoryMonitor().register(
1609: this .getClass().getName() + ".m_cacheOnlineLinks",
1610: lruMap1);
1611:
1612: LRUMap lruMap2 = new LRUMap(2048);
1613: m_cacheExportUris = Collections.synchronizedMap(lruMap2);
1614: // map must be of type "LRUMap" so that memory monitor can acecss all information
1615: OpenCms.getMemoryMonitor().register(
1616: this .getClass().getName() + ".m_cacheExportUris",
1617: lruMap2);
1618:
1619: LRUMap lruMap3 = new LRUMap(2048);
1620: m_cacheSecureLinks = Collections.synchronizedMap(lruMap3);
1621: // map must be of type "LRUMap" so that memory monitor can acecss all information
1622: OpenCms.getMemoryMonitor().register(
1623: this .getClass().getName() + ".m_cacheSecureLinks",
1624: lruMap3);
1625:
1626: LRUMap lruMap4 = new LRUMap(2048);
1627: m_cacheExportLinks = Collections.synchronizedMap(lruMap4);
1628: // map must be of type "LRUMap" so that memory monitor can acecss all information
1629: OpenCms.getMemoryMonitor().register(
1630: this .getClass().getName() + ".m_cacheExportLinks",
1631: lruMap4);
1632:
1633: // register this object as event listener
1634: OpenCms.addCmsEventListener(this , new int[] {
1635: I_CmsEventListener.EVENT_PUBLISH_PROJECT,
1636: I_CmsEventListener.EVENT_CLEAR_CACHES,
1637: I_CmsEventListener.EVENT_UPDATE_EXPORTS });
1638:
1639: m_exportFolderMatcher = new CmsExportFolderMatcher(
1640: m_exportFolders, m_testResource);
1641:
1642: // initialize "exportname" folders
1643: setExportnames();
1644:
1645: // get the default accept-language header value
1646: m_defaultAcceptLanguageHeader = CmsAcceptLanguageHeaderParser
1647: .createLanguageHeader();
1648:
1649: // get the default accept-charset header value
1650: m_defaultAcceptCharsetHeader = OpenCms.getSystemInfo()
1651: .getDefaultEncoding();
1652:
1653: // get the export url prefix
1654: int pos = m_exportUrl.indexOf("://");
1655: if (pos > 0) {
1656: // absolute link, remove http://servername
1657: int pos2 = m_exportUrl.indexOf('/', pos + 3);
1658: if (pos2 > 0) {
1659: m_exportUrlPrefix = m_exportUrl.substring(pos2);
1660: } else {
1661: // should never happen
1662: m_exportUrlPrefix = "";
1663: }
1664: } else {
1665: m_exportUrlPrefix = m_exportUrl;
1666: }
1667: if (CmsLog.INIT.isInfoEnabled()) {
1668: if (isStaticExportEnabled()) {
1669: CmsLog.INIT.info(Messages.get().getBundle().key(
1670: Messages.INIT_STATIC_EXPORT_ENABLED_0));
1671: CmsLog.INIT.info(Messages.get().getBundle().key(
1672: Messages.INIT_EXPORT_DEFAULT_1,
1673: Boolean.valueOf(getExportPropertyDefault())));
1674: itRfsRules = m_rfsRules.iterator();
1675: while (itRfsRules.hasNext()) {
1676: CmsStaticExportRfsRule rfsRule = (CmsStaticExportRfsRule) itRfsRules
1677: .next();
1678: CmsLog.INIT
1679: .info(Messages
1680: .get()
1681: .getBundle()
1682: .key(
1683: Messages.INIT_EXPORT_RFS_RULE_EXPORT_PATH_2,
1684: rfsRule.getSource(),
1685: rfsRule.getExportPath()));
1686: CmsLog.INIT
1687: .info(Messages
1688: .get()
1689: .getBundle()
1690: .key(
1691: Messages.INIT_EXPORT_RFS_RULE_RFS_PREFIX_2,
1692: rfsRule.getSource(),
1693: rfsRule.getRfsPrefix()));
1694: if (rfsRule.getUseRelativeLinks() != null) {
1695: if (rfsRule.getUseRelativeLinks()
1696: .booleanValue()) {
1697: CmsLog.INIT
1698: .info(Messages
1699: .get()
1700: .getBundle()
1701: .key(
1702: Messages.INIT_EXPORT_RFS_RULE_RELATIVE_LINKS_1,
1703: rfsRule.getSource()));
1704: } else {
1705: CmsLog.INIT
1706: .info(Messages
1707: .get()
1708: .getBundle()
1709: .key(
1710: Messages.INIT_EXPORT_RFS_RULE_ABSOLUTE_LINKS_1,
1711: rfsRule.getSource()));
1712: }
1713: }
1714: }
1715: // default rule
1716: CmsLog.INIT.info(Messages.get().getBundle().key(
1717: Messages.INIT_EXPORT_RFS_RULE_EXPORT_PATH_2,
1718: "/", m_staticExportPath));
1719: CmsLog.INIT.info(Messages.get().getBundle().key(
1720: Messages.INIT_EXPORT_RFS_RULE_RFS_PREFIX_2,
1721: "/", m_rfsPrefix));
1722: if (m_exportRelativeLinks) {
1723: CmsLog.INIT
1724: .info(Messages
1725: .get()
1726: .getBundle()
1727: .key(
1728: Messages.INIT_EXPORT_RFS_RULE_RELATIVE_LINKS_1,
1729: "/"));
1730: } else {
1731: CmsLog.INIT
1732: .info(Messages
1733: .get()
1734: .getBundle()
1735: .key(
1736: Messages.INIT_EXPORT_RFS_RULE_ABSOLUTE_LINKS_1,
1737: "/"));
1738: }
1739: CmsLog.INIT.info(Messages.get().getBundle().key(
1740: Messages.INIT_EXPORT_VFS_PREFIX_1,
1741: getVfsPrefix()));
1742: CmsLog.INIT.info(Messages.get().getBundle().key(
1743: Messages.INIT_EXPORT_EXPORT_HANDLER_1,
1744: getHandler().getClass().getName()));
1745: CmsLog.INIT.info(Messages.get().getBundle().key(
1746: Messages.INIT_EXPORT_URL_1, getExportUrl()));
1747: CmsLog.INIT.info(Messages.get().getBundle().key(
1748: Messages.INIT_EXPORT_OPTIMIZATION_1,
1749: getPlainExportOptimization()));
1750: CmsLog.INIT.info(Messages.get().getBundle().key(
1751: Messages.INIT_EXPORT_TESTRESOURCE_1,
1752: getTestResource()));
1753: CmsLog.INIT.info(Messages.get().getBundle().key(
1754: Messages.INIT_LINKSUBSTITUTION_HANDLER_1,
1755: getLinkSubstitutionHandler().getClass()
1756: .getName()));
1757: } else {
1758: CmsLog.INIT.info(Messages.get().getBundle().key(
1759: Messages.INIT_STATIC_EXPORT_DISABLED_0));
1760: }
1761: }
1762: }
1763:
1764: /**
1765: * Checks if the static export is required for the given VFS resource.<p>
1766: *
1767: * Please note that the given OpenCms user context is NOT used to read the resource.
1768: * The check for export is always done with the permissions of the "Export" user.
1769: * The provided user context is just used to get the current site root.<p>
1770: *
1771: * Since the "Export" user always operates in the "Online" project, the resource
1772: * is also read from the "Online" project, not from the current project of the given
1773: * OpenCms context.<p>
1774: *
1775: * @param cms the current users OpenCms context
1776: * @param vfsName the VFS resource name to check
1777: *
1778: * @return <code>true</code> if static export is required for the given VFS resource
1779: */
1780: public boolean isExportLink(CmsObject cms, String vfsName) {
1781:
1782: boolean result = false;
1783: if (isStaticExportEnabled()) {
1784: Boolean exportResource = (Boolean) m_cacheExportLinks
1785: .get(getCacheKey(cms.getRequestContext()
1786: .getSiteRoot(), vfsName));
1787: if (exportResource == null) {
1788: try {
1789: // static export must always be checked with the export users permissions,
1790: // not the current users permissions
1791: CmsObject exportCms = OpenCms.initCmsObject(OpenCms
1792: .getDefaultUsers().getUserExport());
1793: exportCms.getRequestContext().setSiteRoot(
1794: cms.getRequestContext().getSiteRoot());
1795: // let's look up export property in VFS
1796: String exportValue = exportCms
1797: .readPropertyObject(
1798: vfsName,
1799: CmsPropertyDefinition.PROPERTY_EXPORT,
1800: true).getValue();
1801: if (exportValue == null) {
1802: // no setting found for "export" property
1803: if (getExportPropertyDefault()) {
1804: // if the default is "true" we always export
1805: result = true;
1806: } else {
1807: // check if the resource is exportable by suffix
1808: result = isSuffixExportable(vfsName);
1809: }
1810: } else {
1811: // "export" value found, if it was "true" we export
1812: result = Boolean.valueOf(exportValue)
1813: .booleanValue();
1814: }
1815: } catch (Exception e) {
1816: // no export required (probably security issues, e.g. no access for export user)
1817: }
1818: m_cacheExportLinks.put(getCacheKey(cms
1819: .getRequestContext().getSiteRoot(), vfsName),
1820: Boolean.valueOf(result));
1821: } else {
1822: result = exportResource.booleanValue();
1823: }
1824: }
1825: return result;
1826: }
1827:
1828: /**
1829: * Returns true if the export process is a full static export.<p>
1830: *
1831: * @return true if the export process is a full static export
1832: */
1833: public boolean isFullStaticExport() {
1834:
1835: return m_fullStaticExport;
1836: }
1837:
1838: /**
1839: * Returns <code>true</code> if the given VFS resource should be transported through a secure channel.<p>
1840: *
1841: * The secure mode is only checked in the "Online" project.
1842: * If the given OpenCms context is currently not in the "Online" project,
1843: * <code>false</code> is returned.<p>
1844: *
1845: * The given resource is read from the site root of the provided OpenCms context.<p>
1846: *
1847: * @param cms the current users OpenCms context
1848: * @param vfsName the VFS resource name to check
1849: *
1850: * @return <code>true</code> if the given VFS resource should be transported through a secure channel
1851: *
1852: * @see #isSecureLink(CmsObject, String, String)
1853: */
1854: public boolean isSecureLink(CmsObject cms, String vfsName) {
1855:
1856: if (!cms.getRequestContext().currentProject().isOnlineProject()) {
1857: return false;
1858: }
1859: Boolean secureResource = (Boolean) m_cacheSecureLinks
1860: .get(getCacheKey(cms.getRequestContext().getSiteRoot(),
1861: vfsName));
1862: if (secureResource == null) {
1863: try {
1864: String secureProp = cms.readPropertyObject(vfsName,
1865: CmsPropertyDefinition.PROPERTY_SECURE, true)
1866: .getValue();
1867: secureResource = Boolean.valueOf(secureProp);
1868: // only cache result if read was successfull
1869: m_cacheSecureLinks.put(getCacheKey(cms
1870: .getRequestContext().getSiteRoot(), vfsName),
1871: secureResource);
1872: } catch (CmsVfsResourceNotFoundException e) {
1873: secureResource = Boolean.FALSE;
1874: // resource does not exist, no secure link will be required for any user
1875: m_cacheSecureLinks.put(getCacheKey(cms
1876: .getRequestContext().getSiteRoot(), vfsName),
1877: secureResource);
1878: } catch (Exception e) {
1879: // no secure link required (probably security issues, e.g. no access for current user)
1880: // however other users may be allowed to read the resource, so the result can't be cached
1881: secureResource = Boolean.FALSE;
1882: }
1883: }
1884: return secureResource.booleanValue();
1885: }
1886:
1887: /**
1888: * Returns <code>true</code> if the given VFS resource that is located under the
1889: * given site root should be transported through a secure channel.<p>
1890: *
1891: * @param cms the current users OpenCms context
1892: * @param vfsName the VFS resource name to check
1893: * @param siteRoot the site root where the the VFS resource should be read
1894: *
1895: * @return <code>true</code> if the given VFS resource should be transported through a secure channel
1896: *
1897: * @see #isSecureLink(CmsObject, String)
1898: */
1899: public boolean isSecureLink(CmsObject cms, String vfsName,
1900: String siteRoot) {
1901:
1902: if (siteRoot == null) {
1903: return isSecureLink(cms, vfsName);
1904: }
1905:
1906: // the site root of the cms object has to be changed so that the property can be read
1907: String storedSiteRoot = cms.getRequestContext().getSiteRoot();
1908: try {
1909: cms.getRequestContext().setSiteRoot(siteRoot);
1910: return isSecureLink(cms, vfsName);
1911: } finally {
1912: cms.getRequestContext().setSiteRoot(storedSiteRoot);
1913: }
1914: }
1915:
1916: /**
1917: * Returns true if the static export is enabled.<p>
1918: *
1919: * @return true if the static export is enabled
1920: */
1921: public boolean isStaticExportEnabled() {
1922:
1923: return m_staticExportEnabled;
1924: }
1925:
1926: /**
1927: * Returns true if the given resource name is exportable because of it's suffix.<p>
1928: *
1929: * @param resourceName the name to check
1930: * @return true if the given resource name is exportable because of it's suffix
1931: */
1932: public boolean isSuffixExportable(String resourceName) {
1933:
1934: if (resourceName == null) {
1935: return false;
1936: }
1937: int pos = resourceName.lastIndexOf('.');
1938: if (pos >= 0) {
1939: String suffix = resourceName.substring(pos).toLowerCase();
1940: return m_exportSuffixes.contains(suffix);
1941: }
1942: return false;
1943: }
1944:
1945: /**
1946: * Checks if we have to use temporary directories during export.<p>
1947: *
1948: * @return <code>true</code> if using temporary directories
1949: */
1950: public boolean isUseTempDir() {
1951:
1952: return m_useTempDirs;
1953: }
1954:
1955: /**
1956: * Returns true if the links in the static export should be relative.<p>
1957: *
1958: * @param vfsName the name of the resource to export
1959: *
1960: * @return true if the links in the static export should be relative
1961: */
1962: public boolean relativeLinksInExport(String vfsName) {
1963:
1964: if (vfsName != null) {
1965: Iterator it = m_rfsRules.iterator();
1966: while (it.hasNext()) {
1967: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
1968: .next();
1969: if (rule.getSource().matcher(vfsName).matches()) {
1970: return rule.getUseRelativeLinks() != null ? rule
1971: .getUseRelativeLinks().booleanValue()
1972: : m_exportRelativeLinks;
1973: }
1974: }
1975: }
1976: return m_exportRelativeLinks;
1977: }
1978:
1979: /**
1980: * Sets the accept-charset header value.<p>
1981: *
1982: * @param value accept-language header value
1983: */
1984: public void setAcceptCharsetHeader(String value) {
1985:
1986: m_acceptCharsetHeader = value;
1987: }
1988:
1989: /**
1990: * Sets the accept-language header value.<p>
1991: *
1992: * @param value accept-language header value
1993: */
1994: public void setAcceptLanguageHeader(String value) {
1995:
1996: m_acceptLanguageHeader = value;
1997: }
1998:
1999: /**
2000: * Sets the default property value.<p>
2001: *
2002: * @param value must be <code>true</code> or <code>false</code>
2003: */
2004: public void setDefault(String value) {
2005:
2006: m_exportPropertyDefault = Boolean.valueOf(value).booleanValue();
2007: }
2008:
2009: /**
2010: * Sets the number of backups for the static export.<p>
2011: *
2012: * @param backup number of backups
2013: */
2014: public void setExportBackups(String backup) {
2015:
2016: m_staticExportBackups = new Integer(backup);
2017: }
2018:
2019: /**
2020: * Sets the export enabled value.<p>
2021: *
2022: * @param value must be <code>true</code> or <code>false</code>
2023: */
2024: public void setExportEnabled(String value) {
2025:
2026: m_staticExportEnabled = Boolean.valueOf(value).booleanValue();
2027: }
2028:
2029: /**
2030: * Adds a resource pattern to the list of resources which are part of the export.<p>
2031: *
2032: * @param folder the folder pattern to add to the list.
2033: */
2034: public void setExportFolderPattern(String folder) {
2035:
2036: m_exportFolders.add(folder);
2037: }
2038:
2039: /**
2040: * Sets specific http header for the static export.<p>
2041: *
2042: * The format of the headers must be "header:value".<p>
2043: *
2044: * @param exportHeader a specific http header
2045: */
2046: public void setExportHeader(String exportHeader) {
2047:
2048: if (CmsStringUtil.splitAsArray(exportHeader, ':').length == 2) {
2049: if (CmsLog.INIT.isInfoEnabled()) {
2050: CmsLog.INIT.info(Messages.get().getBundle().key(
2051: Messages.INIT_EXPORT_HEADERS_1, exportHeader));
2052: }
2053: m_exportHeaders.add(exportHeader);
2054: } else {
2055: if (CmsLog.INIT.isWarnEnabled()) {
2056: CmsLog.INIT.warn(Messages.get().getBundle().key(
2057: Messages.INIT_INVALID_HEADER_1, exportHeader));
2058: }
2059: }
2060: }
2061:
2062: /**
2063: * Sets the path where the static export is written.<p>
2064: *
2065: * @param path the path where the static export is written
2066: */
2067: public void setExportPath(String path) {
2068:
2069: m_staticExportPathConfigured = path;
2070: }
2071:
2072: /**
2073: * Adds a suffix to the list of resource suffixes which will be exported by default.<p>
2074: *
2075: * @param suffix the suffix to add to the list.
2076: */
2077: public void setExportSuffix(String suffix) {
2078:
2079: m_exportSuffixes.add(suffix.toLowerCase());
2080: }
2081:
2082: /**
2083: * Sets the export url.<p>
2084: *
2085: * @param url the export url
2086: */
2087: public void setExportUrl(String url) {
2088:
2089: m_exportUrl = insertContextStrings(url);
2090: m_exportUrlConfigured = url;
2091: }
2092:
2093: /**
2094: * Sets the path where the static export is temporarily written.<p>
2095: *
2096: * @param path the path where the static export is temporarily written
2097: */
2098: public void setExportWorkPath(String path) {
2099:
2100: m_staticExportWorkPathConfigured = path;
2101: }
2102:
2103: /**
2104: * Sets the link substitution handler class.<p>
2105: *
2106: * @param handlerClassName the link substitution handler class name
2107: */
2108: public void setHandler(String handlerClassName) {
2109:
2110: try {
2111: m_handler = (I_CmsStaticExportHandler) Class.forName(
2112: handlerClassName).newInstance();
2113: } catch (Exception e) {
2114: // should never happen
2115: LOG.error(e.getLocalizedMessage(), e);
2116: }
2117: }
2118:
2119: /**
2120: * Sets the static export handler class.<p>
2121: *
2122: * @param handlerClassName the static export handler class name
2123: */
2124: public void setLinkSubstitutionHandler(String handlerClassName) {
2125:
2126: try {
2127: m_linkSubstitutionHandler = (I_CmsLinkSubstitutionHandler) Class
2128: .forName(handlerClassName).newInstance();
2129: } catch (Exception e) {
2130: // should never happen
2131: LOG.error(e.getLocalizedMessage(), e);
2132: }
2133: }
2134:
2135: /**
2136: * Sets the plain export optimization value.<p>
2137: *
2138: * @param value must be <code>true</code> or <code>false</code>
2139: */
2140: public void setPlainExportOptimization(String value) {
2141:
2142: m_quickPlainExport = Boolean.valueOf(value).booleanValue();
2143: }
2144:
2145: /**
2146: * Sets the relative links value.<p>
2147: *
2148: * @param value must be <code>true</code> or <code>false</code>
2149: */
2150: public void setRelativeLinks(String value) {
2151:
2152: m_exportRelativeLinks = Boolean.valueOf(value).booleanValue();
2153: }
2154:
2155: /**
2156: * Sets the remote address which will be used for internal requests during the static export.<p>
2157: *
2158: * @param addr the remote address to be used
2159: */
2160: public void setRemoteAddr(String addr) {
2161:
2162: m_remoteAddr = addr;
2163: }
2164:
2165: /**
2166: * Sets the prefix for exported links in the "real" file system.<p>
2167: *
2168: * @param rfsPrefix the prefix for exported links in the "real" file system
2169: */
2170: public void setRfsPrefix(String rfsPrefix) {
2171:
2172: m_rfsPrefixConfigured = rfsPrefix;
2173: }
2174:
2175: /**
2176: * Sets the test resource.<p>
2177: *
2178: * @param testResource the vfs name of the test resource
2179: */
2180: public void setTestResource(String testResource) {
2181:
2182: m_testResource = testResource;
2183: }
2184:
2185: /**
2186: * Sets the prefix for internal links in the vfs.<p>
2187: *
2188: * @param vfsPrefix the prefix for internal links in the vfs
2189: */
2190: public void setVfsPrefix(String vfsPrefix) {
2191:
2192: m_vfsPrefixConfigured = vfsPrefix;
2193: }
2194:
2195: /**
2196: * Shuts down all this static export manager.<p>
2197: *
2198: * This is required since there may still be a thread running when the system is being shut down.<p>
2199: */
2200: public synchronized void shutDown() {
2201:
2202: int count = 0;
2203: // if the handler is still running, we must wait up to 30 seconds until it is finished
2204: while ((count < HANDLER_FINISH_TIME) && m_handler.isBusy()) {
2205: count++;
2206: try {
2207: if (CmsLog.INIT.isInfoEnabled()) {
2208: CmsLog.INIT.info(Messages.get().getBundle().key(
2209: Messages.INIT_STATIC_EXPORT_SHUTDOWN_3,
2210: m_handler.getClass().getName(),
2211: String.valueOf(count),
2212: String.valueOf(HANDLER_FINISH_TIME)));
2213: }
2214: wait(1000);
2215: } catch (InterruptedException e) {
2216: // if interrupted we ignore the handler, this will produce some log messages but should be ok
2217: count = HANDLER_FINISH_TIME;
2218: }
2219: }
2220:
2221: if (CmsLog.INIT.isInfoEnabled()) {
2222: CmsLog.INIT.info(Messages.get().getBundle()
2223: .key(Messages.INIT_SHUTDOWN_1,
2224: this .getClass().getName()));
2225: }
2226:
2227: }
2228:
2229: /**
2230: * Clears the caches in the export manager.<p>
2231: *
2232: * @param event the event that requested to clear the caches
2233: */
2234: protected void clearCaches(CmsEvent event) {
2235:
2236: // synchronization of this method is not required as the individual maps are all synchronized maps anyway,
2237: // and setExportnames() is doing it's own synchronization
2238:
2239: // flush all caches
2240: m_cacheOnlineLinks.clear();
2241: m_cacheExportUris.clear();
2242: m_cacheSecureLinks.clear();
2243: m_cacheExportLinks.clear();
2244: setExportnames();
2245: if (LOG.isDebugEnabled()) {
2246: String eventType = "EVENT_CLEAR_CACHES";
2247: if (event.getType() != I_CmsEventListener.EVENT_CLEAR_CACHES) {
2248: eventType = "EVENT_PUBLISH_PROJECT";
2249: }
2250: LOG.debug(Messages.get().getBundle().key(
2251: Messages.LOG_FLUSHED_CACHES_1, eventType));
2252: }
2253: }
2254:
2255: /**
2256: * Creates the backup folders for the given export folder and deletes the oldest if the maximum number is reached.<p>
2257: *
2258: * @param staticExport folder for which a new backup folder has to be created
2259: * @param exportPath export path to create backup path out of it
2260: * @param exportBackups number of maximum
2261: * @param ruleBackupExtension extension for rule based backups
2262: */
2263: protected void createExportBackupFolders(File staticExport,
2264: String exportPath, int exportBackups,
2265: String ruleBackupExtension) {
2266:
2267: if (staticExport.exists()) {
2268: String backupFolderName = exportPath.substring(0,
2269: exportPath.lastIndexOf(File.separator) + 1);
2270: if (ruleBackupExtension != null) {
2271: backupFolderName = backupFolderName
2272: + EXPORT_BACKUP_FOLDER_NAME
2273: + ruleBackupExtension;
2274: } else {
2275: backupFolderName = backupFolderName
2276: + EXPORT_BACKUP_FOLDER_NAME;
2277: }
2278: for (int i = exportBackups; i > 0; i--) {
2279: File staticExportBackupOld = new File(backupFolderName
2280: + new Integer(i).toString());
2281: if (staticExportBackupOld.exists()) {
2282: if ((i + 1) > exportBackups) {
2283: // delete folder if it is the last backup folder
2284: CmsFileUtil
2285: .purgeDirectory(staticExportBackupOld);
2286: } else {
2287: // set backup folder to the next backup folder name
2288: staticExportBackupOld
2289: .renameTo(new File(backupFolderName
2290: + new Integer(i + 1).toString()));
2291: }
2292: }
2293: // old export folder rename to first backup folder
2294: if (i == 1) {
2295: staticExport.renameTo(staticExportBackupOld);
2296: }
2297: }
2298:
2299: // if no backups will be stored the old export folder has to be deleted
2300: if (exportBackups == 0) {
2301: CmsFileUtil.purgeDirectory(staticExport);
2302: }
2303: }
2304: }
2305:
2306: /**
2307: * Creates the parent folder for a exported resource in the RFS.<p>
2308: *
2309: * @param exportPath the path to export the file
2310: * @param rfsName the rfs name of the resource
2311: *
2312: * @throws CmsException if the folder could not be created
2313: */
2314: protected void createExportFolder(String exportPath, String rfsName)
2315: throws CmsException {
2316:
2317: String exportFolderName = CmsFileUtil.normalizePath(exportPath
2318: + CmsResource.getFolderPath(rfsName));
2319: File exportFolder = new File(exportFolderName);
2320: if (!exportFolder.exists()) {
2321: if (!exportFolder.mkdirs()) {
2322: throw new CmsStaticExportException(Messages.get()
2323: .container(Messages.ERR_CREATE_FOLDER_1,
2324: rfsName));
2325:
2326: }
2327: }
2328: }
2329:
2330: /**
2331: * Returns the export data for a requested resource, if null is returned no export is required.<p>
2332: *
2333: * @param rfsName the RFS name of the resource requested
2334: * @param vfsName the VFS name of the resource requested
2335: * @param cms an initialized cms context (should be initialized with the "Guest" user only
2336: *
2337: * @return the export data for the request, if null is returned no export is required
2338: */
2339: protected CmsStaticExportData getExportData(String rfsName,
2340: String vfsName, CmsObject cms) {
2341:
2342: CmsResource resource = null;
2343: String storedSiteRoot = cms.getRequestContext().getSiteRoot();
2344: try {
2345: cms.getRequestContext().setSiteRoot("/");
2346:
2347: // cut export prefix from name
2348: rfsName = rfsName.substring(getRfsPrefixForRfsName(rfsName)
2349: .length());
2350:
2351: String parameters = null;
2352: if (vfsName == null) {
2353: // check if we have the result already in the cache
2354: CmsStaticExportData data = getCachedExportUri(rfsName);
2355: if (data != null) {
2356: vfsName = data.getVfsName();
2357: parameters = data.getParameters();
2358: }
2359: }
2360:
2361: if (vfsName != null) {
2362: // this export uri is already cached
2363: if (CACHEVALUE_404 != vfsName) {
2364: // this uri can be exported
2365: try {
2366: resource = cms.readResource(vfsName);
2367: } catch (CmsException e) {
2368: // the resource has probably been deleted
2369: return null;
2370: }
2371: // valid cache entry, return export data object
2372: return new CmsStaticExportData(vfsName, rfsName,
2373: resource, parameters);
2374: } else {
2375: // this uri can not be exported
2376: return null;
2377: }
2378: } else {
2379: // export uri not in cache, must look up the file in the VFS
2380: vfsName = getVfsNameInternal(cms, rfsName);
2381: if (vfsName != null) {
2382: try {
2383: resource = cms.readResource(vfsName);
2384: } catch (Exception e) {
2385: // ignore, still not found
2386: }
2387: }
2388: if (resource == null) {
2389: // it could be a translated resourcename with parameters, so make a lookup
2390: // in the published resources table
2391: try {
2392: parameters = cms
2393: .readStaticExportPublishedResourceParameters(rfsName);
2394: // there was a match in the db table, so get the StaticExportData
2395: if (CmsStringUtil.isNotEmpty(parameters)) {
2396:
2397: // get the rfs base string without the parameter hashcode
2398: String rfsBaseName = rfsName.substring(0,
2399: rfsName.lastIndexOf('_'));
2400:
2401: // get the vfs base name, which is later used to read the resource in the vfs
2402: String vfsBaseName = getVfsNameInternal(
2403: cms, rfsBaseName);
2404:
2405: // everything is there, so read the resource in the vfs and build the static export data object
2406: if (vfsBaseName != null) {
2407: resource = cms
2408: .readResource(vfsBaseName);
2409: CmsStaticExportData exportData = new CmsStaticExportData(
2410: vfsBaseName, rfsName, resource,
2411: parameters);
2412: cacheExportUri(rfsName, exportData
2413: .getVfsName(), parameters);
2414: return exportData;
2415: }
2416: }
2417: } catch (CmsException e) {
2418: // ignore, resource does not exist
2419: }
2420: // no match found, nothing to export
2421: cacheExportUri(rfsName, CACHEVALUE_404, null);
2422: return null;
2423: } else {
2424: // found a resource to export
2425: cacheExportUri(rfsName, vfsName, null);
2426: return new CmsStaticExportData(vfsName, rfsName,
2427: resource);
2428: }
2429: }
2430: } finally {
2431: cms.getRequestContext().setSiteRoot(storedSiteRoot);
2432: }
2433: }
2434:
2435: /**
2436: * Returns the longest rfs prefix matching a given already translated rfs name.<p>
2437: *
2438: * @param rfsName the rfs name
2439: *
2440: * @return its rfs prefix
2441: *
2442: * @see #getRfsPrefix(String)
2443: */
2444: protected String getRfsPrefixForRfsName(String rfsName) {
2445:
2446: String retVal = "";
2447: // default case
2448: if (rfsName.startsWith(m_rfsPrefix + "/")) {
2449: retVal = m_rfsPrefix;
2450: }
2451: // additional rules
2452: Iterator it = m_rfsRules.iterator();
2453: while (it.hasNext()) {
2454: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
2455: .next();
2456: String rfsPrefix = rule.getRfsPrefix();
2457: if (rfsName.startsWith(rfsPrefix + "/")
2458: && (retVal.length() < rfsPrefix.length())) {
2459: retVal = rfsPrefix;
2460: }
2461: }
2462: return retVal;
2463: }
2464:
2465: /**
2466: * Returns the VFS name from a given RFS name.<p>
2467: *
2468: * The RFS name must not contain the RFS prefix.<p>
2469: *
2470: * @param cms an initialized OpenCms user context
2471: * @param rfsName the name of the RFS resource
2472: *
2473: * @return the name of the VFS resource
2474: */
2475: protected String getVfsNameInternal(CmsObject cms, String rfsName) {
2476:
2477: String vfsName = null;
2478: CmsResource resource;
2479:
2480: boolean match = false;
2481:
2482: try {
2483: resource = cms.readResource(cms.getRequestContext()
2484: .removeSiteRoot(rfsName));
2485: if (resource.isFolder() && !CmsResource.isFolder(rfsName)) {
2486: rfsName += '/';
2487: }
2488: vfsName = rfsName;
2489: match = true;
2490: } catch (Throwable t) {
2491: // resource not found
2492: }
2493:
2494: if (!match) {
2495: // name of export resource could not be resolved by reading the resource directly,
2496: // now try to find a match with the "exportname" folders
2497: Map exportnameFolders = getExportnames();
2498: Iterator i = exportnameFolders.entrySet().iterator();
2499: while (i.hasNext()) {
2500: Map.Entry entry = (Map.Entry) i.next();
2501: String exportName = (String) entry.getKey();
2502: if (rfsName.startsWith(exportName)) {
2503: // prefix match
2504: match = true;
2505: vfsName = "" + entry.getValue()
2506: + rfsName.substring(exportName.length());
2507: try {
2508: resource = cms.readResource(vfsName);
2509: if (resource.isFolder()) {
2510: if (!CmsResource.isFolder(rfsName)) {
2511: rfsName += '/';
2512: }
2513: if (!CmsResource.isFolder(vfsName)) {
2514: vfsName += '/';
2515: }
2516: }
2517: break;
2518: } catch (CmsVfsResourceNotFoundException e) {
2519: // continue with trying out the other exportname to find a match (may be a multiple prefix)
2520: match = false;
2521: continue;
2522: } catch (CmsException e) {
2523: break;
2524: }
2525: }
2526: }
2527: }
2528:
2529: // finally check if its a modified jsp resource
2530: if (!match) {
2531: // first cut of the last extension
2532: int extPos = rfsName.lastIndexOf('.');
2533: if (extPos >= 0) {
2534: String cutName = rfsName.substring(0, extPos);
2535: int pos = cutName.lastIndexOf('.');
2536: if (pos >= 0) {
2537: // now check if remaining String ends with ".jsp"
2538: String extension = cutName.substring(pos)
2539: .toLowerCase();
2540: if (".jsp".equals(extension)) {
2541: return getVfsNameInternal(cms, cutName);
2542: }
2543: }
2544: }
2545: }
2546: if (match) {
2547: return vfsName;
2548: } else {
2549: return null;
2550: }
2551: }
2552:
2553: /**
2554: * Substitutes the ${CONTEXT_NAME} and ${SERVLET_NAME} in a path with the real values.<p>
2555: *
2556: * @param path the path to substitute
2557: * @return path with real context values
2558: */
2559: protected String insertContextStrings(String path) {
2560:
2561: // create a new macro resolver
2562: CmsMacroResolver resolver = CmsMacroResolver.newInstance();
2563:
2564: // add special mappings for macros
2565: resolver.addMacro("CONTEXT_NAME", OpenCms.getSystemInfo()
2566: .getContextPath());
2567: resolver.addMacro("SERVLET_NAME", OpenCms.getSystemInfo()
2568: .getServletPath());
2569:
2570: // resolve the macros
2571: return resolver.resolveMacros(path);
2572: }
2573:
2574: /**
2575: * Returns true if the rfs Name match against any of the defined export urls.<p>
2576: *
2577: * @param rfsName the rfs Name to validate
2578: *
2579: * @return true if the rfs Name match against any of the defined export urls
2580: */
2581: protected boolean isValidRfsName(String rfsName) {
2582:
2583: if (rfsName != null) {
2584: // default case
2585: if (rfsName.startsWith(m_rfsPrefix + "/")) {
2586: return true;
2587: }
2588: // additional rules
2589: Iterator it = m_rfsRules.iterator();
2590: while (it.hasNext()) {
2591: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
2592: .next();
2593: String rfsPrefix = rule.getRfsPrefix() + "/";
2594: if (rfsName.startsWith(rfsPrefix)) {
2595: return true;
2596: }
2597: }
2598: }
2599: return false;
2600: }
2601:
2602: /**
2603: * Checks if a String is a valid URL.<p>
2604: *
2605: * @param inputString The String to check can be <code>null</code>
2606: *
2607: * @return <code>true</code> if the String is not <code>null</code> and a valid URL
2608: */
2609: protected boolean isValidURL(String inputString) {
2610:
2611: boolean isValid = false;
2612: try {
2613: if (inputString != null) {
2614: URL tempURL = new URL(inputString);
2615: isValid = (tempURL.getProtocol() != null);
2616: }
2617: } catch (MalformedURLException mue) {
2618: // ignore because it is not harmful
2619: }
2620: return isValid;
2621: }
2622:
2623: /**
2624: * Returns a normalized export path.<p>
2625: *
2626: * Replacing macros, normalizing the path and taking care of relative paths.<p>
2627: *
2628: * @param exportPath the export path to normalize
2629: *
2630: * @return the normalized export path
2631: */
2632: protected String normalizeExportPath(String exportPath) {
2633:
2634: String result = insertContextStrings(exportPath);
2635: result = OpenCms.getSystemInfo()
2636: .getAbsoluteRfsPathRelativeToWebApplication(result);
2637: if (result.endsWith(File.separator)) {
2638: // ensure export path does NOT end with a File.separator
2639: result = result.substring(0, result.length() - 1);
2640: }
2641: return result;
2642: }
2643:
2644: /**
2645: * Returns a normalized rfs prefix.<p>
2646: *
2647: * Replacing macros and normalizing the path.<p>
2648: *
2649: * @param rfsPrefix the prefix to normalize
2650: *
2651: * @return the normalized rfs prefix
2652: */
2653: protected String normalizeRfsPrefix(String rfsPrefix) {
2654:
2655: String result = insertContextStrings(rfsPrefix);
2656: if (!isValidURL(result)) {
2657: result = CmsFileUtil.normalizePath(result, '/');
2658: }
2659: result = CmsFileUtil.normalizePath(result, '/');
2660: if (CmsResource.isFolder(result)) {
2661: // ensure prefix does NOT end with a folder '/'
2662: result = result.substring(0, result.length() - 1);
2663: }
2664: return result;
2665: }
2666:
2667: /**
2668: * Scrubs all the "export" folders.<p>
2669: *
2670: * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
2671: */
2672: protected void scrubExportFolders(I_CmsReport report) {
2673:
2674: if (report != null) {
2675: report.println(Messages.get().container(
2676: Messages.RPT_DELETING_EXPORT_FOLDERS_BEGIN_0),
2677: I_CmsReport.FORMAT_HEADLINE);
2678: }
2679: synchronized (m_lockScrubExportFolders) {
2680: int count = 0;
2681: Integer size = new Integer(m_rfsRules.size() + 1);
2682: // default case
2683: String exportFolderName = CmsFileUtil
2684: .normalizePath(m_staticExportPath + '/');
2685: try {
2686: File exportFolder = new File(exportFolderName);
2687: // check if export file exists, if so delete it
2688: if (exportFolder.exists() && exportFolder.canWrite()) {
2689: CmsFileUtil.purgeDirectory(exportFolder);
2690: }
2691: count++;
2692: if (report != null) {
2693: report
2694: .println(
2695: Messages
2696: .get()
2697: .container(
2698: Messages.RPT_DELETE_EXPORT_FOLDER_3,
2699: new Integer(count),
2700: size,
2701: exportFolderName),
2702: I_CmsReport.FORMAT_NOTE);
2703: } else {
2704: // write log message
2705: if (LOG.isInfoEnabled()) {
2706: LOG.info(Messages.get().getBundle().key(
2707: Messages.LOG_DEL_MAIN_SE_FOLDER_1,
2708: exportFolderName));
2709: }
2710: }
2711: } catch (Throwable t) {
2712: // ignore, nothing to do about the
2713: if (LOG.isWarnEnabled()) {
2714: LOG.warn(Messages.get().getBundle().key(
2715: Messages.LOG_FOLDER_DELETION_FAILED_1,
2716: exportFolderName), t);
2717: }
2718: }
2719: // iterate over the rules
2720: Iterator it = m_rfsRules.iterator();
2721: while (it.hasNext()) {
2722: CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule) it
2723: .next();
2724: exportFolderName = CmsFileUtil.normalizePath(rule
2725: .getExportPath() + '/');
2726: try {
2727: File exportFolder = new File(exportFolderName);
2728: // check if export file exists, if so delete it
2729: if (exportFolder.exists()
2730: && exportFolder.canWrite()) {
2731: CmsFileUtil.purgeDirectory(exportFolder);
2732: }
2733: count++;
2734: if (report != null) {
2735: report.println(Messages.get().container(
2736: Messages.RPT_DELETE_EXPORT_FOLDER_3,
2737: new Integer(count), size,
2738: exportFolderName),
2739: I_CmsReport.FORMAT_NOTE);
2740: } else {
2741: // write log message
2742: if (LOG.isInfoEnabled()) {
2743: LOG.info(Messages.get().getBundle().key(
2744: Messages.LOG_DEL_MAIN_SE_FOLDER_1,
2745: exportFolderName));
2746: }
2747: }
2748: } catch (Throwable t) {
2749: // ignore, nothing to do about the
2750: if (LOG.isWarnEnabled()) {
2751: LOG.warn(Messages.get().getBundle().key(
2752: Messages.LOG_FOLDER_DELETION_FAILED_1,
2753: exportFolderName), t);
2754: }
2755: }
2756: }
2757: }
2758: if (report != null) {
2759: report.println(Messages.get().container(
2760: Messages.RPT_DELETING_EXPORT_FOLDERS_END_0),
2761: I_CmsReport.FORMAT_HEADLINE);
2762: }
2763: }
2764:
2765: /**
2766: * Set the list of all resources that have the "exportname" property set.<p>
2767: */
2768: protected void setExportnames() {
2769:
2770: if (LOG.isDebugEnabled()) {
2771: LOG.debug(Messages.get().getBundle().key(
2772: Messages.LOG_UPDATE_EXPORTNAME_PROP_START_0));
2773: }
2774:
2775: List resources;
2776: CmsObject cms = null;
2777: try {
2778: cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers()
2779: .getUserExport());
2780: resources = cms
2781: .readResourcesWithProperty(CmsPropertyDefinition.PROPERTY_EXPORTNAME);
2782:
2783: synchronized (m_lockSetExportnames) {
2784: m_exportnameResources = new HashMap(resources.size());
2785: for (int i = 0, n = resources.size(); i < n; i++) {
2786: CmsResource res = (CmsResource) resources.get(i);
2787: try {
2788: String foldername = cms.getSitePath(res);
2789: String exportname = cms
2790: .readPropertyObject(
2791: foldername,
2792: CmsPropertyDefinition.PROPERTY_EXPORTNAME,
2793: false).getValue();
2794: if (exportname != null) {
2795: if (exportname
2796: .charAt(exportname.length() - 1) != '/') {
2797: exportname = exportname + "/";
2798: }
2799: if (exportname.charAt(0) != '/') {
2800: exportname = "/" + exportname;
2801: }
2802: m_exportnameResources.put(exportname,
2803: foldername);
2804: }
2805: } catch (CmsException e) {
2806: // ignore, folder will not be added
2807: }
2808: }
2809: m_exportnameResources = Collections
2810: .unmodifiableMap(m_exportnameResources);
2811: }
2812: } catch (CmsException e) {
2813: // ignore, no resources will be added at all
2814: }
2815: if (LOG.isDebugEnabled()) {
2816: LOG.debug(Messages.get().getBundle().key(
2817: Messages.LOG_UPDATE_EXPORTNAME_PROP_FINISHED_0));
2818: }
2819: }
2820:
2821: /**
2822: * Writes a resource to the given export path with the given rfs name and the given content.<p>
2823: *
2824: * @param req the current request
2825: * @param exportPath the path to export the resource
2826: * @param rfsName the rfs name
2827: * @param resource the resource
2828: * @param content the content
2829: *
2830: * @throws CmsException if something goes wrong
2831: */
2832: protected void writeResource(HttpServletRequest req,
2833: String exportPath, String rfsName, CmsResource resource,
2834: byte[] content) throws CmsException {
2835:
2836: String exportFileName = CmsFileUtil.normalizePath(exportPath
2837: + rfsName);
2838:
2839: // make sure all required parent folder exist
2840: createExportFolder(exportPath, rfsName);
2841: // generate export file instance and output stream
2842: File exportFile = new File(exportFileName);
2843: // write new exported file content
2844: try {
2845: FileOutputStream exportStream = new FileOutputStream(
2846: exportFile);
2847: exportStream.write(content);
2848: exportStream.close();
2849:
2850: // log export success
2851: if (LOG.isInfoEnabled()) {
2852: LOG.info(Messages.get().getBundle().key(
2853: Messages.LOG_STATIC_EXPORTED_2,
2854: resource.getRootPath(), exportFileName));
2855: }
2856:
2857: } catch (Throwable t) {
2858: throw new CmsStaticExportException(Messages.get()
2859: .container(Messages.ERR_OUTPUT_STREAM_1,
2860: exportFileName), t);
2861: }
2862: // update the file with the modification date from the server
2863: if (req != null) {
2864: Long dateLastModified = (Long) req
2865: .getAttribute(CmsRequestUtil.HEADER_OPENCMS_EXPORT);
2866: if ((dateLastModified != null)
2867: && (dateLastModified.longValue() != -1)) {
2868: exportFile.setLastModified((dateLastModified
2869: .longValue() / 1000) * 1000);
2870: if (LOG.isDebugEnabled()) {
2871: LOG
2872: .debug(Messages
2873: .get()
2874: .getBundle()
2875: .key(
2876: Messages.LOG_SET_LAST_MODIFIED_2,
2877: exportFile.getName(),
2878: new Long(
2879: (dateLastModified
2880: .longValue() / 1000) * 1000)));
2881: }
2882: }
2883: } else {
2884: // otherwise take the last modification date form the OpenCms resource
2885: exportFile
2886: .setLastModified((resource.getDateLastModified() / 1000) * 1000);
2887: }
2888: }
2889: }
|