KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > staticexport > CmsStaticExportManager


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/staticexport/CmsStaticExportManager.java,v $
3  * Date : $Date: 2006/07/19 12:38:16 $
4  * Version: $Revision: 1.122 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * For further information about Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.staticexport;
33
34 import org.opencms.file.CmsFile;
35 import org.opencms.file.CmsObject;
36 import org.opencms.file.CmsProperty;
37 import org.opencms.file.CmsPropertyDefinition;
38 import org.opencms.file.CmsResource;
39 import org.opencms.file.CmsVfsResourceNotFoundException;
40 import org.opencms.file.types.CmsResourceTypeJsp;
41 import org.opencms.i18n.CmsAcceptLanguageHeaderParser;
42 import org.opencms.i18n.CmsI18nInfo;
43 import org.opencms.i18n.CmsLocaleManager;
44 import org.opencms.loader.I_CmsResourceLoader;
45 import org.opencms.main.CmsContextInfo;
46 import org.opencms.main.CmsEvent;
47 import org.opencms.main.CmsException;
48 import org.opencms.main.CmsIllegalArgumentException;
49 import org.opencms.main.CmsLog;
50 import org.opencms.main.I_CmsEventListener;
51 import org.opencms.main.OpenCms;
52 import org.opencms.report.CmsLogReport;
53 import org.opencms.report.I_CmsReport;
54 import org.opencms.security.CmsSecurityException;
55 import org.opencms.site.CmsSiteManager;
56 import org.opencms.util.CmsFileUtil;
57 import org.opencms.util.CmsMacroResolver;
58 import org.opencms.util.CmsRequestUtil;
59 import org.opencms.util.CmsStringUtil;
60 import org.opencms.util.CmsUUID;
61 import org.opencms.workplace.CmsWorkplace;
62
63 import java.io.File JavaDoc;
64 import java.io.FileOutputStream JavaDoc;
65 import java.io.IOException JavaDoc;
66 import java.util.ArrayList JavaDoc;
67 import java.util.Collections JavaDoc;
68 import java.util.HashMap JavaDoc;
69 import java.util.Iterator JavaDoc;
70 import java.util.List JavaDoc;
71 import java.util.Map JavaDoc;
72
73 import javax.servlet.ServletException JavaDoc;
74 import javax.servlet.http.HttpServletRequest JavaDoc;
75 import javax.servlet.http.HttpServletResponse JavaDoc;
76
77 import org.apache.commons.collections.map.LRUMap;
78 import org.apache.commons.logging.Log;
79
80 /**
81  * Provides the functionaility to export resources from the OpenCms VFS
82  * to the file system.<p>
83  *
84  * @author Alexander Kandzior
85  * @author Michael Moossen
86  *
87  * @version $Revision: 1.122 $
88  *
89  * @since 6.0.0
90  */

