@Component(service = Servlet.class, property = { Constants.SERVICE_DESCRIPTION +"=Page and Asset Site Map Servlet", ServletResolverConstants.SLING_SERVLET_METHODS +"=" + HttpConstants.METHOD_GET, ServletResolverConstants.SLING_SERVLET_RESOURCE_TYPES +"=.../page-config", ServletResolverConstants.SLING_SERVLET_SELECTORS +"=sitemap_image", ServletResolverConstants.SLING_SERVLET_EXTENSIONS +"=xml" })public final class SiteMapServletImage extends SlingSafeMethodsServlet { private static final long serialVersionUID = -7473561047318806388L; private static final Logger logger = LoggerFactory.getLogger(SiteMapServletImage.class); private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss"); private static final String NS = "http://www.sitemaps.org/schemas/sitemap/0.9"; private static final String[] IMAGE_EXTENSIONS = {".jpg", ".png", ".svg", ".jpeg"}; private static final String HOME_PAGE_TEMPLATE = "page-config"; private static final String UTF_8 = "UTF-8"; @Reference private transient AmpSiteMapImage ampSiteMap; @Reference private transient AmpConfig config; @Reference private transient Externalizer externalizer; private Map<String, String> externalizerDomains; private String externalizerDomain; private boolean includeInheritValue; private boolean includeLastModified; private String lastModifiedFormat; private boolean isExternalizeEnabled; private String[] changefreqProperties; private String[] priorityProperties; private String damAssetProperty; private List<String> damAssetTypes; private String excludeFromSiteMapProperty; private String characterEncoding; private boolean extensionlessUrls; private boolean removeTrailingSlash; private String[] imageProperties; @Activate protected void activate(Map<String, Object> properties) { this.externalizerDomains = config.getExternalizerDomains(); this.isExternalizeEnabled = this.ampSiteMap.isExternalizeEnabled(); this.includeLastModified = ampSiteMap.isIncludeLastModified(); this.lastModifiedFormat = ampSiteMap.getLastModifiedFormat(); this.includeInheritValue = ampSiteMap.isIncludeInheritValue(); this.changefreqProperties = ArrayUtils.isNotEmpty(ampSiteMap.getFrequencyProperties()) ? ampSiteMap.getFrequencyProperties() : ArrayUtils.EMPTY_STRING_ARRAY; this.priorityProperties = ArrayUtils.isNotEmpty(ampSiteMap.getPriorityProperties()) ? ampSiteMap.getPriorityProperties() : ArrayUtils.EMPTY_STRING_ARRAY; this.damAssetProperty = ampSiteMap.getDamFolderProperty(); this.damAssetTypes = ArrayUtils.isNotEmpty(ampSiteMap.getDamAssetsMIMETypes()) ? Arrays.asList(ampSiteMap.getDamAssetsMIMETypes()) : new ArrayList<>(); this.excludeFromSiteMapProperty = ampSiteMap.getExcludeSitemapProperty(); this.characterEncoding = ampSiteMap.getCharacterEncoding(); this.extensionlessUrls = ampSiteMap.hasExtensionlessURL(); this.removeTrailingSlash = ampSiteMap.hasRemovedSlash(); this.imageProperties = ampSiteMap.getImageProperties(); } @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { response.setContentType(request.getResponseContentType()); if (StringUtils.isNotEmpty(this.characterEncoding)) { response.setCharacterEncoding(characterEncoding); } this.externalizerDomain = WcmUtils.getDomain(this.externalizerDomains, getCurrentPage(request).getPath(), getCurrentPage(request).getPath()); ResourceResolver resourceResolver = request.getResourceResolver(); Page page = getCurrentPage(request); XMLStreamWriter stream = null; try { stream = XMLOutputFactory.newFactory().createXMLStreamWriter(response.getWriter()); stream.writeStartDocument(this.characterEncoding, "1.0"); stream.writeStartElement("", "urlset", NS); stream.writeNamespace("", NS); write(page, stream, resourceResolver); for (Iterator<Page> children = page.listChildren(new PageFilter(false, true), true); children.hasNext(); ) { write(children.next(), stream, resourceResolver); } if (!damAssetTypes.isEmpty() && StringUtils.isNotEmpty(damAssetProperty)) { for (Resource assetFolder : getAssetFolders(page, resourceResolver)) { writeAssets(stream, assetFolder, resourceResolver); } } stream.writeEndElement(); stream.writeEndDocument(); } catch (XMLStreamException e) { throw new IOException("Error writing XML response", e); } finally { if (stream != null) { try { stream.close(); } catch (XMLStreamException e) { logger.error("Error closing XMLStreamWriter", e); } } } } private Collection<Resource> getAssetFolders(Page page, ResourceResolver resolver) { List<Resource> allAssetFolders = new ArrayList<>(); ValueMap properties = page.getProperties(); String[] configuredAssetFolderPaths = properties.get(damAssetProperty, String[].class); if (configuredAssetFolderPaths != null) { Arrays.sort(configuredAssetFolderPaths); String prevPath = "#"; for (String configuredAssetFolderPath : configuredAssetFolderPaths) { if (StringUtils.isNotBlank(configuredAssetFolderPath) && !configuredAssetFolderPath.equals(prevPath)&& !StringUtils.startsWith(configuredAssetFolderPath, prevPath +"/")) { Resource assetFolder = resolver.getResource(configuredAssetFolderPath); if (assetFolder != null) { prevPath = configuredAssetFolderPath; allAssetFolders.add(assetFolder); } } } } return allAssetFolders; } private void write(Page page, XMLStreamWriter stream, ResourceResolver resolver) throws XMLStreamException { write(page.getPath(), StringUtils.EMPTY, page, stream, resolver); } private void write(String path, String lastmod, Page page, XMLStreamWriter stream, ResourceResolver resolver) throws XMLStreamException { if (isHidden(page)) { return; } stream.writeStartElement(NS, "url"); String loc = isExternalizeEnabled && StringUtils.isNotBlank(externalizerDomain) ? externalizer.externalLink(resolver, externalizerDomain, formatPath(path)) : path; writeElement(stream, "loc", loc); if (includeLastModified) { FastDateFormat dateFormat = getDateFormat(); if (dateFormat != null) { lastmod = getLastMod(lastmod, page, dateFormat); if (StringUtils.isNotEmpty(lastmod)) { writeElement(stream, "lastmod", lastmod); } } } ValueMap properties = page.getProperties(); if (includeInheritValue) { HierarchyNodeInheritanceValueMap inheritanceValueMap = new HierarchyNodeInheritanceValueMap(page.getContentResource()); writeFirstPropertyValue(stream, "changefreq", changefreqProperties, inheritanceValueMap); writeFirstPropertyValue(stream, "priority", priorityProperties, inheritanceValueMap); } else { writeFirstPropertyValue(stream, "changefreq", changefreqProperties, properties); writeFirstPropertyValue(stream, "priority", priorityProperties, properties); } try { handleImages(page, resolver, stream); } catch (Exception e) { throw new RuntimeException("Error handling images", e); } stream.writeEndElement(); } private String formatPath(String path) { String urlFormat = removeTrailingSlash ? "%s" : "%s/"; return !extensionlessUrls ? String.format("%s.html", path) : String.format(urlFormat, path); } private void handleImages(Page page, ResourceResolver resourceResolver, XMLStreamWriter stream) throws Exception { String pagePath = page.getPath(); Resource pageResource = resourceResolver.getResource(pagePath +"/jcr:content"); ArrayList<String> images = new ArrayList<>(); this.checkImages(pageResource, images); if (!images.isEmpty()) { stream.writeStartElement(NS, "url"); String loc; if (isExternalizeEnabled && StringUtils.isNotBlank(externalizerDomain)) { if (!extensionlessUrls) { loc = externalizer.externalLink(resourceResolver, externalizerDomain, String.format("%s.html", pagePath)); } else { String urlFormat; if (isHomePage(page)) { urlFormat = "%s/"; } else { urlFormat = ampSiteMap.hasRemovedSlash() ? "%s" : "%s/"; } loc = externalizerDomain + String.format(urlFormat, pagePath); } } else { loc = pagePath; } writeElement(stream, "loc", loc); for (String image : images) { stream.setPrefix("image", ""); stream.writeStartElement(NS, "image:image"); stream.writeStartElement(NS, "image:loc"); stream.writeCharacters(externalizerDomain + String.format("%s", image)); stream.writeEndElement(); stream.writeEndElement(); } stream.writeEndElement(); } } private void checkImages(Resource res, ArrayList<String> images) { if (res == null) return; for (Resource child : res.getChildren()) { if (child.hasChildren()) { checkImages(child, images); } else { ValueMap map = child.getValueMap(); for (Map.Entry<String, Object> entry : map.entrySet()) { if (isString(entry.getValue())) { for (String imageExtension : IMAGE_EXTENSIONS) { if (entry.getValue().toString().contains(imageExtension)) { images.add(entry.getValue().toString()); } } } } } } } private boolean isString(Object obj) { return obj instanceof String; } private boolean isHomePage(Page page) { String pageTemplate = page.getProperties().get(NameConstants.NN_TEMPLATE, String.class); return HOME_PAGE_TEMPLATE_LATAM.equals(pageTemplate); } private String getLastMod(String lastmod, Page page, FastDateFormat dateFormat) { if (StringUtils.isEmpty(lastmod)) { Calendar cal = page.getLastModified(); if (cal != null) { lastmod = dateFormat.format(cal); } } else { try { lastmod = dateFormat.format(DATE_FORMAT.parse(lastmod)); } catch (ParseException e) { logger.error("Error parsing last modified date", e); } } return lastmod; } private boolean isHidden(final Page page) { ValueMap pageProperties = page.getProperties(); boolean excludeFromSitemap = pageProperties.get(this.excludeFromSiteMapProperty, false); boolean noIndex = pageProperties.get("noIndex", false); boolean canonical = StringUtils.isNotBlank(pageProperties.get("canonical", "")); boolean redirect = pageProperties.get("redirect", false); boolean disabled = pageProperties.get("disabled", false); int statusCode = pageProperties.get("statusCode", -1); return noIndex || redirect || disabled || statusCode == 404 || statusCode == 410 || (canonical && excludeFromSitemap); } private void writeAsset(Asset asset, XMLStreamWriter stream, ResourceResolver resolver) throws XMLStreamException { stream.writeStartElement(NS, "url"); String loc = externalizer.externalLink(resolver, externalizerDomain, asset.getPath()); writeElement(stream, "loc", loc); if (includeLastModified) { long lastModified = asset.getLastModified(); if (lastModified > 0) { writeElement(stream, "lastmod", DATE_FORMAT.format(lastModified)); } } stream.writeEndElement(); } private void writeAssets(XMLStreamWriter stream, Resource assetFolder, ResourceResolver resolver) throws XMLStreamException { for (Resource child : assetFolder.getChildren()) { Asset asset = child.adaptTo(Asset.class); if (asset != null && isAssetIncluded(asset)) { writeAsset(asset, stream, resolver); } } } private boolean isAssetIncluded(Asset asset) { String[] excludedAssetTypes = getExcludedAssetTypes(); if (excludedAssetTypes != null && excludedAssetTypes.length > 0) { String[] mimeTypes = asset.getMetadataValue("jcr:mimeType").split(","); for (String mimeType : mimeTypes) { if (Arrays.asList(excludedAssetTypes).contains(mimeType)) { return false; } } } return true; } private String[] getExcludedAssetTypes() { String[] excludedAssetTypes = null; if (damAssetTypes.contains("dam:Asset")) { excludedAssetTypes = damAssetTypes.toArray(new String[0]); excludedAssetTypes = Arrays.copyOf(excludedAssetTypes, excludedAssetTypes.length - 1); } return excludedAssetTypes; } private void writeElement(XMLStreamWriter stream, String name, String value) throws XMLStreamException { stream.writeStartElement(NS, name); if (StringUtils.isNotBlank(value)) { stream.writeCharacters(value); } stream.writeEndElement(); } private void writeFirstPropertyValue(XMLStreamWriter stream, String name, String[] properties, ValueMap propertiesMap) throws XMLStreamException { String value = null; if (propertiesMap != null) { value = propertiesMap.get(name, String.class); } if (StringUtils.isEmpty(value)) { for (String property : properties) { value = propertiesMap.get(property, String.class); if (StringUtils.isNotBlank(value)) { break; } } } if (StringUtils.isNotBlank(value)) { writeElement(stream, name, value); } } private FastDateFormat getDateFormat() { FastDateFormat dateFormat = null; if (includeLastModified && StringUtils.isNotBlank(lastModifiedFormat)) { try { dateFormat = FastDateFormat.getInstance(lastModifiedFormat); } catch (IllegalArgumentException e) { logger.error("Invalid lastModifiedFormat: ", e); } } return dateFormat; } private Page getCurrentPage(SlingHttpServletRequest request) { Resource resource = request.getResource(); return resource.adaptTo(Page.class); }}
I have a sitemap that needs to show images, and I took inspiration from another sitemap that doesn't have images, but I added my own. However, this sitemap doesn't load correctly like the other one, and I want to understand if I duplicated the code incorrectly or if I didn't take into account other factors. It should also consider child pages and exceptions.