001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.staticexport;
029
030import org.opencms.ade.detailpage.CmsDetailPageUtil;
031import org.opencms.db.CmsPublishedResource;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsResource;
034import org.opencms.file.CmsResourceFilter;
035import org.opencms.file.CmsVfsResourceNotFoundException;
036import org.opencms.loader.I_CmsResourceLoader;
037import org.opencms.main.CmsException;
038import org.opencms.main.CmsLog;
039import org.opencms.main.OpenCms;
040import org.opencms.report.I_CmsReport;
041import org.opencms.util.CmsFileUtil;
042import org.opencms.util.CmsRequestUtil;
043import org.opencms.util.CmsStringUtil;
044import org.opencms.util.CmsUUID;
045import org.opencms.workplace.CmsWorkplace;
046
047import java.io.File;
048import java.io.IOException;
049import java.net.HttpURLConnection;
050import java.net.URL;
051import java.util.ArrayList;
052import java.util.Collection;
053import java.util.Collections;
054import java.util.HashMap;
055import java.util.HashSet;
056import java.util.Iterator;
057import java.util.List;
058import java.util.Map;
059import java.util.Set;
060
061import javax.servlet.ServletException;
062import javax.servlet.http.HttpServletResponse;
063
064import org.apache.commons.logging.Log;
065
066/**
067 * Implementation for the <code>{@link I_CmsStaticExportHandler}</code> interface.<p>
068 *
069 * This handler exports all changes immediately after something is published.<p>
070 *
071 * @since 6.0.0
072 *
073 * @see I_CmsStaticExportHandler
074 */
075public class CmsAfterPublishStaticExportHandler extends A_CmsStaticExportHandler {
076
077    /** Header field set-cookie constant. */
078    private static final String HEADER_FIELD_SET_COOKIE = "Set-Cookie";
079
080    /** The log object for this class. */
081    private static final Log LOG = CmsLog.getLog(CmsAfterPublishStaticExportHandler.class);
082
083    /** Request method get constant. */
084    private static final String REQUEST_METHOD_GET = "GET";
085
086    /** Request property cookie constant. */
087    private static final String REQUEST_PROPERTY_COOKIE = "Cookie";
088
089    /**
090     * Does the actual static export.<p>
091     *
092     * @param resources a list of CmsPublishedREsources to start the static export with
093     * @param report an <code>{@link I_CmsReport}</code> instance to print output message, or <code>null</code> to write messages to the log file
094     *
095     * @throws CmsException in case of errors accessing the VFS
096     * @throws IOException in case of errors writing to the export output stream
097     * @throws ServletException in case of errors accessing the servlet
098     */
099    public void doExportAfterPublish(List<CmsPublishedResource> resources, I_CmsReport report)
100    throws CmsException, IOException, ServletException {
101
102        boolean templatesFound;
103
104        // export must be done in the context of the export user
105        // this will always use the root site
106        CmsObject cmsExportObject = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
107
108        List<CmsPublishedResource> resourcesToExport = getRelatedResources(cmsExportObject, resources);
109        // first export all non-template resources
110        templatesFound = exportNonTemplateResources(cmsExportObject, resourcesToExport, report);
111        LOG.warn("finished exporting non-template resources. ");
112
113        // export template resources (check "plainoptimization" setting)
114        if ((templatesFound) || (!OpenCms.getStaticExportManager().getQuickPlainExport())) {
115            CmsStaticExportManager manager = OpenCms.getStaticExportManager();
116
117            // build resource filter set
118            Set<String> resourceFilter = new HashSet<String>();
119            for (CmsPublishedResource pubResource : resourcesToExport) {
120                String rfsName = manager.getRfsName(cmsExportObject, pubResource.getRootPath());
121                resourceFilter.add(rfsName.substring(manager.getRfsPrefixForRfsName(rfsName).length()));
122            }
123
124            long timestamp = 0;
125            List<String> publishedTemplateResources;
126            boolean newTemplateLinksFound;
127            int linkMode = CmsStaticExportManager.EXPORT_LINK_WITHOUT_PARAMETER;
128            do {
129                // get all template resources which are potential candidates for a static export
130                publishedTemplateResources = cmsExportObject.readStaticExportResources(linkMode, timestamp);
131                if (publishedTemplateResources == null) {
132                    break;
133                }
134                newTemplateLinksFound = publishedTemplateResources.size() > 0;
135                if (newTemplateLinksFound) {
136                    if (linkMode == CmsStaticExportManager.EXPORT_LINK_WITHOUT_PARAMETER) {
137                        // first loop, switch mode to parameter links, leave the timestamp unchanged
138                        linkMode = CmsStaticExportManager.EXPORT_LINK_WITH_PARAMETER;
139                        // filter without parameter
140                        publishedTemplateResources.retainAll(resourceFilter);
141                    } else {
142                        // second and subsequent loops, only look for links not already exported
143                        // this can only be the case for a link with parameters
144                        // that was present on a page also generated with parameters
145                        timestamp = System.currentTimeMillis();
146                        // filter with parameter
147                        Iterator<String> itPubTemplates = publishedTemplateResources.iterator();
148                        while (itPubTemplates.hasNext()) {
149                            String rfsName = itPubTemplates.next();
150                            if (!resourceFilter.contains(rfsName.substring(0, rfsName.lastIndexOf('_')))) {
151                                itPubTemplates.remove();
152                            }
153                        }
154                    }
155                    // leave if no template left
156                    if (publishedTemplateResources.isEmpty()) {
157                        break;
158                    }
159                    // export
160                    LOG.warn("exporting template resources. ");
161                    exportTemplateResources(cmsExportObject, publishedTemplateResources, report);
162                }
163                // if no new template links where found we are finished
164            } while (newTemplateLinksFound);
165        }
166    }
167
168    /**
169     * Returns all resources within the current OpenCms site that are not marked as internal.<p>
170     *
171     * The result list contains objects of type {@link CmsPublishedResource}.<p>
172     *
173     * @param cms the cms context
174     *
175     * @return all resources within the current OpenCms site that are not marked as internal
176     *
177     * @throws CmsException if something goes wrong
178     */
179    public List<CmsPublishedResource> getAllResources(CmsObject cms) throws CmsException {
180
181        if (LOG.isDebugEnabled()) {
182            LOG.debug(Messages.get().getBundle().key(Messages.LOG_GET_ALL_RESOURCES_0));
183        }
184        // TODO: to improve performance, get here only the resources to render from the configuration
185
186        // read all from the root path, exclude resources flagged as internal
187        List<CmsResource> vfsResources = cms.readResources(
188            "/",
189            CmsResourceFilter.ALL.addExcludeFlags(CmsResource.FLAG_INTERNAL));
190
191        CmsExportFolderMatcher matcher = OpenCms.getStaticExportManager().getExportFolderMatcher();
192        // loop through the list and create the list of CmsPublishedResources
193        List<CmsPublishedResource> resources = new ArrayList<CmsPublishedResource>(vfsResources.size());
194        Iterator<CmsResource> i = vfsResources.iterator();
195        while (i.hasNext()) {
196            CmsResource resource = i.next();
197            if (!matcher.match(resource.getRootPath())) {
198                // filter files that do not match the resources to render
199                continue;
200            }
201            CmsPublishedResource pubRes = new CmsPublishedResource(resource);
202            if (LOG.isDebugEnabled()) {
203                LOG.debug(Messages.get().getBundle().key(Messages.LOG_PROCESSING_1, resource.getRootPath()));
204            }
205            resources.add(pubRes);
206        }
207
208        if (LOG.isDebugEnabled()) {
209            LOG.debug(Messages.get().getBundle().key(Messages.LOG_NUM_RESOURCES_1, new Integer(resources.size())));
210        }
211        return resources;
212    }
213
214    /**
215     * @see org.opencms.staticexport.I_CmsStaticExportHandler#performEventPublishProject(org.opencms.util.CmsUUID, org.opencms.report.I_CmsReport)
216     */
217    @Override
218    public void performEventPublishProject(CmsUUID publishHistoryId, I_CmsReport report) {
219
220        try {
221            m_busy = true;
222            exportAfterPublish(publishHistoryId, report);
223        } catch (Throwable t) {
224            if (LOG.isErrorEnabled()) {
225                LOG.error(Messages.get().getBundle().key(Messages.LOG_STATIC_EXPORT_ERROR_0), t);
226            }
227            if (report != null) {
228                report.addError(t);
229            }
230        } finally {
231            m_busy = false;
232        }
233    }
234
235    /**
236     * Starts the static export on publish.<p>
237     *
238     * Exports all modified resources after a publish process into the real FS.<p>
239     *
240     * @param publishHistoryId the publichHistoryId of the published project
241     * @param report an <code>{@link I_CmsReport}</code> instance to print output message, or <code>null</code> to write messages to the log file
242     *
243     * @throws CmsException in case of errors accessing the VFS
244     * @throws IOException in case of errors writing to the export output stream
245     * @throws ServletException in case of errors accessing the servlet
246     */
247    protected void exportAfterPublish(CmsUUID publishHistoryId, I_CmsReport report)
248    throws CmsException, IOException, ServletException {
249
250        // first check if the test resource was published already
251        // if not, we must do a complete export of all static resources
252        String rfsName = CmsFileUtil.normalizePath(
253            OpenCms.getStaticExportManager().getExportPath(OpenCms.getStaticExportManager().getTestResource())
254                + OpenCms.getStaticExportManager().getTestResource());
255
256        if (LOG.isDebugEnabled()) {
257            LOG.debug(Messages.get().getBundle().key(Messages.LOG_CHECKING_TEST_RESOURCE_1, rfsName));
258        }
259        File file = new File(rfsName);
260        if (!file.exists()) {
261            if (LOG.isDebugEnabled()) {
262                LOG.debug(Messages.get().getBundle().key(Messages.LOG_TEST_RESOURCE_NOT_EXISTANT_0));
263            }
264            // the file is not there, so export everything
265            OpenCms.getStaticExportManager().exportFullStaticRender(true, report);
266        } else {
267            if (LOG.isDebugEnabled()) {
268                LOG.debug(Messages.get().getBundle().key(Messages.LOG_TEST_RESOURCE_EXISTS_0));
269            }
270
271            // delete all resources deleted during the publish process, and retrieve the list of resources to actually export
272            List<CmsPublishedResource> publishedResources = scrubExportFolders(publishHistoryId);
273
274            // do the export
275            doExportAfterPublish(publishedResources, report);
276        }
277
278    }
279
280    /**
281     * Exports all non template resources found in a list of published resources.<p>
282     *
283     * @param cms the current cms object
284     * @param publishedResources the list of published resources
285     * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
286     *
287     * @return true if some template resources were found while looping the list of published resources
288     *
289     * @throws CmsException in case of errors accessing the VFS
290     * @throws IOException in case of errors writing to the export output stream
291     * @throws ServletException in case of errors accessing the servlet
292     */
293    protected boolean exportNonTemplateResources(
294        CmsObject cms,
295        List<CmsPublishedResource> publishedResources,
296        I_CmsReport report)
297    throws CmsException, IOException, ServletException {
298
299        report.println(
300            Messages.get().container(Messages.RPT_STATICEXPORT_NONTEMPLATE_RESOURCES_BEGIN_0),
301            I_CmsReport.FORMAT_HEADLINE);
302
303        if (LOG.isDebugEnabled()) {
304            LOG.debug(
305                Messages.get().getBundle().key(
306                    Messages.LOG_EXPORTING_NON_TEMPLATE_1,
307                    new Integer(publishedResources.size())));
308        }
309
310        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
311        List<CmsStaticExportData> resourcesToExport = new ArrayList<CmsStaticExportData>();
312        boolean templatesFound = readNonTemplateResourcesToExport(cms, publishedResources, resourcesToExport);
313
314        int count = 1;
315        int size = resourcesToExport.size();
316        if (LOG.isDebugEnabled()) {
317            LOG.debug(Messages.get().getBundle().key(Messages.LOG_NUM_EXPORT_1, new Integer(size)));
318        }
319        // now do the export
320        Iterator<CmsStaticExportData> i = resourcesToExport.iterator();
321        while (i.hasNext()) {
322            CmsStaticExportData exportData = i.next();
323            if (LOG.isDebugEnabled()) {
324                LOG.debug(
325                    Messages.get().getBundle().key(
326                        Messages.LOG_EXPORT_FILE_2,
327                        exportData.getVfsName(),
328                        exportData.getRfsName()));
329            }
330
331            report.print(
332                org.opencms.report.Messages.get().container(
333                    org.opencms.report.Messages.RPT_SUCCESSION_2,
334                    new Integer(count++),
335                    new Integer(size)),
336                I_CmsReport.FORMAT_NOTE);
337            report.print(Messages.get().container(Messages.RPT_EXPORTING_0), I_CmsReport.FORMAT_NOTE);
338            report.print(
339                org.opencms.report.Messages.get().container(
340                    org.opencms.report.Messages.RPT_ARGUMENT_1,
341                    exportData.getVfsName()));
342            report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
343            int status = manager.export(null, null, cms, exportData);
344            if (status == HttpServletResponse.SC_OK) {
345                report.println(
346                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
347                    I_CmsReport.FORMAT_OK);
348            } else {
349                report.println(
350                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_IGNORED_0),
351                    I_CmsReport.FORMAT_NOTE);
352            }
353
354            if (LOG.isInfoEnabled()) {
355                Object[] arguments = new Object[] {
356                    exportData.getVfsName(),
357                    exportData.getRfsName(),
358                    new Integer(status)};
359                LOG.info(Messages.get().getBundle().key(Messages.LOG_EXPORT_FILE_STATUS_3, arguments));
360            }
361            //don't lock up the CPU exclusively - allow other Threads to run as well
362            Thread.yield();
363        }
364
365        resourcesToExport = null;
366
367        report.println(
368            Messages.get().container(Messages.RPT_STATICEXPORT_NONTEMPLATE_RESOURCES_END_0),
369            I_CmsReport.FORMAT_HEADLINE);
370
371        return templatesFound;
372    }
373
374    /**
375     * Exports all sitemap resources found in a list of published resources.<p>
376     *
377     * @param entry the sitemap entry
378     * @param data the export data
379     * @param cookies cookies to keep the session
380     *
381     * @return the status of the http request used to perform the export
382     *
383     * @throws IOException if something goes wrong
384     */
385    //    protected int exportSitemapResource(CmsSitemapEntry entry, CmsStaticExportData data, StringBuffer cookies)
386    //    throws IOException {
387    //
388    //        String vfsName = data.getVfsName();
389    //        String rfsName = data.getRfsName();
390    //        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
391    //
392    //        // check if the resource to export is a detail page
393    //        // TODO: later when the property concept is finished this info is unnecessary
394    //        String path = vfsName;
395    //        boolean exportPropertyAlreadyReadFromVFS = false;
396    //        if (path.endsWith("/")) {
397    //            path = path.substring(0, path.length() - 1);
398    //            String detailId = CmsResource.getName(path);
399    //            if (CmsUUID.isValidUUID(detailId)) {
400    //                exportPropertyAlreadyReadFromVFS = true;
401    //            }
402    //        }
403    //
404    //        try {
405    //            CmsObject exportCms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
406    //            Map<String, String> props = entry.getProperties(true);
407    //            // if a container page was found in the vfs and
408    //            if (((props.get("export") != null) && props.get("export").equals("true"))
409    //                || exportPropertyAlreadyReadFromVFS) {
410    //                // calculate rfs name
411    //                rfsName = manager.getRfsName(exportCms, entry.getRootPath());
412    //
413    //                String exportUrlStr;
414    //                if (rfsName.contains(manager.getRfsPrefix(vfsName))) {
415    //                    exportUrlStr = manager.getExportUrl() + rfsName;
416    //                } else {
417    //                    exportUrlStr = manager.getExportUrl() + manager.getRfsPrefix(vfsName) + rfsName;
418    //                }
419    //                if (LOG.isDebugEnabled()) {
420    //                    LOG.debug(Messages.get().getBundle().key(Messages.LOG_SENDING_REQUEST_2, rfsName, exportUrlStr));
421    //                }
422    //                // setup the connection and request the resource
423    //                URL exportUrl = new URL(exportUrlStr);
424    //                HttpURLConnection.setFollowRedirects(false);
425    //                HttpURLConnection urlcon = (HttpURLConnection)exportUrl.openConnection();
426    //                // set request type to GET
427    //                urlcon.setRequestMethod(REQUEST_METHOD_GET);
428    //                // add special export header
429    //                urlcon.setRequestProperty(CmsRequestUtil.HEADER_OPENCMS_EXPORT, CmsStringUtil.TRUE);
430    //                // add additional headers if available
431    //                if (manager.getAcceptLanguageHeader() != null) {
432    //                    urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getAcceptLanguageHeader());
433    //                } else {
434    //                    urlcon.setRequestProperty(
435    //                        CmsRequestUtil.HEADER_ACCEPT_LANGUAGE,
436    //                        manager.getDefaultAcceptLanguageHeader());
437    //                }
438    //                if (manager.getAcceptCharsetHeader() != null) {
439    //                    urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getAcceptCharsetHeader());
440    //                } else {
441    //                    urlcon.setRequestProperty(
442    //                        CmsRequestUtil.HEADER_ACCEPT_CHARSET,
443    //                        manager.getDefaultAcceptCharsetHeader());
444    //                }
445    //
446    //                // get the last modified date and add it to the request
447    //                String exportFileName = CmsFileUtil.normalizePath(manager.getExportPath(vfsName) + rfsName);
448    //                File exportFile = new File(exportFileName);
449    //                long dateLastModified = exportFile.lastModified();
450    //                // system folder case
451    //                if (vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
452    //                    // iterate over all rules
453    //                    for (CmsStaticExportRfsRule rule : manager.getRfsRules()) {
454    //                        if (rule.match(vfsName)) {
455    //                            exportFileName = CmsFileUtil.normalizePath(rule.getExportPath() + rfsName);
456    //                            exportFile = new File(exportFileName);
457    //                            if (dateLastModified > exportFile.lastModified()) {
458    //                                dateLastModified = exportFile.lastModified();
459    //                            }
460    //                        }
461    //                    }
462    //                }
463    //                urlcon.setIfModifiedSince(dateLastModified);
464    //                if (LOG.isDebugEnabled()) {
465    //                    LOG.debug(Messages.get().getBundle().key(
466    //                        Messages.LOG_IF_MODIFIED_SINCE_SET_2,
467    //                        exportFile.getName(),
468    //                        new Long((dateLastModified / 1000) * 1000)));
469    //                }
470    //                if (cookies.length() > 0) {
471    //                    // set the cookies, included the session id to keep the same session
472    //                    urlcon.setRequestProperty(REQUEST_PROPERTY_COOKIE, cookies.toString());
473    //                }
474    //
475    //                // now perform the request
476    //                urlcon.connect();
477    //                int status = urlcon.getResponseCode();
478    //
479    //                if (cookies.length() == 0) {
480    //                    //Now retrieve the cookies. The jsessionid is here
481    //                    cookies.append(urlcon.getHeaderField(HEADER_FIELD_SET_COOKIE));
482    //                    if (LOG.isDebugEnabled()) {
483    //                        LOG.debug(Messages.get().getBundle().key(Messages.LOG_STATICEXPORT_COOKIES_1, cookies));
484    //                    }
485    //                }
486    //                urlcon.disconnect();
487    //                if (LOG.isInfoEnabled()) {
488    //                    LOG.info(Messages.get().getBundle().key(
489    //                        Messages.LOG_REQUEST_RESULT_3,
490    //                        rfsName,
491    //                        exportUrlStr,
492    //                        new Integer(status)));
493    //                }
494    //                return status;
495    //            }
496    //        } catch (CmsException e) {
497    //            LOG.error(e.getLocalizedMessage(), e);
498    //        }
499    //        return HttpServletResponse.SC_SEE_OTHER;
500    //    }
501
502    /**
503     * Exports a single (template) resource specified by its export data.<p>
504     *
505     * @param data the export data
506     * @param cookies cookies to keep the session
507     *
508     * @return the status of the http request used to perform the export
509     *
510     * @throws IOException if the http request fails
511     */
512    protected int exportTemplateResource(CmsStaticExportData data, StringBuffer cookies) throws IOException {
513
514        String vfsName = data.getVfsName();
515        String rfsName = data.getRfsName();
516        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
517
518        String exportUrlStr;
519        if (rfsName.contains(manager.getRfsPrefix(vfsName))) {
520            LOG.info("rfsName " + rfsName + " contains rfsPrefix " + manager.getRfsPrefix(vfsName));
521            exportUrlStr = manager.getExportUrl() + rfsName;
522        } else {
523            exportUrlStr = manager.getExportUrl() + manager.getRfsPrefix(vfsName) + rfsName;
524        }
525        if (LOG.isDebugEnabled()) {
526            LOG.debug(Messages.get().getBundle().key(Messages.LOG_SENDING_REQUEST_2, rfsName, exportUrlStr));
527        }
528        // setup the connection and request the resource
529        URL exportUrl = new URL(exportUrlStr);
530        HttpURLConnection.setFollowRedirects(false);
531        HttpURLConnection urlcon = (HttpURLConnection)exportUrl.openConnection();
532        // set request type to GET
533        urlcon.setRequestMethod(REQUEST_METHOD_GET);
534        // add special export header
535        urlcon.setRequestProperty(CmsRequestUtil.HEADER_OPENCMS_EXPORT, CmsStringUtil.TRUE);
536        // add additional headers if available
537        if (manager.getAcceptLanguageHeader() != null) {
538            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getAcceptLanguageHeader());
539        } else {
540            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getDefaultAcceptLanguageHeader());
541        }
542        if (manager.getAcceptCharsetHeader() != null) {
543            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getAcceptCharsetHeader());
544        } else {
545            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getDefaultAcceptCharsetHeader());
546        }
547
548        // get the last modified date and add it to the request
549        String exportFileName = CmsFileUtil.normalizePath(manager.getExportPath(vfsName) + rfsName);
550        File exportFile = new File(exportFileName);
551        long dateLastModified = exportFile.lastModified();
552        // system folder case
553        if (vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM) || OpenCms.getSiteManager().startsWithShared(vfsName)) {
554            // iterate over all rules
555            Iterator<CmsStaticExportRfsRule> it = manager.getRfsRules().iterator();
556            while (it.hasNext()) {
557                CmsStaticExportRfsRule rule = it.next();
558                if (rule.match(vfsName)) {
559                    exportFileName = CmsFileUtil.normalizePath(rule.getExportPath() + rfsName);
560                    exportFile = new File(exportFileName);
561                    if (dateLastModified > exportFile.lastModified()) {
562                        dateLastModified = exportFile.lastModified();
563                    }
564                }
565            }
566        }
567        urlcon.setIfModifiedSince(dateLastModified);
568        if (LOG.isDebugEnabled()) {
569            LOG.debug(
570                Messages.get().getBundle().key(
571                    Messages.LOG_IF_MODIFIED_SINCE_SET_2,
572                    exportFile.getName(),
573                    new Long((dateLastModified / 1000) * 1000)));
574        }
575        if (cookies.length() > 0) {
576            // set the cookies, included the session id to keep the same session
577            urlcon.setRequestProperty(REQUEST_PROPERTY_COOKIE, cookies.toString());
578        }
579
580        // now perform the request
581        urlcon.connect();
582        int status = urlcon.getResponseCode();
583
584        if (cookies.length() == 0) {
585            //Now retrieve the cookies. The jsessionid is here
586            cookies.append(urlcon.getHeaderField(HEADER_FIELD_SET_COOKIE));
587            if (LOG.isDebugEnabled()) {
588                LOG.debug(Messages.get().getBundle().key(Messages.LOG_STATICEXPORT_COOKIES_1, cookies));
589            }
590        }
591        urlcon.disconnect();
592        if (LOG.isInfoEnabled()) {
593            LOG.info(
594                Messages.get().getBundle().key(
595                    Messages.LOG_REQUEST_RESULT_3,
596                    rfsName,
597                    exportUrlStr,
598                    new Integer(status)));
599        }
600        return status;
601    }
602
603    /**
604     * Exports all template resources found in a list of published resources.<p>
605     *
606     * @param cms the cms context, in the root site as Export user
607     * @param publishedTemplateResources list of potential candidates to export
608     * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
609     */
610    protected void exportTemplateResources(CmsObject cms, List<String> publishedTemplateResources, I_CmsReport report) {
611
612        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
613        int size = publishedTemplateResources.size();
614        int count = 1;
615
616        if (LOG.isDebugEnabled()) {
617            LOG.debug(Messages.get().getBundle().key(Messages.LOG_EXPORT_TEMPLATES_1, new Integer(size)));
618        }
619        report.println(
620            Messages.get().container(Messages.RPT_STATICEXPORT_TEMPLATE_RESOURCES_BEGIN_0),
621            I_CmsReport.FORMAT_HEADLINE);
622
623        StringBuffer cookies = new StringBuffer();
624        // now loop through all of them and request them from the server
625        Iterator<String> i = publishedTemplateResources.iterator();
626        while (i.hasNext()) {
627            String rfsName = i.next();
628            CmsStaticExportData data = null;
629            try {
630                data = manager.getVfsNameInternal(cms, rfsName);
631            } catch (CmsVfsResourceNotFoundException e) {
632                String rfsBaseName = rfsName;
633                int pos = rfsName.lastIndexOf('_');
634                if (pos >= 0) {
635                    rfsBaseName = rfsName.substring(0, pos);
636                }
637                try {
638                    data = manager.getVfsNameInternal(cms, rfsBaseName);
639                } catch (CmsVfsResourceNotFoundException e2) {
640                    if (LOG.isInfoEnabled()) {
641                        LOG.info(
642                            Messages.get().getBundle().key(
643                                Messages.LOG_NO_INTERNAL_VFS_RESOURCE_FOUND_1,
644                                new String[] {rfsName}));
645                    }
646                }
647            }
648            if (data != null) {
649                data.setRfsName(rfsName);
650                report.print(
651                    org.opencms.report.Messages.get().container(
652                        org.opencms.report.Messages.RPT_SUCCESSION_2,
653                        new Integer(count++),
654                        new Integer(size)),
655                    I_CmsReport.FORMAT_NOTE);
656                report.print(Messages.get().container(Messages.RPT_EXPORTING_0), I_CmsReport.FORMAT_NOTE);
657                report.print(
658                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ARGUMENT_1, rfsName));
659                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
660            } else {
661                // no valid resource found for rfs name (already deleted), skip it
662                continue;
663            }
664
665            try {
666                CmsResource resource = data.getResource();
667                try {
668                    Collection<String> detailPages = CmsDetailPageUtil.getAllDetailPagesWithUrlName(cms, resource);
669                    for (String detailPageUri : detailPages) {
670                        String altRfsName = manager.getRfsName(cms, detailPageUri);
671                        CmsStaticExportData detailData = new CmsStaticExportData(
672                            data.getVfsName(),
673                            altRfsName,
674                            data.getResource(),
675                            data.getParameters());
676                        exportTemplateResource(detailData, cookies);
677                    }
678                } catch (CmsException e) {
679                    LOG.error(e.getLocalizedMessage(), e);
680                }
681
682                int status = exportTemplateResource(data, cookies);
683
684                // write the report
685                if (status == HttpServletResponse.SC_OK) {
686                    report.println(
687                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
688                        I_CmsReport.FORMAT_OK);
689                } else if (status == HttpServletResponse.SC_NOT_MODIFIED) {
690                    report.println(
691                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0),
692                        I_CmsReport.FORMAT_NOTE);
693                } else if (status == HttpServletResponse.SC_SEE_OTHER) {
694                    report.println(
695                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_IGNORED_0),
696                        I_CmsReport.FORMAT_NOTE);
697                } else {
698                    report.println(
699                        org.opencms.report.Messages.get().container(
700                            org.opencms.report.Messages.RPT_ARGUMENT_1,
701                            new Integer(status)),
702                        I_CmsReport.FORMAT_OK);
703                }
704            } catch (IOException e) {
705                report.println(e);
706            }
707            //don't lock up the CPU exclusively - allow other Threads to run as well
708            Thread.yield();
709        }
710        report.println(
711            Messages.get().container(Messages.RPT_STATICEXPORT_TEMPLATE_RESOURCES_END_0),
712            I_CmsReport.FORMAT_HEADLINE);
713    }
714
715    /**
716     * @see org.opencms.staticexport.A_CmsStaticExportHandler#getRelatedFilesToPurge(java.lang.String, java.lang.String)
717     */
718    @Override
719    protected List<File> getRelatedFilesToPurge(String exportFileName, String vfsName) {
720
721        return Collections.emptyList();
722    }
723
724    /**
725     * Creates a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects containing all related resources of the VFS tree.<p>
726     *
727     * If the static export has been triggered by the OpenCms workplace, publishedResources is null and all resources in the VFS tree are returned.<p>
728     * If really an after publish static export is triggered, then only the related resources are returned.<p>
729     *
730     * @param cms the current cms object
731     * @param publishedResources the list of published resources
732     *
733     * @return list of CmsPulishedResource objects containing all resources of the VFS tree
734     *
735     * @throws CmsException in case of errors accessing the VFS
736     */
737    protected List<CmsPublishedResource> getRelatedResources(
738        CmsObject cms,
739        List<CmsPublishedResource> publishedResources)
740    throws CmsException {
741
742        String storedSiteRoot = cms.getRequestContext().getSiteRoot();
743        try {
744            // switch to root site
745            cms.getRequestContext().setSiteRoot("/");
746            if (publishedResources == null) {
747                // full static export
748                return getAllResources(cms);
749            } else {
750                // after publish export
751                Map<String, CmsPublishedResource> resourceMap = new HashMap<String, CmsPublishedResource>();
752                Iterator<CmsPublishedResource> itPubRes = publishedResources.iterator();
753                while (itPubRes.hasNext()) {
754                    CmsPublishedResource pubResource = itPubRes.next();
755                    // check the internal flag if the resource does still exist
756                    // we cannot export with an internal flag
757                    if (cms.existsResource(pubResource.getRootPath())) {
758                        CmsResource vfsResource = cms.readResource(pubResource.getRootPath());
759                        if (!vfsResource.isInternal()) {
760                            // add only if not internal
761                            // additionally, add all siblings of the resource
762                            Iterator<CmsPublishedResource> itSiblings = getSiblings(cms, pubResource).iterator();
763                            while (itSiblings.hasNext()) {
764                                CmsPublishedResource sibling = itSiblings.next();
765                                resourceMap.put(sibling.getRootPath(), sibling);
766                            }
767                        }
768                    } else {
769                        // the resource does not exist, so add them for deletion in the static export
770                        resourceMap.put(pubResource.getRootPath(), pubResource);
771                    }
772
773                    boolean match = false;
774                    Iterator<CmsStaticExportExportRule> itExportRules = OpenCms.getStaticExportManager().getExportRules().iterator();
775                    while (itExportRules.hasNext()) {
776                        CmsStaticExportExportRule rule = itExportRules.next();
777                        Set<CmsPublishedResource> relatedResources = rule.getRelatedResources(cms, pubResource);
778                        if (relatedResources != null) {
779                            Iterator<CmsPublishedResource> itRelatedRes = relatedResources.iterator();
780                            while (itRelatedRes.hasNext()) {
781                                CmsPublishedResource relatedRes = itRelatedRes.next();
782                                resourceMap.put(relatedRes.getRootPath(), relatedRes);
783                            }
784                            match = true;
785                        }
786                    }
787                    // if one res does not match any rule, then export all files
788                    if (!match) {
789                        return getAllResources(cms);
790                    }
791                }
792                return new ArrayList<CmsPublishedResource>(resourceMap.values());
793            }
794        } finally {
795            cms.getRequestContext().setSiteRoot(storedSiteRoot);
796        }
797    }
798
799    /**
800     * Returns all siblings of the published resource as list of <code>CmsPublishedResource</code>.<p>
801     *
802     * @param cms the cms object
803     * @param pubResource the published resource
804     *
805     * @return all siblings of the published resource
806     *
807     * @throws CmsException if something goes wrong
808     */
809    protected Set<CmsPublishedResource> getSiblings(CmsObject cms, CmsPublishedResource pubResource)
810    throws CmsException {
811
812        Set<CmsPublishedResource> siblings = new HashSet<CmsPublishedResource>();
813        for (Iterator<String> i = getSiblingsList(cms, pubResource.getRootPath()).iterator(); i.hasNext();) {
814            String sibling = i.next();
815            siblings.add(new CmsPublishedResource(cms.readResource(sibling)));
816        }
817        return siblings;
818    }
819
820    /**
821     * Returns all non template resources found in a list of published resources.<p>
822     *
823     * @param cms the current cms object
824     * @param publishedResources the list of published resources
825     * @param resourcesToExport the list of non-template resources
826     *
827     * @return <code>true</code> if some template resources were found while looping the list of published resources
828     *
829     * @throws CmsException in case of errors accessing the VFS
830     */
831    protected boolean readNonTemplateResourcesToExport(
832        CmsObject cms,
833        List<CmsPublishedResource> publishedResources,
834        List<CmsStaticExportData> resourcesToExport)
835    throws CmsException {
836
837        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
838        boolean templatesFound = false;
839        // loop through all resources
840        Iterator<CmsPublishedResource> i = publishedResources.iterator();
841        while (i.hasNext()) {
842            CmsPublishedResource pubRes = i.next();
843            String vfsName = pubRes.getRootPath();
844            // only process this resource, if it is within the tree of allowed folders for static export
845            if (manager.getExportFolderMatcher().match(vfsName)) {
846                // get the export data object, if null is returned, this resource cannot be exported
847                CmsStaticExportData exportData = manager.getVfsExportData(cms, vfsName);
848                if (exportData != null) {
849                    CmsResource resource = null;
850                    if (pubRes.isFile()) {
851                        resource = exportData.getResource();
852                    } else {
853                        // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder
854                        try {
855                            resource = cms.readDefaultFile(vfsName);
856                            //                            String defaultFileName = cms.readPropertyObject(
857                            //                                vfsName,
858                            //                                CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
859                            //                                false).getValue();
860                            //                            if (defaultFileName != null) {
861                            //                                resource = cms.readResource(vfsName + defaultFileName);
862                            //                            }
863                        } catch (CmsException e) {
864                            // resource is (still) a folder, check default files specified in configuration
865                            for (int j = 0; j < OpenCms.getDefaultFiles().size(); j++) {
866                                String tmpResourceName = vfsName + OpenCms.getDefaultFiles().get(j);
867                                try {
868                                    resource = cms.readResource(tmpResourceName);
869                                    break;
870                                } catch (CmsException e1) {
871                                    // ignore all other exceptions and continue the lookup process
872                                }
873                            }
874                        }
875                    }
876                    if ((resource != null) && resource.isFile()) {
877                        // check loader for current resource if it must be processed before exported
878                        I_CmsResourceLoader loader = OpenCms.getResourceManager().getLoader(resource);
879                        if (!loader.isStaticExportProcessable()) {
880                            // this resource must not be processed, so export it (if it's not marked as deleted)
881                            if (!pubRes.getState().isDeleted()) {
882                                // mark the resource for export to the real file system
883                                resourcesToExport.add(exportData);
884                            }
885                        } else {
886                            // the resource is a template resource or a folder, so store the name of it in the DB for further use
887                            templatesFound = true;
888                            cms.writeStaticExportPublishedResource(
889                                exportData.getRfsName(),
890                                CmsStaticExportManager.EXPORT_LINK_WITHOUT_PARAMETER,
891                                "",
892                                System.currentTimeMillis());
893                        }
894                    }
895                }
896            }
897        }
898
899        return templatesFound;
900    }
901}