91 public class CmsStaticExportManager implements I_CmsEventListener {
92
93     /** Marker for error message attribute. */
94     public static final String JavaDoc EXPORT_ATTRIBUTE_ERROR_MESSAGE = "javax.servlet.error.message";
95
96     /** Marker for error request uri attribute. */
97     public static final String JavaDoc EXPORT_ATTRIBUTE_ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
98
99     /** Marker for error servlet name attribute. */
100     public static final String JavaDoc EXPORT_ATTRIBUTE_ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
101
102     /** Marker for error status code attribute. */
103     public static final String JavaDoc EXPORT_ATTRIBUTE_ERROR_STATUS_CODE = "javax.servlet.error.status_code";
104
105     /** Name for the folder default index file. */
106     public static final String JavaDoc EXPORT_DEFAULT_FILE = "index_export.html";
107
108     /** Flag value for links without paramerters. */
109     public static final int EXPORT_LINK_WITH_PARAMETER = 2;
110
111     /** Flag value for links without paramerters. */
112     public static final int EXPORT_LINK_WITHOUT_PARAMETER = 1;
113
114     /** Marker for externally redirected 404 uri's. */
115     public static final String JavaDoc EXPORT_MARKER = "exporturi";
116
117     /** Time given (in seconds) to the static export handler to finish a publish task. */
118     public static final int HANDLER_FINISH_TIME = 60;
119
120     /** Cache value to indicate a true 404 error. */
121     private static final String JavaDoc CACHEVALUE_404 = "?404";
122
123     /** The log object for this class. */
124     private static final Log LOG = CmsLog.getLog(CmsStaticExportManager.class);
125
126     /** Matcher for selecting those resources which should be part of the staic export. */
127     private static CmsExportFolderMatcher m_exportFolderMatcher;
128
129     /** HTTP header Accept-Charset. */
130     private String JavaDoc m_acceptCharsetHeader;
131
132     /** HTTP header Accept-Language. */
133     private String JavaDoc m_acceptLanguageHeader;
134
135     /** Cache for the export links. */
136     private Map JavaDoc m_cacheExportLinks;
137
138     /** Cache for the export uris. */
139     private Map JavaDoc m_cacheExportUris;
140
141     /** Cache for the online links. */
142     private Map JavaDoc m_cacheOnlineLinks;
143
144     /** Cache for the secure links. */
145     private Map JavaDoc m_cacheSecureLinks;
146
147     /** OpenCms default charset header. */
148     private String JavaDoc m_defaultAcceptCharsetHeader;
149
150     /** OpenCms default locale header. */
151     private String JavaDoc m_defaultAcceptLanguageHeader;
152
153     /** List of export resources which should be part of the static export. */
154     private List JavaDoc m_exportFolders;
155
156     /** The additional http headers for the static export. */
157     private List JavaDoc m_exportHeaders;
158
159     /** List of all resources that have the "exportname" property set. */
160     private Map JavaDoc m_exportnameResources;
161
162     /** Indicates if <code>true</code> is the default value for the property "export". */
163     private boolean m_exportPropertyDefault;
164
165     /** Indicates if links in the static export should be relative. */
166     private boolean m_exportRelativeLinks;
167
168     /** List of export rules. */
169     private List JavaDoc m_exportRules;
170
171     /** List of export suffixes where the "export" property default is always <code>"true"</code>. */
172     private List JavaDoc m_exportSuffixes;
173
174     /** Temporary variable for reading the xml config file. */
175     private CmsStaticExportExportRule m_exportTmpRule;
176
177     /** Export url to send internal requests to. */
178     private String JavaDoc m_exportUrl;
179
180     /** Export url with unstubstituted context values. */
181     private String JavaDoc m_exportUrlConfigured;
182
183     /** Export url to send internal requests to without http://servername. */
184     private String JavaDoc m_exportUrlPrefix;
185
186     /** Handler class for static export. */
187     private I_CmsStaticExportHandler m_handler;
188
189     /** Lock object for write access to the {@link #cmsEvent(CmsEvent)} method. */
190     private Object JavaDoc m_lockCmsEvent;
191
192     /** Lock object for export folder deletion in {@link #scrubExportFolders(I_CmsReport)}. */
193     private Object JavaDoc m_lockScrubExportFolders;
194
195     /** Lock object for write access to the {@link #m_exportnameResources} map in {@link #setExportnames()}. */
196     private Object JavaDoc m_lockSetExportnames;
197
198     /** Indicates if the quick static export for plain resources is enabled. */
199     private boolean m_quickPlainExport;
200
201     /** Remote addr. */
202     private String JavaDoc m_remoteAddr;
203
204     /** Prefix to use for exported files. */
205     private String JavaDoc m_rfsPrefix;
206
207     /** Prefix to use for exported files with unstubstituted context values. */
208     private String JavaDoc m_rfsPrefixConfigured;
209
210     /** List of configured rfs rules. */
211     private List JavaDoc m_rfsRules;
212
213     /** Temporary variable for reading the xml config file. */
214     private CmsStaticExportRfsRule m_rfsTmpRule;
215
216     /** Indicates if the static export is enabled or diabled. */
217     private boolean m_staticExportEnabled;
218
219     /** The path to where the static export will be written. */
220     private String JavaDoc m_staticExportPath;
221
222     /** The path to where the static export will be written without the complete rfs path. */
223     private String JavaDoc m_staticExportPathConfigured;
224
225     /** Vfs Name of a resource used to do a "static export required" test. */
226     private String JavaDoc m_testResource;
227
228     /** Prefix to use for internal OpenCms files. */
229     private String JavaDoc m_vfsPrefix;
230
231     /** Prefix to use for internal OpenCms files with unstubstituted context values. */
232     private String JavaDoc m_vfsPrefixConfigured;
233
234     /**
235      * Creates a new static export property object.<p>
236      *
237      */

238     public CmsStaticExportManager() {
239
240         m_lockCmsEvent = new Object JavaDoc();
241         m_lockScrubExportFolders = new Object JavaDoc();
242         m_lockSetExportnames = new Object JavaDoc();
243         m_exportSuffixes = new ArrayList JavaDoc();
244         m_exportFolders = new ArrayList JavaDoc();
245         m_exportHeaders = new ArrayList JavaDoc();
246         m_rfsRules = new ArrayList JavaDoc();
247         m_exportRules = new ArrayList JavaDoc();
248         m_exportTmpRule = new CmsStaticExportExportRule("", "");
249         m_rfsTmpRule = new CmsStaticExportRfsRule("", "", "", "", "", null);
250     }
251
252     /**
253      * Adds a new export rule to the configuration.<p>
254      *
255      * @param name the name of the rule
256      * @param description the description for the rule
257      */

258     public void addExportRule(String JavaDoc name, String JavaDoc description) {
259
260         m_exportRules.add(new CmsStaticExportExportRule(
261             name,
262             description,
263             m_exportTmpRule.getModifiedResources(),
264             m_exportTmpRule.getExportResourcePatterns()));
265         m_exportTmpRule = new CmsStaticExportExportRule("", "");
266     }
267
268     /**
269      * Adds a regex to the latest export rule.<p>
270      *
271      * @param regex the regex to add
272      */

273     public void addExportRuleRegex(String JavaDoc regex) {
274
275         m_exportTmpRule.addModifiedResource(regex);
276     }
277
278     /**
279      * Adds a export uri to the latest export rule.<p>
280      *
281      * @param exportUri the export uri to add
282      */

283     public void addExportRuleUri(String JavaDoc exportUri) {
284
285         m_exportTmpRule.addExportResourcePattern(exportUri);
286     }
287
288     /**
289      * Adds a new rfs rule to the configuration.<p>
290      *
291      * @param name the name of the rule
292      * @param description the description for the rule
293      * @param source the source regex
294      * @param rfsPrefix the url prefix
295      * @param exportPath the rfs export path
296      * @param useRelativeLinks the relative links value
297      */

298     public void addRfsRule(
299         String JavaDoc name,
300         String JavaDoc description,
301         String JavaDoc source,
302         String JavaDoc rfsPrefix,
303         String JavaDoc exportPath,
304         String JavaDoc useRelativeLinks) {
305
306         Boolean JavaDoc relativeLinks = (useRelativeLinks == null ? null : Boolean.valueOf(useRelativeLinks));
307         m_rfsRules.add(new CmsStaticExportRfsRule(
308             name,
309             description,
310             source,
311             rfsPrefix,
312             exportPath,
313             relativeLinks,
314             m_rfsTmpRule.getRelatedSystemResources()));
315         m_rfsTmpRule = new CmsStaticExportRfsRule("", "", "", "", "", null);
316     }
317
318     /**
319      * Adds a regex of related system resources to the latest rfs-rule.<p>
320      *
321      * @param regex the regex to add
322      */

323     public void addRfsRuleSystemRes(String JavaDoc regex) {
324
325         m_rfsTmpRule.addRelatedSystemRes(regex);
326     }
327
328     /**
329      * Caches a calculated export uri.<p>
330      *
331      * @param rfsName the name of the resource in the "real" file system
332      * @param vfsName the name of the resource in the VFS
333      * @param parameters the optional parameters for the generation of the resource
334      */

335     public void cacheExportUri(String JavaDoc rfsName, String JavaDoc vfsName, String JavaDoc parameters) {
336
337         m_cacheExportUris.put(rfsName, new CmsStaticExportData(vfsName, parameters));
338     }
339
340     /**
341      * Caches a calculated online link.<p>
342      *
343      * @param linkName the link
344      * @param vfsName the name of the VFS resource
345      */

346     public void cacheOnlineLink(Object JavaDoc linkName, Object JavaDoc vfsName) {
347
348         m_cacheOnlineLinks.put(linkName, vfsName);
349     }
350
351     /**
352      * Implements the CmsEvent interface,
353      * the static export properties uses the events to clear
354      * the list of cached keys in case a project is published.<p>
355      *
356      * @param event CmsEvent that has occurred
357      */

358     public void cmsEvent(CmsEvent event) {
359
360         switch (event.getType()) {
361             case I_CmsEventListener.EVENT_UPDATE_EXPORTS:
362                 scrubExportFolders(null);
363                 clearCaches(event);
364                 break;
365             case I_CmsEventListener.EVENT_PUBLISH_PROJECT:
366                 // event data contains a list of the published resources
367
CmsUUID publishHistoryId = new CmsUUID((String JavaDoc)event.getData().get(I_CmsEventListener.KEY_PUBLISHID));
368                 if (LOG.isDebugEnabled()) {
369                     LOG.debug(Messages.get().getBundle().key(Messages.LOG_EVENT_PUBLISH_PROJECT_1, publishHistoryId));
370                 }
371                 I_CmsReport report = (I_CmsReport)event.getData().get(I_CmsEventListener.KEY_REPORT);
372                 if (report == null) {
373                     report = new CmsLogReport(CmsLocaleManager.getDefaultLocale(), getClass());
374                 }
375                 synchronized (m_lockCmsEvent) {
376                     getHandler().performEventPublishProject(publishHistoryId, report);
377                 }
378                 clearCaches(event);
379
380                 if (LOG.isDebugEnabled()) {
381                     LOG.debug(Messages.get().getBundle().key(
382                         Messages.LOG_EVENT_PUBLISH_PROJECT_FINISHED_1,
383                         publishHistoryId));
384                 }
385
386                 break;
387             case I_CmsEventListener.EVENT_CLEAR_CACHES:
388                 clearCaches(event);
389                 break;
390             default:
391                 // no operation
392
}
393     }
394
395     /**
396      * Exports the requested uri and at the same time writes the uri to the response output stream
397      * if required.<p>
398      *
399      * @param req the current request
400      * @param res the current response
401      * @param cms an initialized cms context (should be initialized with the "Guest" user only)
402      * @param data the static export data set
403      * @return status code of the export operation, status codes are the same as http status codes (200,303,304)
404      * @throws CmsException in case of errors accessing the VFS
405      * @throws ServletException in case of errors accessing the servlet
406      * @throws IOException in case of erros writing to the export output stream
407      * @throws CmsStaticExportException if static export is disabled
408      */

409     public int export(HttpServletRequest JavaDoc req, HttpServletResponse JavaDoc res, CmsObject cms, CmsStaticExportData data)
410     throws CmsException, IOException JavaDoc, ServletException JavaDoc, CmsStaticExportException {
411
412         String JavaDoc vfsName = data.getVfsName();
413         String JavaDoc rfsName = data.getRfsName();
414         CmsResource resource = data.getResource();
415
416         // cut the site root from the vfsName and switch to the correct site
417
String JavaDoc siteRoot = CmsSiteManager.getSiteRoot(vfsName);
418
419         CmsI18nInfo i18nInfo = OpenCms.getLocaleManager().getI18nInfo(
420             req,
421             cms.getRequestContext().currentUser(),
422             cms.getRequestContext().currentProject(),
423             vfsName);
424
425         String JavaDoc remoteAddr = m_remoteAddr;
426         if (remoteAddr == null) {
427             remoteAddr = CmsContextInfo.LOCALHOST;
428         }
429         CmsContextInfo contextInfo = new CmsContextInfo(
430             cms.getRequestContext().currentUser(),
431             cms.getRequestContext().currentProject(),
432             vfsName,
433             "/",
434             i18nInfo.getLocale(),
435             i18nInfo.getEncoding(),
436             remoteAddr);
437
438         cms = OpenCms.initCmsObject(null, contextInfo);
439
440         if (siteRoot != null) {
441             vfsName = vfsName.substring(siteRoot.length());
442         } else {
443             siteRoot = "/";
444         }
445
446         if (LOG.isDebugEnabled()) {
447             LOG.debug(Messages.get().getBundle().key(Messages.LOG_STATIC_EXPORT_SITE_ROOT_2, siteRoot, vfsName));
448         }
449         cms.getRequestContext().setSiteRoot(siteRoot);
450
451         String JavaDoc oldUri = null;
452
453         // this flag signals if the export method is used for "on demand" or "after publish".
454
// if no request and result stream are available, it was called during "export on publish"
455
boolean exportOnDemand = ((req != null) && (res != null));
456
457         CmsStaticExportResponseWrapper wrapRes = null;
458         if (res != null) {
459             wrapRes = new CmsStaticExportResponseWrapper(res);
460         }
461         if (LOG.isDebugEnabled()) {
462             LOG.debug(Messages.get().getBundle().key(Messages.LOG_SE_RESOURCE_START_1, data));
463         }
464
465         CmsFile file;
466         // read vfs resource
467
if (resource.isFile()) {
468             file = cms.readFile(vfsName);
469         } else {
470             file = CmsFile.upgrade(OpenCms.initResource(cms, vfsName, req, wrapRes), cms);
471             if (cms.existsResource(vfsName + file.getName())) {
472                 vfsName = vfsName + file.getName();
473             }
474             rfsName += EXPORT_DEFAULT_FILE;
475         }
476
477         // check loader id for resource
478
I_CmsResourceLoader loader = OpenCms.getResourceManager().getLoader(file);
479         if ((loader == null) || (!loader.isStaticExportEnabled())) {
480             Object JavaDoc[] arguments = new Object JavaDoc[] {vfsName, new Integer JavaDoc(file.getTypeId())};
481             throw new CmsStaticExportException(Messages.get().container(Messages.ERR_EXPORT_NOT_SUPPORTED_2, arguments));
482         }
483
484         // ensure we have exactly the same setup as if called "the usual way"
485
// we only have to do this in case of the static export on demand
486
if (exportOnDemand) {
487             String JavaDoc mimetype = OpenCms.getResourceManager().getMimeType(
488                 file.getName(),
489                 cms.getRequestContext().getEncoding());
490             if (wrapRes != null) {
491                 wrapRes.setContentType(mimetype);
492             }
493             oldUri = cms.getRequestContext().getUri();
494             cms.getRequestContext().setUri(vfsName);
495         }
496
497         // do the export
498
byte[] result = loader.export(cms, file, req, wrapRes);
499
500         // release unused resources
501
file = null;
502
503         int status = -1;
504         if (result != null) {
505             // normal case
506
String JavaDoc exportPath = getExportPath(siteRoot + vfsName);
507             // only export those resource where the export property is set
508
if (isExportLink(cms, vfsName)) {
509                 writeResource(req, exportPath, rfsName, vfsName, resource, result);
510                 // system folder case
511
if (vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
512                     // iterate over all rules
513
Iterator JavaDoc it = getRfsRules().iterator();
514                     while (it.hasNext()) {
515                         CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
516                         if (rule.match(vfsName)) {
517                             writeResource(req, rule.getExportPath(), rfsName, vfsName, resource, result);
518                         }
519                     }
520                 }
521                 // get the wrapper status that was set
522
status = (wrapRes != null) ? wrapRes.getStatus() : -1;
523                 if (status < 0) {
524                     // the status was not set, assume everything is o.k.
525
status = HttpServletResponse.SC_OK;
526                 }
527             } else {
528                 // the resource was not used for export, so return HttpServletResponse.SC_SEE_OTHER
529
// as a signal for not exported resource
530
status = HttpServletResponse.SC_SEE_OTHER;
531             }
532         } else {
533             // the resource was not written because it was not modified.
534
// set the status to not modified
535
status = HttpServletResponse.SC_NOT_MODIFIED;
536         }
537
538         // restore context
539
// we only have to do this in case of the static export on demand
540
if (exportOnDemand) {
541             cms.getRequestContext().setUri(oldUri);
542         }
543
544         return status;
545     }
546
547     /**
548      * Starts a complete static export of all resources.<p>
549      *
550      * @param purgeFirst flag to delete all resources in the export folder of the rfs
551      * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
552      *
553      * @throws CmsException in case of errors accessing the VFS
554      * @throws IOException in case of erros writing to the export output stream
555      * @throws ServletException in case of errors accessing the servlet
556      */

557     public synchronized void exportFullStaticRender(boolean purgeFirst, I_CmsReport report)
558     throws CmsException, IOException JavaDoc, ServletException JavaDoc {
559
560         // delete all old exports if the purgeFirst flag is set
561
if (purgeFirst) {
562
563             Map JavaDoc eventData = new HashMap JavaDoc();
564             eventData.put(I_CmsEventListener.KEY_REPORT, report);
565             CmsEvent clearCacheEvent = new CmsEvent(I_CmsEventListener.EVENT_CLEAR_CACHES, eventData);
566             OpenCms.fireCmsEvent(clearCacheEvent);
567
568             scrubExportFolders(report);
569             CmsObject cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
570             cms.deleteAllStaticExportPublishedResources(EXPORT_LINK_WITHOUT_PARAMETER);
571             cms.deleteAllStaticExportPublishedResources(EXPORT_LINK_WITH_PARAMETER);
572         }
573
574         // do the export
575
CmsAfterPublishStaticExportHandler handler = new CmsAfterPublishStaticExportHandler();
576         // export everything
577
handler.doExportAfterPublish(null, report);
578     }
579
580     /**
581      * Returns the accept-charset header used for internal requests.<p>
582      *
583      * @return the accept-charset header
584      */

585     public String JavaDoc getAcceptCharsetHeader() {
586
587         return m_acceptCharsetHeader;
588     }
589
590     /**
591      * Returns the accept-language header used for internal requests.<p>
592      *
593      * @return the accept-language header
594      */

595     public String JavaDoc getAcceptLanguageHeader() {
596
597         return m_acceptLanguageHeader;
598     }
599
600     /**
601      * Returns a cached export data for the given rfs name.<p>
602      *
603      * Please note that this export data contains only the vfs name and the parameters,
604      * not the resource itself. It can therefore not directly be used as a return
605      * value for the static export.<p>
606      *
607      * @param rfsName the name of the ref resource to get the cached vfs resource name for
608      * @return a cached vfs resource name for the given rfs name, or null
609      */

610     public CmsStaticExportData getCachedExportUri(String JavaDoc rfsName) {
611
612         return (CmsStaticExportData)m_cacheExportUris.get(rfsName);
613     }
614
615     /**
616      * Returns a cached link for the given vfs name.<p>
617      *
618      * @param vfsName the name of the vfs resource to get the cached link for
619      * @return a cached link for the given vfs name, or null
620      */

621     public String JavaDoc getCachedOnlineLink(String JavaDoc vfsName) {
622
623         return (String JavaDoc)m_cacheOnlineLinks.get(vfsName);
624     }
625
626     /**
627      * Returns the key for the online, export and secure cache.<p>
628      *
629      * @param siteRoot the site root of the resource
630      * @param uri the URI of the resource
631      * @return a key for the cache
632      */

633     public String JavaDoc getCacheKey(String JavaDoc siteRoot, String JavaDoc uri) {
634
635         return new StringBuffer JavaDoc(siteRoot).append(uri).toString();
636     }
637
638     /**
639      * Gets the default property value as a string representation.<p>
640      *
641      * @return <code>"true"</code> or <code>"false"</code>
642      */

643     public String JavaDoc getDefault() {
644
645         return String.valueOf(m_exportPropertyDefault);
646     }
647
648     /**
649      * Returns the current default charset header.<p>
650      *
651      * @return the current default charset header
652      */

653     public String JavaDoc getDefaultAcceptCharsetHeader() {
654
655         return m_defaultAcceptCharsetHeader;
656     }
657
658     /**
659      * Returns the current default locale header.<p>
660      *
661      * @return the current default locale header
662      */

663     public String JavaDoc getDefaultAcceptLanguageHeader() {
664
665         return m_defaultAcceptLanguageHeader;
666     }
667
668     /**
669      * Returns the default prefix for exported links in the "real" file system.<p>
670      *
671      * @return the default prefix for exported links in the "real" file system
672      */

673     public String JavaDoc getDefaultRfsPrefix() {
674
675         return m_rfsPrefix;
676     }
677
678     /**
679      * Returns the export data for the request, if null is returned no export is required.<p>
680      *
681      * @param request the request to check for export data
682      * @param cms an initialized cms context (should be initialized with the "Guest" user only
683      * @return the export data for the request, if null is returned no export is required
684      */

685     public CmsStaticExportData getExportData(HttpServletRequest JavaDoc request, CmsObject cms) {
686
687         if (!isStaticExportEnabled()) {
688             // export is disabled
689
return null;
690         }
691
692         // build the rfs name for the export "on demand"
693
String JavaDoc rfsName = request.getParameter(EXPORT_MARKER);
694         if ((rfsName == null)) {
695             rfsName = (String JavaDoc)request.getAttribute(EXPORT_ATTRIBUTE_ERROR_REQUEST_URI);
696         }
697
698         if (request.getHeader(CmsRequestUtil.HEADER_OPENCMS_EXPORT) != null) {
699             // this is a request created by the static export and directly send to 404 handler
700
// so remove the leading handler identification
701
int prefix = rfsName.startsWith(getExportUrlPrefix()) ? getExportUrlPrefix().length() : 0;
702             if (prefix > 0) {
703                 rfsName = rfsName.substring(prefix);
704             } else {
705                 return null;
706             }
707         }
708
709         if (!isValidRfsName(rfsName)) {
710             // this is not an export request, no further processing is required
711
return null;
712         }
713
714         return getExportData(rfsName, null, cms);
715     }
716
717     /**
718      * Returns the export data for a requested resource, if null is returned no export is required.<p>
719      *
720      * @param vfsName the VFS name of the resource requested
721      * @param cms an initialized cms context (should be initialized with the "Guest" user only
722      * @return the export data for the request, if null is returned no export is required
723      */

724     public CmsStaticExportData getExportData(String JavaDoc vfsName, CmsObject cms) {
725
726         return getExportData(getRfsName(cms, vfsName), vfsName, cms);
727     }
728
729     /**
730      * Gets the export enbled value as a string representation.<p>
731      *
732      * @return <code>"true"</code> or <code>"false"</code>
733      */

734     public String JavaDoc getExportEnabled() {
735
736         return String.valueOf(m_staticExportEnabled);
737     }
738
739     /**
740      * Returns the current folder matcher.<p>
741      *
742      * @return the current folder matcher
743      */

744     public CmsExportFolderMatcher getExportFolderMatcher() {
745
746         return m_exportFolderMatcher;
747     }
748
749     /**
750      * Returns list of resouces patterns which are part of the export.<p>
751      *
752      * @return the of resouces patterns which are part of the export.
753      */

754     public List JavaDoc getExportFolderPatterns() {
755
756         return Collections.unmodifiableList(m_exportFolders);
757     }
758
759     /**
760      * Returns specific http headers for the static export.<p>
761      *
762      * If the header <code>Cache-Control</code> is set, OpenCms will not use its default headers.<p>
763      *
764      * @return the list of http export headers
765      */

766     public List JavaDoc getExportHeaders() {
767
768         return Collections.unmodifiableList(m_exportHeaders);
769     }
770
771     /**
772      * Returns the list of all resources that have the "exportname" property set.<p>
773      *
774      * @return the list of all resources that have the "exportname" property set
775      */

776     public Map JavaDoc getExportnames() {
777
778         return m_exportnameResources;
779     }
780
781     /**
782      * Returns the export path for the static export, that is the folder where the
783      * static exported resources will be written to.<p>
784      *
785      * The returned value will be a direcory like prefix. The value is configured
786      * in the <code>opencms-importexport.xml</code> configuration file. An optimization
787      * of the configured value will be performed, where all relative path infprmation is resolved
788      * (for example <code>/export/../static</code> will be resolved to <code>/export</code>.
789      * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
790      * (for example <code>/export/</code> becomes <code>/export</code>.<p>
791      *
792      * This is resource name based, and based on the rfs-rules defined in the
793      * <code>opencms-importexport.xml</code> configuration file.
794      *
795      * @param vfsName the name of the resource to export
796      *
797      * @return the export path for the static export, that is the folder where the
798      *
799      * @see #getRfsPrefix(String)
800      * @see #getVfsPrefix()
801      */

802     public String JavaDoc getExportPath(String JavaDoc vfsName) {
803
804         if (vfsName != null) {
805             Iterator JavaDoc it = m_rfsRules.iterator();
806             while (it.hasNext()) {
807                 CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
808                 if (rule.getSource().matcher(vfsName).matches()) {
809                     return rule.getExportPath();
810                 }
811             }
812         }
813         return m_staticExportPath;
814     }
815
816     /**
817      * Returns the original configured export path for the static export without the complete rfs path, to be used
818      * when re-writing the configuration.<p>
819      *
820      * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
821      * This method should <b>not</b> be used otherwise. Use <code>{@link #getExportPath(String)}</code>
822      * to obtain the export path to use when exporting.<p>
823      *
824      * @return the original configured export path for the static export without the complete rfs path
825      */

826     public String JavaDoc getExportPathForConfiguration() {
827
828         return m_staticExportPathConfigured;
829     }
830
831     /**
832      * Returns true if the default value for the resource property "export" is true.<p>
833      *
834      * @return true if the default value for the resource property "export" is true
835      */

836     public boolean getExportPropertyDefault() {
837
838         return m_exportPropertyDefault;
839     }
840
841     /**
842      * Returns the export Rules.<p>
843      *
844      * @return the export Rules
845      */

846     public List JavaDoc getExportRules() {
847
848         return Collections.unmodifiableList(m_exportRules);
849     }
850
851     /**
852      * Gets the list of resouce suffixes which will be exported by default.<p>
853      *
854      * @return list of resouce suffixe
855      */

856     public List JavaDoc getExportSuffixes() {
857
858         return m_exportSuffixes;
859     }
860
861     /**
862      * Returns the export URL used for internal requests for exporting resources that require a
863      * request / response (like JSP).<p>
864      *
865      * @return the export URL used for internal requests for exporting resources like JSP
866      */

867     public String JavaDoc getExportUrl() {
868
869         return m_exportUrl;
870     }
871
872     /**
873      * Returns the export URL used for internal requests with unsubstituted context values, to be used
874      * when re-writing the configuration.<p>
875      *
876      * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
877      * This method should <b>not</b> be used otherwise. Use <code>{@link #getExportUrl()}</code>
878      * to obtain the export path to use when exporting.<p>
879      *
880      * @return the export URL used for internal requests with unsubstituted context values
881      */

882     public String JavaDoc getExportUrlForConfiguration() {
883
884         return m_exportUrlConfigured;
885     }
886
887     /**
888      * Returns the export URL used for internal requests for exporting resources that require a
889      * request / response (like JSP) without http://servername.<p>
890      *
891      * @return the export URL used for internal requests for exporting resources like JSP without http://servername
892      */

893     public String JavaDoc getExportUrlPrefix() {
894
895         return m_exportUrlPrefix;
896     }
897
898     /**
899      * Returns the static export handler class.<p>
900      *
901      * If not set, a new <code>{@link CmsAfterPublishStaticExportHandler}</code> is created and returned.<p>
902      *
903      * @return the static export handler class.
904      */

905     public I_CmsStaticExportHandler getHandler() {
906
907         if (m_handler == null) {
908             setHandler(CmsOnDemandStaticExportHandler.class.getName());
909         }
910         return m_handler;
911     }
912
913     /**
914      * Gets the plain export optimization value as a string representation.<p>
915      *
916      * @return <code>"true"</code> or <code>"false"</code>
917      */

918     public String JavaDoc getPlainExportOptimization() {
919
920         return String.valueOf(m_quickPlainExport);
921     }
922
923     /**
924      * Returns true if the quick plain export is enabled.<p>
925      *
926      * @return true if the quick plain export is enabled
927      */

928     public boolean getQuickPlainExport() {
929
930         return m_quickPlainExport;
931     }
932
933     /**
934      * Gets the relative links value as a string representation.<p>
935      *
936      * @return <code>"true"</code> or <code>"false"</code>
937      */

938     public String JavaDoc getRelativeLinks() {
939
940         return String.valueOf(m_exportRelativeLinks);
941     }
942
943     /**
944      * Returns the remote addr used for internal requests.<p>
945      *
946      * @return the remote addr
947      */

948     public String JavaDoc getRemoteAddr() {
949
950         return m_remoteAddr;
951     }
952
953     /**
954      * Returns the remote address.<p>
955      *
956      * @return the remote address
957      */

958     public String JavaDoc getRemoteAddress() {
959
960         return m_remoteAddr;
961     }
962
963     /**
964      * Returns the static export rfs name for a given vfs resoure.<p>
965      *
966      * @param cms an initialized cms context
967      * @param vfsName the name of the vfs resource
968      *
969      * @return the static export rfs name for a give vfs resoure
970      *
971      * @see #getVfsName(CmsObject, String)
972      * @see #getRfsName(CmsObject, String, String)
973      */

974     public String JavaDoc getRfsName(CmsObject cms, String JavaDoc vfsName) {
975
976         return getRfsName(cms, vfsName, null);
977     }
978
979     /**
980      * Returns the static export rfs name for a given vfs resoure where the link to the
981      * resource includes request parameters.<p>
982      *
983      * @param cms an initialized cms context
984      * @param vfsName the name of the vfs resource
985      * @param parameters the parameters of the link pointing to the resource
986      *
987      * @return the static export rfs name for a give vfs resoure
988      */

989     public String JavaDoc getRfsName(CmsObject cms, String JavaDoc vfsName, String JavaDoc parameters) {
990
991         String JavaDoc rfsName = vfsName;
992         try {
993             // check if the resource folder (or a parent folder) has the "exportname" property set
994
CmsProperty exportNameProperty = cms.readPropertyObject(
995                 CmsResource.getFolderPath(rfsName),
996                 CmsPropertyDefinition.PROPERTY_EXPORTNAME,
997                 true);
998
999             if (exportNameProperty.isNullProperty()) {
1000                // if "exportname" is not set we must add the site root
1001
rfsName = cms.getRequestContext().addSiteRoot(rfsName);
1002            } else {
1003                // "exportname" property is set
1004
String JavaDoc exportname = exportNameProperty.getValue();
1005                if (exportname.charAt(0) != '/') {
1006                    exportname = '/' + exportname;
1007                }
1008                if (exportname.charAt(exportname.length() - 1) != '/') {
1009                    exportname = exportname + '/';
1010                }
1011                String JavaDoc value = null;
1012                boolean cont;
1013                String JavaDoc resourceName = rfsName;
1014                do {
1015                    // find out where the export name was set, to replace these parent folders in the RFS name
1016
try {
1017                        CmsProperty prop = cms.readPropertyObject(
1018                            resourceName,
1019                            CmsPropertyDefinition.PROPERTY_EXPORTNAME,
1020                            false);
1021                        if (prop.isIdentical(exportNameProperty)) {
1022                            // look for the right position in path
1023
value = prop.getValue();
1024                        }
1025                        cont = (value == null) && (resourceName.length() > 1);
1026                    } catch (CmsVfsResourceNotFoundException e) {
1027                        // this is for publishing deleted resources
1028
cont = (resourceName.length() > 1);
1029                    } catch (CmsSecurityException se) {
1030                        // a security exception (probably no read permission) we return the current result
1031
cont = false;
1032                    }
1033                    if (cont) {
1034                        resourceName = CmsResource.getParentFolder(resourceName);
1035                    }
1036                } while (cont);
1037                rfsName = exportname + rfsName.substring(resourceName.length());
1038            }
1039
1040            String JavaDoc extension = CmsFileUtil.getFileExtension(rfsName);
1041            // check if the VFS resouce is a JSP page with a ".jsp" ending
1042
// in this case the rfs name suffix must be build with special care,
1043
// usually it must be set to ".html"
1044
boolean isJsp = extension.equals(".jsp");
1045            if (isJsp) {
1046                CmsResource res = cms.readResource(vfsName);
1047                isJsp = res.getTypeId() == CmsResourceTypeJsp.getStaticTypeId();
1048                // if the resource is a plain resouce then no change in suffix is required
1049
if (isJsp) {
1050                    String JavaDoc suffix = cms.readPropertyObject(vfsName, CmsPropertyDefinition.PROPERTY_EXPORTSUFFIX, true).getValue(
1051                        ".html");
1052                    if (!extension.equals(suffix.toLowerCase())) {
1053                        rfsName += suffix;
1054                        extension = suffix;
1055                    }
1056                }
1057            }
1058            if (parameters != null) {
1059                // build the RFS name for the link with parameters
1060
rfsName = CmsFileUtil.getRfsPath(rfsName, extension, parameters);
1061                // we have found a rfs name for a vfs resource with parameters, save it to the database
1062
try {
1063                    cms.writeStaticExportPublishedResource(
1064                        rfsName,
1065                        EXPORT_LINK_WITH_PARAMETER,
1066                        parameters,
1067                        System.currentTimeMillis());
1068                } catch (CmsException e) {
1069                    LOG.error(Messages.get().getBundle().key(Messages.LOG_WRITE_FAILED_1, rfsName), e);
1070                }
1071            }
1072        } catch (CmsException e) {
1073            // ignore exception, return vfsName as rfsName
1074
rfsName = vfsName;
1075        }
1076
1077        // add export rfs prefix and return result
1078
if (!vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
1079            return getRfsPrefix(cms.getRequestContext().addSiteRoot(vfsName)).concat(rfsName);
1080        } else {
1081            return getRfsPrefix(cms.getRequestContext().getSiteRoot() + "/").concat(rfsName);
1082        }
1083    }
1084
1085    /**
1086     * Returns the prefix for exported links in the "real" file system.<p>
1087     *
1088     * The returned value will be a direcory like prefix. The value is configured
1089     * in the <code>opencms-importexport.xml</code> configuration file. An optimization
1090     * of the configured value will be performed, where all relative path infprmation is resolved
1091     * (for example <code>/export/../static</code> will be resolved to <code>/export</code>.
1092     * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
1093     * (for example <code>/export/</code> becomes <code>/export</code>.<p>
1094     *
1095     * This is resource name based, and based on the rfs-rules defined in the
1096     * <code>opencms-importexport.xml</code> configuration file.
1097     *
1098     * @param vfsName the name of the resource to export
1099     *
1100     * @return the prefix for exported links in the "real" file system
1101     *
1102     * @see #getExportPath(String)
1103     * @see #getVfsPrefix()
1104     */

1105    public String JavaDoc getRfsPrefix(String JavaDoc vfsName) {
1106
1107        if (vfsName != null) {
1108            Iterator JavaDoc it = m_rfsRules.iterator();
1109            while (it.hasNext()) {
1110                CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
1111                if (rule.getSource().matcher(vfsName).matches()) {
1112                    return rule.getRfsPrefix();
1113                }
1114            }
1115        }
1116        return m_rfsPrefix;
1117    }
1118
1119    /**
1120     * Returns the original configured prefix for exported links in the "real" file, to be used
1121     * when re-writing the configuration.<p>
1122     *
1123     * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1124     * This method should <b>not</b> be used otherwise. Use <code>{@link #getRfsPrefix(String)}</code>
1125     * to obtain the rfs prefix to use for the exported links.<p>
1126     *
1127     * @return the original configured prefix for exported links in the "real" file
1128     */

1129    public String JavaDoc getRfsPrefixForConfiguration() {
1130
1131        return m_rfsPrefixConfigured;
1132    }
1133
1134    /**
1135     * Returns the rfs Rules.<p>
1136     *
1137     * @return the rfs Rules
1138     */

1139    public List JavaDoc getRfsRules() {
1140
1141        return Collections.unmodifiableList(m_rfsRules);
1142    }
1143
1144    /**
1145     * Returns the vfs name of the test resource.<p>
1146     *
1147     * @return the vfs name of the test resource.
1148     */

1149    public String JavaDoc getTestResource() {
1150
1151        return m_testResource;
1152    }
1153
1154    /**
1155     * Returns the VFS name for the given RFS name, being the exact reverse of <code>{@link #getRfsName(CmsObject, String)}</code>.<p>
1156     *
1157     * Returns <code>null</code> if no matching VFS resource can be found for the given RFS name.<p>
1158     *
1159     * @param cms the current users OpenCms context
1160     * @param rfsName the RFS name to get the VFS name for
1161     *
1162     * @return the VFS name for the given RFS name, or <code>null</code> if the RFS name does not match to the VFS
1163     *
1164     * @see #getRfsName(CmsObject, String)
1165     */

1166    public String JavaDoc getVfsName(CmsObject cms, String JavaDoc rfsName) {
1167
1168        CmsStaticExportData data = getExportData(rfsName, null, cms);
1169        if (data != null) {
1170            String JavaDoc result = data.getVfsName();
1171            if ((result != null) && result.startsWith(cms.getRequestContext().getSiteRoot())) {
1172                result = result.substring(cms.getRequestContext().getSiteRoot().length());
1173            }
1174            return result;
1175        }
1176        return null;
1177    }
1178
1179    /**
1180     * Returns the prefix for the internal in the VFS.<p>
1181     *
1182     * The returned value will be a direcory like prefix. The value is configured
1183     * in the <code>opencms-importexport.xml</code> configuration file. An optimization
1184     * of the configured value will be performed, where all relative path infprmation is resolved
1185     * (for example <code>/opencms/../mycms</code> will be resolved to <code>/mycms</code>.
1186     * Moreover, if the configured path ends with a <code>/</code>, this will be cut off
1187     * (for example <code>/opencms/</code> becomes <code>/opencms</code>.<p>
1188     *
1189     * @return the prefix for the internal in the VFS
1190     *
1191     * @see #getExportPath(String)
1192     * @see #getRfsPrefix(String)
1193     */

1194    public String JavaDoc getVfsPrefix() {
1195
1196        return m_vfsPrefix;
1197    }
1198
1199    /**
1200     * Returns the original configured prefix for internal links in the VFS, to be used
1201     * when re-writing the configuration.<p>
1202     *
1203     * This is required <b>only</b> to serialize the configuration again exactly as it was configured.
1204     * This method should <b>not</b> be used otherwise. Use <code>{@link #getVfsPrefix()}</code>
1205     * to obtain the VFS prefix to use for the internal links.<p>
1206     *
1207     * @return the original configured prefix for internal links in the VFS
1208     */

1209    public String JavaDoc getVfsPrefixForConfiguration() {
1210
1211        return m_vfsPrefixConfigured;
1212    }
1213
1214    /**
1215     * Initializes the static export manager with the OpenCms system configuration.<p>
1216     *
1217     * @param cms an OpenCms context object
1218     */

1219    public void initialize(CmsObject cms) {
1220
1221        // initialize static export RFS path (relative to web application)
1222
m_staticExportPath = normalizeExportPath(m_staticExportPathConfigured);
1223        if (m_staticExportPath.equals(OpenCms.getSystemInfo().getWebApplicationRfsPath())) {
1224            throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_INVALID_EXPORT_PATH_0));
1225        }
1226        // initialize prefix variables
1227
m_rfsPrefix = normalizeRfsPrefix(m_rfsPrefixConfigured);
1228        Iterator JavaDoc itRfsRules = m_rfsRules.iterator();
1229        while (itRfsRules.hasNext()) {
1230            CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)itRfsRules.next();
1231            try {
1232                rule.setExportPath(normalizeExportPath(rule.getExportPathConfigured()));
1233            } catch (CmsIllegalArgumentException e) {
1234                CmsLog.INIT.info(e.getMessageContainer());
1235                rule.setExportPath(m_staticExportPath);
1236            }
1237            rule.setRfsPrefix(normalizeRfsPrefix(rule.getRfsPrefixConfigured()));
1238        }
1239        m_vfsPrefix = insertContextStrings(m_vfsPrefixConfigured);
1240        m_vfsPrefix = CmsFileUtil.normalizePath(m_vfsPrefix, '/');
1241        if (CmsResource.isFolder(m_vfsPrefix)) {
1242            // ensure prefix does NOT end with a folder '/'
1243
m_vfsPrefix = m_vfsPrefix.substring(0, m_vfsPrefix.length() - 1);
1244        }
1245        if (CmsLog.INIT.isDebugEnabled()) {
1246            if (cms != null) {
1247                CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_SE_MANAGER_CREATED_1, cms));
1248            } else {
1249                CmsLog.INIT.debug(Messages.get().getBundle().key(Messages.INIT_SE_MANAGER_CREATED_0));
1250            }
1251        }
1252
1253        LRUMap lruMap1 = new LRUMap(2048);
1254        m_cacheOnlineLinks = Collections.synchronizedMap(lruMap1);
1255        if (OpenCms.getMemoryMonitor().enabled()) {
1256            // map must be of type "LRUMap" so that memory monitor can acecss all information
1257
OpenCms.getMemoryMonitor().register(this.getClass().getName() + ".m_cacheOnlineLinks", lruMap1);
1258        }
1259
1260        LRUMap lruMap2 = new LRUMap(2048);
1261        m_cacheExportUris = Collections.synchronizedMap(lruMap2);
1262        if (OpenCms.getMemoryMonitor().enabled()) {
1263            // map must be of type "LRUMap" so that memory monitor can acecss all information
1264
OpenCms.getMemoryMonitor().register(this.getClass().getName() + ".m_cacheExportUris", lruMap2);
1265        }
1266
1267        LRUMap lruMap3 = new LRUMap(2048);
1268        m_cacheSecureLinks = Collections.synchronizedMap(lruMap3);
1269        if (OpenCms.getMemoryMonitor().enabled()) {
1270            // map must be of type "LRUMap" so that memory monitor can acecss all information
1271
OpenCms.getMemoryMonitor().register(this.getClass().getName() + ".m_cacheSecureLinks", lruMap3);
1272        }
1273
1274        LRUMap lruMap4 = new LRUMap(2048);
1275        m_cacheExportLinks = Collections.synchronizedMap(lruMap4);
1276        if (OpenCms.getMemoryMonitor().enabled()) {
1277            // map must be of type "LRUMap" so that memory monitor can acecss all information
1278
OpenCms.getMemoryMonitor().register(this.getClass().getName() + ".m_cacheExportLinks", lruMap4);
1279        }
1280
1281        // register this object as event listener
1282
OpenCms.addCmsEventListener(this, new int[] {
1283            I_CmsEventListener.EVENT_PUBLISH_PROJECT,
1284            I_CmsEventListener.EVENT_CLEAR_CACHES,
1285            I_CmsEventListener.EVENT_UPDATE_EXPORTS});
1286
1287        m_exportFolderMatcher = new CmsExportFolderMatcher(m_exportFolders, m_testResource);
1288
1289        // initialize "exportname" folders
1290
setExportnames();
1291
1292        // get the default accept-language header value
1293
m_defaultAcceptLanguageHeader = CmsAcceptLanguageHeaderParser.createLanguageHeader();
1294
1295        // get the default accept-charset header value
1296
m_defaultAcceptCharsetHeader = OpenCms.getSystemInfo().getDefaultEncoding();
1297
1298        // get the export url prefix
1299
int pos = m_exportUrl.indexOf("://");
1300        if (pos > 0) {
1301            // absolute link, remove http://servername
1302
int pos2 = m_exportUrl.indexOf('/', pos + 3);
1303            if (pos2 > 0) {
1304                m_exportUrlPrefix = m_exportUrl.substring(pos2);
1305            } else {
1306                // should never happen
1307
m_exportUrlPrefix = "";
1308            }
1309        } else {
1310            m_exportUrlPrefix = m_exportUrl;
1311        }
1312        if (CmsLog.INIT.isInfoEnabled()) {
1313            if (isStaticExportEnabled()) {
1314                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_STATIC_EXPORT_ENABLED_0));
1315                CmsLog.INIT.info(Messages.get().getBundle().key(
1316                    Messages.INIT_EXPORT_DEFAULT_1,
1317                    new Boolean JavaDoc(getExportPropertyDefault())));
1318                itRfsRules = m_rfsRules.iterator();
1319                while (itRfsRules.hasNext()) {
1320                    CmsStaticExportRfsRule rfsRule = (CmsStaticExportRfsRule)itRfsRules.next();
1321                    CmsLog.INIT.info(Messages.get().getBundle().key(
1322                        Messages.INIT_EXPORT_RFS_RULE_EXPORT_PATH_2,
1323                        rfsRule.getSource(),
1324                        rfsRule.getExportPath()));
1325                    CmsLog.INIT.info(Messages.get().getBundle().key(
1326                        Messages.INIT_EXPORT_RFS_RULE_RFS_PREFIX_2,
1327                        rfsRule.getSource(),
1328                        rfsRule.getRfsPrefix()));
1329                    if (rfsRule.getUseRelativeLinks() != null) {
1330                        if (rfsRule.getUseRelativeLinks().booleanValue()) {
1331                            CmsLog.INIT.info(Messages.get().getBundle().key(
1332                                Messages.INIT_EXPORT_RFS_RULE_RELATIVE_LINKS_1,
1333                                rfsRule.getSource()));
1334                        } else {
1335                            CmsLog.INIT.info(Messages.get().getBundle().key(
1336                                Messages.INIT_EXPORT_RFS_RULE_ABSOLUTE_LINKS_1,
1337                                rfsRule.getSource()));
1338                        }
1339                    }
1340                }
1341                // default rule
1342
CmsLog.INIT.info(Messages.get().getBundle().key(
1343                    Messages.INIT_EXPORT_RFS_RULE_EXPORT_PATH_2,
1344                    "/",
1345                    m_staticExportPath));
1346                CmsLog.INIT.info(Messages.get().getBundle().key(
1347                    Messages.INIT_EXPORT_RFS_RULE_RFS_PREFIX_2,
1348                    "/",
1349                    m_rfsPrefix));
1350                if (m_exportRelativeLinks) {
1351                    CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_RFS_RULE_RELATIVE_LINKS_1, "/"));
1352                } else {
1353                    CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_RFS_RULE_ABSOLUTE_LINKS_1, "/"));
1354                }
1355                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_VFS_PREFIX_1, getVfsPrefix()));
1356                CmsLog.INIT.info(Messages.get().getBundle().key(
1357                    Messages.INIT_EXPORT_EXPORT_HANDLER_1,
1358                    getHandler().getClass().getName()));
1359                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_URL_1, getExportUrl()));
1360                CmsLog.INIT.info(Messages.get().getBundle().key(
1361                    Messages.INIT_EXPORT_OPTIMIZATION_1,
1362                    getPlainExportOptimization()));
1363                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_TESTRESOURCE_1, getTestResource()));
1364            } else {
1365                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_STATIC_EXPORT_DISABLED_0));
1366            }
1367        }
1368    }
1369
1370    /**
1371     * Checks if the static export is required for the given VFS resource.<p>
1372     *
1373     * Please note that the given OpenCms user context is NOT used to read the resource.
1374     * The check for export is always done with the permissions of the "Export" user.
1375     * The provided user context is just used to get the current site root.<p>
1376     *
1377     * Since the "Export" user always operates in the "Online" project, the resource
1378     * is also read from the "Online" project, not from the current project of the given
1379     * OpenCms context.<p>
1380     *
1381     * @param cms the current users OpenCms context
1382     * @param vfsName the VFS resource name to check
1383     *
1384     * @return <code>true</code> if static export is required for the given VFS resource
1385     */

