001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (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, 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.site.xmlsitemap;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.file.CmsResourceFilter;
033import org.opencms.file.types.I_CmsResourceType;
034import org.opencms.jsp.CmsJspActionElement;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsLog;
037import org.opencms.main.OpenCms;
038import org.opencms.util.CmsFileUtil;
039import org.opencms.util.CmsStringUtil;
040
041import java.lang.reflect.Constructor;
042import java.util.List;
043
044import javax.servlet.http.HttpServletRequest;
045import javax.servlet.http.HttpServletResponse;
046import javax.servlet.jsp.PageContext;
047
048import org.apache.commons.logging.Log;
049
050/**
051 * Action element class for displaying the XML sitemap from a JSP.<p>
052 */
053public class CmsXmlSitemapActionElement extends CmsJspActionElement {
054
055    /** The logger instance for this class. */
056    private static final Log LOG = CmsLog.getLog(CmsXmlSitemapActionElement.class);
057
058    /** Runtime property name for the default sitemap generator class. */
059    private static final String PARAM_DEFAULT_SITEMAP_GENERATOR = "sitemap.generator";
060
061    /** The configuration bean. */
062    protected CmsXmlSeoConfiguration m_configuration;
063
064    /**
065     * Constructor, with parameters.
066     *
067     * @param pageContext the JSP page context object
068     * @param request the JSP request
069     * @param response the JSP response
070     */
071    public CmsXmlSitemapActionElement(
072        PageContext pageContext,
073        HttpServletRequest request,
074        HttpServletResponse response) {
075
076        super(pageContext, request, response);
077    }
078
079    /**
080     * Creates an XML sitemap generator instance given a class name and the root path for the sitemap.<p>
081     *
082     * @param className the class name of the sitemap generator (may be null for the default
083     * @param folderRootPath the root path of the start folder for the sitemap
084     * @return the sitemap generator instance
085     *
086     * @throws CmsException if something goes wrong
087     */
088    public static CmsXmlSitemapGenerator createSitemapGenerator(String className, String folderRootPath)
089    throws CmsException {
090
091        if (CmsStringUtil.isEmptyOrWhitespaceOnly(className)) {
092            className = (String)(OpenCms.getRuntimeProperty(PARAM_DEFAULT_SITEMAP_GENERATOR));
093        }
094        if (CmsStringUtil.isEmptyOrWhitespaceOnly(className)) {
095            className = CmsXmlSitemapGenerator.class.getName();
096        }
097        try {
098            Class<? extends CmsXmlSitemapGenerator> generatorClass = Class.forName(className).asSubclass(
099                CmsXmlSitemapGenerator.class);
100            Constructor<? extends CmsXmlSitemapGenerator> constructor = generatorClass.getConstructor(String.class);
101            CmsXmlSitemapGenerator generator = constructor.newInstance(folderRootPath);
102            return generator;
103        } catch (Exception e) {
104            LOG.error(
105                "Could not create configured sitemap generator " + className + ", using the default class instead",
106                e);
107            return new CmsXmlSitemapGenerator(folderRootPath);
108        }
109    }
110
111    /**
112     * Constructs an XML sitemap generator given an XML sitemap configuration file.<p>
113     *
114     * @param seoFileRes the sitemap XML file
115     * @param config the parsed configuration
116     *
117     * @return the sitemap generator, or null if the given configuration is not an XML sitemap configuration
118     *
119     * @throws CmsException if something goes wrong
120     */
121    public static CmsXmlSitemapGenerator prepareSitemapGenerator(CmsResource seoFileRes, CmsXmlSeoConfiguration config)
122    throws CmsException {
123
124        if (config.getMode().equals(CmsXmlSeoConfiguration.MODE_XML_SITEMAP)) {
125            String baseFolderRootPath = CmsFileUtil.removeTrailingSeparator(
126                CmsResource.getParentFolder(seoFileRes.getRootPath()));
127            CmsXmlSitemapGenerator xmlSitemapGenerator = createSitemapGenerator(
128                config.getSitemapGeneratorClassName(),
129                baseFolderRootPath);
130            xmlSitemapGenerator.setComputeContainerPageDates(config.shouldComputeContainerPageModificationDates());
131            CmsPathIncludeExcludeSet inexcludeSet = xmlSitemapGenerator.getIncludeExcludeSet();
132            for (String include : config.getIncludes()) {
133                inexcludeSet.addInclude(include);
134            }
135            for (String exclude : config.getExcludes()) {
136                inexcludeSet.addExclude(exclude);
137            }
138            xmlSitemapGenerator.setServerUrl(config.getServerUrl());
139            return xmlSitemapGenerator;
140        }
141        return null;
142    }
143
144    /**
145     * Displays either the generated sitemap.xml or the generated robots.txt, depending on the configuration.<p>
146     *
147     * @throws Exception if something goes wrong
148     */
149    public void run() throws Exception {
150
151        CmsObject cms = getCmsObject();
152        String seoFilePath = cms.getRequestContext().getUri();
153        CmsResource seoFile = cms.readResource(seoFilePath);
154        m_configuration = new CmsXmlSeoConfiguration();
155        m_configuration.load(cms, seoFile);
156        String mode = m_configuration.getMode();
157        if (mode.equals(CmsXmlSeoConfiguration.MODE_ROBOTS_TXT)) {
158            showRobotsTxt();
159        } else {
160            boolean updateCache = Boolean.parseBoolean(getRequest().getParameter("updateCache"));
161            String value = "";
162            if (updateCache && m_configuration.usesCache()) {
163                // update request, and caching is configured -> update the cache
164                CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration);
165                value = generator.renderSitemap();
166                CmsXmlSitemapCache.INSTANCE.put(seoFile.getRootPath(), value);
167            } else if (!updateCache && m_configuration.usesCache()) {
168                // normal request, and caching is configured -> look in the cache first, and if not found, calculate sitemap and store it in cache
169                String cachedValue = CmsXmlSitemapCache.INSTANCE.get(seoFile.getRootPath());
170                if (cachedValue != null) {
171                    value = cachedValue;
172                } else {
173                    CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration);
174                    value = generator.renderSitemap();
175                    CmsXmlSitemapCache.INSTANCE.put(seoFile.getRootPath(), value);
176                }
177            } else if (!updateCache && !m_configuration.usesCache()) {
178                // normal request, caching is not configured -> always generate a fresh sitemap
179                CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration);
180                value = generator.renderSitemap();
181            } else if (updateCache && !m_configuration.usesCache()) {
182                // update request with no caching configured -> ignore
183            }
184            getResponse().getWriter().print(value);
185        }
186
187    }
188
189    /**
190     * Renders the robots.txt data containing the sitemaps automatically.<p>
191     *
192     * @throws Exception if something goes wrong
193     */
194    private void showRobotsTxt() throws Exception {
195
196        CmsObject cms = getCmsObject();
197        StringBuffer buffer = new StringBuffer();
198        I_CmsResourceType seoFileType = OpenCms.getResourceManager().getResourceType(
199            CmsXmlSeoConfiguration.SEO_FILE_TYPE);
200        List<CmsResource> seoFiles = cms.readResources(
201            "/",
202            CmsResourceFilter.DEFAULT_FILES.addRequireVisible().addRequireType(seoFileType));
203        for (CmsResource seoFile : seoFiles) {
204            try {
205                if (seoFile.getName().contains("test")) {
206                    continue;
207                }
208                CmsXmlSeoConfiguration seoFileConfig = new CmsXmlSeoConfiguration();
209                seoFileConfig.load(cms, seoFile);
210                if (seoFileConfig.isXmlSitemapMode()) {
211                    buffer.append(
212                        "Sitemap: "
213                            + CmsXmlSitemapGenerator.replaceServerUri(
214                                OpenCms.getLinkManager().getOnlineLink(cms, cms.getSitePath(seoFile)),
215                                m_configuration.getServerUrl()));
216                    buffer.append("\n");
217                }
218            } catch (CmsException e) {
219                LOG.error("Error while generating robots.txt : " + e.getLocalizedMessage(), e);
220            }
221        }
222        buffer.append("\n");
223        buffer.append(m_configuration.getRobotsTxtText());
224        buffer.append("\n");
225        getResponse().getWriter().print(buffer.toString());
226    }
227
228}