001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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 org.apache.commons.logging.Log; 045 046import jakarta.servlet.http.HttpServletRequest; 047import jakarta.servlet.http.HttpServletResponse; 048import jakarta.servlet.jsp.PageContext; 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 * 085 * @return the sitemap generator instance 086 * 087 * @throws CmsException if something goes wrong 088 */ 089 public static CmsXmlSitemapGenerator createSitemapGenerator(String className, String folderRootPath) 090 throws CmsException { 091 092 if (CmsStringUtil.isEmptyOrWhitespaceOnly(className)) { 093 className = (String)(OpenCms.getRuntimeProperty(PARAM_DEFAULT_SITEMAP_GENERATOR)); 094 } 095 if (CmsStringUtil.isEmptyOrWhitespaceOnly(className)) { 096 className = CmsXmlSitemapGenerator.class.getName(); 097 } 098 try { 099 Class<? extends CmsXmlSitemapGenerator> generatorClass = Class.forName(className).asSubclass( 100 CmsXmlSitemapGenerator.class); 101 Constructor<? extends CmsXmlSitemapGenerator> constructor = generatorClass.getConstructor(String.class); 102 CmsXmlSitemapGenerator generator = constructor.newInstance(folderRootPath); 103 return generator; 104 } catch (Exception e) { 105 LOG.error( 106 "Could not create configured sitemap generator " + className + ", using the default class instead", 107 e); 108 return new CmsXmlSitemapGenerator(folderRootPath); 109 } 110 } 111 112 /** 113 * Constructs an XML sitemap generator given an XML sitemap configuration file.<p> 114 * 115 * @param seoFileRes the sitemap XML file 116 * @param config the parsed configuration 117 * 118 * @return the sitemap generator, or null if the given configuration is not an XML sitemap configuration 119 * 120 * @throws CmsException if something goes wrong 121 */ 122 public static CmsXmlSitemapGenerator prepareSitemapGenerator(CmsResource seoFileRes, CmsXmlSeoConfiguration config) 123 throws CmsException { 124 125 if (config.getMode().equals(CmsXmlSeoConfiguration.MODE_XML_SITEMAP)) { 126 String baseFolderRootPath = CmsFileUtil.removeTrailingSeparator( 127 CmsResource.getParentFolder(seoFileRes.getRootPath())); 128 CmsXmlSitemapGenerator xmlSitemapGenerator = createSitemapGenerator( 129 config.getSitemapGeneratorClassName(), 130 baseFolderRootPath); 131 xmlSitemapGenerator.setComputeContainerPageDates(config.shouldComputeContainerPageModificationDates()); 132 CmsPathIncludeExcludeSet inexcludeSet = xmlSitemapGenerator.getIncludeExcludeSet(); 133 for (String include : config.getIncludes()) { 134 inexcludeSet.addInclude(include); 135 } 136 for (String exclude : config.getExcludes()) { 137 inexcludeSet.addExclude(exclude); 138 } 139 xmlSitemapGenerator.setServerUrl(config.getServerUrl()); 140 return xmlSitemapGenerator; 141 } 142 return null; 143 } 144 145 /** 146 * Displays either the generated sitemap.xml or the generated robots.txt, depending on the configuration.<p> 147 * 148 * @throws Exception if something goes wrong 149 */ 150 public void run() throws Exception { 151 152 CmsObject cms = getCmsObject(); 153 String seoFilePath = cms.getRequestContext().getUri(); 154 CmsResource seoFile = cms.readResource(seoFilePath); 155 m_configuration = new CmsXmlSeoConfiguration(); 156 m_configuration.load(cms, seoFile); 157 String mode = m_configuration.getMode(); 158 if (mode.equals(CmsXmlSeoConfiguration.MODE_ROBOTS_TXT)) { 159 showRobotsTxt(); 160 } else { 161 boolean updateCache = Boolean.parseBoolean(getRequest().getParameter("updateCache")); 162 String value = ""; 163 if (updateCache && m_configuration.usesCache()) { 164 // update request, and caching is configured -> update the cache 165 CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration); 166 value = generator.renderSitemap(); 167 CmsXmlSitemapCache.INSTANCE.put(seoFile.getRootPath(), value); 168 } else if (!updateCache && m_configuration.usesCache()) { 169 // normal request, and caching is configured -> look in the cache first, and if not found, calculate sitemap and store it in cache 170 String cachedValue = CmsXmlSitemapCache.INSTANCE.get(seoFile.getRootPath()); 171 if (cachedValue != null) { 172 value = cachedValue; 173 } else { 174 CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration); 175 value = generator.renderSitemap(); 176 CmsXmlSitemapCache.INSTANCE.put(seoFile.getRootPath(), value); 177 } 178 } else if (!updateCache && !m_configuration.usesCache()) { 179 // normal request, caching is not configured -> always generate a fresh sitemap 180 CmsXmlSitemapGenerator generator = prepareSitemapGenerator(seoFile, m_configuration); 181 value = generator.renderSitemap(); 182 } else if (updateCache && !m_configuration.usesCache()) { 183 // update request with no caching configured -> ignore 184 } 185 getResponse().getWriter().print(value); 186 } 187 188 } 189 190 /** 191 * Renders the robots.txt data containing the sitemaps automatically.<p> 192 * 193 * @throws Exception if something goes wrong 194 */ 195 private void showRobotsTxt() throws Exception { 196 197 CmsObject cms = getCmsObject(); 198 StringBuffer buffer = new StringBuffer(); 199 I_CmsResourceType seoFileType = OpenCms.getResourceManager().getResourceType( 200 CmsXmlSeoConfiguration.SEO_FILE_TYPE); 201 List<CmsResource> seoFiles = cms.readResources( 202 "/", 203 CmsResourceFilter.DEFAULT_FILES.addRequireVisible().addRequireType(seoFileType)); 204 for (CmsResource seoFile : seoFiles) { 205 try { 206 if (seoFile.getName().contains("test")) { 207 continue; 208 } 209 CmsXmlSeoConfiguration seoFileConfig = new CmsXmlSeoConfiguration(); 210 seoFileConfig.load(cms, seoFile); 211 if (seoFileConfig.isXmlSitemapMode()) { 212 buffer.append( 213 "Sitemap: " 214 + CmsXmlSitemapGenerator.replaceServerUri( 215 OpenCms.getLinkManager().getOnlineLink(cms, cms.getSitePath(seoFile)), 216 m_configuration.getServerUrl())); 217 buffer.append("\n"); 218 } 219 } catch (CmsException e) { 220 LOG.error("Error while generating robots.txt : " + e.getLocalizedMessage(), e); 221 } 222 } 223 buffer.append("\n"); 224 buffer.append(m_configuration.getRobotsTxtText()); 225 buffer.append("\n"); 226 getResponse().getWriter().print(buffer.toString()); 227 } 228 229}