1386    public boolean isExportLink(CmsObject cms, String JavaDoc vfsName) {
1387
1388        boolean result = false;
1389        if (isStaticExportEnabled()) {
1390            Boolean JavaDoc exportResource = (Boolean JavaDoc)m_cacheExportLinks.get(getCacheKey(
1391                cms.getRequestContext().getSiteRoot(),
1392                vfsName));
1393            if (exportResource == null) {
1394                try {
1395                    // static export must always be checked with the export users permissions,
1396
// not the current users permissions
1397
CmsObject exportCms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
1398                    exportCms.getRequestContext().setSiteRoot(cms.getRequestContext().getSiteRoot());
1399                    // let's look up export property in VFS
1400
String JavaDoc exportValue = exportCms.readPropertyObject(
1401                        vfsName,
1402                        CmsPropertyDefinition.PROPERTY_EXPORT,
1403                        true).getValue();
1404                    if (exportValue == null) {
1405                        // no setting found for "export" property
1406
if (getExportPropertyDefault()) {
1407                            // if the default is "true" we always export
1408
result = true;
1409                        } else {
1410                            // check if the resource is exportable by suffix
1411
result = isSuffixExportable(vfsName);
1412                        }
1413                    } else {
1414                        // "export" value found, if it was "true" we export
1415
result = Boolean.valueOf(exportValue).booleanValue();
1416                    }
1417                } catch (Exception JavaDoc e) {
1418                    // no export required (probably security issues, e.g. no access for export user)
1419
}
1420                m_cacheExportLinks.put(getCacheKey(cms.getRequestContext().getSiteRoot(), vfsName), new Boolean JavaDoc(result));
1421            } else {
1422                result = exportResource.booleanValue();
1423            }
1424        }
1425        return result;
1426    }
1427
1428    /**
1429     * Returns <code>true</code> if the given VFS resource should be transported through a secure channel.<p>
1430     *
1431     * The secure mode is only checked in the "Online" project.
1432     * If the given OpenCms context is currently not in the "Online" project,
1433     * <code>false</code> is returned.<p>
1434     *
1435     * The given resource is read from the site root of the provided OpenCms context.<p>
1436     *
1437     * @param cms the current users OpenCms context
1438     * @param vfsName the VFS resource name to check
1439     *
1440     * @return <code>true</code> if the given VFS resource should be transported through a secure channel
1441     *
1442     * @see #isSecureLink(CmsObject, String, String)
1443     */

