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) throws CmsException, IOException, ServletException {
297
298        report.println(
299            Messages.get().container(Messages.RPT_STATICEXPORT_NONTEMPLATE_RESOURCES_BEGIN_0),
300            I_CmsReport.FORMAT_HEADLINE);
301
302        if (LOG.isDebugEnabled()) {
303            LOG.debug(
304                Messages.get().getBundle().key(
305                    Messages.LOG_EXPORTING_NON_TEMPLATE_1,
306                    new Integer(publishedResources.size())));
307        }
308
309        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
310        List<CmsStaticExportData> resourcesToExport = new ArrayList<CmsStaticExportData>();
311        boolean templatesFound = readNonTemplateResourcesToExport(cms, publishedResources, resourcesToExport);
312
313        int count = 1;
314        int size = resourcesToExport.size();
315        if (LOG.isDebugEnabled()) {
316            LOG.debug(Messages.get().getBundle().key(Messages.LOG_NUM_EXPORT_1, new Integer(size)));
317        }
318        // now do the export
319        Iterator<CmsStaticExportData> i = resourcesToExport.iterator();
320        while (i.hasNext()) {
321            CmsStaticExportData exportData = i.next();
322            if (LOG.isDebugEnabled()) {
323                LOG.debug(
324                    Messages.get().getBundle().key(
325                        Messages.LOG_EXPORT_FILE_2,
326                        exportData.getVfsName(),
327                        exportData.getRfsName()));
328            }
329
330            report.print(
331                org.opencms.report.Messages.get().container(
332                    org.opencms.report.Messages.RPT_SUCCESSION_2,
333                    new Integer(count++),
334                    new Integer(size)),
335                I_CmsReport.FORMAT_NOTE);
336            report.print(Messages.get().container(Messages.RPT_EXPORTING_0), I_CmsReport.FORMAT_NOTE);
337            report.print(
338                org.opencms.report.Messages.get().container(
339                    org.opencms.report.Messages.RPT_ARGUMENT_1,
340                    exportData.getVfsName()));
341            report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
342            int status = manager.export(null, null, cms, exportData);
343            if (status == HttpServletResponse.SC_OK) {
344                report.println(
345                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
346                    I_CmsReport.FORMAT_OK);
347            } else {
348                report.println(
349                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_IGNORED_0),
350                    I_CmsReport.FORMAT_NOTE);
351            }
352
353            if (LOG.isInfoEnabled()) {
354                Object[] arguments = new Object[] {
355                    exportData.getVfsName(),
356                    exportData.getRfsName(),
357                    new Integer(status)};
358                LOG.info(Messages.get().getBundle().key(Messages.LOG_EXPORT_FILE_STATUS_3, arguments));
359            }
360            //don't lock up the CPU exclusively - allow other Threads to run as well
361            Thread.yield();
362        }
363
364        resourcesToExport = null;
365
366        report.println(
367            Messages.get().container(Messages.RPT_STATICEXPORT_NONTEMPLATE_RESOURCES_END_0),
368            I_CmsReport.FORMAT_HEADLINE);
369
370        return templatesFound;
371    }
372
373    /**
374     * Exports all sitemap resources found in a list of published resources.<p>
375     *
376     * @param entry the sitemap entry
377     * @param data the export data
378     * @param cookies cookies to keep the session
379     *
380     * @return the status of the http request used to perform the export
381     *
382     * @throws IOException if something goes wrong
383     */
384    //    protected int exportSitemapResource(CmsSitemapEntry entry, CmsStaticExportData data, StringBuffer cookies)
385    //    throws IOException {
386    //
387    //        String vfsName = data.getVfsName();
388    //        String rfsName = data.getRfsName();
389    //        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
390    //
391    //        // check if the resource to export is a detail page
392    //        // TODO: later when the property concept is finished this info is unnecessary
393    //        String path = vfsName;
394    //        boolean exportPropertyAlreadyReadFromVFS = false;
395    //        if (path.endsWith("/")) {
396    //            path = path.substring(0, path.length() - 1);
397    //            String detailId = CmsResource.getName(path);
398    //            if (CmsUUID.isValidUUID(detailId)) {
399    //                exportPropertyAlreadyReadFromVFS = true;
400    //            }
401    //        }
402    //
403    //        try {
404    //            CmsObject exportCms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserExport());
405    //            Map<String, String> props = entry.getProperties(true);
406    //            // if a container page was found in the vfs and
407    //            if (((props.get("export") != null) && props.get("export").equals("true"))
408    //                || exportPropertyAlreadyReadFromVFS) {
409    //                // calculate rfs name
410    //                rfsName = manager.getRfsName(exportCms, entry.getRootPath());
411    //
412    //                String exportUrlStr;
413    //                if (rfsName.contains(manager.getRfsPrefix(vfsName))) {
414    //                    exportUrlStr = manager.getExportUrl() + rfsName;
415    //                } else {
416    //                    exportUrlStr = manager.getExportUrl() + manager.getRfsPrefix(vfsName) + rfsName;
417    //                }
418    //                if (LOG.isDebugEnabled()) {
419    //                    LOG.debug(Messages.get().getBundle().key(Messages.LOG_SENDING_REQUEST_2, rfsName, exportUrlStr));
420    //                }
421    //                // setup the connection and request the resource
422    //                URL exportUrl = new URL(exportUrlStr);
423    //                HttpURLConnection.setFollowRedirects(false);
424    //                HttpURLConnection urlcon = (HttpURLConnection)exportUrl.openConnection();
425    //                // set request type to GET
426    //                urlcon.setRequestMethod(REQUEST_METHOD_GET);
427    //                // add special export header
428    //                urlcon.setRequestProperty(CmsRequestUtil.HEADER_OPENCMS_EXPORT, CmsStringUtil.TRUE);
429    //                // add additional headers if available
430    //                if (manager.getAcceptLanguageHeader() != null) {
431    //                    urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getAcceptLanguageHeader());
432    //                } else {
433    //                    urlcon.setRequestProperty(
434    //                        CmsRequestUtil.HEADER_ACCEPT_LANGUAGE,
435    //                        manager.getDefaultAcceptLanguageHeader());
436    //                }
437    //                if (manager.getAcceptCharsetHeader() != null) {
438    //                    urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getAcceptCharsetHeader());
439    //                } else {
440    //                    urlcon.setRequestProperty(
441    //                        CmsRequestUtil.HEADER_ACCEPT_CHARSET,
442    //                        manager.getDefaultAcceptCharsetHeader());
443    //                }
444    //
445    //                // get the last modified date and add it to the request
446    //                String exportFileName = CmsFileUtil.normalizePath(manager.getExportPath(vfsName) + rfsName);
447    //                File exportFile = new File(exportFileName);
448    //                long dateLastModified = exportFile.lastModified();
449    //                // system folder case
450    //                if (vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
451    //                    // iterate over all rules
452    //                    for (CmsStaticExportRfsRule rule : manager.getRfsRules()) {
453    //                        if (rule.match(vfsName)) {
454    //                            exportFileName = CmsFileUtil.normalizePath(rule.getExportPath() + rfsName);
455    //                            exportFile = new File(exportFileName);
456    //                            if (dateLastModified > exportFile.lastModified()) {
457    //                                dateLastModified = exportFile.lastModified();
458    //                            }
459    //                        }
460    //                    }
461    //                }
462    //                urlcon.setIfModifiedSince(dateLastModified);
463    //                if (LOG.isDebugEnabled()) {
464    //                    LOG.debug(Messages.get().getBundle().key(
465    //                        Messages.LOG_IF_MODIFIED_SINCE_SET_2,
466    //                        exportFile.getName(),
467    //                        new Long((dateLastModified / 1000) * 1000)));
468    //                }
469    //                if (cookies.length() > 0) {
470    //                    // set the cookies, included the session id to keep the same session
471    //                    urlcon.setRequestProperty(REQUEST_PROPERTY_COOKIE, cookies.toString());
472    //                }
473    //
474    //                // now perform the request
475    //                urlcon.connect();
476    //                int status = urlcon.getResponseCode();
477    //
478    //                if (cookies.length() == 0) {
479    //                    //Now retrieve the cookies. The jsessionid is here
480    //                    cookies.append(urlcon.getHeaderField(HEADER_FIELD_SET_COOKIE));
481    //                    if (LOG.isDebugEnabled()) {
482    //                        LOG.debug(Messages.get().getBundle().key(Messages.LOG_STATICEXPORT_COOKIES_1, cookies));
483    //                    }
484    //                }
485    //                urlcon.disconnect();
486    //                if (LOG.isInfoEnabled()) {
487    //                    LOG.info(Messages.get().getBundle().key(
488    //                        Messages.LOG_REQUEST_RESULT_3,
489    //                        rfsName,
490    //                        exportUrlStr,
491    //                        new Integer(status)));
492    //                }
493    //                return status;
494    //            }
495    //        } catch (CmsException e) {
496    //            LOG.error(e.getLocalizedMessage(), e);
497    //        }
498    //        return HttpServletResponse.SC_SEE_OTHER;
499    //    }
500
501    /**
502     * Exports a single (template) resource specified by its export data.<p>
503     *
504     * @param data the export data
505     * @param cookies cookies to keep the session
506     *
507     * @return the status of the http request used to perform the export
508     *
509     * @throws IOException if the http request fails
510     */
511    protected int exportTemplateResource(CmsStaticExportData data, StringBuffer cookies) throws IOException {
512
513        String vfsName = data.getVfsName();
514        String rfsName = data.getRfsName();
515        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
516
517        String exportUrlStr;
518        if (rfsName.contains(manager.getRfsPrefix(vfsName))) {
519            LOG.info("rfsName " + rfsName + " contains rfsPrefix " + manager.getRfsPrefix(vfsName));
520            exportUrlStr = manager.getExportUrl() + rfsName;
521        } else {
522            exportUrlStr = manager.getExportUrl() + manager.getRfsPrefix(vfsName) + rfsName;
523        }
524        if (LOG.isDebugEnabled()) {
525            LOG.debug(Messages.get().getBundle().key(Messages.LOG_SENDING_REQUEST_2, rfsName, exportUrlStr));
526        }
527        // setup the connection and request the resource
528        URL exportUrl = new URL(exportUrlStr);
529        HttpURLConnection.setFollowRedirects(false);
530        HttpURLConnection urlcon = (HttpURLConnection)exportUrl.openConnection();
531        // set request type to GET
532        urlcon.setRequestMethod(REQUEST_METHOD_GET);
533        // add special export header
534        urlcon.setRequestProperty(CmsRequestUtil.HEADER_OPENCMS_EXPORT, CmsStringUtil.TRUE);
535        // add additional headers if available
536        if (manager.getAcceptLanguageHeader() != null) {
537            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getAcceptLanguageHeader());
538        } else {
539            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_LANGUAGE, manager.getDefaultAcceptLanguageHeader());
540        }
541        if (manager.getAcceptCharsetHeader() != null) {
542            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getAcceptCharsetHeader());
543        } else {
544            urlcon.setRequestProperty(CmsRequestUtil.HEADER_ACCEPT_CHARSET, manager.getDefaultAcceptCharsetHeader());
545        }
546
547        // get the last modified date and add it to the request
548        String exportFileName = CmsFileUtil.normalizePath(manager.getExportPath(vfsName) + rfsName);
549        File exportFile = new File(exportFileName);
550        long dateLastModified = exportFile.lastModified();
551        // system folder case
552        if (vfsName.startsWith(CmsWorkplace.VFS_PATH_SYSTEM)) {
553            // iterate over all rules
554            Iterator<CmsStaticExportRfsRule> it = manager.getRfsRules().iterator();
555            while (it.hasNext()) {
556                CmsStaticExportRfsRule rule = it.next();
557                if (rule.match(vfsName)) {
558                    exportFileName = CmsFileUtil.normalizePath(rule.getExportPath() + rfsName);
559                    exportFile = new File(exportFileName);
560                    if (dateLastModified > exportFile.lastModified()) {
561                        dateLastModified = exportFile.lastModified();
562                    }
563                }
564            }
565        }
566        urlcon.setIfModifiedSince(dateLastModified);
567        if (LOG.isDebugEnabled()) {
568            LOG.debug(
569                Messages.get().getBundle().key(
570                    Messages.LOG_IF_MODIFIED_SINCE_SET_2,
571                    exportFile.getName(),
572                    new Long((dateLastModified / 1000) * 1000)));
573        }
574        if (cookies.length() > 0) {
575            // set the cookies, included the session id to keep the same session
576            urlcon.setRequestProperty(REQUEST_PROPERTY_COOKIE, cookies.toString());
577        }
578
579        // now perform the request
580        urlcon.connect();
581        int status = urlcon.getResponseCode();
582
583        if (cookies.length() == 0) {
584            //Now retrieve the cookies. The jsessionid is here
585            cookies.append(urlcon.getHeaderField(HEADER_FIELD_SET_COOKIE));
586            if (LOG.isDebugEnabled()) {
587                LOG.debug(Messages.get().getBundle().key(Messages.LOG_STATICEXPORT_COOKIES_1, cookies));
588            }
589        }
590        urlcon.disconnect();
591        if (LOG.isInfoEnabled()) {
592            LOG.info(
593                Messages.get().getBundle().key(
594                    Messages.LOG_REQUEST_RESULT_3,
595                    rfsName,
596                    exportUrlStr,
597                    new Integer(status)));
598        }
599        return status;
600    }
601
602    /**
603     * Exports all template resources found in a list of published resources.<p>
604     *
605     * @param cms the cms context, in the root site as Export user
606     * @param publishedTemplateResources list of potential candidates to export
607     * @param report an I_CmsReport instance to print output message, or null to write messages to the log file
608     */
609    protected void exportTemplateResources(CmsObject cms, List<String> publishedTemplateResources, I_CmsReport report) {
610
611        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
612        int size = publishedTemplateResources.size();
613        int count = 1;
614
615        if (LOG.isDebugEnabled()) {
616            LOG.debug(Messages.get().getBundle().key(Messages.LOG_EXPORT_TEMPLATES_1, new Integer(size)));
617        }
618        report.println(
619            Messages.get().container(Messages.RPT_STATICEXPORT_TEMPLATE_RESOURCES_BEGIN_0),
620            I_CmsReport.FORMAT_HEADLINE);
621
622        StringBuffer cookies = new StringBuffer();
623        // now loop through all of them and request them from the server
624        Iterator<String> i = publishedTemplateResources.iterator();
625        while (i.hasNext()) {
626            String rfsName = i.next();
627            CmsStaticExportData data = null;
628            try {
629                data = manager.getVfsNameInternal(cms, rfsName);
630            } catch (CmsVfsResourceNotFoundException e) {
631                String rfsBaseName = rfsName;
632                int pos = rfsName.lastIndexOf('_');
633                if (pos >= 0) {
634                    rfsBaseName = rfsName.substring(0, pos);
635                }
636                try {
637                    data = manager.getVfsNameInternal(cms, rfsBaseName);
638                } catch (CmsVfsResourceNotFoundException e2) {
639                    if (LOG.isInfoEnabled()) {
640                        LOG.info(
641                            Messages.get().getBundle().key(
642                                Messages.LOG_NO_INTERNAL_VFS_RESOURCE_FOUND_1,
643                                new String[] {rfsName}));
644                    }
645                }
646            }
647            if (data != null) {
648                data.setRfsName(rfsName);
649                report.print(
650                    org.opencms.report.Messages.get().container(
651                        org.opencms.report.Messages.RPT_SUCCESSION_2,
652                        new Integer(count++),
653                        new Integer(size)),
654                    I_CmsReport.FORMAT_NOTE);
655                report.print(Messages.get().container(Messages.RPT_EXPORTING_0), I_CmsReport.FORMAT_NOTE);
656                report.print(
657                    org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_ARGUMENT_1, rfsName));
658                report.print(org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_DOTS_0));
659            } else {
660                // no valid resource found for rfs name (already deleted), skip it
661                continue;
662            }
663
664            try {
665                CmsResource resource = data.getResource();
666                try {
667                    Collection<String> detailPages = CmsDetailPageUtil.getAllDetailPagesWithUrlName(cms, resource);
668                    for (String detailPageUri : detailPages) {
669                        String altRfsName = manager.getRfsName(cms, detailPageUri);
670                        CmsStaticExportData detailData = new CmsStaticExportData(
671                            data.getVfsName(),
672                            altRfsName,
673                            data.getResource(),
674                            data.getParameters());
675                        exportTemplateResource(detailData, cookies);
676                    }
677                } catch (CmsException e) {
678                    LOG.error(e.getLocalizedMessage(), e);
679                }
680
681                int status = exportTemplateResource(data, cookies);
682
683                // write the report
684                if (status == HttpServletResponse.SC_OK) {
685                    report.println(
686                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_OK_0),
687                        I_CmsReport.FORMAT_OK);
688                } else if (status == HttpServletResponse.SC_NOT_MODIFIED) {
689                    report.println(
690                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_SKIPPED_0),
691                        I_CmsReport.FORMAT_NOTE);
692                } else if (status == HttpServletResponse.SC_SEE_OTHER) {
693                    report.println(
694                        org.opencms.report.Messages.get().container(org.opencms.report.Messages.RPT_IGNORED_0),
695                        I_CmsReport.FORMAT_NOTE);
696                } else {
697                    report.println(
698                        org.opencms.report.Messages.get().container(
699                            org.opencms.report.Messages.RPT_ARGUMENT_1,
700                            new Integer(status)),
701                        I_CmsReport.FORMAT_OK);
702                }
703            } catch (IOException e) {
704                report.println(e);
705            }
706            //don't lock up the CPU exclusively - allow other Threads to run as well
707            Thread.yield();
708        }
709        report.println(
710            Messages.get().container(Messages.RPT_STATICEXPORT_TEMPLATE_RESOURCES_END_0),
711            I_CmsReport.FORMAT_HEADLINE);
712    }
713
714    /**
715     * @see org.opencms.staticexport.A_CmsStaticExportHandler#getRelatedFilesToPurge(java.lang.String, java.lang.String)
716     */
717    @Override
718    protected List<File> getRelatedFilesToPurge(String exportFileName, String vfsName) {
719
720        return Collections.emptyList();
721    }
722
723    /**
724     * Creates a list of <code>{@link org.opencms.db.CmsPublishedResource}</code> objects containing all related resources of the VFS tree.<p>
725     *
726     * If the static export has been triggered by the OpenCms workplace, publishedResources is null and all resources in the VFS tree are returned.<p>
727     * If really an after publish static export is triggered, then only the related resources are returned.<p>
728     *
729     * @param cms the current cms object
730     * @param publishedResources the list of published resources
731     *
732     * @return list of CmsPulishedResource objects containing all resources of the VFS tree
733     *
734     * @throws CmsException in case of errors accessing the VFS
735     */
736    protected List<CmsPublishedResource> getRelatedResources(
737        CmsObject cms,
738        List<CmsPublishedResource> publishedResources) throws CmsException {
739
740        String storedSiteRoot = cms.getRequestContext().getSiteRoot();
741        try {
742            // switch to root site
743            cms.getRequestContext().setSiteRoot("/");
744            if (publishedResources == null) {
745                // full static export
746                return getAllResources(cms);
747            } else {
748                // after publish export
749                Map<String, CmsPublishedResource> resourceMap = new HashMap<String, CmsPublishedResource>();
750                Iterator<CmsPublishedResource> itPubRes = publishedResources.iterator();
751                while (itPubRes.hasNext()) {
752                    CmsPublishedResource pubResource = itPubRes.next();
753                    // check the internal flag if the resource does still exist
754                    // we cannot export with an internal flag
755                    if (cms.existsResource(pubResource.getRootPath())) {
756                        CmsResource vfsResource = cms.readResource(pubResource.getRootPath());
757                        if (!vfsResource.isInternal()) {
758                            // add only if not internal
759                            // additionally, add all siblings of the resource
760                            Iterator<CmsPublishedResource> itSiblings = getSiblings(cms, pubResource).iterator();
761                            while (itSiblings.hasNext()) {
762                                CmsPublishedResource sibling = itSiblings.next();
763                                resourceMap.put(sibling.getRootPath(), sibling);
764                            }
765                        }
766                    } else {
767                        // the resource does not exist, so add them for deletion in the static export
768                        resourceMap.put(pubResource.getRootPath(), pubResource);
769                    }
770
771                    boolean match = false;
772                    Iterator<CmsStaticExportExportRule> itExportRules = OpenCms.getStaticExportManager().getExportRules().iterator();
773                    while (itExportRules.hasNext()) {
774                        CmsStaticExportExportRule rule = itExportRules.next();
775                        Set<CmsPublishedResource> relatedResources = rule.getRelatedResources(cms, pubResource);
776                        if (relatedResources != null) {
777                            Iterator<CmsPublishedResource> itRelatedRes = relatedResources.iterator();
778                            while (itRelatedRes.hasNext()) {
779                                CmsPublishedResource relatedRes = itRelatedRes.next();
780                                resourceMap.put(relatedRes.getRootPath(), relatedRes);
781                            }
782                            match = true;
783                        }
784                    }
785                    // if one res does not match any rule, then export all files
786                    if (!match) {
787                        return getAllResources(cms);
788                    }
789                }
790                return new ArrayList<CmsPublishedResource>(resourceMap.values());
791            }
792        } finally {
793            cms.getRequestContext().setSiteRoot(storedSiteRoot);
794        }
795    }
796
797    /**
798     * Returns all siblings of the published resource as list of <code>CmsPublishedResource</code>.<p>
799     *
800     * @param cms the cms object
801     * @param pubResource the published resource
802     *
803     * @return all siblings of the published resource
804     *
805     * @throws CmsException if something goes wrong
806     */
807    protected Set<CmsPublishedResource> getSiblings(CmsObject cms, CmsPublishedResource pubResource)
808    throws CmsException {
809
810        Set<CmsPublishedResource> siblings = new HashSet<CmsPublishedResource>();
811        for (Iterator<String> i = getSiblingsList(cms, pubResource.getRootPath()).iterator(); i.hasNext();) {
812            String sibling = i.next();
813            siblings.add(new CmsPublishedResource(cms.readResource(sibling)));
814        }
815        return siblings;
816    }
817
818    /**
819     * Returns all non template resources found in a list of published resources.<p>
820     *
821     * @param cms the current cms object
822     * @param publishedResources the list of published resources
823     * @param resourcesToExport the list of non-template resources
824     *
825     * @return <code>true</code> if some template resources were found while looping the list of published resources
826     *
827     * @throws CmsException in case of errors accessing the VFS
828     */
829    protected boolean readNonTemplateResourcesToExport(
830        CmsObject cms,
831        List<CmsPublishedResource> publishedResources,
832        List<CmsStaticExportData> resourcesToExport) throws CmsException {
833
834        CmsStaticExportManager manager = OpenCms.getStaticExportManager();
835        boolean templatesFound = false;
836        // loop through all resources
837        Iterator<CmsPublishedResource> i = publishedResources.iterator();
838        while (i.hasNext()) {
839            CmsPublishedResource pubRes = i.next();
840            String vfsName = pubRes.getRootPath();
841            // only process this resource, if it is within the tree of allowed folders for static export
842            if (manager.getExportFolderMatcher().match(vfsName)) {
843                // get the export data object, if null is returned, this resource cannot be exported
844                CmsStaticExportData exportData = manager.getVfsExportData(cms, vfsName);
845                if (exportData != null) {
846                    CmsResource resource = null;
847                    if (pubRes.isFile()) {
848                        resource = exportData.getResource();
849                    } else {
850                        // the resource is a folder, check if PROPERTY_DEFAULT_FILE is set on folder
851                        try {
852                            resource = cms.readDefaultFile(vfsName);
853                            //                            String defaultFileName = cms.readPropertyObject(
854                            //                                vfsName,
855                            //                                CmsPropertyDefinition.PROPERTY_DEFAULT_FILE,
856                            //                                false).getValue();
857                            //                            if (defaultFileName != null) {
858                            //                                resource = cms.readResource(vfsName + defaultFileName);
859                            //                            }
860                        } catch (CmsException e) {
861                            // resource is (still) a folder, check default files specified in configuration
862                            for (int j = 0; j < OpenCms.getDefaultFiles().size(); j++) {
863                                String tmpResourceName = vfsName + OpenCms.getDefaultFiles().get(j);
864                                try {
865                                    resource = cms.readResource(tmpResourceName);
866                                    break;
867                                } catch (CmsException e1) {
868                                    // ignore all other exceptions and continue the lookup process
869                                }
870                            }
871                        }
872                    }
873                    if ((resource != null) && resource.isFile()) {
874                        // check loader for current resource if it must be processed before exported
875                        I_CmsResourceLoader loader = OpenCms.getResourceManager().getLoader(resource);
876                        if (!loader.isStaticExportProcessable()) {
877                            // this resource must not be processed, so export it (if it's not marked as deleted)
878                            if (!pubRes.getState().isDeleted()) {
879                                // mark the resource for export to the real file system
880                                resourcesToExport.add(exportData);
881                            }
882                        } else {
883                            // the resource is a template resource or a folder, so store the name of it in the DB for further use
884                            templatesFound = true;
885                            cms.writeStaticExportPublishedResource(
886                                exportData.getRfsName(),
887                                CmsStaticExportManager.EXPORT_LINK_WITHOUT_PARAMETER,
888                                "",
889                                System.currentTimeMillis());
890                        }
891                    }
892                }
893            }
894        }
895
896        return templatesFound;
897    }
898}