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