1444    public boolean isSecureLink(CmsObject cms, String JavaDoc vfsName) {
1445
1446        if (!cms.getRequestContext().currentProject().isOnlineProject()) {
1447            return false;
1448        }
1449        Boolean JavaDoc secureResource = (Boolean JavaDoc)m_cacheSecureLinks.get(getCacheKey(
1450            cms.getRequestContext().getSiteRoot(),
1451            vfsName));
1452        if (secureResource == null) {
1453            try {
1454                String JavaDoc secureProp = cms.readPropertyObject(vfsName, CmsPropertyDefinition.PROPERTY_SECURE, true).getValue();
1455                secureResource = Boolean.valueOf(secureProp);
1456                // only cache result if read was successfull
1457
m_cacheSecureLinks.put(getCacheKey(cms.getRequestContext().getSiteRoot(), vfsName), secureResource);
1458            } catch (CmsVfsResourceNotFoundException e) {
1459                secureResource = Boolean.FALSE;
1460                // resource does not exist, no secure link will be required for any user
1461
m_cacheSecureLinks.put(getCacheKey(cms.getRequestContext().getSiteRoot(), vfsName), secureResource);
1462            } catch (Exception JavaDoc e) {
1463                // no secure link required (probably security issues, e.g. no access for current user)
1464
// however other users may be allowed to read the resource, so the result can't be cached
1465
secureResource = Boolean.FALSE;
1466            }
1467        }
1468        return secureResource.booleanValue();
1469    }
1470
1471    /**
1472     * Returns <code>true</code> if the given VFS resource that is located under the
1473     * given site root should be transported through a secure channel.<p>
1474     *
1475     * @param cms the current users OpenCms context
1476     * @param vfsName the VFS resource name to check
1477     * @param siteRoot the site root where the the VFS resource should be read
1478     *
1479     * @return <code>true</code> if the given VFS resource should be transported through a secure channel
1480     *
1481     * @see #isSecureLink(CmsObject, String)
1482     */

1483    public boolean isSecureLink(CmsObject cms, String JavaDoc vfsName, String JavaDoc siteRoot) {
1484
1485        if (siteRoot == null) {
1486            return isSecureLink(cms, vfsName);
1487        }
1488
1489        // the site root of the cms object has to be changed so that the property can be read
1490
try {
1491            cms.getRequestContext().saveSiteRoot();
1492            cms.getRequestContext().setSiteRoot(siteRoot);
1493            return isSecureLink(cms, vfsName);
1494        } finally {
1495            cms.getRequestContext().restoreSiteRoot();
1496        }
1497    }
1498
1499    /**
1500     * Returns true if the static export is enabled.<p>
1501     *
1502     * @return true if the static export is enabled
1503     */

1504    public boolean isStaticExportEnabled() {
1505
1506        return m_staticExportEnabled;
1507    }
1508
1509    /**
1510     * Returns true if the given resource name is exportable because of it's suffix.<p>
1511     *
1512     * @param resourceName the name to check
1513     * @return true if the given resource name is exportable because of it's suffix
1514     */

1515    public boolean isSuffixExportable(String JavaDoc resourceName) {
1516
1517        if (resourceName == null) {
1518            return false;
1519        }
1520        int pos = resourceName.lastIndexOf('.');
1521        if (pos >= 0) {
1522            String JavaDoc suffix = resourceName.substring(pos).toLowerCase();
1523            return m_exportSuffixes.contains(suffix);
1524        }
1525        return false;
1526    }
1527
1528    /**
1529     * Returns true if the links in the static export should be relative.<p>
1530     *
1531     * @param vfsName the name of the resource to export
1532     *
1533     * @return true if the links in the static export should be relative
1534     */

1535    public boolean relativeLinksInExport(String JavaDoc vfsName) {
1536
1537        if (vfsName != null) {
1538            Iterator JavaDoc it = m_rfsRules.iterator();
1539            while (it.hasNext()) {
1540                CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
1541                if (rule.getSource().matcher(vfsName).matches()) {
1542                    return rule.getUseRelativeLinks() != null ? rule.getUseRelativeLinks().booleanValue()
1543                    : m_exportRelativeLinks;
1544                }
1545            }
1546        }
1547        return m_exportRelativeLinks;
1548    }
1549
1550    /**
1551     * Sets the accept-charset header value.<p>
1552     *
1553     * @param value accept-language header value
1554     */

1555    public void setAcceptCharsetHeader(String JavaDoc value) {
1556
1557        m_acceptCharsetHeader = value;
1558    }
1559
1560    /**
1561     * Sets the accept-language header value.<p>
1562     *
1563     * @param value accept-language header value
1564     */

1565    public void setAcceptLanguageHeader(String JavaDoc value) {
1566
1567        m_acceptLanguageHeader = value;
1568    }
1569
1570    /**
1571     * Sets the default property value.<p>
1572     *
1573     * @param value must be <code>"true"</code> or <code>"false"</code>
1574     */

1575    public void setDefault(String JavaDoc value) {
1576
1577        m_exportPropertyDefault = Boolean.valueOf(value).booleanValue();
1578    }
1579
1580    /**
1581     * Sets the export enbled value.<p>
1582     *
1583     * @param value must be <code>"true"</code> or <code>"false"</code>
1584     */

1585    public void setExportEnabled(String JavaDoc value) {
1586
1587        m_staticExportEnabled = Boolean.valueOf(value).booleanValue();
1588    }
1589
1590    /**
1591     * Adds a resource pattern to the list of resouces which are part of the export.<p>
1592     *
1593     * @param folder the folder pattern to add to the list.
1594     */

1595    public void setExportFolderPattern(String JavaDoc folder) {
1596
1597        m_exportFolders.add(folder);
1598    }
1599
1600    /**
1601     * Sets specific http header for the static export.<p>
1602     *
1603     * The format of the headers must be "header:value".<p>
1604     *
1605     * @param exportHeader a specific http header
1606     */

1607    public void setExportHeader(String JavaDoc exportHeader) {
1608
1609        if (CmsStringUtil.splitAsArray(exportHeader, ':').length == 2) {
1610            if (CmsLog.INIT.isInfoEnabled()) {
1611                CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_EXPORT_HEADERS_1, exportHeader));
1612            }
1613            m_exportHeaders.add(exportHeader);
1614        } else {
1615            if (CmsLog.INIT.isWarnEnabled()) {
1616                CmsLog.INIT.warn(Messages.get().getBundle().key(Messages.INIT_INVALID_HEADER_1, exportHeader));
1617            }
1618        }
1619    }
1620
1621    /**
1622     * Sets the path where the static export is written.<p>
1623     *
1624     * @param path the path where the static export is written
1625     */

1626    public void setExportPath(String JavaDoc path) {
1627
1628        m_staticExportPathConfigured = path;
1629    }
1630
1631    /**
1632     * Adds a suffix to the list of resouce suffixes which will be exported by default.<p>
1633     *
1634     * @param suffix the suffix to add to the list.
1635     */

1636    public void setExportSuffix(String JavaDoc suffix) {
1637
1638        m_exportSuffixes.add(suffix.toLowerCase());
1639    }
1640
1641    /**
1642     * Sets the export url.<p>
1643     *
1644     * @param url the export url
1645     */

1646    public void setExportUrl(String JavaDoc url) {
1647
1648        m_exportUrl = insertContextStrings(url);
1649        m_exportUrlConfigured = url;
1650    }
1651
1652    /**
1653     * Sets the static export handler class.<p>
1654     *
1655     * @param handlerClassName the static export handler class name.
1656     */

1657    public void setHandler(String JavaDoc handlerClassName) {
1658
1659        try {
1660            m_handler = (I_CmsStaticExportHandler)Class.forName(handlerClassName).newInstance();
1661        } catch (Exception JavaDoc e) {
1662            // should never happen
1663
LOG.error(e);
1664        }
1665    }
1666
1667    /**
1668     * Sets the plain export optimization value.<p>
1669     *
1670     * @param value must be <code>"true"</code> or <code>"false"</code>
1671     */

1672    public void setPlainExportOptimization(String JavaDoc value) {
1673
1674        m_quickPlainExport = Boolean.valueOf(value).booleanValue();
1675    }
1676
1677    /**
1678     * Sets the relative links value.<p>
1679     *
1680     * @param value must be <code>"true"</code> or <code>"false"</code>
1681     */

1682    public void setRelativeLinks(String JavaDoc value) {
1683
1684        m_exportRelativeLinks = Boolean.valueOf(value).booleanValue();
1685    }
1686
1687    /**
1688     * Sets the remote addr. wirch will be used for internal requests during the static export.<p>
1689     *
1690     * @param addr the remote addr. to be used
1691     */

1692    public void setRemoteAddr(String JavaDoc addr) {
1693
1694        m_remoteAddr = addr;
1695    }
1696
1697    /**
1698     * Sets the prefix for exported links in the "real" file system.<p>
1699     *
1700     * @param rfsPrefix the prefix for exported links in the "real" file system
1701     */

1702    public void setRfsPrefix(String JavaDoc rfsPrefix) {
1703
1704        m_rfsPrefixConfigured = rfsPrefix;
1705    }
1706
1707    /**
1708     * Sets the test resource.<p>
1709     *
1710     * @param testResource the vfs name of the test resource
1711     */

1712    public void setTestResource(String JavaDoc testResource) {
1713
1714        m_testResource = testResource;
1715    }
1716
1717    /**
1718     * Sets the prefix for internal links in the vfs.<p>
1719     *
1720     * @param vfsPrefix the prefix for internal links in the vfs
1721     */

1722    public void setVfsPrefix(String JavaDoc vfsPrefix) {
1723
1724        m_vfsPrefixConfigured = vfsPrefix;
1725    }
1726
1727    /**
1728     * Shuts down all this static export manager.<p>
1729     *
1730     * This is required since there may still be a thread running when the system is being shut down.<p>
1731     */

1732    public synchronized void shutDown() {
1733
1734        int count = 0;
1735        // if the handler is still running, we must wait up to 30 secounds until it is finished
1736
while ((count < HANDLER_FINISH_TIME) && m_handler.isBusy()) {
1737            count++;
1738            try {
1739                if (CmsLog.INIT.isInfoEnabled()) {
1740                    CmsLog.INIT.info(Messages.get().getBundle().key(
1741                        Messages.INIT_STATIC_EXPORT_SHUTDOWN_3,
1742                        m_handler.getClass().getName(),
1743                        String.valueOf(count),
1744                        String.valueOf(HANDLER_FINISH_TIME)));
1745                }
1746                Thread.sleep(1000);
1747            } catch (InterruptedException JavaDoc e) {
1748                // if interrupted we ignore the handler, this will produce some log messages but should be ok
1749
count = HANDLER_FINISH_TIME;
1750            }
1751        }
1752
1753        if (CmsLog.INIT.isInfoEnabled()) {
1754            CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_1, this.getClass().getName()));
1755        }
1756
1757    }
1758
1759    /**
1760     * Returns the longest rfs prefix matching a given already translated rfs name.<p>
1761     *
1762     * @param rfsName the rfs name
1763     *
1764     * @return its rfs prefix
1765     *
1766     * @see #getRfsPrefix(String)
1767     */

1768    protected String JavaDoc getRfsPrefixForRfsName(String JavaDoc rfsName) {
1769
1770        String JavaDoc retVal = "";
1771        // default case
1772
if (rfsName.startsWith(m_rfsPrefix + "/")) {
1773            retVal = m_rfsPrefix;
1774        }
1775        // additional rules
1776
Iterator JavaDoc it = m_rfsRules.iterator();
1777        while (it.hasNext()) {
1778            CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
1779            String JavaDoc rfsPrefix = rule.getRfsPrefix();
1780            if (rfsName.startsWith(rfsPrefix + "/") && (retVal.length() < rfsPrefix.length())) {
1781                retVal = rfsPrefix;
1782            }
1783        }
1784        return retVal;
1785    }
1786
1787    /**
1788     * Returns the VFS name from a given RFS name.<p>
1789     *
1790     * The RFS name must not contain the RFS prefix.<p>
1791     *
1792     * @param cms an initialized OpenCms user context
1793     * @param rfsName the name of the RFS resource
1794     * @return the name of the VFS resource
1795     */

1796    /*package*/String JavaDoc getVfsNameInternal(CmsObject cms, String JavaDoc rfsName) {
1797
1798        String JavaDoc vfsName = null;
1799        CmsResource resource;
1800
1801        boolean match = false;
1802
1803        try {
1804            resource = cms.readResource(cms.getRequestContext().removeSiteRoot(rfsName));
1805            if (resource.isFolder() && !CmsResource.isFolder(rfsName)) {
1806                rfsName += '/';
1807            }
1808            vfsName = rfsName;
1809            match = true;
1810        } catch (Throwable JavaDoc t) {
1811            // resource not found
1812
}
1813
1814        if (!match) {
1815            // name of export resource could not be resolved by reading the resource directly,
1816
// now try to find a match with the "exportname" folders
1817
Map JavaDoc exportnameFolders = getExportnames();
1818            Iterator JavaDoc i = exportnameFolders.keySet().iterator();
1819            while (i.hasNext()) {
1820                String JavaDoc exportName = (String JavaDoc)i.next();
1821                if (rfsName.startsWith(exportName)) {
1822                    // prefix match
1823
match = true;
1824                    vfsName = exportnameFolders.get(exportName) + rfsName.substring(exportName.length());
1825                    try {
1826                        resource = cms.readResource(vfsName);
1827                        if (resource.isFolder()) {
1828                            if (!CmsResource.isFolder(rfsName)) {
1829                                rfsName += '/';
1830                            }
1831                            if (!CmsResource.isFolder(vfsName)) {
1832                                vfsName += '/';
1833                            }
1834                        }
1835                        break;
1836                    } catch (CmsVfsResourceNotFoundException e) {
1837                        // continue with trying out the other exportname to find a match (may be a multiple prefix)
1838
match = false;
1839                        continue;
1840                    } catch (CmsException e) {
1841                        rfsName = null;
1842                        break;
1843                    }
1844                }
1845            }
1846        }
1847
1848        // finally check if its a modified jsp resource
1849
if (!match) {
1850            // first cut of the last extension
1851
int extPos = rfsName.lastIndexOf('.');
1852            if (extPos >= 0) {
1853                String JavaDoc cutName = rfsName.substring(0, extPos);
1854                int pos = cutName.lastIndexOf('.');
1855                if (pos >= 0) {
1856                    // now check if remaining String ends with ".jsp"
1857
String JavaDoc extension = cutName.substring(pos).toLowerCase();
1858                    if (".jsp".equals(extension)) {
1859                        return getVfsNameInternal(cms, cutName);
1860                    }
1861                }
1862            }
1863        }
1864        if (match) {
1865            return vfsName;
1866        } else {
1867            return null;
1868        }
1869    }
1870
1871    /**
1872     * Clears the caches in the export manager.<p>
1873     *
1874     * @param event the event that requested to clear the caches
1875     */

1876    private void clearCaches(CmsEvent event) {
1877
1878        // synchronization of this method is not required as the individual maps are all synchronized maps anyway,
1879
// and setExportnames() is doing it's own synchronization
1880

1881        // flush all caches
1882
m_cacheOnlineLinks.clear();
1883        m_cacheExportUris.clear();
1884        m_cacheSecureLinks.clear();
1885        m_cacheExportLinks.clear();
1886        setExportnames();
1887        if (LOG.isDebugEnabled()) {
1888            String JavaDoc eventType = "EVENT_CLEAR_CACHES";
1889            if (event.getType() != I_CmsEventListener.EVENT_CLEAR_CACHES) {
1890                eventType = "EVENT_PUBLISH_PROJECT";
1891            }
1892            LOG.debug(Messages.get().getBundle().key(Messages.LOG_FLUSHED_CACHES_1, eventType));
1893        }
1894    }
1895
1896    /**
1897     * Creates the parent folder for a exported resource in the RFS.<p>
1898     *
1899     * @param exportPath the path to export the file
1900     * @param rfsName the rfs name of the resource
1901     *
1902     * @throws CmsException if the folder could not be created
1903     */

1904    private void createExportFolder(String JavaDoc exportPath, String JavaDoc rfsName) throws CmsException {
1905
1906        String JavaDoc exportFolderName = CmsFileUtil.normalizePath(exportPath + CmsResource.getFolderPath(rfsName));
1907        File exportFolder = new File(exportFolderName);
1908        if (!exportFolder.exists()) {
1909            if (!exportFolder.mkdirs()) {
1910                throw new CmsStaticExportException(Messages.get().container(Messages.ERR_CREATE_FOLDER_1, rfsName));
1911
1912            }
1913        }
1914    }
1915
1916    /**
1917     * Returns the export data for a requested resource, if null is returned no export is required.<p>
1918     *
1919     * @param rfsName the RFS name of the resource requested
1920     * @param vfsName the VFS name of the resource requested
1921     * @param cms an initialized cms context (should be initialized with the "Guest" user only
1922     * @return the export data for the request, if null is returned no export is required
1923     */

1924    private CmsStaticExportData getExportData(String JavaDoc rfsName, String JavaDoc vfsName, CmsObject cms) {
1925
1926        CmsResource resource = null;
1927        cms.getRequestContext().saveSiteRoot();
1928        try {
1929            cms.getRequestContext().setSiteRoot("/");
1930
1931            // cut export prefix from name
1932
rfsName = rfsName.substring(getRfsPrefixForRfsName(rfsName).length());
1933
1934            String JavaDoc parameters = null;
1935            if (vfsName == null) {
1936                // check if we have the result already in the cache
1937
CmsStaticExportData data = getCachedExportUri(rfsName);
1938                if (data != null) {
1939                    vfsName = data.getVfsName();
1940                    parameters = data.getParameters();
1941                }
1942            }
1943
1944            if (vfsName != null) {
1945                // this export uri is already cached
1946
if (CACHEVALUE_404 != vfsName) {
1947                    // this uri can be exported
1948
try {
1949                        resource = cms.readResource(vfsName);
1950                    } catch (CmsException e) {
1951                        // the resource has probably been deleted
1952
return null;
1953                    }
1954                    // valid cache entry, return export data object
1955
return new CmsStaticExportData(vfsName, rfsName, resource, parameters);
1956                } else {
1957                    // this uri can not be exported
1958
return null;
1959                }
1960            } else {
1961                // export uri not in cache, must look up the file in the VFS
1962
boolean match = false;
1963
1964                vfsName = getVfsNameInternal(cms, rfsName);
1965                if (vfsName != null) {
1966                    match = true;
1967                    try {
1968                        resource = cms.readResource(vfsName);
1969                    } catch (CmsException e) {
1970                        rfsName = null;
1971                    }
1972                }
1973
1974                if (!match) {
1975                    // it could be a translated resourcename with parameters, so make a lookup
1976
// in the published resources table
1977
try {
1978                        parameters = cms.readStaticExportPublishedResourceParameters(rfsName);
1979                        // there was a match in the db table, so get the StaticExportData
1980
if (CmsStringUtil.isNotEmpty(parameters)) {
1981
1982                            // get the rfs base string without the parameter hashcode
1983
String JavaDoc rfsBaseName = rfsName.substring(0, rfsName.lastIndexOf('_'));
1984
1985                            // get the vfs base name, which is later used to read the resource in the vfs
1986
String JavaDoc vfsBaseName = getVfsNameInternal(cms, rfsBaseName);
1987
1988                            // everything is there, so read the resource in the vfs and build the static export data object
1989
if (vfsBaseName != null) {
1990                                resource = cms.readResource(vfsBaseName);
1991                                CmsStaticExportData exportData = new CmsStaticExportData(
1992                                    vfsBaseName,
1993                                    rfsName,
1994                                    resource,
1995                                    parameters);
1996                                cacheExportUri(rfsName, exportData.getVfsName(), parameters);
1997                                return exportData;
1998                            }
1999                        }
2000                    } catch (CmsException e) {
2001                        // ignore, resource does not exist
2002
}
2003                    // no match found, nothing to export
2004
cacheExportUri(rfsName, CACHEVALUE_404, null);
2005                    return null;
2006                } else {
2007                    // found a resource to export
2008
cacheExportUri(rfsName, vfsName, null);
2009                    return new CmsStaticExportData(vfsName, rfsName, resource);
2010                }
2011            }
2012        } finally {
2013            cms.getRequestContext().restoreSiteRoot();
2014        }
2015    }
2016
2017    /**
2018     * Substitutes the ${CONTEXT_NAME} and ${SERVLET_NAME} in a path with the real values.<p>
2019     *
2020     * @param path the path to substitute
2021     * @return path with real context values
2022     */

2023    private String JavaDoc insertContextStrings(String JavaDoc path) {
2024
2025        // create a new macro resolver
2026
CmsMacroResolver resolver = CmsMacroResolver.newInstance();
2027
2028        // add special mappings for macros
2029
resolver.addMacro("CONTEXT_NAME", OpenCms.getSystemInfo().getContextPath());
2030        resolver.addMacro("SERVLET_NAME", OpenCms.getSystemInfo().getServletPath());
2031
2032        // resolve the macros
2033
return resolver.resolveMacros(path);
2034    }
2035
2036    /**
2037     * Returns true if the rfs Name match against any of the defined export urls.<p>
2038     *
2039     * @param rfsName the rfs Name to validate
2040     * @return true if the rfs Name match against any of the defined export urls
2041     */

2042    private boolean isValidRfsName(String JavaDoc rfsName) {
2043
2044        if (rfsName != null) {
2045            // default case
2046
if (rfsName.startsWith(m_rfsPrefix + "/")) {
2047                return true;
2048            }
2049            // additional rules
2050
Iterator JavaDoc it = m_rfsRules.iterator();
2051            while (it.hasNext()) {
2052                CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
2053                String JavaDoc rfsPrefix = rule.getRfsPrefix() + "/";
2054                if (rfsName.startsWith(rfsPrefix)) {
2055                    return true;
2056                }
2057            }
2058        }
2059        return false;
2060    }
2061
2062    /**
2063     * Returns a normalized export path.<p>
2064     *
2065     * Replacing macros, normalizing the path and taking care of relative paths.<p>
2066     *
2067     * @param exportPath the export path to normalize
2068     *
2069     * @return the normalized export path
2070     */

2071    private String JavaDoc normalizeExportPath(String JavaDoc exportPath) {
2072
2073        String JavaDoc result = insertContextStrings(exportPath);
2074        result = OpenCms.getSystemInfo().getAbsoluteRfsPathRelativeToWebApplication(result);
2075        if (result.endsWith(File.separator)) {
2076            // ensure export path does NOT end with a File.separator
2077
result = result.substring(0, result.length() - 1);
2078        }
2079        return result;
2080    }
2081
2082    /**
2083     * Returns a normalized rfs prefix.<p>
2084     *
2085     * Replacing macros and normalizing the path.<p>
2086     *
2087     * @param rfsPrefix the prefix to normalize
2088     *
2089     * @return the normalized rfs prefix
2090     */

2091    private String JavaDoc normalizeRfsPrefix(String JavaDoc rfsPrefix) {
2092
2093        String JavaDoc result = insertContextStrings(rfsPrefix);
2094        result = CmsFileUtil.normalizePath(result, '/');
2095        if (CmsResource.isFolder(result)) {
2096            // ensure prefix does NOT end with a folder '/'
2097
result = result.substring(0, result.length() - 1);
2098        }
2099        return result;
2100    }
2101
2102    /**
2103     * Scrubs all the "export" folders.<p>
2104     *
2105     * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
2106     */

2107    private void scrubExportFolders(I_CmsReport report) {
2108
2109        if (report != null) {
2110            report.println(
2111                Messages.get().container(Messages.RPT_DELETING_EXPORT_FOLDERS_BEGIN_0),
2112                I_CmsReport.FORMAT_HEADLINE);
2113        }
2114        synchronized (m_lockScrubExportFolders) {
2115            int count = 0;
2116            Integer JavaDoc size = new Integer JavaDoc(m_rfsRules.size() + 1);
2117            // default case
2118
String JavaDoc exportFolderName = CmsFileUtil.normalizePath(m_staticExportPath + '/');
2119            try {
2120                File exportFolder = new File(exportFolderName);
2121                // check if export file exists, if so delete it
2122
if (exportFolder.exists() && exportFolder.canWrite()) {
2123                    CmsFileUtil.purgeDirectory(exportFolder);
2124                }
2125                count++;
2126                if (report != null) {
2127                    report.println(Messages.get().container(
2128                        Messages.RPT_DELETE_EXPORT_FOLDER_3,
2129                        new Integer JavaDoc(count),
2130                        size,
2131                        exportFolderName), I_CmsReport.FORMAT_NOTE);
2132                } else {
2133                    // write log message
2134
if (LOG.isInfoEnabled()) {
2135                        LOG.info(Messages.get().getBundle().key(Messages.LOG_DEL_MAIN_SE_FOLDER_1, exportFolderName));
2136                    }
2137                }
2138            } catch (Throwable JavaDoc t) {
2139                // ignore, nothing to do about the
2140
if (LOG.isWarnEnabled()) {
2141                    LOG.warn(Messages.get().getBundle().key(Messages.LOG_FOLDER_DELETION_FAILED_1, exportFolderName), t);
2142                }
2143            }
2144            // iterate over the rules
2145
Iterator JavaDoc it = m_rfsRules.iterator();
2146            while (it.hasNext()) {
2147                CmsStaticExportRfsRule rule = (CmsStaticExportRfsRule)it.next();
2148                exportFolderName = CmsFileUtil.normalizePath(rule.getExportPath() + '/');
2149                try {
2150                    File exportFolder = new File(exportFolderName);
2151                    // check if export file exists, if so delete it
2152
if (exportFolder.exists() && exportFolder.canWrite()) {
2153                        CmsFileUtil.purgeDirectory(exportFolder);
2154                    }
2155                    count++;
2156                    if (report != null) {
2157                        report.println(Messages.get().container(
2158                            Messages.RPT_DELETE_EXPORT_FOLDER_3,
2159                            new Integer JavaDoc(count),
2160                            size,
2161                            exportFolderName), I_CmsReport.FORMAT_NOTE);
2162                    } else {
2163                        // write log message
2164
if (LOG.isInfoEnabled()) {
2165                            LOG.info(Messages.get().getBundle().key(Messages.LOG_DEL_MAIN_SE_FOLDER_1, exportFolderName));
2166                        }
2167                    }
2168                } catch (Throwable JavaDoc t) {
2169                    // ignore, nothing to do about the
2170
if (LOG.isWarnEnabled()) {
2171                        LOG.warn(
2172                            Messages.get().getBundle().key(Messages.LOG_FOLDER_DELETION_FAILED_1, exportFolderName),
2173                            t);
2174                    }
2175                }
2176            }
2177        }
2178        if (report != null) {
2179            report.println(
2180                Messages.get().container(Messages.RPT_DELETING_EXPORT_FOLDERS_END_0),
2181                I_CmsReport.FORMAT_HEADLINE);
2182        }
2183    }
2184
2185    /**
2186     * Set the list of all resources that have the "exportname" property set.<p>
2187     */

2188    private void setExportnames() {
2189
2190        if (LOG.isDebugEnabled()) {
2191            LOG.debug(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXPORTNAME_PROP_START_0));
2192        }
2193
2194        List JavaDoc resources;
2195        CmsObject cms = null;
2196        try {
2197            cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
2198            resources = cms.readResourcesWithProperty(CmsPropertyDefinition.PROPERTY_EXPORTNAME);
2199        } catch (CmsException e) {
2200            resources = Collections.EMPTY_LIST;
2201        }
2202
2203        synchronized (m_lockSetExportnames) {
2204            m_exportnameResources = new HashMap JavaDoc(resources.size());
2205            for (int i = 0, n = resources.size(); i < n; i++) {
2206                CmsResource res = (CmsResource)resources.get(i);
2207                try {
2208                    String JavaDoc foldername = cms.getSitePath(res);
2209                    String JavaDoc exportname = cms.readPropertyObject(
2210                        foldername,
2211                        CmsPropertyDefinition.PROPERTY_EXPORTNAME,
2212                        false).getValue();
2213                    if (exportname != null) {
2214                        if (exportname.charAt(exportname.length() - 1) != '/') {
2215                            exportname = exportname + "/";
2216                        }
2217                        if (exportname.charAt(0) != '/') {
2218                            exportname = "/" + exportname;
2219                        }
2220                        m_exportnameResources.put(exportname, foldername);
2221                    }
2222                } catch (CmsException e) {
2223                    // ignore exception, folder will no be added
2224
}
2225            }
2226            m_exportnameResources = Collections.unmodifiableMap(m_exportnameResources);
2227        }
2228        if (LOG.isDebugEnabled()) {
2229            LOG.debug(Messages.get().getBundle().key(Messages.LOG_UPDATE_EXPORTNAME_PROP_FINISHED_0));
2230        }
2231    }
2232
2233    /**
2234     * Writes a resource to the given export path with the given rfs name and the given content.<p>
2235     *
2236     * @param req the current request
2237     * @param exportPath the path to export the resource
2238     * @param rfsName the rfs name
2239     * @param vfsName the vfs name
2240     * @param resource the resource
2241     * @param content the content
2242     *
2243     * @throws CmsException if something goes wrong
2244     */

2245    private void writeResource(
2246        HttpServletRequest JavaDoc req,
2247        String JavaDoc exportPath,
2248        String JavaDoc rfsName,
2249        String JavaDoc vfsName,
2250        CmsResource resource,
2251        byte[] content) throws CmsException {
2252
2253        File exportFile = null;
2254        String JavaDoc exportFileName = CmsFileUtil.normalizePath(exportPath + rfsName);
2255
2256        // make sure all required parent folder exist
2257
createExportFolder(exportPath, rfsName);
2258        // generate export file instance and output stream
2259
exportFile = new File(exportFileName);
2260
2261        if (exportFile != null) {
2262            // write new exported file content
2263
try {
2264                FileOutputStream JavaDoc exportStream = new FileOutputStream JavaDoc(exportFile);
2265                exportStream.write(content);
2266                exportStream.close();
2267
2268                // log export success
2269
if (LOG.isInfoEnabled()) {
2270                    LOG.info(Messages.get().getBundle().key(Messages.LOG_STATIC_EXPORTED_2, vfsName, exportFileName));
2271                }
2272
2273            } catch (Throwable JavaDoc t) {
2274                throw new CmsStaticExportException(Messages.get().container(
2275                    Messages.ERR_OUTPUT_STREAM_1,
2276                    exportFileName), t);
2277            }
2278            // update the file with the modification date from the server
2279
if (req != null) {
2280                Long JavaDoc dateLastModified = (Long JavaDoc)req.getAttribute(CmsRequestUtil.HEADER_OPENCMS_EXPORT);
2281                if ((dateLastModified != null) && (dateLastModified.longValue() != -1)) {
2282                    exportFile.setLastModified((dateLastModified.longValue() / 1000) * 1000);
2283                    if (LOG.isDebugEnabled()) {
2284                        LOG.debug(Messages.get().getBundle().key(
2285                            Messages.LOG_SET_LAST_MODIFIED_2,
2286                            exportFile.getName(),
2287                            new Long JavaDoc((dateLastModified.longValue() / 1000) * 1000)));
2288                    }
2289                }
2290            } else {
2291                // otherweise take the last modification date form the OpenCms resource
2292
exportFile.setLastModified((resource.getDateLastModified() / 1000) * 1000);
2293            }
2294        }
2295    }
2296}
Popular Tags