You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by di...@apache.org on 2021/06/25 13:47:01 UTC
[sling-whiteboard] branch master updated: SLING-10556: move sitemap
into its own module
This is an automated email from the ASF dual-hosted git repository.
diru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
The following commit(s) were added to refs/heads/master by this push:
new 8cc34e3 SLING-10556: move sitemap into its own module
8cc34e3 is described below
commit 8cc34e341a2f05ff6cb2ed8e007547803763d9b4
Author: Dirk Rudolph <di...@apache.org>
AuthorDate: Fri Jun 25 15:46:35 2021 +0200
SLING-10556: move sitemap into its own module
---
sitemap/README.md | 142 ------
sitemap/pom.xml | 286 ------------
.../org/apache/sling/sitemap/SitemapException.java | 37 --
.../java/org/apache/sling/sitemap/SitemapInfo.java | 103 ----
.../org/apache/sling/sitemap/SitemapService.java | 111 -----
.../apache/sling/sitemap/builder/Extension.java | 31 --
.../org/apache/sling/sitemap/builder/Sitemap.java | 47 --
.../java/org/apache/sling/sitemap/builder/Url.java | 75 ---
.../builder/extensions/AbstractExtension.java | 47 --
.../extensions/AlternateLanguageExtension.java | 44 --
.../builder/extensions/ExtensionProvider.java | 69 ---
.../sitemap/builder/extensions/package-info.java | 22 -
.../apache/sling/sitemap/builder/package-info.java | 26 --
.../sitemap/common/SitemapLinkExternalizer.java | 66 ---
.../apache/sling/sitemap/common/SitemapUtil.java | 268 -----------
.../apache/sling/sitemap/common/package-info.java | 22 -
.../generator/ResourceTreeSitemapGenerator.java | 123 -----
.../sling/sitemap/generator/SitemapGenerator.java | 135 ------
.../sitemap/generator/SitemapGeneratorManager.java | 75 ---
.../sling/sitemap/generator/package-info.java | 22 -
.../sling/sitemap/impl/SitemapEventUtil.java | 54 ---
.../sitemap/impl/SitemapGeneratorExecutor.java | 443 ------------------
.../sitemap/impl/SitemapGeneratorManagerImpl.java | 123 -----
.../sling/sitemap/impl/SitemapScheduler.java | 189 --------
.../sitemap/impl/SitemapServiceConfiguration.java | 59 ---
.../sling/sitemap/impl/SitemapServiceImpl.java | 314 -------------
.../apache/sling/sitemap/impl/SitemapServlet.java | 230 ---------
.../apache/sling/sitemap/impl/SitemapStorage.java | 516 ---------------------
.../sling/sitemap/impl/SitemapStorageInfo.java | 79 ----
.../sitemap/impl/builder/ExtensionWriter.java | 210 ---------
.../sling/sitemap/impl/builder/SitemapImpl.java | 130 ------
.../sitemap/impl/builder/SitemapIndexImpl.java | 82 ----
.../sling/sitemap/impl/builder/StringWriter.java | 97 ----
.../apache/sling/sitemap/impl/builder/UrlImpl.java | 217 ---------
.../AlternateLanguageExtensionProvider.java | 88 ----
.../impl/builder/extensions/ExtensionFactory.java | 62 ---
.../extensions/ExtensionProviderManager.java | 146 ------
.../impl/console/SitemapInventoryPlugin.java | 226 ---------
.../org/apache/sling/sitemap/package-info.java | 22 -
.../org/apache/sling/sitemap/TestGenerator.java | 75 ---
.../sitemap/TestResourceTreeSitemapGenerator.java | 51 --
.../sling/sitemap/common/SitemapUtilTest.java | 167 -------
.../ResourceTreeSitemapGeneratorTest.java | 138 ------
.../sitemap/impl/SitemapGeneratorExecutorTest.java | 484 -------------------
.../impl/SitemapGeneratorManagerImplTest.java | 142 ------
.../sling/sitemap/impl/SitemapSchedulerTest.java | 256 ----------
.../impl/SitemapServiceImplSchedulingTest.java | 181 --------
.../sling/sitemap/impl/SitemapServiceImplTest.java | 251 ----------
.../sling/sitemap/impl/SitemapServletTest.java | 346 --------------
.../sling/sitemap/impl/SitemapStorageTest.java | 351 --------------
.../sitemap/impl/builder/SitemapImplTest.java | 146 ------
.../sling/sitemap/impl/builder/UrlImplTest.java | 204 --------
.../extensions/AlternateLanguageExtensionTest.java | 110 -----
.../impl/console/SitemapInventoryPluginTest.java | 214 ---------
.../ResourceTreeSitemapGeneratorTest/sitetree.json | 28 --
.../SitemapInventoryPluginTest/inventory.json | 41 --
sitemap/src/test/resources/logback.xml | 32 --
57 files changed, 8255 deletions(-)
diff --git a/sitemap/README.md b/sitemap/README.md
deleted file mode 100644
index 88903d7..0000000
--- a/sitemap/README.md
+++ /dev/null
@@ -1,142 +0,0 @@
-[![Apache Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org)
-
-# Apache Sling Sitemap
-
-The Apache Sling Sitemap module is an extension for Apache Sling that helps to serve XML Sitemaps to Search Engines. It
-was designed to cover various uses cases from small sites serving sitemaps on-demand, large sites generating them in the
-background to even sites that collect 3rd party data to include dynamically rendered pages.
-
-## Highlights
-
-* A simple, builder-like API to create Sitemaps, that hides all the XML specifics from the implementation
-* Support for on-demand and background generation w/ continuation after job interruption
-* Support for nested sitemaps, that are automatically collected into a sitemap indexes
-* Support for auto-balancing sitemaps into multiple files for background generation
-
-## Open Topics
-
-* Implement Google specific sitemap extensions (image/video/news)
-
-## Getting Started
-
-The Sling Sitemap module is rather abstract, as it highly depends on the content structure of the application it is
-used in. To get started a few things must be done:
-
-1) Implement at least one `SitemapGenerator`. The abstract `ResourceTreeSitemapGenator` may be a good starting
- point, for any generator walking the resource tree.
-2) Configure the `SitemapServlet` to register for the resource type(s) which may be a sitemap root resource.
-3) Configure a service user mapping for `org.apache.sling.sitemap:sitemap-reader` granting read access to the content
-4) Configure another service user mapping one for `org.apache.sling.sitemap:sitemap-writer` granting read access to the
- content and write access to the storage path for background generation (per default /var/sitemaps)
-5) Finally, either configure a `SitemapScheduler` to create a job for background generation, or implement the
- `SitemapGenerator` to serve the sitemaps on-demand.
-
-## Implementation Details
-
-### Content Model
-
-In order to serve a sitemap, a resource must be marked as sitemap root resource. This is done by adding
-a `sling:sitemapRoot = true` property either to the resource, or it's `jcr:content` child.
-
-When multiple resources in a resource tree are marked as as sitemap roots, the on closest to the repository root is
-considered to top level sitemap root and serves a sitemap-index additionally to the sitemap.
-
-```
-/content/site/ch/
- + de-ch/
- - sitemapRoot = true
- + faqs/
- + news/
- + products/
- - sitemapRoot = true
- + it-ch/
- - sitemapRoot = true
- ...
-```
-
-In the example above the paths `de-ch/` and `fr-ch/` are both top level sitemap roots and `de-ch/products/` is a nested
-sitemap root. The sitemaps will be served as following, assuming the appropriate mappings being in place:
-
-```
-https://site.ch/de-ch.sitemap-index.xml
-https://site.ch/de-ch.sitemap.xml
-https://site.ch/de-ch.sitemap.products-sitemap.xml
-https://site.ch/fr-ch.sitemap.xml
-```
-
-### Sitemap Generation
-
-The module does not ship a specific `SitemapGenerator` implementation. Products/Projects using the Apache Sling Sitemap
-module must implement an appropriate `SitemapGenerator` that fits their content model. An abstract
-`ResourcceTreeSitemapGenator` implementation is available to cover the most common use cases.
-
-Each `SitemapGenerator` may produce multiple sitemaps for a given sitemap root. For example a default sitemap and a news
-specific sitemap, that contains only 1000 urls that were changed in the past 2 days. Or as another example would a
-product sitemap for each of a catalogues top level categories. To enable that, a `SitemapGenerator` can return _0..n_
-names for a given resource, each name representing a single sitemap at the given resource.
-
-`SitemapGenerator` implementations need to be registered as OSGI services. In case of an overlap, it depends on
-the `service.ranking` which `SitemapGenerators` are used for a particular sitemap root. For example, it may be that two
-`SitemapGenerators` can generator a default sitemap for a sitemap root, but the second one can also generate a news
-sitemap. The first, higher-ranked `SitemapGenerator` will be used for the default sitemap, but the second will still be
-taken into account for the news sitemap.
-
-#### Background Generation
-
-A configurable scheduler can be configured to trigger background generation. Multiple schedules can be created for
-different sitemap names or generators as described before. This enables the background generation to align on the
-cadence in which particular content may be updated, for example product catalogues that sync on a regular basis.
-
-For each sitemap root in the repository and for each sitemap name returned for them, a job with the topic
-`org/apache/sling/sitemap/build` will be queued. The `SitemapGeneratorExecutor` implementation consumes those jobs and
-calls the corresponding `SitemapGeneator`. It is recommended to create an unordered queue for those jobs so that they
-can be distributed across multiple instances within a cluster.
-
-The `SitemapGeneratorExecutor` provides an execution context to the `SitemapGenerator`, that it may use to keep track on
-the progress. The implementation on the other hand will persist this state along with the already written sitemap after
-a configurable amount of urls has been added. This allows to resume jobs after an instance gets restarted or discarded
-in a dynamic cluster. Per default the `SitemapGeneratorExecutor` is configured with a chunk size of `Integer.MAX_VALUE`,
-which effectively means that no checkpoints will be written. When using this feature make sure to find a good balance
-between write overhead and performance gain for those particular cases.
-
-Background generation supports auto-balancing according to configurable limits for size (in bytes), and the number of
-urls in a single sitemap file. This is transparently handled by the `SitemapGeneatorExecutor`, providing a `Sitemap`
-instance which pipes added urls to multiple files when needed. As a consequence returning sitemap files from storage for
-a given name and sitemap root may result in multiple return values.
-
-#### On-demand Generation
-
-For smaller sites, calculating sitemaps in the background may not be necessary and serving sitemaps when they get
-requested may even result in higher accuracy. On the other hand serving a sitemap on-demand within the timeout of
-different crawlers highly depends on the amount of content and the `SitemapGeneator` implementation(s) used. Because of
-that, serving sitemaps on-demand must be explicitly enabled.
-
-To enable serving sitemaps on-demand, a `SitemapGenerator` must indicate that a particular sitemap name should be served
-on demand. Alternatively the `SitemapGeneatorManagerImpl` can be configured to force all sitemaps to be served
-on-demand. In both cases, the `SitemapServlet` changes its behaviour slightly:
-
-- When serving a sitemap-index, it queries for all sitemap roots and adds the sitemaps of those, that should be served
- on-demand. Additionally, all sitemaps form the top level sitemap root's storage location are added if not already
- present.
-- For serving sitemap files it first checks if the sitemap should be served on-demand, if not it falls back to the
- sitemap file from the top level sitemap root's storage location.
-
-**On-demand generation does NOT support auto-balancing. The configured limits will be ignored.**
-
-### Sitemap Extensions
-
-The builder-like API supports adding sitemap extensions (image, video, news, alternate languages ...) by implementing an
-`ExtensionProvider`. This provider has to be registered specifying `namespace`, `prefix`, and `localName` of the xml
-element the extension adds to an url object.
-
-In order to hide the implementation detail from the consumer API, the `ExtensionProvider` works with an abstract
-`AbstractExtension` class, and the consumer API only with an `Extension` marker interface. To implement an Extension:
-
-* Extend the marker interface with methods to capture the extension specific data
-* Implement this extension interface by extending the `AbstractExtension` class
-* Provide an instance of the extension implementation by implementing an `ExtensionProvider`
-* Make sure to register the `ExtensionProvider` with the `extension.interface` set to the fqn of the extension interface
-
-An example extension implementation can be found with
-the [AlternateLanguageExtension](src/main/java/org/apache/sling/sitemap/builder/extensions/AlternateLanguageExtension.java)
-.
\ No newline at end of file
diff --git a/sitemap/pom.xml b/sitemap/pom.xml
deleted file mode 100644
index 2b96385..0000000
--- a/sitemap/pom.xml
+++ /dev/null
@@ -1,286 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <parent>
- <groupId>org.apache.sling</groupId>
- <artifactId>sling-bundle-parent</artifactId>
- <version>41</version>
- </parent>
-
- <artifactId>org.apache.sling.sitemap</artifactId>
- <version>1.0.0-SNAPSHOT</version>
- <name>Apache Sling Sitemaps</name>
- <description>An Apache Sling extension to generate XML Sitemaps.</description>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-
- <build>
- <plugins>
- <plugin>
- <groupId>biz.aQute.bnd</groupId>
- <artifactId>bnd-maven-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>biz.aQute.bnd</groupId>
- <artifactId>bnd-baseline-maven-plugin</artifactId>
- <configuration>
- <failOnMissing>false</failOnMissing>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-jar-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- </plugin>
- <plugin>
- <groupId>org.apache.sling</groupId>
- <artifactId>sling-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.log</artifactId>
- <version>1.4.0</version>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.converter</artifactId>
- <version>1.0.1</version>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.function</artifactId>
- <version>1.1.0</version>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.tracker</artifactId>
- <version>1.5.1</version>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.resource</artifactId>
- <version>1.0.0</version>
- </dependency>
-
- <dependency>
- <groupId>org.junit</groupId>
- <artifactId>junit-bom</artifactId>
- <version>5.7.2</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <dependencies>
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.framework</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.annotation.versioning</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.component.annotations</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.metatype.annotations</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.event</artifactId>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.tracker</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.jackrabbit</groupId>
- <artifactId>jackrabbit-jcr-commons</artifactId>
- <version>2.21.3</version>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.api</artifactId>
- <version>2.22.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.event</artifactId>
- <version>4.2.12</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.commons.scheduler</artifactId>
- <version>2.7.12</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.serviceusermapper</artifactId>
- <version>1.5.4</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.commons.metrics</artifactId>
- <version>1.2.8</version>
- </dependency>
- <dependency>
- <groupId>org.apache.felix</groupId>
- <artifactId>org.apache.felix.inventory</artifactId>
- <version>1.0.6</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.8.0</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>ch.qos.logback</groupId>
- <artifactId>logback-classic</artifactId>
- <version>1.2.3</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.log</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.component</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.service.cm</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.converter</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.util.function</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.resource</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.apache.sling</groupId>
- <artifactId>org.apache.sling.testing.sling-mock.junit5</artifactId>
- <version>3.0.2</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-junit-jupiter</artifactId>
- <version>3.10.0</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <version>2.1</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.12.3</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.fasterxml.jackson.dataformat</groupId>
- <artifactId>jackson-dataformat-yaml</artifactId>
- <version>2.12.3</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <profiles>
- <profile>
- <id>autoInstallBundle</id>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.sling</groupId>
- <artifactId>sling-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>install-bundle</id>
- <goals>
- <goal>install</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
-
-</project>
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapException.java b/sitemap/src/main/java/org/apache/sling/sitemap/SitemapException.java
deleted file mode 100644
index cac36ea..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap;
-
-import org.osgi.annotation.versioning.ProviderType;
-
-@ProviderType
-public class SitemapException extends Exception {
-
- public SitemapException(String message) {
- super(message);
- }
-
- public SitemapException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public SitemapException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapInfo.java b/sitemap/src/main/java/org/apache/sling/sitemap/SitemapInfo.java
deleted file mode 100644
index d36cee9..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapInfo.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ProviderType;
-
-/**
- * An info object with details about a sitemap/sitemap-index.
- */
-@ProviderType
-public interface SitemapInfo {
-
- enum Status {
- /**
- * Sitemap is available but served on-demand.
- */
- ON_DEMAND,
- /**
- * Sitemap is available and served from storage.
- */
- STORAGE,
- /**
- * Sitemap is not yet available, generation is pending.
- */
- SCHEDULED,
- /**
- * Sitemap not served on-demand, not served from storage, no schedule registered.
- * <p>
- * That may mean the sitemap is generated on a different host.
- */
- UNKNOWN
- }
-
- /**
- * Returns the resource path to the resource the sitemap is stored at. May return null if the sitemap or
- * sitemap-index is served on-demand.
- *
- * @return
- */
- @Nullable
- String getStoragePath();
-
- /**
- * Returns the absolute, external url for the sitemap/sitemap-index.
- *
- * @return
- */
- @NotNull
- String getUrl();
-
- /**
- * Returns the name of the sitemap.
- * <p>
- * For sitemap-indexes this will be equal to {@link SitemapService#SITEMAP_INDEX_NAME} and for sitemaps generated
- * for the default name this will be equal to {@link SitemapService#DEFAULT_SITEMAP_NAME}.
- *
- * @return
- */
- @NotNull
- String getName();
-
- /**
- * Returns the status of the sitemap.
- *
- * @return
- * @see SitemapInfo.Status
- */
- @NotNull
- Status getStatus();
-
- /**
- * Returns the size of the sitemap in bytes. -1 for sitemap-index or sitemaps served on-demand.
- *
- * @return
- */
- int getSize();
-
- /**
- * Returns the number of urls in the sitemap. -1 for sitemap-index or sitemaps served on-demand.
- *
- * @return
- */
- int getEntries();
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java b/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java
deleted file mode 100644
index 7fe29f7..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.common.SitemapLinkExternalizer;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ProviderType;
-
-import java.util.Collection;
-
-/**
- * A service that gives consumers access to minimal information about sitemaps.
- */
-@ProviderType
-public interface SitemapService {
-
- /**
- * The name of a boolean property marking a resource as sitemap root {@link Resource}. It may either be set to a
- * {@link Resource} or to a {@link Resource}'s jcr:content child.
- */
- String PROPERTY_SITEMAP_ROOT = "sling:sitemapRoot";
-
- /**
- * The default name used for (unnamed) sitemaps.
- */
- String DEFAULT_SITEMAP_NAME = "<default>";
-
- /**
- * The name used for sitemap indexes.
- */
- String SITEMAP_INDEX_NAME = "<sitemap-index>";
-
- /**
- * Returns the configured maximum size (bytes) a sitemap must not exceed.
- *
- * @return
- */
- int getMaxSize();
-
- /**
- * Returns the configured maximum number of urls a sitemap must not exceed.
- *
- * @return
- */
- int getMaxEntries();
-
- /**
- * Calls all registered sitemap schedulers to schedule (re)generation for all sitemap roots and names.
- */
- void scheduleGeneration();
-
- /**
- * Calls all registered sitemap schedulers registered for the given name to schedule (re)generation.
- *
- * @param name
- */
- void scheduleGeneration(String name);
-
- /**
- * Calls all registered sitemap schedulers with a search path containing the given resource to schedule
- * (re)generation for all names.
- *
- * @param sitemapRoot
- */
- void scheduleGeneration(Resource sitemapRoot);
-
- /**
- * Calls all registered sitemap schedulers with a search path containing the given resource and being registered for
- * the given name to schedule (re)generation.
- *
- * @param sitemapRoot
- * @param name
- */
- void scheduleGeneration(Resource sitemapRoot, String name);
-
- /**
- * Returns the urls to the given {@link Resource}'s sitemaps, if any.
- * <p>
- * The returned urls may contain a sitemap index when there are multiple sitemaps generated for the given sitemap
- * root {@link Resource}. Or it may contain urls to another sitemap root, if the sitemap is nested below a top level
- * sitemap root.
- * <p>
- * Numbers for size and entries can only be provided for sitemaps served from storage. For sitemap index or
- * any sitemap not served from storage {@code -1} will be returned.
- * <p>
- * The default implementation uses {@link SitemapLinkExternalizer#externalize(Resource)} to create absolute urls.
- *
- * @param sitemapRoot a {@link Resource} having {@link SitemapService#PROPERTY_SITEMAP_ROOT} set to true
- * @return a {@link Collection} of {@link SitemapInfo} objects
- */
- @NotNull
- Collection<SitemapInfo> getSitemapInfo(@NotNull Resource sitemapRoot);
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Extension.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/Extension.java
deleted file mode 100644
index b080624..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Extension.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder;
-
-import org.osgi.annotation.versioning.ProviderType;
-
-/**
- * A marker interface used as consumer facing API for custom extensions.
- * <p>
- * Implementations must extend this interface to provide a sub type of it that can be added to a {@link Url} using
- * {@link Url#addExtension(Class)}.
- */
-@ProviderType
-public interface Extension {
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Sitemap.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/Sitemap.java
deleted file mode 100644
index 3a07e3b..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Sitemap.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ProviderType;
-
-/**
- * A builder-like object that allows to add locations. For each added location an {@link Url} object is returned which
- * again allows to add more details to the added location.
- * <p>
- * Implementations may build an in-memory data structure when {@link Sitemap#addUrl(String)} is called, or write each
- * location on an underlying stream immediately.
- * </p>
- */
-@ProviderType
-public interface Sitemap {
-
- /**
- * Adds a location to the {@link Sitemap}. The returned {@link Url} can be used to add more details to the so
- * created object.
- *
- * @param location the required location of the entry to add.
- * @return an {@link Url} object giving access to the location's details
- * @throws SitemapException if any internal operation of the Sitemap fails
- */
- @NotNull
- Url addUrl(@NotNull String location) throws SitemapException;
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Url.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/Url.java
deleted file mode 100644
index e1598aa..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/Url.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ProviderType;
-
-import java.time.Instant;
-
-/**
- * A builder-like object that allows to add details to a location added to a {@link Sitemap}.
- */
-@ProviderType
-public interface Url {
-
- enum ChangeFrequency {
- ALWAYS, HOURLY, DAILY, WEEKLY, MONTHLY, YEARLY, NEVER
- }
-
- /**
- * Sets the change frequency of the url.
- *
- * @param changeFrequency
- * @return
- */
- @NotNull
- Url setChangeFrequency(@NotNull ChangeFrequency changeFrequency);
-
- /**
- * Sets the last modified time of the url.
- *
- * @param pointInTime
- * @return
- */
- @NotNull
- Url setLastModified(@NotNull Instant pointInTime);
-
- /**
- * Sets the priority of the url. According to the sitemap protocol the priority must be a number between 0.0 and
- * 1.0. Values smaller or greater will be corrected to the lower and upper bound respectively.
- *
- * @param priority
- * @return
- */
- @NotNull
- Url setPriority(double priority);
-
- /**
- * Adds an extension to the url.
- *
- * @param extensionInterface the interface of the extension to add
- * @param <T> the type of the extension
- * @return an instance of the given interface, or null when no
- * {@link org.apache.sling.sitemap.builder.extensions.ExtensionProvider} is registered for the given interface
- */
- @Nullable <T extends Extension> T addExtension(Class<T> extensionInterface);
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AbstractExtension.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AbstractExtension.java
deleted file mode 100644
index b6f91a7..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AbstractExtension.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder.extensions;
-
-import org.apache.sling.sitemap.builder.Extension;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ConsumerType;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-
-/**
- * The producer API of an extension.
- */
-@ConsumerType
-public abstract class AbstractExtension implements Extension {
-
- /**
- * Implementations must write their content to the given {@link XMLStreamWriter}.
- * <p>
- * The extension must not open/close its own surrounding tag. This is done by the caller in order to guarantee
- * proper isolation between the core implementation and the extensions. Furthermore, when an extension fails and
- * throws an {@link XMLStreamException} the extensions output will simply be discarded but the sitemap generation
- * will not fail.
- *
- * @param writer
- * @throws XMLStreamException
- */
- public abstract void writeTo(@NotNull XMLStreamWriter writer) throws XMLStreamException;
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AlternateLanguageExtension.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AlternateLanguageExtension.java
deleted file mode 100644
index aa26616..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/AlternateLanguageExtension.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder.extensions;
-
-import org.apache.sling.sitemap.builder.Extension;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ProviderType;
-
-import java.util.Locale;
-
-/**
- * An extension to add alternate language links to an {@link org.apache.sling.sitemap.builder.Url}.
- *
- * @see <a href="https://developers.google.com/search/docs/advanced/crawling/localized-versions#sitemap">Tell Google about localized versions of your page</a>
- */
-@ProviderType
-public interface AlternateLanguageExtension extends Extension {
-
- @NotNull
- AlternateLanguageExtension setLocale(@NotNull Locale locale);
-
- @NotNull
- AlternateLanguageExtension setDefaultLocale();
-
- @NotNull
- AlternateLanguageExtension setHref(@NotNull String location);
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/ExtensionProvider.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/ExtensionProvider.java
deleted file mode 100644
index d4eea11..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/ExtensionProvider.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.builder.extensions;
-
-import org.jetbrains.annotations.NotNull;
-import org.osgi.annotation.versioning.ConsumerType;
-
-/**
- * To provide an implementation for a defined sub type of {@link org.apache.sling.sitemap.builder.Extension} an
- * {@link ExtensionProvider} needs to be registered as OSGI service.
- * <p>
- * In order to hide the implementation detail of an extension from the the consumer API, the return type of
- * {@link ExtensionProvider#newInstance()} is {@link AbstractExtension}, the provider facing API of the extension.
- * However to use the returned instance, it has to also implement the extension sub type interface and be registered
- * with it's full qualified class name.
- * <p>
- * There may be multiple {@link ExtensionProvider}s using the same namespace. If so the one with the highest ranking
- * according to the OSGI specification will define the namespace's prefix, which means that lower ranking services
- * may use another prefix then they were registered with.
- */
-@ConsumerType
-public interface ExtensionProvider {
-
- /**
- * The mandatory property to set to the {@link org.apache.sling.sitemap.builder.Extension} sub-type.
- */
- String PROPERTY_INTERFACE = "extension.interface";
- /**
- * The xml namespace prefix to use for the extension.
- */
- String PROPERTY_PREFIX = "extension.prefix";
- /**
- * The xml namespace to use for the extension.
- */
- String PROPERTY_NAMESPACE = "extension.namespace";
- /**
- * The local tag name to use when adding the extension to the sitemap xml.
- */
- String PROPERTY_LOCAL_NAME = "extension.localName";
- /**
- * An optional property to be set for extensions that only write attributes to the added xml tag and want to make
- * use of an empty-tag. If not set an open and close tag will be written.
- */
- String PROPERTY_EMPTY_TAG = "extension.emptyTag";
-
- /**
- * Returns a new instance of the extension provided by the {@link ExtensionProvider}.
- *
- * @return
- */
- @NotNull
- AbstractExtension newInstance();
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/package-info.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/package-info.java
deleted file mode 100644
index 7e266e0..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/extensions/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-@Version("1.0.0")
-package org.apache.sling.sitemap.builder.extensions;
-
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/builder/package-info.java b/sitemap/src/main/java/org/apache/sling/sitemap/builder/package-info.java
deleted file mode 100644
index 0c5627b..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/builder/package-info.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * This package provide a builder-like API to create XML sitemaps.
- */
-@Version("1.0.0")
-package org.apache.sling.sitemap.builder;
-
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapLinkExternalizer.java b/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapLinkExternalizer.java
deleted file mode 100644
index 0354d9f..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapLinkExternalizer.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.common;
-
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.resource.Resource;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ConsumerType;
-
-/**
- * Consumers may implement this interface to override the default externalisation behaviour.
- */
-@ConsumerType
-public interface SitemapLinkExternalizer {
-
- /**
- * A default implementation of the {@link SitemapLinkExternalizer} which may be used as fallback.
- */
- SitemapLinkExternalizer DEFAULT = new SitemapLinkExternalizer() {
- @Override
- public @Nullable String externalize(SlingHttpServletRequest context, String uri) {
- return context.getResourceResolver().map(context, uri);
- }
-
- @Override
- public @Nullable String externalize(Resource resource) {
- return resource.getResourceResolver().map(resource.getPath());
- }
- };
-
- /**
- * Implementations must return an absolute url for the given path in the context of the given request.
- *
- * @param request
- * @param path
- * @return an absolute url
- */
- @Nullable
- String externalize(SlingHttpServletRequest request, String path);
-
- /**
- * Implementations must return an absolute url for the given resource.
- *
- * @param resource
- * @return an absolute url
- */
- @Nullable
- String externalize(Resource resource);
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapUtil.java b/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapUtil.java
deleted file mode 100644
index 01b7cc0..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/common/SitemapUtil.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.common;
-
-import org.apache.jackrabbit.JcrConstants;
-import org.apache.jackrabbit.util.ISO9075;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.sitemap.SitemapService;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.jcr.query.Query;
-import java.util.*;
-
-/**
- * A utility class to give access to common functionality used for sitemaps.
- */
-public class SitemapUtil {
-
- private static final String JCR_SYSTEM_PATH = "/" + JcrConstants.JCR_SYSTEM + "/";
-
- private SitemapUtil() {
- super();
- }
-
- /**
- * Returns the {@link Resource} marked as sitemap root that is closest to the repository root starting with the
- * given {@link Resource}.
- *
- * @param sitemapRoot
- * @return
- */
- @NotNull
- public static Resource getTopLevelSitemapRoot(@NotNull Resource sitemapRoot) {
- Resource topLevelSitemapRoot = sitemapRoot;
- Resource parent = sitemapRoot.getParent();
-
- while (parent != null) {
- if (isSitemapRoot(parent)) {
- topLevelSitemapRoot = parent;
- }
- parent = parent.getParent();
- }
-
- return topLevelSitemapRoot;
- }
-
- /**
- * Returns the parent of the given {@link Resource} when the {@link Resource}'s name is
- * {@link JcrConstants#JCR_CONTENT}.
- *
- * @param resource
- * @return
- */
- @Nullable
- public static Resource normalizeSitemapRoot(@Nullable Resource resource) {
- if (!isSitemapRoot(resource)) {
- return null;
- }
- if (resource.getName().equals(JcrConstants.JCR_CONTENT)) {
- return resource.getParent();
- } else {
- return resource;
- }
- }
-
- /**
- * Returns true when the given {@link Resource} is a sitemap root.
- *
- * @param resource
- * @return
- */
- public static boolean isSitemapRoot(@Nullable Resource resource) {
- if (resource == null) {
- return false;
- }
-
- Boolean sitemapRoot = resource.getValueMap().get(SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.class);
- if (sitemapRoot == null) {
- Resource content = resource.getChild(JcrConstants.JCR_CONTENT);
- if (content != null) {
- sitemapRoot = content.getValueMap().get(SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.FALSE);
- } else {
- sitemapRoot = Boolean.FALSE;
- }
- }
- return sitemapRoot;
- }
-
- /**
- * Returns true when the given {@link Resource} is a top level sitemap root.
- *
- * @param resource
- * @return
- */
- public static boolean isTopLevelSitemapRoot(@Nullable Resource resource) {
- return isSitemapRoot(resource) && getTopLevelSitemapRoot(resource).getPath().equals(resource.getPath());
- }
-
- /**
- * Returns the selector for the given sitemap root {@link Resource} and the given name.
- *
- * @param sitemapRoot
- * @param topLevelSitemapRoot
- * @param name
- * @return
- */
- @NotNull
- public static String getSitemapSelector(@NotNull Resource sitemapRoot, @NotNull Resource topLevelSitemapRoot,
- @NotNull String name) {
- name = SitemapService.DEFAULT_SITEMAP_NAME.equals(name) ? "sitemap" : name + "-sitemap";
-
- if (!sitemapRoot.getPath().equals(topLevelSitemapRoot.getPath())) {
- String sitemapRootSubpath = sitemapRoot.getPath().substring(topLevelSitemapRoot.getPath().length() + 1);
- name = sitemapRootSubpath.replace('/', '-') + '-' + name;
- }
-
- return name;
- }
-
- /**
- * Resolves all sitemap root {@link Resource}s for the given selector within the given top level sitemap root
- * {@link Resource}. This is the inversion of {@link SitemapUtil#getSitemapSelector(Resource, Resource, String)} with
- * sitemap root being a top level sitemap root.
- * <p>
- * The returned {@link Map} only contains {@link Resource}s, that are sitemap roots according to
- * {@link SitemapUtil#isSitemapRoot(Resource)}. Each returned {@link Resource} is mapped to the name which when
- * passed to {@link SitemapUtil#getSitemapSelector(Resource, Resource, String)} would return the same selector,
- * omitting the optional multi-file index part.
- * <p>
- * As this resolution may be ambiguous, the returned {@link Map} is sorted with the sitemap root/name combinations
- * closest to the top level sitemap root taking precedence.
- *
- * @param topLevelSitemapRoot
- * @param sitemapSelector
- * @return a sorted {@link Map}
- */
- @NotNull
- public static Map<Resource, String> resolveSitemapRoots(@NotNull Resource topLevelSitemapRoot,
- @NotNull String sitemapSelector) {
- if (!isTopLevelSitemapRoot(topLevelSitemapRoot)) {
- // selectors are always relative to a top level sitemap root
- return Collections.emptyMap();
- }
- if (sitemapSelector.equals("sitemap")) {
- return Collections.singletonMap(topLevelSitemapRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- }
-
- List<String> parts = Arrays.asList(sitemapSelector.split("-"));
- List<String> relevantParts;
-
- if (parts.size() == 2 && parts.get(0).equals("sitemap") && isInteger(parts.get(1))) {
- // default name with file index
- return Collections.singletonMap(topLevelSitemapRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- } else if (parts.size() > 1 && parts.get(parts.size() - 1).equals("sitemap")) {
- // no file index part
- relevantParts = parts.subList(0, parts.size() - 1);
- } else if (parts.size() > 2 && parts.get(parts.size() - 2).equals("sitemap")
- && isInteger(parts.get(parts.size() - 1))) {
- // with file index part
- relevantParts = parts.subList(0, parts.size() - 2);
- } else {
- return Collections.emptyMap();
- }
-
- Map<Resource, String> roots = new LinkedHashMap<>();
- resolveSitemapRoots(topLevelSitemapRoot, relevantParts, roots);
- return roots;
- }
-
- /**
- * Returns all sitemap root {@link Resource}s within the given search path.
- *
- * @param resolver
- * @param searchPath
- * @return
- */
- @NotNull
- public static Iterator<Resource> findSitemapRoots(ResourceResolver resolver, String searchPath) {
- String correctedSearchPath = searchPath == null ? "/" : searchPath;
- StringBuilder query = new StringBuilder(correctedSearchPath.length() + 35);
- query.append("/jcr:root").append(ISO9075.encodePath(correctedSearchPath));
- if (!correctedSearchPath.endsWith("/")) {
- query.append('/');
- }
- query.append("/*[@").append(SitemapService.PROPERTY_SITEMAP_ROOT).append('=').append(Boolean.TRUE).append(']');
- query.append(" option(index tag slingSitemaps)");
-
- return new Iterator<Resource>() {
- private final Iterator<Resource> hits = resolver.findResources(query.toString(), Query.XPATH);
- private Resource next = seek();
-
- private Resource seek() {
- while (hits.hasNext()) {
- Resource nextHit = normalizeSitemapRoot(hits.next());
- // skip a hit on the given searchPath itself. This may be when a search is done for descendant
- // sitemaps given the normalized sitemap root path and the sitemap root's jcr:content is in the
- // result set.
- if (nextHit == null
- || nextHit.getPath().equals(correctedSearchPath)
- || nextHit.getPath().startsWith(JCR_SYSTEM_PATH)) {
- continue;
- }
- return nextHit;
- }
- return null;
- }
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public Resource next() {
- Resource ret = next;
- next = seek();
- return ret;
- }
- };
- }
-
- private static boolean isInteger(String text) {
- try {
- Integer.parseUnsignedInt(text);
- return true;
- } catch (NumberFormatException ex) {
- return false;
- }
- }
-
- private static void resolveSitemapRoots(@NotNull Resource sitemapRoot, @NotNull List<String> parts,
- @NotNull Map<Resource, String> result) {
- if (isSitemapRoot(sitemapRoot)) {
- result.put(sitemapRoot, String.join("-", parts));
- }
- for (int i = 0, j; i < parts.size(); i++) {
- // products product page tops
- j = i + 1;
- String childName = String.join("-", parts.subList(0, j));
- Resource namedChild = sitemapRoot.getChild(childName);
- if (namedChild != null) {
- if (j == parts.size() && isSitemapRoot(namedChild)) {
- result.put(namedChild, SitemapService.DEFAULT_SITEMAP_NAME);
- } else if (parts.size() > j) {
- resolveSitemapRoots(namedChild, parts.subList(j, parts.size()), result);
- }
- }
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/common/package-info.java b/sitemap/src/main/java/org/apache/sling/sitemap/common/package-info.java
deleted file mode 100644
index 42c3595..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/common/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-@Version("1.0.0")
-package org.apache.sling.sitemap.common;
-
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGenerator.java b/sitemap/src/main/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGenerator.java
deleted file mode 100644
index 88190c4..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGenerator.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.generator;
-
-import org.apache.jackrabbit.JcrConstants;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ConsumerType;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.isSitemapRoot;
-
-/**
- * A default implementation of {@link SitemapGenerator} that traverses a resource tree.
- * <p>
- * Implementations may change the traversal behaviour by overriding
- * {@link ResourceTreeSitemapGenerator#shouldFollow(Resource)} or
- * {@link ResourceTreeSitemapGenerator#shouldInclude(Resource)} but it is recommended to always consider the default
- * implementation. The default implementation includes only {@link Resource}s that have a "jcr:content" child and
- * follows through only on content that is not below the "jcr:content" or any other sitemap root.
- * <p>
- * This implementation keeps track of the traversal's state in the
- * {@link SitemapGenerator.GenerationContext}. It is capable to continue from a previous
- * persisted state, when the generation got aborted.
- */
-@ConsumerType
-public abstract class ResourceTreeSitemapGenerator implements SitemapGenerator {
-
- protected static final String PROPERTY_LAST_PATH = "lastPath";
-
- @Override
- public final void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap,
- @NotNull GenerationContext context) throws SitemapException {
- String lastPath = context.getProperty(PROPERTY_LAST_PATH, String.class);
- for (Resource descendant : (Iterable<? extends Resource>) traverse(sitemapRoot, lastPath)::iterator) {
- addResource(name, sitemap, descendant);
- context.setProperty(PROPERTY_LAST_PATH, descendant.getPath());
- }
- }
-
- /**
- * Implementations add the given {@link Resource} to the given {@link Sitemap}.
- *
- * @param name the name of the sitemap currently being generated
- * @param sitemap
- * @param resource
- * @throws SitemapException
- */
- protected abstract void addResource(@NotNull String name, @NotNull Sitemap sitemap, @NotNull Resource resource)
- throws SitemapException;
-
- /**
- * When implementations return true, the given resource will be passed to
- * {@link ResourceTreeSitemapGenerator#addResource(String, Sitemap, Resource)} to be added to the {@link Sitemap}.
- *
- * @param resource
- * @return
- */
- protected boolean shouldInclude(@NotNull Resource resource) {
- return resource.getChild(JcrConstants.JCR_CONTENT) != null;
- }
-
- /**
- * When implementations return true, the children of the given {@link Resource} will be followed through by the
- * traversal.
- *
- * @param resource
- * @return
- */
- protected boolean shouldFollow(@NotNull Resource resource) {
- return !JcrConstants.JCR_CONTENT.equals(resource.getName()) && !isSitemapRoot(resource);
- }
-
- private Stream<Resource> traverse(@NotNull Resource sitemapRoot, @Nullable String skipTo) {
- Stream<Resource> children = StreamSupport.stream(sitemapRoot.getChildren().spliterator(), false)
- .filter(this::shouldFollow);
-
- if (skipTo != null) {
- AtomicBoolean found = new AtomicBoolean(false);
- // advance children until skipTo starts either with the child's path or it is equal to it
- return children.flatMap(child -> {
- if (found.get()) {
- return traverse(child, null);
- } else if (skipTo.equals(child.getPath())) {
- found.set(true);
- return traverse(child, null);
- } else if (skipTo.startsWith(child.getPath() + '/')) {
- found.set(true);
- return traverse(child, skipTo);
- } else {
- return Stream.empty();
- }
- });
- } else {
- return Stream.concat(
- shouldInclude(sitemapRoot) ? Stream.of(sitemapRoot) : Stream.empty(),
- children.flatMap(child -> traverse(child, null))
- );
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGenerator.java b/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGenerator.java
deleted file mode 100644
index 984e78c..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGenerator.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.generator;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ConsumerType;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * {@link SitemapGenerator} implementations are responsible to generate one or many sitemaps for a given sitemap root
- * {@link Resource}. When a {@link SitemapGenerator} generates multiple sitemaps for a given {@link Resource} it has to
- * return their names using {@link SitemapGenerator#getNames(Resource)}. Also, the {@link SitemapGenerator} may decide
- * that any of those names should be served on-demand by returning a subset of names for
- * {@link SitemapGenerator#getOnDemandNames(Resource)}.
- * <p>
- * {@link SitemapGenerator#generate(Resource, String, Sitemap, GenerationContext)} may be called for each name and
- * each sitemap root {@link Resource}, the implementation returned an non-empty {@link Set} of names for.
- * <p>
- * It is possible to register multiple {@link SitemapGenerator}s for a single name. In this case the one with the
- * highest ranking according to the OSGI specification is used.
- */
-@ConsumerType
-public interface SitemapGenerator {
-
- /**
- * The background generation will send events with that topic right after a generated sitemap was persisted.
- */
- String EVENT_TOPIC_SITEMAP_UPDATED = "org/apache/sling/sitemap/UPDATED";
- /**
- * The background cleanup will send events with that topic right after an obsolete sitemap file was purged.
- */
- String EVENT_TOPIC_SITEMAP_PURGED = "org/apache/sling/sitemap/PURGED";
- /**
- * The event property storing the generated sitemap's root path.
- */
- String EVENT_PROPERTY_SITEMAP_ROOT = "sitemap.root";
- /**
- * The event property storing the generated sitemap's name.
- */
- String EVENT_PROPERTY_SITEMAP_NAME = "sitemap.name";
- /**
- * The event property storing the generated sitemap's count of urls.
- */
- String EVENT_PROPERTY_SITEMAP_URLS = "sitemap.urls";
- /**
- * The event property storing the generated sitemap's storage path.
- */
- String EVENT_PROPERTY_SITEMAP_STORAGE_PATH = "sitemap.storagePath";
- /**
- * The event property storing the generated sitemap's binary size.
- */
- String EVENT_PROPERTY_SITEMAP_STORAGE_SIZE = "sitemap.storageSize";
-
- /**
- * Returns a {@link Set} of sitemap names this {@link SitemapGenerator} can generate for a particular sitemap
- * root {@link Resource}. If the implementation does not generate a sitemap for a particular root it must return an
- * empty {@link Set}, if it does but does not differentiate by name, it must return a {@link Set} containing only
- * the {@link SitemapService#DEFAULT_SITEMAP_NAME}.
- * <p>
- * The default implementation returns a {@link Set} of only {@link SitemapService#DEFAULT_SITEMAP_NAME}.
- *
- * @return a {@link Set} of names
- */
- @NotNull
- default Set<String> getNames(@NotNull Resource sitemapRoot) {
- return Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME);
- }
-
- /**
- * Implementations may return a subset of the names returned by {@link SitemapGenerator#getNames(Resource)}
- * that should be served on-demand.
- * <p>
- * The default implementation returns an empty {@link Set}, meaning none of the names should be served on-demand.
- *
- * @param sitemapRoot
- * @return
- */
- @NotNull
- default Set<String> getOnDemandNames(@NotNull Resource sitemapRoot) {
- return Collections.emptySet();
- }
-
- /**
- * Generates a {@link Sitemap} with the given name at the given {@link Resource}.
- * <p>
- * This process may be stateful and the given {@link GenerationContext} can be used to keep track of the state. For
- * example a traversal that keeps track on the last {@link Resource} added to the {@link Sitemap}.
- *
- * @param sitemapRoot the root at which the sitemap should be created
- * @param name the name, one of the names returned by {@link SitemapGenerator#getNames(Resource)} for the given
- * sitemapRoot
- * @param sitemap the {@link Sitemap} object to add locations to
- * @param context the context under which the sitemap is generated
- * @throws SitemapException may be thrown in unrecoverable exceptional cases
- */
- void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap,
- @NotNull GenerationContext context) throws SitemapException;
-
- /**
- * A context object that gives the {@link SitemapGenerator} access to additional configurations and methods to
- * track state.
- */
- interface GenerationContext {
-
- @Nullable <T> T getProperty(@NotNull String name, @NotNull Class<T> cls);
-
- @NotNull <T> T getProperty(@NotNull String name, @NotNull T defaultValue);
-
- void setProperty(@NotNull String name, @Nullable Object data);
-
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGeneratorManager.java b/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGeneratorManager.java
deleted file mode 100644
index ac5ba71..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/generator/SitemapGeneratorManager.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.generator;
-
-import org.apache.sling.api.resource.Resource;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.annotation.versioning.ProviderType;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A service that manages all registered {@link SitemapGenerator} services.
- */
-@ProviderType
-public interface SitemapGeneratorManager {
-
- /**
- * Returns all names of all {@link SitemapGenerator}s for the given sitemap root.
- *
- * @param sitemapRoot
- * @return
- */
- Set<String> getNames(@NotNull Resource sitemapRoot);
-
- /**
- * Returns all names of all {@link SitemapGenerator}s for the given sitemap root, limited to those that are
- * configured to be served on demand.
- *
- * @param sitemapRoot
- * @return
- */
- @NotNull
- Set<String> getOnDemandNames(@NotNull Resource sitemapRoot);
-
- /**
- * Returns the {@link SitemapGenerator} for the given sitemap root {@link Resource} and name. This may be null
- * when no {@link SitemapGenerator} service exists that generates a sitemap with the name for the given root.
- *
- * @param sitemapRoot
- * @param name
- * @return
- */
- @Nullable
- SitemapGenerator getGenerator(@NotNull Resource sitemapRoot, @NotNull String name);
-
- /**
- * Returns a {@link Map} of {@link SitemapGenerator}s for each name returned by
- * {@link SitemapGeneratorManager#getNames(Resource)}.
- *
- * @param sitemapRoot
- * @return
- */
- @NotNull
- Map<String, SitemapGenerator> getGenerators(@NotNull Resource sitemapRoot);
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/generator/package-info.java b/sitemap/src/main/java/org/apache/sling/sitemap/generator/package-info.java
deleted file mode 100644
index 31283f3..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/generator/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-@Version("1.0.0")
-package org.apache.sling.sitemap.generator;
-
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapEventUtil.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapEventUtil.java
deleted file mode 100644
index c0eefa1..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapEventUtil.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.Resource;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventProperties;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.apache.sling.sitemap.generator.SitemapGenerator.*;
-
-/**
- * A utility class to create new {@link Event}s for sitemap storage operations.
- */
-class SitemapEventUtil {
-
- private SitemapEventUtil() {
- super();
- }
-
- static Event newUpdateEvent(SitemapStorageInfo storageInfo, Resource sitemapRoot) {
- Map<String, Object> props = new HashMap<>(5);
- props.put(EVENT_PROPERTY_SITEMAP_NAME, storageInfo.getName());
- props.put(EVENT_PROPERTY_SITEMAP_ROOT, sitemapRoot.getPath());
- props.put(EVENT_PROPERTY_SITEMAP_URLS, storageInfo.getEntries());
- props.put(EVENT_PROPERTY_SITEMAP_STORAGE_PATH, storageInfo.getPath());
- props.put(EVENT_PROPERTY_SITEMAP_STORAGE_SIZE, storageInfo.getSize());
- return new Event(EVENT_TOPIC_SITEMAP_UPDATED, new EventProperties(props));
- }
-
- static Event newPurgeEvent(String path) {
- Map<String, Object> properties = Collections.singletonMap(EVENT_PROPERTY_SITEMAP_STORAGE_PATH, path);
- return new Event(EVENT_TOPIC_SITEMAP_PURGED, new EventProperties(properties));
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutor.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutor.java
deleted file mode 100644
index c667172..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutor.java
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.JcrConstants;
-import org.apache.sling.api.resource.*;
-import org.apache.sling.api.wrappers.ValueMapDecorator;
-import org.apache.sling.event.jobs.Job;
-import org.apache.sling.event.jobs.consumer.JobExecutionContext;
-import org.apache.sling.event.jobs.consumer.JobExecutionResult;
-import org.apache.sling.event.jobs.consumer.JobExecutor;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.builder.Url;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.apache.sling.sitemap.impl.builder.SitemapImpl;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.event.EventProperties;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.*;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.normalizeSitemapRoot;
-
-@Component(
- service = JobExecutor.class,
- property = {
- JobExecutor.PROPERTY_TOPICS + "=" + SitemapGeneratorExecutor.JOB_TOPIC
- }
-)
-@Designate(ocd = SitemapGeneratorExecutor.Configuration.class)
-public class SitemapGeneratorExecutor implements JobExecutor {
-
- @ObjectClassDefinition(name = "Apache Sling Sitemap - Background Generator")
- @interface Configuration {
-
- @AttributeDefinition(name = "Chunk size", description = "If set to a positive integer, the background job " +
- "writes incomplete sitemaps after the given number of urls to the repository and persists progress. " +
- "This allows the job to be interrupted and resumed.")
- int chunkSize() default Integer.MAX_VALUE;
- }
-
- static final String JOB_TOPIC = "org/apache/sling/sitemap/build";
- static final String JOB_PROPERTY_SITEMAP_ROOT = "sitemap.root";
- static final String JOB_PROPERTY_SITEMAP_NAME = "sitemap.name";
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapGeneratorExecutor.class);
- private static final Map<String, Object> AUTH = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
- "sitemap-reader");
-
- @Reference
- private ResourceResolverFactory resourceResolverFactory;
- @Reference
- private SitemapGeneratorManager generatorManager;
- @Reference
- private ExtensionProviderManager extensionProviderManager;
- @Reference
- private SitemapStorage storage;
- @Reference(target = "(subServiceName=sitemap-reader)")
- private ServiceUserMapped serviceUserMapped;
- @Reference
- private SitemapServiceConfiguration configuration;
-
- private int chunkSize = 10;
-
- @Activate
- protected void activate(Configuration configuration) {
- chunkSize = configuration.chunkSize();
- }
-
- @Override
- public JobExecutionResult process(Job job, JobExecutionContext context) {
- String sitemapRootPath = job.getProperty(JOB_PROPERTY_SITEMAP_ROOT, String.class);
- String sitemapName = job.getProperty(JOB_PROPERTY_SITEMAP_NAME, SitemapService.DEFAULT_SITEMAP_NAME);
- JobExecutionContext.ResultBuilder result = context.result();
-
- try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Resource sitemapRoot = normalizeSitemapRoot(resourceResolver.getResource(sitemapRootPath));
-
- if (sitemapRoot == null) {
- return result.message("Cannot find sitemap root at: " + sitemapRootPath).cancelled();
- }
-
- SitemapGenerator generator = generatorManager.getGenerator(sitemapRoot, sitemapName);
-
- if (generator == null) {
- return result.message("Generator of '" + sitemapName + "' unavailable at: " + sitemapRootPath).failed();
- }
-
- generate(sitemapRoot, sitemapName, generator, context);
-
- return result.succeeded();
- } catch (LoginException ex) {
- LOG.warn("Failed to login service user for sitemap generation", ex);
- return result.message(ex.getMessage()).cancelled();
- } catch (IOException | SitemapException ex) {
- LOG.error("Failed to write sitemap", ex);
- return result.message(ex.getMessage()).failed();
- }
- }
-
- private void generate(Resource res, String name, SitemapGenerator generator, JobExecutionContext jobCtxt)
- throws SitemapException, IOException {
- try {
- CopyableByteArrayOutputStream buffer = new CopyableByteArrayOutputStream();
- GenerationContextImpl genCtxt = new GenerationContextImpl();
-
- // prefill the buffer with existing data from storage
- ValueMap state = storage.getState(res, name);
- InputStream existingData = state.get(JcrConstants.JCR_DATA, InputStream.class);
- if (existingData != null) {
- IOUtils.copy(existingData, buffer);
- }
- // prefill the state from storage
- for (String key : state.keySet()) {
- if (key.indexOf(':') < 0) {
- genCtxt.data.put(key, state.get(key));
- }
- }
- // get the file index, if any
- int fileIndex = state.get(SitemapStorage.PN_SITEMAP_FILE_INDEX, 1);
- int urlCount = state.get(SitemapStorage.PN_SITEMAP_ENTRIES, 0);
-
- MultiFileSitemap sitemap = new MultiFileSitemap(res, name, fileIndex, buffer, genCtxt, jobCtxt);
- sitemap.currentSitemap.urlCount = urlCount;
-
- generator.generate(res, name, sitemap, genCtxt);
-
- sitemap.close();
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Generated sitemaps: {}", String.join(", ", sitemap.files));
- }
-
- // when the max(fileIndex) is smaller then in previous iterations, cleanup old files.
- Collection<String> purgedFiles = storage.deleteSitemaps(res, name, i -> i.getFileIndex() >= sitemap.fileIndex);
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Purged obsolete sitemaps: {}", String.join(", ", purgedFiles));
- }
- } catch (JobAbandonedException ex) {
- throw ex;
- } catch (JobStoppedException ex) {
- LOG.debug("Job stopped, removing state", ex);
- storage.deleteState(res, name);
- } catch (RuntimeException | SitemapException | IOException ex) {
- storage.deleteState(res, name);
- if (ex instanceof IOException) {
- throw (IOException) ex;
- } else if (ex.getCause() instanceof IOException) {
- throw (IOException) ex.getCause();
- } else if (ex instanceof RuntimeException) {
- throw new SitemapException(ex);
- } else {
- throw ex;
- }
- }
- }
-
- private static class ByteBufferInputStream extends InputStream {
-
- private final ByteBuffer buf;
-
- public ByteBufferInputStream(ByteBuffer buf) {
- this.buf = buf;
- }
-
- public int read() {
- if (!buf.hasRemaining()) {
- return -1;
- }
- return buf.get() & 0xFF;
- }
-
- public int read(byte @NotNull [] bytes, int off, int len) {
- if (!buf.hasRemaining()) {
- return -1;
- }
-
- len = Math.min(len, buf.remaining());
- buf.get(bytes, off, len);
- return len;
- }
- }
-
- private static class CopyableByteArrayOutputStream extends ByteArrayOutputStream {
-
- int checkpoint = -1;
-
- void checkpoint() {
- checkpoint = count;
- }
-
- void rollback() {
- ensureCheckpoint();
- count = checkpoint;
- }
-
- @Override
- public synchronized void reset() {
- super.reset();
- checkpoint = -1;
- }
-
- private InputStream copyAfterCheckpoint() {
- ensureCheckpoint();
- return new ByteBufferInputStream(ByteBuffer.wrap(buf, checkpoint, size() - checkpoint));
- }
-
- private InputStream copy() {
- return new ByteBufferInputStream(ByteBuffer.wrap(buf, 0, size()));
- }
-
- private void ensureCheckpoint() {
- if (checkpoint < 0) {
- throw new IllegalStateException("no checkpoint");
- }
- }
- }
-
- private static class JobStoppedException extends RuntimeException {
- JobStoppedException() {
- super("Stopped");
- }
- }
-
- protected static class JobAbandonedException extends RuntimeException {
- JobAbandonedException() {
- super("Abandoned");
- }
- }
-
- private class MultiFileSitemap implements Sitemap, Closeable {
-
- private final Resource sitemapRoot;
- private final String name;
- private final JobExecutionContext jobContext;
- private final GenerationContextImpl generationContext;
- private final List<String> files = new ArrayList<>(1);
- private final CopyableByteArrayOutputStream buffer;
- private final CopyableByteArrayOutputStream overflowBuffer = new CopyableByteArrayOutputStream();
-
- private int fileIndex;
- private StatefulSitemap currentSitemap;
-
- MultiFileSitemap(Resource sitemapRoot, String name, int fileIndex, CopyableByteArrayOutputStream buffer,
- GenerationContextImpl generationContext, JobExecutionContext jobContext) throws IOException {
- this.sitemapRoot = sitemapRoot;
- this.name = name;
- this.fileIndex = fileIndex;
- this.buffer = buffer;
- this.jobContext = jobContext;
- this.generationContext = generationContext;
- this.currentSitemap = newSitemap();
- }
-
- @NotNull
- @Override
- public Url addUrl(@NotNull String location) throws SitemapException {
- try {
- rotateIfNecessary();
- } catch (IOException ex) {
- throw new SitemapException(ex);
- }
- return currentSitemap.addUrl(location);
- }
-
- @Override
- public void close() throws IOException {
- rotateIfNecessary();
- // no add() will happen so the counter will miss the <url> form the overflow buffer
- closeSitemap();
- }
-
- private StatefulSitemap newSitemap() throws IOException {
- return new StatefulSitemap(sitemapRoot, name, buffer, jobContext, generationContext);
- }
-
- private void closeSitemap() throws IOException {
- currentSitemap.close();
- int urlCount = currentSitemap.urlCount;
- if (urlCount == 0) {
- // don't persist empty sitemaps
- return;
- }
- String path = storage.writeSitemap(sitemapRoot, name, buffer.copy(), fileIndex, buffer.size(), urlCount);
- // increment the file index for the next sitemap and store it in the context
- generationContext.data.put(SitemapStorage.PN_SITEMAP_FILE_INDEX, ++fileIndex);
- files.add(path);
- }
-
- private boolean rotateIfNecessary() throws IOException {
- // create a checkpoint before flushing the pending url.
- buffer.checkpoint();
- // flush the sitemap to write the last url to the underlying buffer
- currentSitemap.flush();
- // if the buffer size exceeds the limit (-10 bytes for the closing tag)
- if (buffer.size() + 10 > configuration.getMaxSize()) {
- // retain bytes of the last written url in a temporary buffer
- overflowBuffer.reset();
- IOUtils.copy(buffer.copyAfterCheckpoint(), overflowBuffer);
- // rollback the buffer to the checkpoint
- buffer.rollback();
- // decrease the url count as we effectively move one url
- currentSitemap.urlCount --;
- // close, write and rotate the sitemap
- rotateSitemap();
- // write the overflow
- currentSitemap.flush();
- IOUtils.copy(overflowBuffer.copy(), buffer);
- currentSitemap.urlCount ++;
- return true;
- } else if (currentSitemap.urlCount + 1 > configuration.getMaxEntries()) {
- rotateSitemap();
- return true;
- }
-
- return false;
- }
-
- private void rotateSitemap() throws IOException {
- closeSitemap();
- // reset the buffer for the new sitemap
- buffer.reset();
- // create a new sitemap and if there is any initial data, write it to the buffer
- currentSitemap = newSitemap();
- }
- }
-
- private class StatefulSitemap extends SitemapImpl {
-
- private final Resource sitemapRoot;
- private final String name;
- private final CopyableByteArrayOutputStream buffer;
- private final JobExecutionContext jobContext;
- private final GenerationContextImpl generationContext;
-
- private int urlCount = 0;
- private int writtenUrls = 0;
-
- StatefulSitemap(Resource sitemapRoot, String name, CopyableByteArrayOutputStream buffer,
- JobExecutionContext jobContext, GenerationContextImpl generationContext) throws IOException {
- super(new OutputStreamWriter(buffer, StandardCharsets.UTF_8), extensionProviderManager, buffer.size() == 0);
- this.sitemapRoot = sitemapRoot;
- this.name = name;
- this.buffer = buffer;
- this.jobContext = jobContext;
- this.generationContext = generationContext;
- }
-
- @NotNull
- @Override
- public Url addUrl(@NotNull String location) throws SitemapException {
- if (jobContext.isStopped()) {
- throw new JobStoppedException();
- }
- Url url = super.addUrl(location);
- urlCount ++;
- return url;
- }
-
- @Override
- protected boolean writePendingUrl() throws SitemapException {
- boolean written = super.writePendingUrl();
- if (written && ++writtenUrls == chunkSize) {
- try {
- // make sure the buffer has all data from the writer
- out.flush();
- // copy the state and add the buffer's data
- Map<String, Object> copy = new HashMap<>(generationContext.data.size() + 1);
- copy.putAll(generationContext.data);
- copy.put(SitemapStorage.PN_SITEMAP_ENTRIES, urlCount);
- copy.put(JcrConstants.JCR_DATA, buffer.copy());
- // write the state and reset the counter for the next iteration
- storage.writeState(sitemapRoot, name, copy);
- writtenUrls = 0;
- } catch (IOException ex) {
- throw new SitemapException(ex);
- }
- }
- return written;
- }
- }
-
- private class GenerationContextImpl implements SitemapGenerator.GenerationContext {
-
- private final ValueMap data = new ValueMapDecorator(new HashMap<>());
-
- @Nullable
- @Override
- public <T> T getProperty(@NotNull String name, @NotNull Class<T> cls) {
- return data.get(name, cls);
- }
-
- @Override
- public <T> @NotNull T getProperty(@NotNull String name, @NotNull T defaultValue) {
- return data.get(name, defaultValue);
- }
-
- @Override
- public void setProperty(@NotNull String name, @Nullable Object data) {
- if (name.indexOf(':') > 0) {
- // don't allow using properties from a namespace
- return;
- }
- this.data.put(name, data);
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImpl.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImpl.java
deleted file mode 100644
index c28b0de..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImpl.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.service.component.annotations.*;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.function.Function;
-
-@Component(service = SitemapGeneratorManager.class)
-@Designate(ocd = SitemapGeneratorManagerImpl.Configuration.class)
-public class SitemapGeneratorManagerImpl implements SitemapGeneratorManager {
-
- @ObjectClassDefinition(name = "Apache Sling Sitemap - Sitemap Generator Manager")
- @interface Configuration {
-
- @AttributeDefinition(name = "All on-demand", description = "If enabled, forces all registered " +
- "SitemapGenerators to serve all sitemaps on-demand.")
- boolean allOnDemand() default false;
- }
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapGeneratorManagerImpl.class);
-
- @Reference(service = SitemapGenerator.class, cardinality = ReferenceCardinality.AT_LEAST_ONE,
- policyOption = ReferencePolicyOption.GREEDY)
- private List<SitemapGenerator> generators;
-
- private boolean allOnDemand;
-
- @Activate
- protected void activate(Configuration configuration) {
- // highest ranked first
- Collections.reverse(generators);
- allOnDemand = configuration.allOnDemand();
- }
-
- @Override
- @Nullable
- public SitemapGenerator getGenerator(@NotNull Resource sitemapRoot, @NotNull String name) {
- for (SitemapGenerator generator : this.generators) {
- Set<String> providedNames = new HashSet<>(generator.getNames(sitemapRoot));
-
- if (providedNames.contains(name)) {
- return generator;
- }
- }
-
- return null;
- }
-
- @Override
- public Set<String> getNames(@NotNull Resource sitemapRoot) {
- return getGenerators(sitemapRoot).keySet();
- }
-
- @Override
- @NotNull
- public Map<String, SitemapGenerator> getGenerators(@NotNull Resource sitemapRoot) {
- return Collections.unmodifiableMap(consolidateGenerators(sitemapRoot, generator -> generator::getNames));
- }
-
- @Override
- @NotNull
- public Set<String> getOnDemandNames(@NotNull Resource sitemapRoot) {
- return allOnDemand ? getNames(sitemapRoot) : Collections.unmodifiableSet(
- consolidateGenerators(sitemapRoot, generator -> generator::getOnDemandNames).keySet());
- }
-
- private Map<String, SitemapGenerator> consolidateGenerators(Resource sitemapRoot,
- Function<SitemapGenerator, Function<Resource, Set<String>>> namesProvider) {
- Map<String, SitemapGenerator> consolidatedGenerators = new HashMap<>();
-
- for (SitemapGenerator generator : this.generators) {
- Set<String> providedNames = namesProvider.apply(generator).apply(sitemapRoot);
- Set<String> names = new HashSet<>(providedNames);
-
- if (names.removeAll(consolidatedGenerators.keySet()) && LOG.isDebugEnabled()) {
- String alreadyGenerated = String.join(",", consolidatedGenerators.keySet());
- String provided = String.join(",", providedNames);
- LOG.debug("Removed duplicated names. Already generated: '{}', provided by {}: '{}'",
- alreadyGenerated, generator.getClass().getName(), provided);
- }
-
- if (names.isEmpty()) {
- LOG.debug("Skipping {} as it did not provide any names for '{}'", generator.getClass().getName(),
- sitemapRoot.getPath());
- continue;
- }
-
- for (String name : names) {
- consolidatedGenerators.put(name, generator);
- }
- }
-
- return consolidatedGenerators;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java
deleted file mode 100644
index 53f8e46..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.commons.scheduler.Scheduler;
-import org.apache.sling.event.jobs.Job;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.ConfigurationPolicy;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.findSitemapRoots;
-
-@Component(
- service = {SitemapScheduler.class, Runnable.class},
- configurationPolicy = ConfigurationPolicy.REQUIRE,
- property = {
- Scheduler.PROPERTY_SCHEDULER_CONCURRENT + ":Boolean=false",
- Scheduler.PROPERTY_SCHEDULER_RUN_ON + "=" + Scheduler.VALUE_RUN_ON_SINGLE
- }
-)
-@Designate(ocd = SitemapScheduler.Configuration.class, factory = true)
-public class SitemapScheduler implements Runnable {
-
- @ObjectClassDefinition(name = "Apache Sling Sitemap - Scheduler")
- @interface Configuration {
-
- @AttributeDefinition(name = "Name", description = "The name of the scheduler configuration")
- String scheduler_name();
-
- @AttributeDefinition(name = "Schedule", description = "A cron expression defining the schedule at which the " +
- "sitemap generation jobs will be scheduled.")
- String scheduler_expression();
-
- @AttributeDefinition(name = "Include Generators", description = "A list of full qualified class names of " +
- "SitemapGenerator implementations. If set only the listed SitemapGenerators will be called. If left " +
- "empty all will be called.")
- String[] includeGenerators() default {};
-
- @AttributeDefinition(name = "Exclude Generators", description = "A list of full qualified class names of " +
- "SitemapGenerator implementations. If set the listed SitemapGenerators will not be called. If left " +
- "empty all will be called.")
- String[] excludeGenerators() default {};
-
- @AttributeDefinition(name = "Names", description = "A list of names. If set only sitemaps for the given " +
- "names will be generated by. If left empty all will be generated.")
- String[] names() default {};
-
- @AttributeDefinition(name = "Search Path", description = "The path under which sitemap roots should be " +
- "searched for")
- String searchPath() default "/content";
- }
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapScheduler.class);
- private static final Map<String, Object> AUTH = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
- "sitemap-reader");
-
- @Reference
- private JobManager jobManager;
- @Reference
- private ResourceResolverFactory resourceResolverFactory;
- @Reference
- private SitemapGeneratorManager generatorManager;
- @Reference(target = "(subServiceName=sitemap-reader)")
- private ServiceUserMapped serviceUserMapped;
-
- private Set<String> names;
- private Set<String> includeGenerators;
- private Set<String> excludeGenerators;
- private String searchPath;
-
- @Activate
- protected void activate(Configuration configuration) {
- includeGenerators = asSet(configuration.includeGenerators());
- excludeGenerators = asSet(configuration.excludeGenerators());
- names = asSet(configuration.names());
- searchPath = configuration.searchPath();
- }
-
- @Override
- public void run() {
- schedule(null);
- }
-
- public void schedule(@Nullable Collection<String> includeNames) {
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Iterator<Resource> sitemapRoots = findSitemapRoots(resolver, searchPath);
- while (sitemapRoots.hasNext()) {
- schedule(sitemapRoots.next(), includeNames);
- }
- } catch (LoginException ex) {
- LOG.warn("Failed start sitemap jobs: {}", ex.getMessage(), ex);
- }
- }
-
- public void schedule(Resource sitemapRoot, @Nullable Collection<String> includeNames) {
- Set<String> configuredNames = getApplicableNames(sitemapRoot);
-
- if (includeNames != null) {
- configuredNames.retainAll(includeNames);
- }
-
- for (String applicableName : configuredNames) {
- addJob(sitemapRoot.getPath(), applicableName);
- }
- }
-
-
- /**
- * Returns the names for the given sitemap root this {@link SitemapScheduler} is applicable to. This depends on the
- * configured generators. If no generators were configured the names of all are returned. If some where configured
- * the names provided only by those where the class name matches are returned.
- *
- * @param sitemapRoot
- * @return
- */
- public Set<String> getApplicableNames(Resource sitemapRoot) {
- Set<String> onDemandNames = generatorManager.getOnDemandNames(sitemapRoot);
- Set<String> toSchedule = generatorManager.getGenerators(sitemapRoot).entrySet().stream()
- .filter(entry -> includeGenerators == null
- || includeGenerators.contains(entry.getValue().getClass().getName()))
- .filter(entry -> excludeGenerators == null
- || !excludeGenerators.contains(entry.getValue().getClass().getName()))
- .filter(entry -> !onDemandNames.contains(entry.getKey()))
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
-
- // limit to the configured names
- if (names != null) {
- toSchedule.retainAll(names);
- }
-
- return toSchedule;
- }
-
- protected void addJob(String sitemapRoot, String applicableName) {
- Map<String, Object> jobProperties = new HashMap<>();
- jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_NAME, applicableName);
- jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_ROOT, sitemapRoot);
- Job job = jobManager.addJob(SitemapGeneratorExecutor.JOB_TOPIC, jobProperties);
- LOG.debug("Added job {}", job.getId());
- }
-
- @Nullable
- private static Set<String> asSet(@Nullable String[] configuration) {
- if (configuration == null || configuration.length == 0) {
- return null;
- }
-
- Set<String> result = Arrays.stream(configuration)
- .filter(Objects::nonNull)
- .filter(entry -> !"".equals(entry.trim()))
- .collect(Collectors.toSet());
-
- return result.isEmpty() ? null : result;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceConfiguration.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceConfiguration.java
deleted file mode 100644
index c29ed76..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceConfiguration.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@Component(service = SitemapServiceConfiguration.class)
-@Designate(ocd = SitemapServiceConfiguration.Configuration.class)
-public class SitemapServiceConfiguration {
-
- @ObjectClassDefinition(name = "Apache Sling Sitemap - Sitemap Service")
- @interface Configuration {
-
- @AttributeDefinition(name = "Max Size", description = "The maximum size of a sitemap in bytes. Files that " +
- "exceed the size will be flagged with a warning.")
- int maxSize() default 10 * 1024 * 1024;
-
- @AttributeDefinition(name = "Max Entries", description = "The maximum number of urls of a sitemap. Files " +
- "that exceed this number will be flagged with a warning.")
- int maxEntries() default 50000;
- }
-
- private int maxSize;
- private int maxEntries;
-
- @Activate
- protected void activate(Configuration configuration) {
- maxSize = configuration.maxSize();
- maxEntries = configuration.maxEntries();
- }
-
- public int getMaxSize() {
- return maxSize;
- }
-
- public int getMaxEntries() {
- return maxEntries;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java
deleted file mode 100644
index 60acd81..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.SitemapInfo;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.common.SitemapLinkExternalizer;
-import org.apache.sling.sitemap.common.SitemapUtil;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.*;
-import org.osgi.util.tracker.ServiceTracker;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.*;
-
-@Component(service = SitemapService.class)
-public class SitemapServiceImpl implements SitemapService {
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapServiceImpl.class);
-
- @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
- private SitemapLinkExternalizer externalizer;
- @Reference
- private SitemapGeneratorManager generatorManager;
- @Reference
- private SitemapStorage storage;
- @Reference
- private SitemapServiceConfiguration sitemapServiceConfiguration;
-
- private ServiceTracker<SitemapScheduler, SitemapScheduler> schedulers;
-
- @Activate
- protected void activate(BundleContext bundleContext) {
- schedulers = new ServiceTracker<>(bundleContext, SitemapScheduler.class, null);
- schedulers.open();
- }
-
- @Deactivate
- protected void deactivate() {
- schedulers.close();
- }
-
- @Override
- public int getMaxSize() {
- return sitemapServiceConfiguration.getMaxSize();
- }
-
- @Override
- public int getMaxEntries() {
- return sitemapServiceConfiguration.getMaxEntries();
- }
-
- @Override
- public void scheduleGeneration() {
- if (schedulers.getServiceReferences() != null) {
- for (ServiceReference<SitemapScheduler> scheduler : schedulers.getServiceReferences()) {
- schedulers.getService(scheduler).run();
- }
- }
- }
-
- @Override
- public void scheduleGeneration(String name) {
- if (schedulers.getServiceReferences() != null) {
- for (ServiceReference<SitemapScheduler> scheduler : schedulers.getServiceReferences()) {
- schedulers.getService(scheduler).schedule(Collections.singleton(name));
- }
- }
- }
-
- @Override
- public void scheduleGeneration(Resource sitemapRoot) {
- if (schedulers.getServiceReferences() == null || !SitemapUtil.isSitemapRoot(sitemapRoot)) {
- return;
- }
- for (ServiceReference<SitemapScheduler> scheduler : schedulers.getServiceReferences()) {
- Object searchPath = scheduler.getProperty("searchPath");
- if (searchPath instanceof String && sitemapRoot.getPath().startsWith(searchPath + "/")) {
- schedulers.getService(scheduler).schedule(sitemapRoot, null);
- }
- }
- }
-
- @Override
- public void scheduleGeneration(Resource sitemapRoot, String name) {
- if (schedulers.getServiceReferences() == null || !SitemapUtil.isSitemapRoot(sitemapRoot)) {
- return;
- }
-
- for (ServiceReference<SitemapScheduler> scheduler : schedulers.getServiceReferences()) {
- Object searchPath = scheduler.getProperty("searchPath");
- if (searchPath instanceof String && sitemapRoot.getPath().startsWith(searchPath + "/")) {
- schedulers.getService(scheduler).schedule(sitemapRoot, Collections.singleton(name));
- }
- }
- }
-
- @NotNull
- @Override
- public Collection<SitemapInfo> getSitemapInfo(@NotNull Resource resource) {
- Resource sitemapRoot = normalizeSitemapRoot(resource);
-
- if (sitemapRoot == null) {
- LOG.debug("Not a sitemap root: {}", resource.getPath());
- return Collections.emptySet();
- }
-
- Resource topLevelSitemapRoot = isTopLevelSitemapRoot(sitemapRoot)
- ? sitemapRoot
- : getTopLevelSitemapRoot(sitemapRoot);
- String baseUrl = externalize(topLevelSitemapRoot);
-
- if (baseUrl == null) {
- LOG.debug("Could not get absolute url to sitemap: {}", resource.getPath());
- return Collections.emptySet();
- }
-
- Collection<String> names = new HashSet<>(generatorManager.getNames(sitemapRoot));
- Set<String> onDemandNames = generatorManager.getOnDemandNames(sitemapRoot);
- Collection<SitemapInfo> infos = new ArrayList<>(names.size() + 1);
-
- if (requiresSitemapIndex(sitemapRoot)) {
- String location = baseUrl + '.' + SitemapServlet.SITEMAP_INDEX_SELECTOR + '.' +
- SitemapServlet.SITEMAP_EXTENSION;
- infos.add(newSitemapIndexInfo(location));
- }
-
- // write on demand sitemaps
- for (Iterator<String> it = names.iterator(); it.hasNext(); ) {
- String name = it.next();
-
- if (!onDemandNames.contains(name)) {
- continue;
- }
-
- it.remove();
- String selector = getSitemapSelector(sitemapRoot, topLevelSitemapRoot, name);
- String location = newSitemapUrl(baseUrl, selector);
- infos.add(newOnDemandSitemapInfo(location, name));
- }
-
- if (names.isEmpty()) {
- // early exit when only sitemap-index / on-demand sitemaps are served for the given root
- return infos;
- }
-
- for (SitemapStorageInfo storageInfo : storage.getSitemaps(sitemapRoot, names)) {
- String location = newSitemapUrl(baseUrl, storageInfo.getSitemapSelector());
- infos.add(
- newStoredSitemapInfo(storageInfo.getPath(), location, storageInfo.getName(), storageInfo.getSize(),
- storageInfo.getEntries()));
- names.remove(storageInfo.getName());
- }
-
- if (names.isEmpty()) {
- // early exit when all sitemaps are either on-demand or stored
- return infos;
- }
-
- // now names will contain only sitemaps that are either scheduled or not scheduled (generated on a different
- // host)
- ServiceReference<SitemapScheduler>[] schedulerRefs = schedulers.getServiceReferences();
- for (String name : names) {
- // check if a scheduler applicable for the name exists
- boolean hasApplicableScheduler = schedulerRefs != null && Arrays.stream(schedulerRefs)
- .map(schedulers::getService)
- .map(scheduler -> scheduler.getApplicableNames(resource))
- .anyMatch(applicableNames -> applicableNames.contains(name));
- String selector = getSitemapSelector(sitemapRoot, topLevelSitemapRoot, name);
- String location = newSitemapUrl(baseUrl, selector);
- infos.add(newOnDemandSitemapInfo(location, name,
- hasApplicableScheduler ? SitemapInfo.Status.SCHEDULED : SitemapInfo.Status.UNKNOWN));
- }
-
-
- return infos;
- }
-
- private boolean requiresSitemapIndex(@NotNull Resource sitemapRoot) {
- Set<String> names = generatorManager.getGenerators(sitemapRoot).keySet();
- return isTopLevelSitemapRoot(sitemapRoot)
- && (names.size() > 1
- || findSitemapRoots(sitemapRoot.getResourceResolver(), sitemapRoot.getPath()).hasNext()
- || storage.getSitemaps(sitemapRoot, names).size() > 1);
- }
-
- private String externalize(Resource resource) {
- return (externalizer == null ? SitemapLinkExternalizer.DEFAULT : externalizer).externalize(resource);
- }
-
- private static SitemapInfo newSitemapIndexInfo(@NotNull String url) {
- return new SitemapInfoImpl(null, url, SITEMAP_INDEX_NAME, SitemapInfo.Status.ON_DEMAND, -1, -1);
- }
-
- private static SitemapInfo newOnDemandSitemapInfo(@NotNull String url, @NotNull String name) {
- return newOnDemandSitemapInfo(url, name, SitemapInfo.Status.ON_DEMAND);
- }
-
- private static SitemapInfo newOnDemandSitemapInfo(@NotNull String url, @NotNull String name, @NotNull
- SitemapInfo.Status status) {
- return new SitemapInfoImpl(null, url, name, status, -1, -1);
- }
-
- private static SitemapInfo newStoredSitemapInfo(@NotNull String path, @NotNull String url, @NotNull String name,
- int size, int entries) {
- return new SitemapInfoImpl(path, url, name, SitemapInfo.Status.STORAGE, size, entries);
- }
-
- private static String newSitemapUrl(String baseUrl, String selector) {
- // worst case: 1x baseUrl, 1x selector, 1x sitemap-selector, 1x extension, 3x <dot>
- StringBuilder builder = new StringBuilder(baseUrl.length() + selector.length() +
- SitemapServlet.SITEMAP_SELECTOR.length() + SitemapServlet.SITEMAP_EXTENSION.length() + 3);
- builder.append(baseUrl);
- builder.append('.');
-
- if (!selector.equals(SitemapServlet.SITEMAP_SELECTOR)) {
- builder.append(SitemapServlet.SITEMAP_SELECTOR);
- builder.append('.');
- }
-
- builder.append(selector);
- builder.append('.');
- builder.append(SitemapServlet.SITEMAP_EXTENSION);
-
- return builder.toString();
- }
-
- private static class SitemapInfoImpl implements SitemapInfo {
-
- private final String url;
- private final String path;
- private final String name;
- private final Status status;
- private final int size;
- private final int entries;
-
- private SitemapInfoImpl(@Nullable String path, @NotNull String url, @NotNull String name,
- @NotNull Status status, int size, int entries) {
- this.path = path;
- this.url = url;
- this.name = name;
- this.status = status;
- this.size = size;
- this.entries = entries;
- }
-
- @Nullable
- @Override
- public String getStoragePath() {
- return path;
- }
-
- @NotNull
- @Override
- public String getUrl() {
- return url;
- }
-
- @Override
- @NotNull
- public String getName() {
- return name;
- }
-
- @NotNull
- @Override
- public SitemapInfo.Status getStatus() {
- return status;
- }
-
- @Override
- public int getSize() {
- return size;
- }
-
- @Override
- public int getEntries() {
- return entries;
- }
-
- @Override
- public String toString() {
- return "SitemapInfoImpl{" +
- "url='" + url + '\'' +
- ", size=" + size +
- ", entries=" + entries +
- '}';
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServlet.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServlet.java
deleted file mode 100644
index ff5eaaf..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServlet.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.SlingHttpServletRequest;
-import org.apache.sling.api.SlingHttpServletResponse;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.servlets.ServletResolverConstants;
-import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.common.SitemapLinkExternalizer;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.apache.sling.sitemap.impl.builder.SitemapImpl;
-import org.apache.sling.sitemap.impl.builder.SitemapIndexImpl;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.service.component.annotations.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.servlet.Servlet;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.*;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.*;
-import static org.apache.sling.sitemap.impl.SitemapServlet.*;
-
-@Component(
- service = Servlet.class,
- configurationPolicy = ConfigurationPolicy.REQUIRE,
- property = {
- ServletResolverConstants.SLING_SERVLET_SELECTORS + "=" + SITEMAP_SELECTOR,
- ServletResolverConstants.SLING_SERVLET_SELECTORS + "=" + SITEMAP_INDEX_SELECTOR,
- ServletResolverConstants.SLING_SERVLET_EXTENSIONS + "=" + SITEMAP_EXTENSION,
- }
-)
-public class SitemapServlet extends SlingSafeMethodsServlet {
-
- static final String SITEMAP_SELECTOR = "sitemap";
- static final String SITEMAP_INDEX_SELECTOR = "sitemap-index";
- static final String SITEMAP_EXTENSION = "xml";
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapServlet.class);
- private static SitemapGenerator.GenerationContext NOOP_CONTEXT = new SitemapGenerator.GenerationContext() {
- @Nullable
- @Override
- public <T> T getProperty(@NotNull String name, @NotNull Class<T> cls) {
- return null;
- }
-
- @NotNull
- @Override
- public <T> T getProperty(@NotNull String name, @NotNull T defaultValue) {
- return defaultValue;
- }
-
- @Override
- public void setProperty(@NotNull String name, @Nullable Object data) {
- }
- };
-
- @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
- private SitemapLinkExternalizer externalizer;
- @Reference
- private SitemapGeneratorManager generatorManager;
- @Reference
- private ExtensionProviderManager extensionProviderManager;
- @Reference
- private SitemapStorage storage;
- @Reference
- private SitemapServiceConfiguration sitemapServiceConfiguration;
-
- @Override
- protected void doGet(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response)
- throws ServletException, IOException {
- try {
- Resource requestedResource = normalizeSitemapRoot(request.getResource());
-
- if (!isSitemapRoot(requestedResource)) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- return;
- }
-
- response.setCharacterEncoding("utf-8");
- response.setContentType("application/xml");
-
- List<String> selectors = Arrays.asList(request.getRequestPathInfo().getSelectors());
-
- if (selectors.size() == 1 && selectors.contains(SITEMAP_INDEX_SELECTOR)) {
- doGetSitemapIndex(request, response, requestedResource);
- } else if (selectors.size() == 1 && selectors.contains(SITEMAP_SELECTOR)) {
- // when only one selector is provided, that means the default sitemap got requested
- doGetSitemap(request, response, requestedResource, selectors.get(0));
- } else if (selectors.size() == 2 && selectors.get(0).equals(SITEMAP_SELECTOR)) {
- doGetSitemap(request, response, requestedResource, selectors.get(1));
- } else {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST);
- }
- } catch (SitemapException ex) {
- if (ex.getCause() instanceof IOException) {
- throw (IOException) ex.getCause();
- } else {
- throw new ServletException(ex);
- }
- }
- }
-
- protected void doGetSitemapIndex(@NotNull SlingHttpServletRequest request,
- @NotNull SlingHttpServletResponse response,
- Resource topLevelSitemapRoot) throws IOException, SitemapException {
- // when sitemaps may be served on demand, we have to query for the sitemap roots of the current resource,
- // otherwise we can simply get the top level's storage path and serve all sitemaps in there
- SitemapIndexImpl sitemapIndex = new SitemapIndexImpl(response.getWriter());
- Set<String> addedSitemapSelectors = addOnDemandSitemapsToIndex(request, topLevelSitemapRoot, sitemapIndex);
-
- // add any sitemap from the storage
- for (SitemapStorageInfo storageInfo : storage.getSitemaps(topLevelSitemapRoot)) {
- if (!addedSitemapSelectors.contains(storageInfo.getSitemapSelector())) {
- String location = externalize(request,
- getSitemapLink(topLevelSitemapRoot, storageInfo.getSitemapSelector()));
- Calendar lastModified = storageInfo.getLastModified();
- if (location != null && lastModified != null) {
- sitemapIndex.addSitemap(location, lastModified.toInstant());
- } else if (location != null) {
- sitemapIndex.addSitemap(location);
- } else {
- LOG.debug("Could not get absolute url for sitemap served from {}",
- storageInfo.getSitemapSelector());
- }
- }
- }
-
- sitemapIndex.close();
- }
-
- protected void doGetSitemap(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response,
- Resource topLevelSitemapRoot, String sitemapSelector) throws SitemapException, IOException {
- Set<String> onDemandNames = generatorManager.getOnDemandNames(topLevelSitemapRoot);
- if (onDemandNames.size() > 0) {
- // resolve the actual sitemap root from the sitemapSelector
- Map<Resource, String> candidates = resolveSitemapRoots(topLevelSitemapRoot, sitemapSelector);
-
- for (Map.Entry<Resource, String> entry : candidates.entrySet()) {
- Resource sitemapRoot = entry.getKey();
- String name = entry.getValue();
- SitemapGenerator generator = generatorManager.getGenerator(sitemapRoot, name);
-
- if (generator != null && onDemandNames.contains(name)) {
- SitemapImpl sitemap = new SitemapImpl(response.getWriter(), extensionProviderManager);
- generator.generate(sitemapRoot, name, sitemap, NOOP_CONTEXT);
- sitemap.close();
- return;
- }
- }
- }
-
- if (!storage.copySitemap(topLevelSitemapRoot, sitemapSelector, response.getOutputStream())) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND);
- }
- }
-
- /**
- * Adds all on-demand sitemaps to the index within the given sitemap root.
- *
- * @param request
- * @param parentSitemapRoot
- * @param index
- * @return
- * @throws SitemapException
- */
- private Set<String> addOnDemandSitemapsToIndex(SlingHttpServletRequest request, Resource parentSitemapRoot,
- SitemapIndexImpl index) throws SitemapException {
- Set<String> addedSitemapSelectors = new HashSet<>();
- Iterator<Resource> sitemapRoots = findSitemapRoots(request.getResourceResolver(), parentSitemapRoot.getPath());
- if (!sitemapRoots.hasNext()) {
- // serve at least the top level sitemap
- sitemapRoots = Collections.singleton(parentSitemapRoot).iterator();
- }
- while (sitemapRoots.hasNext()) {
- Resource sitemapRoot = sitemapRoots.next();
- Set<String> applicableNames = generatorManager.getOnDemandNames(sitemapRoot);
- // applicable names we may serve directly, not applicable names, if any, we have to serve from storage
- for (String applicableName : applicableNames) {
- String sitemapSelector = getSitemapSelector(sitemapRoot, parentSitemapRoot, applicableName);
- String location = externalize(request, getSitemapLink(sitemapRoot, sitemapSelector));
- if (location != null) {
- index.addSitemap(location);
- addedSitemapSelectors.add(sitemapSelector);
- } else {
- LOG.debug("Could not get absolute url for on-demand sitemap: {}", sitemapSelector);
- }
- }
- }
-
- return addedSitemapSelectors;
- }
-
- private String externalize(SlingHttpServletRequest request, String uri) {
- return (externalizer == null ? SitemapLinkExternalizer.DEFAULT : externalizer).externalize(request, uri);
- }
-
- private static String getSitemapLink(Resource sitemapRoot, String sitemapSelector) {
- String link = sitemapRoot.getPath() + '.' + SITEMAP_SELECTOR + '.';
- if (SITEMAP_SELECTOR.equals(sitemapSelector)) {
- return link + SITEMAP_EXTENSION;
- } else {
- return link + sitemapSelector + '.' + SITEMAP_EXTENSION;
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorage.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorage.java
deleted file mode 100644
index e3c4388..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorage.java
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.jackrabbit.JcrConstants;
-import org.apache.sling.api.SlingConstants;
-import org.apache.sling.api.resource.*;
-import org.apache.sling.api.wrappers.ValueMapDecorator;
-import org.apache.sling.commons.metrics.Counter;
-import org.apache.sling.commons.metrics.MetricsService;
-import org.apache.sling.commons.scheduler.Scheduler;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.common.SitemapUtil;
-import org.apache.sling.sitemap.generator.SitemapGeneratorManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.service.component.annotations.*;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventAdmin;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.*;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-
-import static org.apache.sling.sitemap.common.SitemapUtil.*;
-import static org.apache.sling.sitemap.impl.SitemapEventUtil.newPurgeEvent;
-import static org.apache.sling.sitemap.impl.SitemapEventUtil.newUpdateEvent;
-
-@Component(
- service = {SitemapStorage.class, Runnable.class},
- property = {
- Scheduler.PROPERTY_SCHEDULER_NAME + "=sitemap-storage-cleanup",
- Scheduler.PROPERTY_SCHEDULER_CONCURRENT + ":Boolean=false",
- Scheduler.PROPERTY_SCHEDULER_RUN_ON + "=" + Scheduler.VALUE_RUN_ON_SINGLE
- }
-)
-@Designate(ocd = SitemapStorage.Configuration.class)
-public class SitemapStorage implements Runnable {
-
- @ObjectClassDefinition(name = "Apache Sling Sitemap - Storage")
- @interface Configuration {
-
- @AttributeDefinition(name = "Path", description = "The path under which sitemap files generated in the " +
- "background will be stored.")
- String storagePath() default "/var/sitemaps";
-
- @AttributeDefinition(name = "Max State Age", description = "The number of milliseconds after which an " +
- "intermediate state will deleted.")
- int stateMaxAge() default 60 * 30 * 1000;
-
- @AttributeDefinition(name = "Cleanup Schedule", description = "A cron expression defining the schedule at " +
- "which stale intermediate states and old sitemaps will be removed.")
- String scheduler_expression() default "0 0 1 * * ?";
- }
-
- static final String PN_SITEMAP_ENTRIES = "sling:sitemapEntries";
- static final String PN_SITEMAP_SIZE = "sling:sitemapFileSize";
- static final String PN_SITEMAP_NAME = "sling:sitemapName";
- static final String PN_SITEMAP_FILE_INDEX = "sling:sitemapFileIndex";
-
- private static final Logger LOG = LoggerFactory.getLogger(SitemapStorage.class);
- private static final Map<String, Object> AUTH = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE,
- "sitemap-writer");
- private static final String STATE_EXTENSION = ".part";
- private static final String XML_EXTENSION = ".xml";
- private static final String RT_SITEMAP_PART = "sling/sitemap/part";
- private static final String RT_SITEMAP_FILE = "sling/sitemap/file";
- private static final String PN_RESOURCE_TYPE = SlingConstants.NAMESPACE_PREFIX + ':' + SlingConstants.PROPERTY_RESOURCE_TYPE;
-
- @Reference
- private ResourceResolverFactory resourceResolverFactory;
- @Reference(target = "(subServiceName=sitemap-writer)")
- private ServiceUserMapped serviceUserMapped;
- @Reference
- private SitemapGeneratorManager generatorManager;
- @Reference
- private EventAdmin eventAdmin;
- @Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL)
- private MetricsService metricsService;
-
- private String rootPath = "/var/sitemaps";
- private int maxStateAge = Integer.MAX_VALUE;
-
- private Counter checkpointReadsExpired;
- private Counter checkpointReads;
- private Counter checkpointMisses;
- private Counter checkpointWrites;
-
- @Activate
- protected void activate(Configuration configuration) {
- rootPath = configuration.storagePath();
- maxStateAge = configuration.stateMaxAge();
-
- if (metricsService == null) {
- metricsService = MetricsService.NOOP;
- }
-
- // metrics to measure efficiency of checkpoint persistence
- checkpointReadsExpired = metricsService.counter("SitemapStorage-checkpointReadsExpired");
- checkpointMisses = metricsService.counter("SitemapStorage-checkpointMisses");
- checkpointReads = metricsService.counter("SitemapStorage-checkpointReads");
- checkpointWrites = metricsService.counter("SitemapStorage-checkpointWrites");
- }
-
- @Override
- public void run() {
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Iterator<Resource> descendants = traverse(resolver.getResource(rootPath)).iterator();
- List<Resource> toDelete = new LinkedList<>();
- List<Event> purgeEvents = new LinkedList<>();
- while (descendants.hasNext()) {
- Resource descendant = descendants.next();
- if (descendant.isResourceType(RT_SITEMAP_PART) && isExpired(descendant)) {
- toDelete.add(descendant);
- } else if (descendant.isResourceType(RT_SITEMAP_FILE)
- && (!doesSitemapRootExist(descendant) || !isValidSitemapFile(descendant))) {
- toDelete.add(descendant);
- purgeEvents.add(newPurgeEvent(descendant.getPath()));
- }
- }
- for (Resource resource : toDelete) {
- resolver.delete(resource);
- }
- resolver.commit();
- purgeEvents.forEach(eventAdmin::postEvent);
- } catch (LoginException | PersistenceException ex) {
- LOG.warn("Failed to cleanup storage: {}", ex.getMessage(), ex);
- }
- }
-
- @NotNull
- public ValueMap getState(@NotNull Resource sitemapRoot, @NotNull String name) throws IOException {
- String statePath = getSitemapFilePath(sitemapRoot, name) + STATE_EXTENSION;
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Resource state = resolver.getResource(statePath);
-
- if (state == null) {
- checkpointMisses.increment();
- return ValueMap.EMPTY;
- }
-
- if (isExpired(state)) {
- checkpointReadsExpired.increment();
- return ValueMap.EMPTY;
- }
-
- // make a copy to read properties fully
- checkpointReads.increment();
- return new ValueMapDecorator(new HashMap<>(state.getValueMap()));
- } catch (LoginException ex) {
- throw new IOException("Cannot read state at " + statePath, ex);
- }
- }
-
- public void writeState(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Map<String, Object> state)
- throws IOException {
- String statePath = getSitemapFilePath(sitemapRoot, name) + STATE_EXTENSION;
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Resource folder = getOrCreateFolder(resolver, ResourceUtil.getParent(statePath));
- String stateName = ResourceUtil.getName(statePath);
- Resource stateResource = folder.getChild(stateName);
-
- if (stateResource == null) {
- Map<String, Object> properties = new HashMap<>(state.size() + 1);
- properties.putAll(state);
- properties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
- properties.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
- properties.put(PN_RESOURCE_TYPE, RT_SITEMAP_PART);
- resolver.create(folder, stateName, properties);
- } else {
- ModifiableValueMap properties = stateResource.adaptTo(ModifiableValueMap.class);
- if (properties == null) {
- throw new IOException("Cannot modify properties of existing state: " + statePath);
- }
- properties.putAll(state);
- properties.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
- }
-
- checkpointWrites.increment();
- resolver.commit();
- } catch (LoginException | PersistenceException ex) {
- throw new IOException("Cannot create state at " + statePath, ex);
- }
- }
-
- public void deleteState(@NotNull Resource sitemapRoot, @NotNull String name) throws IOException {
- String statePath = getSitemapFilePath(sitemapRoot, name) + STATE_EXTENSION;
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Resource stateResource = resolver.getResource(statePath);
- if (stateResource != null) {
- resolver.delete(stateResource);
- resolver.commit();
- }
- } catch (LoginException | PersistenceException ex) {
- throw new IOException("Cannot create state at " + statePath, ex);
- }
- }
-
- public String writeSitemap(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull InputStream data,
- int index, int size, int entries) throws IOException {
- if (index < 1) {
- throw new IllegalArgumentException("only unsigned integer greater then zero permitted");
- }
-
- String sitemapFilePath = getSitemapFilePath(sitemapRoot, name);
- String statePath = sitemapFilePath + STATE_EXTENSION;
- sitemapFilePath += index > 1 ? "-" + index + XML_EXTENSION : XML_EXTENSION;
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- String sitemapFileName = ResourceUtil.getName(sitemapFilePath);
- Resource folder = getOrCreateFolder(resolver, ResourceUtil.getParent(sitemapFilePath));
-
- Resource sitemapResource = folder.getChild(sitemapFileName);
-
- if (sitemapResource == null) {
- Map<String, Object> properties = new HashMap<>(3);
- properties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED);
- properties.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
- properties.put(JcrConstants.JCR_DATA, data);
- properties.put(PN_SITEMAP_NAME, name);
- properties.put(PN_SITEMAP_FILE_INDEX, index);
- properties.put(PN_SITEMAP_ENTRIES, entries);
- properties.put(PN_SITEMAP_SIZE, size);
- properties.put(PN_RESOURCE_TYPE, RT_SITEMAP_FILE);
- sitemapResource = resolver.create(folder, sitemapFileName, properties);
- } else {
- ModifiableValueMap properties = sitemapResource.adaptTo(ModifiableValueMap.class);
- if (properties == null) {
- throw new IOException("Cannot overwrite existing sitemap at: " + sitemapFilePath);
- }
- properties.put(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
- properties.put(JcrConstants.JCR_DATA, data);
- properties.put(PN_SITEMAP_ENTRIES, entries);
- properties.put(PN_SITEMAP_SIZE, size);
- }
-
- Resource stateResource = resolver.getResource(statePath);
- if (stateResource != null) {
- resolver.delete(stateResource);
- }
-
- resolver.commit();
- eventAdmin.postEvent(newUpdateEvent(newSitemapStorageInfo(sitemapResource), sitemapRoot));
- } catch (LoginException | PersistenceException ex) {
- throw new IOException("Cannot create sitemap at " + sitemapFilePath, ex);
- }
-
- return sitemapFilePath;
- }
-
- public Collection<String> deleteSitemaps(@NotNull Resource sitemapRoot, @NotNull String name,
- Predicate<SitemapStorageInfo> storageInfoPredicate) throws IOException {
- Collection<SitemapStorageInfo> storageInfo = getSitemaps(sitemapRoot, Collections.singleton(name));
- Iterator<SitemapStorageInfo> toDelete = storageInfo.stream().filter(storageInfoPredicate).iterator();
-
- if (!toDelete.hasNext()) {
- // nothing to delete according to the given predicate
- return Collections.emptyList();
- }
-
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- List<String> result = new ArrayList<>(storageInfo.size());
- List<Event> events = new ArrayList<>(storageInfo.size());
-
- while (toDelete.hasNext()) {
- String path = toDelete.next().getPath();
- Resource resource = resolver.getResource(path);
- if (resource != null) {
- resolver.delete(resource);
- result.add(path);
- events.add(newPurgeEvent(path));
- }
- }
- resolver.commit();
- events.forEach(eventAdmin::postEvent);
- return result;
- } catch (LoginException | PersistenceException ex) {
- throw new IOException("Failed to delete sitemaps: " + ex.getMessage(), ex);
- }
- }
-
- public Collection<SitemapStorageInfo> getSitemaps(Resource sitemapRoot) {
- return getSitemaps(sitemapRoot, Collections.emptySet());
- }
-
- /**
- * Returns an info object of all sitemaps for the given sitemap root.
- * <p>
- * When the sitemap root is not a top level sitemap root then only the sitemaps corresponding to the given names
- * are returned. If the sitemap is a top level sitemap root and no names are passed, an info for all sitemaps will
- * be returned.
- *
- * @param sitemapRoot
- * @param names
- * @return
- */
- public Collection<SitemapStorageInfo> getSitemaps(Resource sitemapRoot, Collection<String> names) {
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Resource topLevelSitemapRoot = getTopLevelSitemapRoot(sitemapRoot);
- Predicate<SitemapStorageInfo> filter;
-
- if (!isTopLevelSitemapRoot(sitemapRoot) || names.size() > 0) {
- // return only those that match at least on of the names requested
- filter = info -> names.stream()
- .map(name -> SitemapUtil.getSitemapSelector(sitemapRoot, topLevelSitemapRoot, name))
- .anyMatch(selector -> info.getSitemapSelector().equals(selector)
- || info.getSitemapSelector().equals(selector + '-' + info.getFileIndex()));
- } else {
- filter = any -> true;
- }
-
- String storagePath = rootPath + topLevelSitemapRoot.getPath();
- Resource storageResource = resolver.getResource(storagePath);
-
- if (storageResource == null) {
- LOG.debug("Resource at {} does not exist.", storagePath);
- return Collections.emptySet();
- }
-
- return StreamSupport.stream(storageResource.getChildren().spliterator(), false)
- .filter(SitemapStorage::isValidSitemapFile)
- .map(SitemapStorage::newSitemapStorageInfo)
- .filter(filter)
- .collect(Collectors.toList());
- } catch (LoginException ex) {
- LOG.warn("Could not list sitemaps from storage: {}", ex.getMessage());
- return Collections.emptySet();
- }
- }
-
- public boolean copySitemap(Resource sitemapRoot, String sitemapSelector, OutputStream output) throws IOException {
- if (!isTopLevelSitemapRoot(sitemapRoot)) {
- return false;
- }
- String sitemapFilePath = rootPath + sitemapRoot.getPath() + '/' + sitemapSelector + XML_EXTENSION;
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- InputStream data = Optional.ofNullable(resolver.getResource(sitemapFilePath))
- .filter(r -> r.getName().endsWith(XML_EXTENSION))
- .filter(r -> r.isResourceType(RT_SITEMAP_FILE))
- .map(r -> r.getValueMap().get(JcrConstants.JCR_DATA, InputStream.class))
- .orElse(null);
-
- if (data != null) {
- IOUtils.copyLarge(data, output);
- return true;
- } else {
- LOG.debug("Could not copy data from resource: {}", sitemapFilePath);
- return false;
- }
- } catch (LoginException ex) {
- LOG.warn("Could not copy sitemap to output: {}", ex.getMessage());
- return false;
- }
- }
-
- /**
- * Returns true the sitemap root of the given file exists. This first checks if the file is in a top level sitemap's
- * storage path and if so, checks if the selector resolves to at least one root.
- *
- * @param sitemapFile
- * @return
- */
- private boolean doesSitemapRootExist(Resource sitemapFile) {
- String path = sitemapFile.getPath();
- String parentPath = ResourceUtil.getParent(path);
- String sitemapRootPath = parentPath.substring(rootPath.length());
- Resource sitemapRoot = sitemapFile.getResourceResolver().getResource(sitemapRootPath);
-
- if (sitemapRoot == null || !SitemapUtil.isTopLevelSitemapRoot(sitemapRoot)) {
- LOG.debug("Sitemap file's top level sitemap root does not exist: {}", sitemapRootPath);
- return false;
- }
-
- String name = sitemapFile.getName();
- int lastDot = name.lastIndexOf('.');
-
- if (lastDot < 0) {
- LOG.debug("Unexpected name, missing extension: {}", name);
- return false;
- }
-
- Map<Resource, String> candidates = SitemapUtil.resolveSitemapRoots(sitemapRoot, name.substring(0, lastDot));
- // check if for any of the candidate resource roots a generator with the name exists
- return candidates.entrySet().stream()
- .anyMatch(entry -> {
- Resource resource = entry.getKey();
- Set<String> names = generatorManager.getNames(resource);
- Set<String> onDemandNames = generatorManager.getOnDemandNames(resource);
- return names.contains(entry.getValue()) && !onDemandNames.contains(entry.getValue());
- });
- }
-
- /**
- * Returns true when the state expired according to the configured maximum age.
- *
- * @param state
- * @return
- */
- private boolean isExpired(@NotNull Resource state) {
- ValueMap stateProperties = state.getValueMap();
- Calendar lastModified = stateProperties.get(JcrConstants.JCR_LASTMODIFIED, Calendar.class);
- if (lastModified != null) {
- // advance lastModified by maxStateAge to get the point in time the state would expire
- lastModified.add(Calendar.MILLISECOND, maxStateAge);
- // check if the expire time is in the future
- if (lastModified.after(Calendar.getInstance())) {
- return false;
- } else if (LOG.isDebugEnabled()) {
- LOG.debug("State at {} expired at {}", state.getPath(), lastModified.getTime().toGMTString());
- }
- }
- return true;
- }
-
- @NotNull
- private String getSitemapFilePath(@NotNull Resource sitemapRoot, @NotNull String name) {
- Resource topLevelSitemapRoot = getTopLevelSitemapRoot(sitemapRoot);
- return rootPath + topLevelSitemapRoot.getPath() + '/' + getSitemapSelector(sitemapRoot, topLevelSitemapRoot,
- name);
- }
-
- /**
- * Creates the given path and its ancestors using sling:Folder jcr:primaryType for all.
- *
- * @param resolver
- * @param path
- * @return
- * @throws PersistenceException
- */
- private static Resource getOrCreateFolder(@NotNull ResourceResolver resolver, @NotNull String path)
- throws PersistenceException {
- Resource folder = resolver.getResource(path);
- if (folder == null) {
- String parentPath = ResourceUtil.getParent(path);
-
- if (parentPath == null) {
- throw new PersistenceException("Cannot create parent path of " + path);
- }
-
- Resource parent = getOrCreateFolder(resolver, parentPath);
- folder = resolver.create(parent, ResourceUtil.getName(path), Collections.singletonMap(
- JcrConstants.JCR_PRIMARYTYPE, "sling:Folder"
- ));
- }
- return folder;
- }
-
- private static Stream<Resource> traverse(@Nullable Resource resource) {
- if (resource == null) {
- return Stream.empty();
- }
-
- return Stream.concat(
- Stream.of(resource),
- StreamSupport.stream(resource.getChildren().spliterator(), false).flatMap(SitemapStorage::traverse)
- );
- }
-
- /**
- * Returns true when the given resource is a vaild sitemap file. That means, it ends with the sitemap extension,
- * is of the sitemap file resource type and has a name and file index property.
- *
- * @param child
- * @return
- */
- private static boolean isValidSitemapFile(Resource child) {
- ValueMap properties = child.getValueMap();
- return child.getName().endsWith(XML_EXTENSION)
- && child.isResourceType(RT_SITEMAP_FILE)
- && properties.get(PN_SITEMAP_NAME, String.class) != null
- && properties.get(PN_SITEMAP_FILE_INDEX, Integer.class) != null;
- }
-
- /**
- * Returns a new {@link SitemapStorageInfo} instance for a given valid sitemap file {@link Resource}.
- *
- * @param child
- * @return
- */
- private static SitemapStorageInfo newSitemapStorageInfo(Resource child) {
- return new SitemapStorageInfo(
- child.getPath(),
- child.getName().substring(0, child.getName().lastIndexOf('.')),
- child.getValueMap().get(PN_SITEMAP_NAME, String.class),
- child.getValueMap().get(PN_SITEMAP_FILE_INDEX, Integer.class),
- child.getValueMap().get(JcrConstants.JCR_LASTMODIFIED, Calendar.class),
- child.getValueMap().get(PN_SITEMAP_SIZE, 0),
- child.getValueMap().get(PN_SITEMAP_ENTRIES, 0));
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorageInfo.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorageInfo.java
deleted file mode 100644
index d683386..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapStorageInfo.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Calendar;
-
-class SitemapStorageInfo {
-
- private final String sitemapSelector;
- private final String name;
- private final String path;
- private final int fileIndex;
- private final Calendar lastModified;
- private final int size;
- private final int entries;
-
- SitemapStorageInfo(@NotNull String path, @NotNull String sitemapSelector, @NotNull String name, int fileIndex,
- @Nullable Calendar lastModified, int size,
- int entries) {
- this.path = path;
- this.sitemapSelector = sitemapSelector;
- this.name = name;
- this.fileIndex = fileIndex;
- this.lastModified = lastModified;
- this.size = size;
- this.entries = entries;
- }
-
- @NotNull
- public String getPath() {
- return path;
- }
-
- @NotNull
- public String getSitemapSelector() {
- return sitemapSelector;
- }
-
- @NotNull
- public String getName() {
- return name;
- }
-
- public int getFileIndex() {
- return fileIndex;
- }
-
- @Nullable
- public Calendar getLastModified() {
- return lastModified;
- }
-
- public int getSize() {
- return size;
- }
-
- public int getEntries() {
- return entries;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/ExtensionWriter.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/ExtensionWriter.java
deleted file mode 100644
index 942d5ca..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/ExtensionWriter.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import javax.xml.namespace.NamespaceContext;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-
-/**
- * This class implements a {@link XMLStreamWriter} calling a delegate {@link XMLStreamWriter}. It is restricted to
- * writer operations only within a given namespace. Also prefixes are ignored and the declared one for the given
- * namespace from the root scope is used. Any other methods throw {@link UnsupportedOperationException}.
- */
-class ExtensionWriter implements XMLStreamWriter {
-
- private final XMLStreamWriter delegate;
- private final String namespace;
-
- ExtensionWriter(XMLStreamWriter delegate, String namespace) {
- this.delegate = delegate;
- this.namespace = namespace;
- }
-
- private void ensureCurrentNamespace(String givenNamespace) {
- if (!namespace.equals(givenNamespace)) {
- throw new IllegalArgumentException("Writing with another namespace not permitted: " + givenNamespace);
- }
- }
-
- @Override
- public void writeStartElement(String localName) throws XMLStreamException {
- writeStartElement(namespace, localName);
- }
-
- @Override
- public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
- ensureCurrentNamespace(namespaceURI);
- delegate.writeStartElement(namespaceURI, localName);
- }
-
- @Override
- public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
- ensureCurrentNamespace(namespaceURI);
- delegate.writeStartElement(localName, namespaceURI);
- }
-
- @Override
- public void writeEmptyElement(String localName) throws XMLStreamException {
- delegate.writeEmptyElement(namespace, localName);
- }
-
- @Override
- public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
- ensureCurrentNamespace(namespaceURI);
- delegate.writeEmptyElement(namespaceURI, localName);
- }
-
- @Override
- public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
- ensureCurrentNamespace(namespaceURI);
- delegate.writeEmptyElement(localName, namespaceURI);
- }
-
- @Override
- public void writeEndElement() throws XMLStreamException {
- delegate.writeEndElement();
- }
-
- @Override
- public void writeEndDocument() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void close() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void flush() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeAttribute(String localName, String value) throws XMLStreamException {
- delegate.writeAttribute(localName, value);
- }
-
- @Override
- public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
- delegate.writeAttribute(namespaceURI, localName, value);
- }
-
- @Override
- public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException {
- ensureCurrentNamespace(namespaceURI);
- delegate.writeAttribute(namespaceURI, localName, value);
- }
-
- @Override
- public void writeNamespace(String prefix, String namespaceURI) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeDefaultNamespace(String namespaceURI) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeComment(String data) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeProcessingInstruction(String target) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeProcessingInstruction(String target, String data) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeCData(String data) throws XMLStreamException {
- delegate.writeCData(data);
- }
-
- @Override
- public void writeDTD(String dtd) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeEntityRef(String name) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeStartDocument() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeStartDocument(String version) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeStartDocument(String encoding, String version) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void writeCharacters(String text) throws XMLStreamException {
- delegate.writeCharacters(text);
- }
-
- @Override
- public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
- delegate.writeCharacters(text, start, len);
- }
-
- @Override
- public String getPrefix(String uri) throws XMLStreamException {
- return delegate.getPrefix(uri);
- }
-
- @Override
- public void setPrefix(String prefix, String uri) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void setDefaultNamespace(String uri) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public NamespaceContext getNamespaceContext() {
- return delegate.getNamespaceContext();
- }
-
- @Override
- public void setNamespaceContext(NamespaceContext context) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public Object getProperty(String name) throws IllegalArgumentException {
- return delegate.getProperty(name);
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapImpl.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapImpl.java
deleted file mode 100644
index 98603d0..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapImpl.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.builder.Url;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.jetbrains.annotations.NotNull;
-
-import javax.xml.stream.XMLOutputFactory;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.Map;
-
-/**
- * An {@link Sitemap} implementation based on a {@link Writer} that keeps the last {@link Url} object in memory until
- * another location gets added to the {@link SitemapImpl}. Call {@link SitemapImpl#flush()} to write the pending
- * {@link Url} to the underlying {@link Writer}.
- */
-public class SitemapImpl implements Sitemap, Closeable {
-
- static final String SITEMAP_NAMESPACE = "http://www.sitemaps.org/schemas/sitemap/0.9";
-
- protected final Writer out;
-
- private final ExtensionProviderManager extensionProviderManager;
- private final XMLOutputFactory xmlWriterFactory;
- private boolean closed = false;
- private UrlImpl pendingUrl;
-
- public SitemapImpl(Writer writer, ExtensionProviderManager extensionProviderManager) throws IOException {
- this(writer, extensionProviderManager, true);
- }
-
- public SitemapImpl(Writer writer, ExtensionProviderManager extensionProviderManager, boolean writeHeader)
- throws IOException {
- this.extensionProviderManager = extensionProviderManager;
- this.xmlWriterFactory = XMLOutputFactory.newFactory();
- this.out = writer;
-
- if (writeHeader) {
- out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
- out.write("<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"");
-
- for (Map.Entry<String, String> entry : extensionProviderManager.getNamespaces().entrySet()) {
- out.write(' ');
- out.write("xmlns:");
- out.write(entry.getValue());
- out.write("=\"");
- out.write(entry.getKey());
- out.write('"');
- }
-
- out.write('>');
- }
- }
-
- @Override
- public void close() throws IOException {
- try {
- ensureNotClosed();
- writePendingUrl();
- out.write("</urlset>");
- out.flush();
- closed = true;
- } catch (SitemapException ex) {
- unwrapIOException(ex);
- }
- }
-
- public void flush() throws IOException {
- try {
- ensureNotClosed();
- writePendingUrl();
- out.flush();
- } catch (SitemapException ex) {
- unwrapIOException(ex);
- }
- }
-
- @Override
- public @NotNull Url addUrl(@NotNull String location) throws SitemapException {
- ensureNotClosed();
- writePendingUrl();
- pendingUrl = new UrlImpl(location, out, xmlWriterFactory, extensionProviderManager);
- return pendingUrl;
- }
-
- protected boolean writePendingUrl() throws SitemapException {
- if (pendingUrl != null) {
- pendingUrl.write();
- pendingUrl = null;
- return true;
- }
-
- return false;
- }
-
- private void ensureNotClosed() {
- if (closed) {
- throw new IllegalStateException("Sitemap already closed");
- }
- }
-
- private static void unwrapIOException(Exception ex) throws IOException {
- if (ex.getCause() instanceof IOException) {
- throw (IOException) ex.getCause();
- } else {
- throw new IOException(ex);
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapIndexImpl.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapIndexImpl.java
deleted file mode 100644
index 50b2a02..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/SitemapIndexImpl.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.Writer;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-
-public class SitemapIndexImpl implements Closeable {
-
- private final XMLStreamWriter out;
-
- public SitemapIndexImpl(Writer writer) throws IOException {
- try {
- out = XMLOutputFactory.newFactory().createXMLStreamWriter(writer);
-
- out.writeStartDocument("UTF-8", "1.0");
- out.writeStartElement("sitemapindex");
- out.writeDefaultNamespace(SitemapImpl.SITEMAP_NAMESPACE);
- } catch (XMLStreamException ex) {
- throw new IOException("Failed to open sitemap index", ex);
- }
- }
-
- @Override
- public void close() throws IOException {
- try {
- out.writeEndElement();
- out.flush();
- out.close();
- } catch (XMLStreamException ex) {
- throw new IOException("Failed to close sitemap index", ex);
- }
- }
-
- public void addSitemap(@NotNull String location) throws SitemapException {
- addSitemap(location, null);
- }
-
- public void addSitemap(@NotNull String location, @Nullable Instant lastModified) throws SitemapException {
- try {
- out.writeStartElement("sitemap");
- out.writeStartElement("loc");
- out.writeCharacters(location);
- out.writeEndElement();
- if (lastModified != null) {
- out.writeStartElement("lastmod");
- out.writeCharacters(DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(lastModified.atOffset(ZoneOffset.UTC)));
- out.writeEndElement();
- }
- out.writeEndElement();
- } catch (XMLStreamException ex) {
- throw new SitemapException("Failed to add sitemap to index", ex);
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/StringWriter.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/StringWriter.java
deleted file mode 100644
index 753e4a9..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/StringWriter.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.io.Writer;
-
-/**
- * This class implements an in-memory {@link Writer} using a {@link StringBuilder} as buffer. It can be reused by
- * calling {@link StringWriter#reset()}.
- */
-class StringWriter extends Writer {
-
- private final StringBuilder buf;
-
- StringWriter() {
- this.buf = new StringBuilder();
- }
-
- @Override
- public void write(int c) {
- buf.append((char) c);
- }
-
- @Override
- public void write(char @NotNull [] cbuf) {
- buf.append(cbuf);
- }
-
- @Override
- public void write(@NotNull String str) {
- buf.append(str);
- }
-
- @Override
- public void write(@NotNull String str, int off, int len) {
- buf.append(str, off, off + len);
- }
-
- @Override
- public StringWriter append(CharSequence csq) {
- buf.append(csq);
- return this;
- }
-
- @Override
- public StringWriter append(CharSequence csq, int start, int end) {
- buf.append(csq, start, end);
- return this;
- }
-
- @Override
- public StringWriter append(char c) {
- buf.append(c);
- return this;
- }
-
- @Override
- public void write(char @NotNull [] cbuf, int off, int len) {
- buf.append(cbuf, off, len);
- }
-
- @Override
- public void flush() {
-
- }
-
- @Override
- public void close() {
-
- }
-
- public void reset() {
- buf.setLength(0);
- }
-
- public CharSequence asCharSequence() {
- return buf;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/UrlImpl.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/UrlImpl.java
deleted file mode 100644
index c0dcbee..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/UrlImpl.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.builder.Extension;
-import org.apache.sling.sitemap.builder.Url;
-import org.apache.sling.sitemap.builder.extensions.AbstractExtension;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionFactory;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.xml.XMLConstants;
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.time.Instant;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-public class UrlImpl implements Url {
-
- private static final Logger LOG = LoggerFactory.getLogger(UrlImpl.class);
-
- private final String location;
- private final Writer out;
- private final XMLOutputFactory xmlWriterFactory;
- private final ExtensionProviderManager extensionProviderManager;
-
- private boolean written = false;
- private ChangeFrequency changeFrequency;
- private Instant lastModified;
- private Double priority;
- private List<ExtensionMeta> extensions;
-
- UrlImpl(String location, Writer out, XMLOutputFactory xmlWriterFactory, ExtensionProviderManager extensionProviderManager) {
- this.location = location;
- this.out = out;
- this.xmlWriterFactory = xmlWriterFactory;
- this.extensionProviderManager = extensionProviderManager;
- }
-
- @Override
- public @NotNull Url setChangeFrequency(@NotNull ChangeFrequency changeFrequency) {
- ensureNotWritten();
- this.changeFrequency = changeFrequency;
- return this;
- }
-
- @Override
- public @NotNull Url setLastModified(@NotNull Instant pointInTime) {
- ensureNotWritten();
- this.lastModified = pointInTime;
- return this;
- }
-
- @Override
- public @NotNull Url setPriority(double priority) {
- ensureNotWritten();
- this.priority = priority;
- return this;
- }
-
- @Override
- @Nullable
- public <T extends Extension> T addExtension(Class<T> extensionInterface) {
- ensureNotWritten();
- ExtensionFactory extensionFactory = extensionProviderManager.getExtensionFactory(extensionInterface);
-
- if (extensionFactory == null) {
- return null;
- }
-
- AbstractExtension extension = extensionFactory.newExtension();
-
- if (!extensionInterface.isInstance(extension)) {
- LOG.warn("Extension registered by factory for type '{}' is of incompatible type: {}",
- extensionInterface.getName(), extension.getClass().getName());
- return null;
- }
-
- if (extensions == null) {
- extensions = new LinkedList<>();
- }
-
- extensions.add(new ExtensionMeta(extension, extensionFactory.getNamespace(), extensionFactory.getPrefix(),
- extensionFactory.getLocalName(), extensionFactory.isEmptyTag()));
-
- return extensionInterface.cast(extension);
- }
-
- void write() throws SitemapException {
- ensureNotWritten();
- written = true;
- try {
- StringWriter urlChunk = new StringWriter();
- XMLStreamWriter urlWriter = xmlWriterFactory.createXMLStreamWriter(urlChunk);
- urlWriter.setPrefix(XMLConstants.DEFAULT_NS_PREFIX, SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "url", SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "loc", SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeCharacters(location);
- urlWriter.writeEndElement();
-
- if (lastModified != null) {
- urlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "lastmod", SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeCharacters(lastModified.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
- urlWriter.writeEndElement();
- }
- if (changeFrequency != null) {
- urlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "changefreq", SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeCharacters(changeFrequency.name().toLowerCase(Locale.ROOT));
- urlWriter.writeEndElement();
- }
- if (priority != null) {
- urlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "priority", SitemapImpl.SITEMAP_NAMESPACE);
- urlWriter.writeCharacters(String.valueOf(Math.max(Math.min(priority, 1.0), 0.0)));
- urlWriter.writeEndElement();
- }
- urlWriter.flush();
-
- // write the extensions as separate chunks to the same output
- if (extensions != null) {
- // use a single StringWriter and reset it for each extension to save memory
- StringWriter extensionChunk = new StringWriter();
- for (ExtensionMeta extension : extensions) {
- writeExtension(extensionChunk, extension);
- urlChunk.append(extensionChunk.asCharSequence());
- extensionChunk.reset();
- }
- }
-
- urlWriter.writeEndElement();
- urlWriter.flush();
-
- out.append(urlChunk.asCharSequence());
- } catch (XMLStreamException ex) {
- LOG.warn("Failed to serialize url", ex);
- } catch (IOException ex) {
- throw new SitemapException(ex);
- }
- }
-
- private void ensureNotWritten() {
- if (written) {
- throw new IllegalStateException("Url already written");
- }
- }
-
- private void writeExtension(StringWriter out, ExtensionMeta extension) {
- try {
- XMLStreamWriter extensionWriter = xmlWriterFactory.createXMLStreamWriter(out);
- extensionWriter.setPrefix(extension.prefix, extension.namespace);
- if (extension.emptyTag) {
- extensionWriter.writeEmptyElement(extension.prefix, extension.localName, extension.namespace);
- } else {
- extensionWriter.writeStartElement(extension.prefix, extension.localName, extension.namespace);
- }
- extension.extension.writeTo(new ExtensionWriter(extensionWriter, extension.namespace));
- if (!extension.emptyTag) {
- extensionWriter.writeEndElement();
- } else {
- // in order to properly close the empty tag at the end of the xml-fragment
- extensionWriter.writeCharacters("");
- }
- extensionWriter.flush();
- } catch (XMLStreamException ex) {
- if (LOG.isDebugEnabled()) {
- LOG.warn("Failed to serialize extension {}", extension.extension.getClass().getName(), ex);
- } else {
- LOG.warn("Failed to serialize extension {}: {}", extension.extension.getClass().getName(), ex.getMessage());
- }
- out.reset();
- }
- }
-
- private static class ExtensionMeta {
- private final AbstractExtension extension;
- private final String namespace;
- private final String prefix;
- private final String localName;
- private final boolean emptyTag;
-
- public ExtensionMeta(AbstractExtension extension, String namespace, String prefix, String localName,
- boolean emptyTag) {
- this.extension = extension;
- this.namespace = namespace;
- this.prefix = prefix;
- this.localName = localName;
- this.emptyTag = emptyTag;
- }
- }
-
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionProvider.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionProvider.java
deleted file mode 100644
index 2f31ff0..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionProvider.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder.extensions;
-
-import org.apache.sling.sitemap.builder.extensions.AbstractExtension;
-import org.apache.sling.sitemap.builder.extensions.AlternateLanguageExtension;
-import org.apache.sling.sitemap.builder.extensions.ExtensionProvider;
-import org.jetbrains.annotations.NotNull;
-import org.osgi.service.component.annotations.Component;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.util.Locale;
-
-@Component(
- property = {
- ExtensionProvider.PROPERTY_INTERFACE + "=org.apache.sling.sitemap.builder.extensions.AlternateLanguageExtension",
- ExtensionProvider.PROPERTY_PREFIX + "=xhtml",
- ExtensionProvider.PROPERTY_NAMESPACE + "=http://www.w3.org/1999/xhtml",
- ExtensionProvider.PROPERTY_LOCAL_NAME + "=link",
- ExtensionProvider.PROPERTY_EMPTY_TAG + "=true"
- }
-)
-public class AlternateLanguageExtensionProvider implements ExtensionProvider {
-
- @Override
- @NotNull
- public AbstractExtension newInstance() {
- return new ExtensionImpl();
- }
-
- public static class ExtensionImpl extends AbstractExtension implements AlternateLanguageExtension {
-
- private String hreflang;
- private String href;
-
- private static <T> T required(T object, String message) throws XMLStreamException {
- if (object == null) {
- throw new XMLStreamException(message);
- }
- return object;
- }
-
- @Override
- @NotNull
- public AlternateLanguageExtension setLocale(@NotNull Locale locale) {
- hreflang = locale.toLanguageTag();
- return this;
- }
-
- @Override
- @NotNull
- public AlternateLanguageExtension setDefaultLocale() {
- hreflang = "x-default";
- return this;
- }
-
- @Override
- @NotNull
- public AlternateLanguageExtension setHref(@NotNull String location) {
- href = location;
- return this;
- }
-
- @Override
- public void writeTo(@NotNull XMLStreamWriter writer) throws XMLStreamException {
- writer.writeAttribute("rel", "alternate");
- writer.writeAttribute("hreflang", required(hreflang, "hreflang is missing"));
- writer.writeAttribute("href", required(href, "href is missing"));
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionFactory.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionFactory.java
deleted file mode 100644
index 6c36ca2..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionFactory.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder.extensions;
-
-import org.apache.sling.sitemap.builder.extensions.AbstractExtension;
-import org.apache.sling.sitemap.builder.extensions.ExtensionProvider;
-import org.jetbrains.annotations.NotNull;
-
-public class ExtensionFactory {
-
- private final ExtensionProvider provider;
- private final String namespace;
- private final String prefix;
- private final String localName;
- private final boolean emptyTag;
-
- ExtensionFactory(ExtensionProvider provider,
- String namespace, String prefix, String localName, boolean emptyTag) {
- this.provider = provider;
- this.namespace = namespace;
- this.prefix = prefix;
- this.localName = localName;
- this.emptyTag = emptyTag;
- }
-
- @NotNull
- public AbstractExtension newExtension() {
- return provider.newInstance();
- }
-
- public String getNamespace() {
- return namespace;
- }
-
- public String getPrefix() {
- return prefix;
- }
-
- public String getLocalName() {
- return localName;
- }
-
- public boolean isEmptyTag() {
- return emptyTag;
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionProviderManager.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionProviderManager.java
deleted file mode 100644
index 17061ca..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/builder/extensions/ExtensionProviderManager.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder.extensions;
-
-import org.apache.sling.sitemap.builder.Extension;
-import org.apache.sling.sitemap.builder.extensions.ExtensionProvider;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.*;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-@Component(
- service = ExtensionProviderManager.class,
- reference = {
- @Reference(
- service = ExtensionProvider.class,
- name = "providers",
- bind = "bindExtensionProvider",
- unbind = "unbindExtensionProvider",
- cardinality = ReferenceCardinality.OPTIONAL,
- policyOption = ReferencePolicyOption.GREEDY
- )
- })
-public class ExtensionProviderManager {
-
- private static final Logger LOG = LoggerFactory.getLogger(ExtensionProviderManager.class);
-
- private final Map<ServiceReference<?>, Holder> providers = new TreeMap<>(Collections.reverseOrder());
- private Map<String, String> namespaces;
- private BundleContext bundleContext;
-
- @Activate
- protected void activate(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- protected void bindExtensionProvider(ServiceReference<ExtensionProvider> ref) {
- try {
- namespaces = null;
- providers.put(ref, new Holder(ref));
- } catch (ClassCastException ex) {
- LOG.warn("Failed to register ExtensionProvider '{}' as on of the mandatory properties if not of type String.", ref, ex);
- }
- }
-
- protected void unbindExtensionProvider(ServiceReference<ExtensionProvider> ref) {
- Holder holder = providers.remove(ref);
-
- if (holder != null && holder.provider != null) {
- bundleContext.ungetService(ref);
- }
- }
-
- /**
- * Returns an unique mapping from namespace to prefix.
- *
- * @return
- */
- @NotNull
- public Map<String, String> getNamespaces() {
- if (namespaces == null) {
- namespaces = new HashMap<>();
- for (Holder holder : providers.values()) {
- namespaces.putIfAbsent(holder.namespace, holder.prefix);
- }
- }
-
- return namespaces;
- }
-
- @Nullable
- public ExtensionFactory getExtensionFactory(Class<? extends Extension> extensionInterface) {
- for (Holder holder : providers.values()) {
- if (holder.extensionInterface.equals(extensionInterface.getName())) {
- // get the right prefix for the namespace. this may be different then the holder's prefix as there may
- // be many providers for one namespace defining different prefixes. the one with the highest service
- // ranking wins.
- return new ExtensionFactory(holder.getProvider(), holder.namespace,
- getNamespaces().get(holder.namespace), holder.localName, holder.emptyTag);
- }
- }
- return null;
- }
-
- private class Holder {
- private final ServiceReference<ExtensionProvider> ref;
- private final String extensionInterface;
- private final String prefix;
- private final String namespace;
- private final String localName;
- private final boolean emptyTag;
-
- private ExtensionProvider provider;
-
- private Holder(ServiceReference<ExtensionProvider> ref) {
- this.ref = ref;
- prefix = Objects
- .requireNonNull((String) ref.getProperty(ExtensionProvider.PROPERTY_PREFIX), "prefix missing");
- namespace = Objects
- .requireNonNull((String) ref.getProperty(ExtensionProvider.PROPERTY_NAMESPACE), "namespace missing");
- localName = Objects
- .requireNonNull((String) ref.getProperty(ExtensionProvider.PROPERTY_LOCAL_NAME), "local name missing");
- extensionInterface = Objects
- .requireNonNull((String) ref.getProperty(ExtensionProvider.PROPERTY_INTERFACE), "prefix missing");
-
- Object emptyTagProp = ref.getProperty(ExtensionProvider.PROPERTY_EMPTY_TAG);
-
- if (emptyTagProp instanceof Boolean) {
- emptyTag = (Boolean) emptyTagProp;
- } else if (emptyTagProp instanceof String) {
- emptyTag = Boolean.parseBoolean((String) emptyTagProp);
- } else {
- emptyTag = false;
- LOG.debug("Unknown type for emptyTag: " + emptyTagProp);
- }
- }
-
- private ExtensionProvider getProvider() {
- if (provider == null) {
- provider = bundleContext.getService(ref);
- }
- return provider;
- }
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java b/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java
deleted file mode 100644
index ef7f106..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPlugin.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.console;
-
-import org.apache.felix.inventory.Format;
-import org.apache.felix.inventory.InventoryPrinter;
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.commons.scheduler.Scheduler;
-import org.apache.sling.sitemap.SitemapInfo;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.common.SitemapUtil;
-import org.apache.sling.sitemap.impl.SitemapServiceConfiguration;
-import org.jetbrains.annotations.Nullable;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.PrintWriter;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-
-@Component(
- service = InventoryPrinter.class,
- property = {
- InventoryPrinter.NAME + "=slingsitemap",
- InventoryPrinter.TITLE + "=Sling Sitemap",
- InventoryPrinter.FORMAT + "=JSON",
- InventoryPrinter.FORMAT + "=TEXT",
- InventoryPrinter.WEBCONSOLE + "=true"
-
- }
-)
-public class SitemapInventoryPlugin implements InventoryPrinter {
-
- private static final Map<String, Object> AUTH = Collections.singletonMap(
- ResourceResolverFactory.SUBSERVICE, "sitemap-reader");
- private static final Logger LOG = LoggerFactory.getLogger(SitemapInventoryPlugin.class);
-
- @Reference
- private SitemapService sitemapService;
- @Reference
- private ResourceResolverFactory resourceResolverFactory;
- @Reference
- private SitemapServiceConfiguration configuration;
-
- private BundleContext bundleContext;
-
- @Activate
- protected void activate(BundleContext bundleContext) {
- this.bundleContext = bundleContext;
- }
-
- @Override
- public void print(PrintWriter printWriter, Format format, boolean isZip) {
- if (Format.JSON.equals(format)) {
- printJson(printWriter);
- } else if (Format.TEXT.equals(format)) {
- printText(printWriter);
- }
- }
-
- private void printJson(PrintWriter pw) {
- pw.print('{');
- pw.print("\"schedulers\":[");
- boolean hasScheduler = false;
- for (ServiceReference<?> ref : bundleContext.getBundle().getRegisteredServices()) {
- Object schedulerExp = ref.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION);
- Object schedulerName = ref.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME);
- if (schedulerExp instanceof String && schedulerName instanceof String) {
- if (hasScheduler) {
- pw.print(',');
- }
- hasScheduler = true;
- pw.print("{\"name\":\"");
- pw.print(escapeDoubleQuotes((String) schedulerName));
- pw.print("\",\"expression\":\"");
- pw.print(escapeDoubleQuotes((String) schedulerExp));
- pw.print("\"}");
- }
- }
- pw.print("],");
-
- pw.print("\"roots\":{");
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Iterator<Resource> roots = SitemapUtil.findSitemapRoots(resolver, "/");
- while (roots.hasNext()) {
- Resource root = roots.next();
- pw.print('"');
- pw.print(escapeDoubleQuotes(root.getPath()));
- pw.print("\":[");
- Iterator<SitemapInfo> infoIt = sitemapService.getSitemapInfo(root).iterator();
- while (infoIt.hasNext()) {
- SitemapInfo info = infoIt.next();
- pw.print('{');
- pw.print("\"name\":\"");
- pw.print(escapeDoubleQuotes(info.getName()));
- pw.print('"');
- pw.print(",\"url\":\"");
- pw.print(escapeDoubleQuotes(info.getUrl()));
- pw.print("\",\"status\":\"");
- pw.print(info.getStatus());
- pw.print('"');
- if (info.getStoragePath() != null) {
- pw.print(",\"path\":\"");
- pw.print(escapeDoubleQuotes(info.getStoragePath()));
- pw.print("\",\"size\":");
- pw.print(info.getSize());
- pw.print(",\"urls\":");
- pw.print(info.getEntries());
- pw.print(",\"inLimits\":");
- pw.print(isWithinLimits(info));
- }
- pw.print('}');
- if (infoIt.hasNext()) {
- pw.print(',');
- }
- }
- pw.print(']');
- if (roots.hasNext()) {
- pw.print(',');
- }
- }
- } catch (LoginException ex) {
- pw.println("Failed to list sitemaps: " + ex.getMessage());
- LOG.warn("Failed to get inventory of sitemaps: {}", ex.getMessage(), ex);
- }
- pw.print('}');
- pw.print('}');
- }
-
- private void printText(PrintWriter pw) {
- pw.println("# Apache Sling Sitemap Schedulers");
- pw.println("# -------------------------------");
- pw.println("schedulers:");
-
- for (ServiceReference<?> ref : bundleContext.getBundle().getRegisteredServices()) {
- Object schedulerExp = ref.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION);
- Object schedulerName = ref.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME);
- if (schedulerExp != null && schedulerName != null) {
- pw.print(" - Name: ");
- pw.print(schedulerName);
- pw.println();
- pw.print(" Expression: ");
- pw.print(schedulerExp);
- pw.println();
- }
- }
-
- pw.println();
- pw.println();
- pw.println("# Apache Sling Sitemap Roots");
- pw.println("# --------------------------");
- pw.println("roots:");
-
- try (ResourceResolver resolver = resourceResolverFactory.getServiceResourceResolver(AUTH)) {
- Iterator<Resource> roots = SitemapUtil.findSitemapRoots(resolver, "/");
- while (roots.hasNext()) {
- Resource root = roots.next();
- pw.print(" ");
- pw.print(root.getPath());
- pw.print(':');
- pw.println();
- for (SitemapInfo info : sitemapService.getSitemapInfo(root)) {
- pw.print(" - Name: ");
- pw.print(info.getName());
- pw.println();
- pw.print(" Url: ");
- pw.print(info.getUrl());
- pw.println();
- pw.print(" Status: ");
- pw.print(info.getStatus());
- pw.println();
- if (info.getStoragePath() != null) {
- pw.print(" Path: ");
- pw.print(info.getStoragePath());
- pw.println();
- pw.print(" Size: ");
- pw.print(info.getSize());
- pw.println();
- pw.print(" Urls: ");
- pw.print(info.getEntries());
- pw.println();
- pw.print(" Within Limits: ");
- pw.print(isWithinLimits(info) ? "yes" : "no");
- pw.println();
- }
- }
- }
- } catch (LoginException ex) {
- pw.println("Failed to list sitemaps: " + ex.getMessage());
- LOG.warn("Failed to get inventory of sitemaps: {}", ex.getMessage(), ex);
- }
- }
-
- private boolean isWithinLimits(SitemapInfo info) {
- return info.getSize() <= configuration.getMaxSize() && info.getEntries() <= configuration.getMaxEntries();
- }
-
- private static String escapeDoubleQuotes(@Nullable String text) {
- return text == null ? "" : text.replace("\"", "\\\"");
- }
-}
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/package-info.java b/sitemap/src/main/java/org/apache/sling/sitemap/package-info.java
deleted file mode 100644
index 8e3bd6d..0000000
--- a/sitemap/src/main/java/org/apache/sling/sitemap/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-@Version("1.0.0")
-package org.apache.sling.sitemap;
-
-import org.osgi.annotation.versioning.Version;
\ No newline at end of file
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/TestGenerator.java b/sitemap/src/test/java/org/apache/sling/sitemap/TestGenerator.java
deleted file mode 100644
index 1fb92f3..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/TestGenerator.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.*;
-
-import static org.junit.jupiter.api.Assertions.fail;
-
-public abstract class TestGenerator implements SitemapGenerator {
-
- private Map<String, List<String>> names = new HashMap<>();
- private Map<String, List<String>> onDemandNames = new HashMap<>();
-
- public TestGenerator() {
- this.names.put(null, Collections.emptyList());
- this.onDemandNames.put(null, Collections.emptyList());
- }
-
- public void setNames(String... names) {
- this.names.put(null, Arrays.asList(names));
- }
-
- public void setNames(Resource resource, String... names) {
- this.names.put(resource.getPath(), Arrays.asList(names));
- }
-
- public void setOnDemandNames(String... names) {
- this.onDemandNames.put(null, Arrays.asList(names));
- }
-
- public void setOnDemandNames(Resource resource, String... names) {
- this.onDemandNames.put(resource.getPath(), Arrays.asList(names));
- }
-
- @Override
- @NotNull
- public Set<String> getNames(@NotNull Resource sitemapRoot) {
- return new HashSet<>(names.containsKey(sitemapRoot.getPath())
- ? names.get(sitemapRoot.getPath())
- : names.get(null));
- }
-
- @Override
- public @NotNull Set<String> getOnDemandNames(@NotNull Resource sitemapRoot) {
- return new HashSet<>(onDemandNames.containsKey(sitemapRoot.getPath())
- ? onDemandNames.get(sitemapRoot.getPath())
- : onDemandNames.get(null));
- }
-
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap, @NotNull GenerationContext context) throws SitemapException {
- fail();
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/TestResourceTreeSitemapGenerator.java b/sitemap/src/test/java/org/apache/sling/sitemap/TestResourceTreeSitemapGenerator.java
deleted file mode 100644
index c683244..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/TestResourceTreeSitemapGenerator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.generator.ResourceTreeSitemapGenerator;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collections;
-import java.util.Set;
-
-public class TestResourceTreeSitemapGenerator extends ResourceTreeSitemapGenerator {
-
- private boolean serveOnDemand;
-
- public void setServeOnDemand(boolean b) {
- serveOnDemand = b;
- }
-
- @Override
- public @NotNull Set<String> getNames(@NotNull Resource sitemapRoot) {
- return Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME);
- }
-
- @Override
- public @NotNull Set<String> getOnDemandNames(@NotNull Resource sitemapRoot) {
- return serveOnDemand ? getNames(sitemapRoot) : super.getOnDemandNames(sitemapRoot);
- }
-
- @Override
- protected void addResource(String name, Sitemap sitemap, Resource resource) throws SitemapException {
- sitemap.addUrl(resource.getPath());
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/common/SitemapUtilTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/common/SitemapUtilTest.java
deleted file mode 100644
index 702cd13..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/common/SitemapUtilTest.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.common;
-
-import com.google.common.collect.ImmutableMap;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.hamcrest.CustomMatcher;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.hamcrest.collection.IsIterableContainingInOrder;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.aMapWithSize;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@ExtendWith({SlingContextExtension.class})
-public class SitemapUtilTest {
-
- public final SlingContext context = new SlingContext();
-
- @Test
- public void testSingleRootSitemapName() {
- // given
- Resource root = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- String unnamedSitemap = SitemapUtil.getSitemapSelector(root, root, SitemapService.DEFAULT_SITEMAP_NAME);
- String namedSitemap = SitemapUtil.getSitemapSelector(root, root, "foo");
-
- // then
- assertEquals("sitemap", unnamedSitemap);
- assertEquals("foo-sitemap", namedSitemap);
- }
-
- @Test
- public void testMultiRootSitemapName() {
- // given
- context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- Resource secondLevelRoot = context.create().resource("/content/site/de/faqs", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- Resource thirdLevelRoot = context.create().resource("/content/site/de/faqs/many", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- Resource topLevelRoot = SitemapUtil.getTopLevelSitemapRoot(thirdLevelRoot);
- String unnamedSecondLevelSitemap = SitemapUtil.getSitemapSelector(secondLevelRoot, topLevelRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- String namedSecondLevelSitemap = SitemapUtil.getSitemapSelector(secondLevelRoot, topLevelRoot, "foo");
- String unnamedThirdLevelSitemap = SitemapUtil.getSitemapSelector(thirdLevelRoot, topLevelRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- String namedThirdLevelSitemap = SitemapUtil.getSitemapSelector(thirdLevelRoot, topLevelRoot, "bar");
-
- // then
- assertEquals("faqs-sitemap", unnamedSecondLevelSitemap);
- assertEquals("faqs-foo-sitemap", namedSecondLevelSitemap);
- assertEquals("faqs-many-sitemap", unnamedThirdLevelSitemap);
- assertEquals("faqs-many-bar-sitemap", namedThirdLevelSitemap);
- }
-
- @Test
- public void testSitemapResolutionFromFileName() {
- // given
- Resource root = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- Resource news = context.create().resource("/content/site/de/news", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- context.create().resource("/content/site/de/products");
- Resource productPage = context.create().resource("/content/site/de/products/product-page", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- Resource product = context.create().resource("/content/site/de/products/product", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- List<Function<String, String>> selectorMutations = Arrays.asList(
- Function.identity(),
- // mutate the selctor and append a file index
- selector -> selector + "-" + 2
- );
-
- // when
- for (Function<String, String> mutator : selectorMutations) {
- Map<Resource, String> invalid = SitemapUtil.resolveSitemapRoots(root, mutator.apply("foobar"));
- Map<Resource, String> defaultSitemap = SitemapUtil.resolveSitemapRoots(root, mutator.apply("sitemap"));
- Map<Resource, String> newsCandidates = SitemapUtil.resolveSitemapRoots(root, mutator.apply("news-sitemap"));
- Map<Resource, String> productPageCandidates = SitemapUtil.resolveSitemapRoots(root, mutator.apply("products-product-page-tops-sitemap"));
- Map<Resource, String> productCandidates = SitemapUtil.resolveSitemapRoots(root, mutator.apply("products-sitemap"));
-
- // then
- assertThat(invalid, aMapWithSize(0));
- assertThat(defaultSitemap, aMapWithSize(1));
- assertThat(defaultSitemap.entrySet(), hasInOrder(anEntry(root, SitemapService.DEFAULT_SITEMAP_NAME)));
- assertThat(newsCandidates, aMapWithSize(2));
- assertThat(newsCandidates.entrySet(), hasInOrder(
- anEntry(root, "news"),
- anEntry(news, SitemapService.DEFAULT_SITEMAP_NAME))
- );
- assertThat(productPageCandidates, aMapWithSize(3));
- assertThat(productPageCandidates.entrySet(), hasInOrder(
- anEntry(root, "products-product-page-tops"),
- anEntry(product, "page-tops"),
- anEntry(productPage, "tops")
- ));
- assertThat(productCandidates, aMapWithSize(1));
- assertThat(productCandidates.entrySet(), hasInOrder(anEntry(root, "products")));
- }
- }
-
-
- private static <V> Matcher<Iterable<? extends Map.Entry<Resource, V>>> hasInOrder(Matcher<Map.Entry<Resource, V>>... entries) {
- return IsIterableContainingInOrder.contains(entries);
- }
-
- private static <V> Matcher<Map.Entry<Resource, V>> anEntry(Resource key, V value) {
- Matcher<Resource> keyMatcher = equalTo(key);
- Matcher<V> valueMatcher = Matchers.equalTo(value);
- return new CustomMatcher<Map.Entry<Resource, V>>("key = " + key.getPath() + ", value = " + value) {
- @Override
- public boolean matches(Object o) {
- return o instanceof Map.Entry
- && keyMatcher.matches(((Map.Entry<?, ?>) o).getKey())
- && valueMatcher.matches(((Map.Entry<?, ?>) o).getValue());
- }
- };
- }
-
- private static Matcher<Resource> equalTo(Resource resource) {
- return new CustomMatcher<Resource>("resource with path " + resource.getPath()) {
- @Override
- public boolean matches(Object o) {
- return o instanceof Resource && ((Resource) o).getPath().equals(resource.getPath());
- }
- };
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGeneratorTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGeneratorTest.java
deleted file mode 100644
index 3f55698..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/generator/ResourceTreeSitemapGeneratorTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.generator;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.TestResourceTreeSitemapGenerator;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.apache.sling.sitemap.impl.builder.SitemapImpl;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.IOException;
-import java.io.StringWriter;
-
-import static org.apache.sling.sitemap.impl.builder.SitemapImplTest.XML_HEADER;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.when;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class ResourceTreeSitemapGeneratorTest {
-
- public final SlingContext context = new SlingContext();
-
- private final SitemapGenerator subject = new TestResourceTreeSitemapGenerator();
- private final ExtensionProviderManager extensionProviderManager = new ExtensionProviderManager();
-
- @Mock
- private SitemapGenerator.GenerationContext generationContext;
- private Resource sitemapRoot;
-
- @BeforeEach
- public void setup() {
- sitemapRoot = context.load()
- .json("/ResourceTreeSitemapGeneratorTest/sitetree.json", "/content/site/de");
-
- context.registerInjectActivateService(extensionProviderManager);
- }
-
- @Test
- public void testSitemapContainsAllResourcesButJcrContent() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
-
- // when
- subject.generate(sitemapRoot, SitemapService.DEFAULT_SITEMAP_NAME, sitemap, generationContext);
- sitemap.close();
-
- // then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/de</loc></url>"
- + "<url><loc>/content/site/de/child1</loc></url>"
- + "<url><loc>/content/site/de/child1/grandchild11</loc></url>"
- + "<url><loc>/content/site/de/child2</loc></url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testSitemapDoesNotContainsResourcesWithoutJcrContent() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
- context.create().resource("/content/site/de/child3");
-
- // when
- subject.generate(sitemapRoot, SitemapService.DEFAULT_SITEMAP_NAME, sitemap, generationContext);
- sitemap.close();
-
- // then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/de</loc></url>"
- + "<url><loc>/content/site/de/child1</loc></url>"
- + "<url><loc>/content/site/de/child1/grandchild11</loc></url>"
- + "<url><loc>/content/site/de/child2</loc></url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testSkipTo() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
- when(generationContext.getProperty("lastPath", String.class)).thenReturn("/content/site/de/child2/grandchild21");
- context.create().resource("/content/site/de/child2/grandchild21");
- context.create().resource("/content/site/de/child2/grandchild21/jcr:content");
- context.create().resource("/content/site/de/child2/grandchild22");
- context.create().resource("/content/site/de/child2/grandchild22/jcr:content");
- context.create().resource("/content/site/de/child3");
- context.create().resource("/content/site/de/child3/jcr:content");
- context.create().resource("/content/site/de/child3/grandchild31");
- context.create().resource("/content/site/de/child3/grandchild31/jcr:content");
-
- // when
- subject.generate(sitemapRoot, SitemapService.DEFAULT_SITEMAP_NAME, sitemap, generationContext);
- sitemap.close();
-
- // then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/de/child2/grandchild21</loc></url>"
- + "<url><loc>/content/site/de/child2/grandchild22</loc></url>"
- + "<url><loc>/content/site/de/child3</loc></url>"
- + "<url><loc>/content/site/de/child3/grandchild31</loc></url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutorTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutorTest.java
deleted file mode 100644
index 4e7291f..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorExecutorTest.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.event.jobs.Job;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.event.jobs.consumer.JobExecutionContext;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.sitemap.impl.builder.SitemapImplTest;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.apache.sling.testing.mock.osgi.MockOsgi;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-
-import static org.apache.sling.sitemap.impl.SitemapStorageTest.assertResourceDataEquals;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.Mockito.*;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapGeneratorExecutorTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
-
- private final SitemapGeneratorExecutor subject = new SitemapGeneratorExecutor();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final ExtensionProviderManager extensionProviderManager = new ExtensionProviderManager();
- private final SitemapStorage storage = spy(new SitemapStorage());
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
-
- @Mock
- private JobManager jobManager;
- @Mock
- private ServiceUserMapped serviceUser;
- @Mock
- private Job job;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private JobExecutionContext executionContext;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private JobExecutionContext.ResultBuilder resultBuilder;
- @Mock
- private SitemapGenerator generator;
-
- private Resource rootResource;
- private Resource storageRoot;
-
- @BeforeEach
- public void setup() {
- rootResource = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- storageRoot = context.create().resource("/var/sitemaps");
-
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-reader");
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
- context.registerService(SitemapGenerator.class, generator);
- context.registerService(JobManager.class, jobManager);
- context.registerInjectActivateService(sitemapServiceConfiguration);
- context.registerInjectActivateService(generatorManager);
- context.registerInjectActivateService(storage);
- context.registerInjectActivateService(extensionProviderManager);
-
- when(job.getProperty(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_NAME, SitemapService.DEFAULT_SITEMAP_NAME))
- .thenReturn(SitemapService.DEFAULT_SITEMAP_NAME);
- when(job.getProperty(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_ROOT, String.class))
- .thenReturn(rootResource.getPath());
- }
-
- @Test
- public void testNoStateWrittenOnLargerChunkSize() throws IOException {
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(Integer.MAX_VALUE,
- "http://example.com/page1.html",
- "http://example.com/page2.html",
- "http://example.com/page3.html",
- "http://example.com/page4.html",
- "http://example.com/page5.html"
- ));
- context.registerInjectActivateService(subject, "chunkSize", 10);
-
- // when
- subject.process(job, executionContext);
-
- // then
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "<url><loc>http://example.com/page4.html</loc></url>"
- + "<url><loc>http://example.com/page5.html</loc></url>"
- + "</urlset>",
- 5,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- verify(storage, never()).writeState(any(), any(), any());
- }
-
- @Test
- public void testStateRemovedOnJobStopped() {
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(1,
- "http://example.com/page1.html",
- "http://example.com/page2.html"
- ));
- context.registerInjectActivateService(subject, "chunkSize", 1);
-
- // when
- try {
- subject.process(job, executionContext);
- fail();
- } catch (RuntimeException ex) {
- // ignore
- }
-
- // then
- assertNotNull(storageRoot.getChild("content/site/de/sitemap.part"));
-
- // and when
- when(executionContext.isStopped()).thenReturn(true);
- subject.process(job, executionContext);
-
- // then
- assertNull(storageRoot.getChild("content/site/de/sitemap.part"));
- assertNull(storageRoot.getChild("content/site/de/sitemap.xml"));
- }
-
- @Test
- public void testGeneratorExceptionRethrown() {
- // given
- ThrowingGenerator generator = new ThrowingGenerator();
- context.registerService(SitemapGenerator.class, generator);
- context.registerInjectActivateService(subject, "chunkSize", 100);
- when(executionContext.result()).thenReturn(resultBuilder);
-
- // when
- generator.ex = new SitemapException("sitemapexception");
- subject.process(job, executionContext);
-
- // then
- verify(resultBuilder, times(1)).message("sitemapexception");
-
- // and when
- generator.ex = new SitemapException(new IOException("ioexception"));
- subject.process(job, executionContext);
-
- // then
- verify(resultBuilder, times(1)).message("ioexception");
- }
-
- @Test
- public void testJobResumesAfterBeingAborted() throws IOException {
- // given
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(4,
- "http://example.com/page1.html",
- "http://example.com/page2.html",
- "http://example.com/page3.html",
- "http://example.com/page4.html",
- "http://example.com/page5.html"
- ));
- context.registerInjectActivateService(subject, "chunkSize", 3);
-
- // when
- try {
- subject.process(job, executionContext);
- fail();
- } catch (RuntimeException ex) {
- // ignore exception from FailOnceGenerator
- }
-
- // then
- assertResourceDataEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "<url><loc>http://example.com/page3.html</loc></url>",
- storageRoot.getChild("content/site/de/sitemap.part")
- );
-
- // and when (resume)
- subject.process(job, executionContext);
-
- // then
- assertNull(storageRoot.getChild("content/site/de/sitemap.part"));
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "<url><loc>http://example.com/page4.html</loc></url>"
- + "<url><loc>http://example.com/page5.html</loc></url>"
- + "</urlset>",
- 5,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- }
-
- @Test
- public void testJobResumesAfterBeingAbortedMultiFile() throws IOException {
- // given
- MockOsgi.activate(sitemapServiceConfiguration, context.bundleContext(), "maxEntries", 3);
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(5,
- "http://example.com/page1.html",
- "http://example.com/page2.html",
- "http://example.com/page3.html",
- "http://example.com/page4.html",
- "http://example.com/page5.html",
- "http://example.com/page6.html"
- ));
- context.registerInjectActivateService(subject, "chunkSize", 2);
-
- // when
- try {
- subject.process(job, executionContext);
- fail();
- } catch (RuntimeException ex) {
- // ignore exception from FailOnceGenerator
- }
-
- // then
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "</urlset>",
- 3,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- assertResourceDataEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page4.html</loc></url>"
- + "<url><loc>http://example.com/page5.html</loc></url>",
- storageRoot.getChild("content/site/de/sitemap.part")
- );
-
- // and when (resume)
- subject.process(job, executionContext);
-
- // then
- assertNull(storageRoot.getChild("content/site/de/sitemap.part"));
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "</urlset>",
- 3,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page4.html</loc></url>"
- + "<url><loc>http://example.com/page5.html</loc></url>"
- + "<url><loc>http://example.com/page6.html</loc></url>"
- + "</urlset>",
- 3,
- storageRoot.getChild("content/site/de/sitemap-2.xml")
- );
- }
-
- @Test
- public void testMultiFileConsistentWithSizeOverflow() throws IOException {
- // 200 = 38 (header) + 60 (urlset) + 2 * 51 (url)
- MockOsgi.activate(sitemapServiceConfiguration, context.bundleContext(), "maxSize", 200);
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(Integer.MAX_VALUE,
- "http://example.com/page1.html",
- "http://example.com/page2.html",
- "http://example.com/page3.html"
- ));
- context.registerInjectActivateService(subject, "chunkSize", 10);
-
- // when
- subject.process(job, executionContext);
-
- // then
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap-2.xml")
- );
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap-3.xml")
- );
- }
-
- @Test
- public void testObsoleteFilesPurgedWhenMultiFileUpdated() throws IOException {
- // given
- FailOnceGenerator generator = new FailOnceGenerator(Integer.MAX_VALUE,
- "http://example.com/page1.html",
- "http://example.com/page2.html",
- "http://example.com/page3.html"
- );
- MockOsgi.activate(sitemapServiceConfiguration, context.bundleContext(), "maxEntries", 1);
- context.registerService(SitemapGenerator.class, generator);
- context.registerInjectActivateService(subject, "chunkSize", Integer.MAX_VALUE);
-
- // when
- subject.process(job, executionContext);
-
- // then
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap-2.xml")
- );
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page3.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap-3.xml")
- );
-
- // and when
- generator.setLocations("http://example.com/pageX.html");
- subject.process(job, executionContext);
-
- // then
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/pageX.html</loc></url>"
- + "</urlset>",
- 1,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- assertNull(storageRoot.getChild("content/site/de/sitemap-2.xml"));
- assertNull(storageRoot.getChild("content/site/de/sitemap-3.xml"));
- }
-
- @Test
- public void testGenerationContextDoesNotLeakRawJcrData() throws IOException {
- // given
- String injectedText = "foobar";
- context.registerService(SitemapGenerator.class, new FailOnceGenerator(1,
- "http://example.com/page1.html",
- "http://example.com/page2.html"
- ) {
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap, @NotNull GenerationContext context) throws SitemapException {
- try {
- super.generate(sitemapRoot, name, sitemap, context);
- } finally {
- // try to get jcr:data
- assertNull(context.getProperty("jcr:data", InputStream.class));
- Object mark = new Object();
- assertEquals(mark, context.getProperty("jcr:data", mark));
- // try to write jcr:data
- context.setProperty("jcr:data", new ByteArrayInputStream(injectedText.getBytes(StandardCharsets.UTF_8)));
- }
- }
- });
- context.registerInjectActivateService(subject, "chunkSize", 1);
-
- // when
- try {
- subject.process(job, executionContext);
- } catch (RuntimeException ex) {
- // ignore exception from FailOnceGenerator
- }
- subject.process(job, executionContext);
-
- assertResourceDataAndEntriesEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com/page1.html</loc></url>"
- + "<url><loc>http://example.com/page2.html</loc></url>"
- + "</urlset>",
- 2,
- storageRoot.getChild("content/site/de/sitemap.xml")
- );
- }
-
- static void assertResourceDataAndEntriesEquals(String expectedValue, int entries, @Nullable Resource resource) throws IOException {
- assertNotNull(resource);
- assertEquals(entries, resource.getValueMap().get(SitemapStorage.PN_SITEMAP_ENTRIES, -1));
- assertResourceDataEquals(expectedValue, resource);
- }
-
- private static class ThrowingGenerator implements SitemapGenerator {
-
- private SitemapException ex;
-
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap, @NotNull GenerationContext context) throws SitemapException {
- throw ex;
- }
- }
-
- private static class FailOnceGenerator implements SitemapGenerator {
-
- private final int failAfterIdx;
- private boolean failed = false;
-
- private String[] locations;
-
- /**
- * Creates a new test generator adding the given to the sitemap. It fails once after the given index throwing
- * an {@link IOException}.
- *
- * @param failAfterIdx
- * @param locations
- */
- public FailOnceGenerator(int failAfterIdx, String... locations) {
- this.locations = locations;
- this.failAfterIdx = failAfterIdx;
- }
-
- public void setLocations(String... locations) {
- this.locations = locations;
- }
-
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap,
- @NotNull GenerationContext context) throws SitemapException {
- int i = context.getProperty("i", 0);
- for (; i < locations.length; i++) {
- context.setProperty("i", i);
- // addUrl will flush with the MultiFileSitemap
- sitemap.addUrl(locations[i]);
-
- if (!failed && failAfterIdx == i) {
- failed = true;
- throw new SitemapGeneratorExecutor.JobAbandonedException();
- }
- }
- }
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImplTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImplTest.java
deleted file mode 100644
index aa81bdc..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapGeneratorManagerImplTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.testing.mock.osgi.MockOsgi;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import java.util.*;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapGeneratorManagerImplTest {
-
- public final SlingContext context = new SlingContext();
-
- private final SitemapGeneratorManagerImpl subject = new SitemapGeneratorManagerImpl();
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
-
- @Mock
- private SitemapGenerator generator1;
- @Mock
- private SitemapGenerator generator2;
- @Mock
- private SitemapGenerator generator3;
-
- @BeforeEach
- public void setup() {
- context.registerService(SitemapGenerator.class, generator1, "service.ranking", 1);
- context.registerService(SitemapGenerator.class, generator2, "service.ranking", 2);
- context.registerService(SitemapGenerator.class, generator3, "service.ranking", 3);
-
- context.registerInjectActivateService(new SitemapServiceConfiguration());
- context.registerInjectActivateService(subject);
- }
-
- @Test
- public void testAllGeneratorsReturnedWhenAllGenerateDifferentNames() {
- // given
- when(generator1.getNames(any())).thenReturn(Collections.singleton("sitemap1"));
- when(generator2.getNames(any())).thenReturn(Collections.singleton("sitemap2"));
- when(generator3.getNames(any())).thenReturn(Collections.singleton("sitemap3"));
-
- // when
- Map<String, SitemapGenerator> generators = subject.getGenerators(context.currentResource("/"));
- SitemapGenerator sitemap1Generator = subject.getGenerator(context.currentResource(), "sitemap1");
- SitemapGenerator sitemap2Generator = subject.getGenerator(context.currentResource(), "sitemap2");
- SitemapGenerator sitemap3Generator = subject.getGenerator(context.currentResource(), "sitemap3");
-
-
- // then
- assertThat(generators, aMapWithSize(3));
- assertThat(generators, hasEntry("sitemap1", generator1));
- assertThat(generators, hasEntry("sitemap2", generator2));
- assertThat(generators, hasEntry("sitemap3", generator3));
- assertEquals(generator1, sitemap1Generator);
- assertEquals(generator2, sitemap2Generator);
- assertEquals(generator3, sitemap3Generator);
- }
-
- @Test
- public void testAllGeneratorsOnDemand() {
- // given
- when(generator1.getNames(any())).thenReturn(Collections.singleton("sitemap1"));
- when(generator2.getNames(any())).thenReturn(Collections.singleton("sitemap2"));
- when(generator3.getNames(any())).thenReturn(Collections.singleton("sitemap3"));
- when(generator3.getOnDemandNames(any())).thenReturn(Collections.singleton("sitemap3"));
-
- // when
- Set<String> names = subject.getNames(context.currentResource("/"));
- Set<String> onDemandNames = subject.getOnDemandNames(context.currentResource("/"));
-
- // then
- assertThat(names, hasItems("sitemap1", "sitemap2", "sitemap3"));
- assertThat(onDemandNames, hasItems("sitemap3"));
-
- // and when
- MockOsgi.activate(subject, context.bundleContext(), "allOnDemand", Boolean.TRUE);
- names = subject.getNames(context.currentResource("/"));
- onDemandNames = subject.getOnDemandNames(context.currentResource("/"));
-
- // then
- assertThat(names, hasItems("sitemap1", "sitemap2", "sitemap3"));
- assertThat(onDemandNames, hasItems("sitemap1", "sitemap2", "sitemap3"));
- }
-
- @Test
- public void testThatHigherRankedGeneratorsTakePrecedenceOnConflictingNames() {
- // given
- when(generator1.getNames(any())).thenReturn(Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME));
- when(generator2.getNames(any())).thenReturn(Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME));
- when(generator3.getNames(any())).thenReturn(Collections.singleton("sitemap3"));
-
- // when
- Map<String, SitemapGenerator> generators = subject.getGenerators(context.currentResource("/"));
- SitemapGenerator sitemap3Generator = subject.getGenerator(context.currentResource(), "sitemap3");
- SitemapGenerator defaultSitemapGenerator = subject.getGenerator(context.currentResource(), SitemapService.DEFAULT_SITEMAP_NAME);
- Set<String> applicableNames = new HashSet<>(subject.getNames(context.currentResource()));
- applicableNames.retainAll(Arrays.asList(
- "sitemap1", "sitemap2", "sitemap3", SitemapService.DEFAULT_SITEMAP_NAME
- ));
-
- // then
- assertThat(generators, aMapWithSize(2));
- assertThat(generators, hasEntry(SitemapService.DEFAULT_SITEMAP_NAME, generator2));
- assertThat(generators, hasEntry("sitemap3", generator3));
- assertEquals(defaultSitemapGenerator, generator2);
- assertEquals(sitemap3Generator, generator3);
- assertThat(applicableNames, hasSize(2));
- assertThat(applicableNames, hasItem("sitemap3"));
- assertThat(applicableNames, hasItem(SitemapService.DEFAULT_SITEMAP_NAME));
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapSchedulerTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapSchedulerTest.java
deleted file mode 100644
index fb792cb..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapSchedulerTest.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.event.jobs.Job;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.TestGenerator;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.testing.mock.jcr.MockJcr;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.internal.util.reflection.Fields;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.*;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapSchedulerTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
-
- private final SitemapScheduler subject = new SitemapScheduler();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
-
- private final TestGenerator generator1 = new TestGenerator() {};
- private final TestGenerator generator2 = new TestGenerator() {};
-
- @Mock
- private ServiceUserMapped serviceUser;
- @Mock
- private JobManager jobManager;
-
- private Resource rootDe;
- private Resource rootEn;
- private Resource rootEnContent;
-
- @BeforeEach
- public void setup() {
- rootDe = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE));
- rootEn = context.create().resource("/content/site/en");
- rootEnContent = context.create().resource("/content/site/en/jcr:content", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE));
-
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-reader");
- context.registerService(JobManager.class, jobManager);
- context.registerService(SitemapGenerator.class, generator1, "service.ranking", 1);
- context.registerService(SitemapGenerator.class, generator2, "service.ranking", 2);
- context.registerInjectActivateService(sitemapServiceConfiguration);
- context.registerInjectActivateService(generatorManager);
-
- AtomicInteger jobCount = new AtomicInteger(0);
-
- when(jobManager.addJob(any(), any())).then(inv -> {
- Job job = mock(Job.class);
- when(job.getId()).thenReturn(String.valueOf(jobCount.incrementAndGet()));
- return job;
- });
- }
-
- @Test
- public void testOneDefaultSitemapJobStartedForEachRoot() {
- // given
- context.registerInjectActivateService(subject);
- initResourceResolver(subject, resolver -> MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- ImmutableList.of(
- rootDe.adaptTo(Node.class),
- rootEnContent.adaptTo(Node.class)
- )
- ));
- generator1.setNames(SitemapService.DEFAULT_SITEMAP_NAME);
- generator2.setNames(SitemapService.DEFAULT_SITEMAP_NAME);
-
- // when
- subject.run();
-
- // then
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch(SitemapService.DEFAULT_SITEMAP_NAME, "/content/site/de"))
- );
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch(SitemapService.DEFAULT_SITEMAP_NAME, "/content/site/en"))
- );
- }
-
- @Test
- public void testOneSitemapJobStartedForEachName() {
- // given
- context.registerInjectActivateService(subject);
- initResourceResolver(subject, resolver -> MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Collections.singletonList(rootDe.adaptTo(Node.class))
- ));
- generator1.setNames(SitemapService.DEFAULT_SITEMAP_NAME, "sitemap1");
- generator2.setNames(SitemapService.DEFAULT_SITEMAP_NAME, "sitemap2");
-
- // when
- subject.run();
-
- // then
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch(SitemapService.DEFAULT_SITEMAP_NAME, "/content/site/de"))
- );
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch("sitemap1", "/content/site/de"))
- );
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch("sitemap2", "/content/site/de"))
- );
- }
-
- @Test
- public void testNothingScheduledWhenNameDoesNotMatchGeneratorFromConfiguration() {
- // given
- context.registerInjectActivateService(subject, "includeGenerators", new String[]{
- generator1.getClass().getName()
- });
- initResourceResolver(subject, resolver -> MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Collections.singletonList(rootDe.adaptTo(Node.class))
- ));
- generator1.setNames("sitemap1");
- generator2.setNames(SitemapService.DEFAULT_SITEMAP_NAME, "sitemap2");
-
- // when
- subject.schedule(Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME));
-
- // then
- verify(jobManager, never()).addJob(any(), any());
-
- // and when
- subject.schedule(Collections.singleton("sitemap1"));
-
- // then
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch("sitemap1", "/content/site/de"))
- );
- }
-
- @Test
- public void testNothingScheduledWhenNameDoesNotNamesFromConfiguration() {
- // given
- context.registerInjectActivateService(subject, "names", new String[]{
- "foobar"
- });
- initResourceResolver(subject, resolver -> MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Collections.singletonList(rootDe.adaptTo(Node.class))
- ));
- generator1.setNames("sitemap1");
- generator2.setNames(SitemapService.DEFAULT_SITEMAP_NAME, "sitemap2");
-
- // when
- subject.schedule(Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME));
-
- // then
- verify(jobManager, never()).addJob(any(), any());
-
- // and when
- generator1.setNames("sitemap1", "foobar");
- subject.schedule(Collections.singleton("foobar"));
-
- // then
- verify(jobManager, times(1)).addJob(
- eq("org/apache/sling/sitemap/build"),
- argThat(sitemapJobPropertiesMatch("foobar", "/content/site/de"))
- );
- }
-
- private void initResourceResolver(SitemapScheduler scheduler, Consumer<ResourceResolver> resolverConsumer) {
- initResourceResolver(context, scheduler, resolverConsumer);
- }
-
- static void initResourceResolver(SlingContext context, SitemapScheduler scheduler,
- Consumer<ResourceResolver> resolverConsumer) {
- try {
- ResourceResolverFactory original = context.getService(ResourceResolverFactory.class);
- ResourceResolverFactory mock = mock(ResourceResolverFactory.class);
- Fields.allDeclaredFieldsOf(scheduler).instanceFields().stream()
- .filter(instanceField -> "resourceResolverFactory".equals(instanceField.name()))
- .forEach(instanceField -> instanceField.set(mock));
-
- lenient().when(mock.getServiceResourceResolver(any())).then(inv -> {
- ResourceResolver resolver = original.getServiceResourceResolver(inv.getArgument(0));
- resolverConsumer.accept(resolver);
- return resolver;
- });
- } catch (LoginException ex) {
- fail("Did not expect LoginException while mocking.");
- }
- }
-
- private static ArgumentMatcher<Map<String, Object>> sitemapJobPropertiesMatch(String name, String path) {
- return map -> map.get("sitemap.root").equals(path) && map.get("sitemap.name").equals(name);
- }
-
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java
deleted file mode 100644
index 02bf845..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import com.google.common.collect.ImmutableSet;
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.builder.Sitemap;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.testing.mock.jcr.MockJcr;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import java.util.Collections;
-import java.util.Set;
-
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Mockito.*;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapServiceImplSchedulingTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
-
- private final SitemapServiceImpl subject = new SitemapServiceImpl();
- private final SitemapStorage storage = new SitemapStorage();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
-
- @Mock
- private ServiceUserMapped serviceUser;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private JobManager jobManager;
- private SitemapGenerator generator1 = new SitemapGenerator() {
- @Override
- public @NotNull Set<String> getNames(@NotNull Resource sitemapRoot) {
- return Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME);
- }
-
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap, @NotNull GenerationContext context) throws SitemapException {
- fail();
- }
- };
- private SitemapGenerator generator2 = new SitemapGenerator() {
- @Override
- public @NotNull Set<String> getNames(@NotNull Resource sitemapRoot) {
- return ImmutableSet.of("foo");
- }
-
- @Override
- public void generate(@NotNull Resource sitemapRoot, @NotNull String name, @NotNull Sitemap sitemap, @NotNull GenerationContext context) throws SitemapException {
- fail();
- }
- };
-
- private Resource siteRoot;
- private Resource micrositeRoot;
- private SitemapScheduler schedulerWithGenerator1OnSite;
- private SitemapScheduler schedulerWithGenerator2OnMicrosite;
-
- @BeforeEach
- public void setup() throws LoginException {
- siteRoot = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- micrositeRoot = context.create().resource("/content/microsite/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-reader");
- context.registerService(SitemapGenerator.class, generator1);
- context.registerService(SitemapGenerator.class, generator2);
- context.registerService(JobManager.class, jobManager);
- context.registerInjectActivateService(sitemapServiceConfiguration);
- context.registerInjectActivateService(generatorManager);
- context.registerInjectActivateService(storage);
- context.registerInjectActivateService(subject);
-
- schedulerWithGenerator1OnSite = context.registerInjectActivateService(spy(new SitemapScheduler()),
- "searchPath", "/content/site"
- );
- schedulerWithGenerator2OnMicrosite = context.registerInjectActivateService(spy(new SitemapScheduler()),
- "searchPath", "/content/microsite"
- );
-
- SitemapSchedulerTest.initResourceResolver(context, schedulerWithGenerator1OnSite, this::setupResourceResolver);
- SitemapSchedulerTest.initResourceResolver(context, schedulerWithGenerator2OnMicrosite, this::setupResourceResolver);
- }
-
- private void setupResourceResolver(ResourceResolver resolver) {
- MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content/site//*[@sling:sitemapRoot=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Collections.singletonList(siteRoot.adaptTo(Node.class))
- );
- MockJcr.setQueryResult(
- resolver.adaptTo(Session.class),
- "/jcr:root/content/microsite//*[@sling:sitemapRoot=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Collections.singletonList(micrositeRoot.adaptTo(Node.class))
- );
- }
-
- @Test
- public void testAllSchedulersCalled() {
- // when
- subject.scheduleGeneration();
-
- // then
- verify(schedulerWithGenerator1OnSite, atLeastOnce()).addJob(any(), any());
- verify(schedulerWithGenerator2OnMicrosite, atLeastOnce()).addJob(any(), any());
- }
-
- @Test
- public void testSchedulersCalledForName() {
- // when
- subject.scheduleGeneration("<default>");
-
- // then
- verify(schedulerWithGenerator1OnSite, atLeastOnce()).addJob(any(), eq("<default>"));
- verify(schedulerWithGenerator2OnMicrosite, atLeastOnce()).addJob(any(), eq("<default>"));
- }
-
- @Test
- public void testSchedulersCalledForPath() {
- // when
- subject.scheduleGeneration(siteRoot);
-
- // then
- verify(schedulerWithGenerator1OnSite, atLeastOnce()).addJob(eq(siteRoot.getPath()), any());
- verify(schedulerWithGenerator2OnMicrosite, never()).addJob(any(), any());
- }
-
- @Test
- public void testSchedulersCalledForPathAndName() {
- // when
- subject.scheduleGeneration(siteRoot, "foo");
- subject.scheduleGeneration(micrositeRoot, "foo");
-
- // then
- verify(schedulerWithGenerator1OnSite, times(1)).addJob(eq(siteRoot.getPath()), eq("foo"));
- verify(schedulerWithGenerator2OnMicrosite, times(1)).addJob(eq(micrositeRoot.getPath()), eq("foo"));
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java
deleted file mode 100644
index dc78fae..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplTest.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapInfo;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.TestGenerator;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.testing.mock.jcr.MockJcr;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.hamcrest.CustomMatcher;
-import org.hamcrest.Matcher;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Set;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.hasSize;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapServiceImplTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
-
- private final SitemapServiceImpl subject = new SitemapServiceImpl();
- private final SitemapStorage storage = new SitemapStorage();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final SitemapScheduler scheduler = new SitemapScheduler();
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
-
- private TestGenerator generator = new TestGenerator() {
- };
- private TestGenerator newsGenerator = new TestGenerator() {
- @Override
- public @NotNull Set<String> getOnDemandNames(@NotNull Resource sitemapRoot) {
- return getNames(sitemapRoot);
- }
- };
-
- @Mock
- private ServiceUserMapped serviceUser;
- @Mock
- private JobManager jobManager;
-
- private Resource deRoot;
- private Resource enRoot;
- private Resource enFaqs;
- private Resource enNews;
- private Resource frRoot;
- private Resource itRoot;
- private Resource noRoot;
-
- @BeforeEach
- public void setup() {
- deRoot = context.create().resource("/content/site/de", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- enRoot = context.create().resource("/content/site/en", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- enFaqs = context.create().resource("/content/site/en/fags", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- enNews = context.create().resource("/content/site/en/news", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- frRoot = context.create().resource("/content/site/fr", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- itRoot = context.create().resource("/content/site/it", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- noRoot = context.create().resource("/content/site/nothing");
-
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-reader");
- context.registerService(SitemapGenerator.class, generator, "service.ranking", 1);
- context.registerService(SitemapGenerator.class, newsGenerator, "service.ranking", 2);
- context.registerService(JobManager.class, jobManager);
- context.registerInjectActivateService(sitemapServiceConfiguration);
- context.registerInjectActivateService(generatorManager);
- context.registerInjectActivateService(storage);
- context.registerInjectActivateService(scheduler,
- "scheduler.name", "default",
- "scheduler.expression", "never",
- "names", new String[] { SitemapService.DEFAULT_SITEMAP_NAME });
- context.registerInjectActivateService(subject);
- }
-
- @Test
- public void testSitemapIndexUrlReturned() {
- // given
- generator.setNames(SitemapService.DEFAULT_SITEMAP_NAME);
- generator.setNames(frRoot, "a", "b");
-
- MockJcr.setQueryResult(
- context.resourceResolver().adaptTo(Session.class),
- "/jcr:root/content/site/en//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Arrays.asList(enRoot.adaptTo(Node.class), enFaqs.adaptTo(Node.class))
- );
-
- // when
- // has descendants
- Collection<SitemapInfo> enInfo = subject.getSitemapInfo(enRoot);
- // has multiple names
- Collection<SitemapInfo> frInfo = subject.getSitemapInfo(frRoot);
-
- // then
- assertThat(enInfo, hasSize(2));
- assertThat(enInfo, hasItems(
- eqSitemapInfo("/site/en.sitemap-index.xml", -1, -1, SitemapInfo.Status.ON_DEMAND),
- eqSitemapInfo("/site/en.sitemap.xml", -1, -1, SitemapInfo.Status.SCHEDULED)
- ));
- assertThat(frInfo, hasSize(3));
- assertThat(frInfo, hasItems(
- eqSitemapInfo("/site/fr.sitemap-index.xml", -1, -1, SitemapInfo.Status.ON_DEMAND),
- eqSitemapInfo("/site/fr.sitemap.a-sitemap.xml", -1, -1, SitemapInfo.Status.UNKNOWN),
- eqSitemapInfo("/site/fr.sitemap.a-sitemap.xml", -1, -1, SitemapInfo.Status.UNKNOWN)
- ));
- }
-
- @Test
- public void testSitemapUrlReturnEmptyCollections() {
- // no name
- assertThat(subject.getSitemapInfo(deRoot), hasSize(0));
- // no names nested
- assertThat(subject.getSitemapInfo(enFaqs), hasSize(0));
- // not a root
- assertThat(subject.getSitemapInfo(noRoot), hasSize(0));
- }
-
- @Test
- public void testSitemapUrlReturnsProperSelectors() throws IOException {
- // given
- storage.writeSitemap(deRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
- 1);
- storage.writeSitemap(frRoot, "foobar", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
- storage.writeSitemap(enRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
- 1);
- storage.writeSitemap(enNews, "foo", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
- storage.writeSitemap(enNews, "bar", new ByteArrayInputStream(new byte[0]), 1, 100, 1);
- storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 100,
- 1);
- storage.writeSitemap(itRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 2, 100,
- 1);
-
- generator.setNames(deRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- generator.setNames(frRoot, "foobar");
- generator.setNames(enNews, "foo", "bar");
- generator.setNames(enRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- generator.setNames(itRoot, SitemapService.DEFAULT_SITEMAP_NAME);
- newsGenerator.setNames(enNews, "news");
-
- MockJcr.setQueryResult(
- context.resourceResolver().adaptTo(Session.class),
- "/jcr:root/content/site/en//*[@" + SitemapService.PROPERTY_SITEMAP_ROOT + "=true]" +
- " option(index tag slingSitemaps)",
- Query.XPATH,
- Arrays.asList(enRoot.adaptTo(Node.class), enNews.adaptTo(Node.class))
- );
-
- // when
- Collection<SitemapInfo> deInfos = subject.getSitemapInfo(deRoot);
- Collection<SitemapInfo> itInfos = subject.getSitemapInfo(itRoot);
- Collection<SitemapInfo> frInfos = subject.getSitemapInfo(frRoot);
- Collection<SitemapInfo> enInfos = subject.getSitemapInfo(enRoot);
- Collection<SitemapInfo> enNestedInfos = subject.getSitemapInfo(enNews);
-
- // no name
- assertThat(deInfos, hasSize(1));
- assertThat(deInfos, hasItems(eqSitemapInfo("/site/de.sitemap.xml", 100, 1)));
- // no name, multi file
- assertThat(itInfos, hasSize(3));
- assertThat(itInfos, hasItems(
- eqSitemapInfo("/site/it.sitemap-index.xml", -1, -1),
- eqSitemapInfo("/site/it.sitemap.xml", 100, 1),
- eqSitemapInfo("/site/it.sitemap.sitemap-2.xml", 100, 1)
- ));
- // with single name
- assertThat(frInfos, hasSize(1));
- assertThat(frInfos, hasItems(eqSitemapInfo("/site/fr.sitemap.foobar-sitemap.xml", 100, 1)));
- // nested with multiple names
- assertThat(enNestedInfos, hasSize(3));
- assertThat(enNestedInfos, hasItems(
- eqSitemapInfo("/site/en.sitemap.news-foo-sitemap.xml", 100, 1),
- eqSitemapInfo("/site/en.sitemap.news-bar-sitemap.xml", 100, 1),
- // on-demand
- eqSitemapInfo("/site/en.sitemap.news-news-sitemap.xml", -1, -1)
- ));
- // nested => index and default self
- assertThat(enInfos, hasSize(2));
- assertThat(enInfos, hasItems(
- eqSitemapInfo("/site/en.sitemap-index.xml", -1, -1),
- eqSitemapInfo("/site/en.sitemap.xml", 100, 1)
- ));
- }
-
- private static Matcher<SitemapInfo> eqSitemapInfo(String url, int size, int entries) {
- return eqSitemapInfo(url, size, entries, null);
- }
-
- private static Matcher<SitemapInfo> eqSitemapInfo(String url, int size, int entries, SitemapInfo.Status status) {
- return new CustomMatcher<SitemapInfo>("with url " + url + ", with size " + size + ", with entries " + entries) {
- @Override
- public boolean matches(Object o) {
- return o instanceof SitemapInfo &&
- url.equals(((SitemapInfo) o).getUrl()) &&
- size == ((SitemapInfo) o).getSize() &&
- entries == ((SitemapInfo) o).getEntries() &&
- (status == null || status.equals(((SitemapInfo) o).getStatus()));
- }
- };
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServletTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServletTest.java
deleted file mode 100644
index 669d403..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServletTest.java
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import com.google.common.collect.ImmutableSet;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.event.jobs.JobManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.TestResourceTreeSitemapGenerator;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.sitemap.impl.builder.SitemapImplTest;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.apache.sling.testing.mock.jcr.MockJcr;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.apache.sling.testing.mock.sling.servlet.MockRequestPathInfo;
-import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletRequest;
-import org.apache.sling.testing.mock.sling.servlet.MockSlingHttpServletResponse;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.servlet.ServletException;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-import java.util.*;
-import java.util.stream.Collectors;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.*;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapServletTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
-
- private final SitemapServlet subject = new SitemapServlet();
- private final SitemapStorage storage = spy(new SitemapStorage());
- private final SitemapServiceConfiguration sitemapServiceConfiguration = new SitemapServiceConfiguration();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final ExtensionProviderManager extensionProviderManager = new ExtensionProviderManager();
-
- private TestResourceTreeSitemapGenerator generator = new TestResourceTreeSitemapGenerator() {
- @Override
- public @NotNull Set<String> getNames(@NotNull Resource sitemapRoot) {
- return ImmutableSet.of(SitemapService.DEFAULT_SITEMAP_NAME);
- }
- };
- private TestResourceTreeSitemapGenerator newsGenerator = new TestResourceTreeSitemapGenerator() {
- @Override
- public @NotNull Set<String> getNames(@NotNull Resource sitemapRoot) {
- return ImmutableSet.of("news");
- }
- };
-
- @Mock
- private JobManager jobManager;
- @Mock
- private ServiceUserMapped serviceUser;
- private final Calendar pointInTime;
- private final String pointInTimeAtUtc;
- private Resource root;
-
- public SitemapServletTest() {
- pointInTime = Calendar.getInstance();
- pointInTime.setTimeInMillis(123456789L);
- pointInTimeAtUtc = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(pointInTime.toInstant().atOffset(ZoneOffset.UTC));
- }
-
- @BeforeEach
- public void setup() {
- root = context.create().resource("/content/site/de");
- context.create().resource("/content/site/de/jcr:content", Collections.singletonMap(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE));
-
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
- context.registerService(SitemapGenerator.class, generator);
- context.registerService(SitemapGenerator.class, newsGenerator);
- context.registerService(JobManager.class, jobManager);
- context.registerInjectActivateService(extensionProviderManager);
- context.registerInjectActivateService(sitemapServiceConfiguration);
- context.registerInjectActivateService(generatorManager);
- context.registerInjectActivateService(storage);
- context.registerInjectActivateService(subject);
-
- // overrule the getLastModified() of all SitemapStorageInfos to get predictable behaviour
- doAnswer(inv -> ((Collection<SitemapStorageInfo>) inv.callRealMethod()).stream()
- .map(info -> {
- info = spy(info);
- when(info.getLastModified()).thenReturn(pointInTime);
- return info;
- })
- .collect(Collectors.toCollection(LinkedHashSet::new))
- ).when(storage).getSitemaps(any());
-
- MockJcr.setQueryResult(
- context.resourceResolver().adaptTo(Session.class),
- Collections.singletonList(root.adaptTo(Node.class))
- );
- }
-
- @Test
- public void testBadRequestForResourcesThatAreNotSitemapRoot() throws ServletException, IOException {
- // given
- Resource notATopLevelRoot = context.create().resource("/content/site/en");
-
- MockSlingHttpServletRequest request = newSitemapIndexReq(notATopLevelRoot);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(400, response.getStatus());
- }
-
- @Test
- public void testBadRequestForInvalidSelectors() throws ServletException, IOException {
- // given
- MockSlingHttpServletResponse response = context.response();
-
- // when, then
- subject.doGet(newReq("sitemap-index.somethingelse", root), response);
- assertEquals(400, response.getStatus());
- response = context.response();
- subject.doGet(newReq("sitemap.something.else", root), response);
- assertEquals(400, response.getStatus());
- response = context.response();
- subject.doGet(newReq("unknown", root), response);
- assertEquals(400, response.getStatus());
- }
-
- @Test
- public void testSitemapIndexContainsOnlySitemapsFromStorage() throws IOException, ServletException {
- // given
- storage.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
- storage.writeSitemap(root, "news", new ByteArrayInputStream(new byte[0]), 1, 0, 0);
-
- MockSlingHttpServletRequest request = newSitemapIndexReq(root);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- SitemapImplTest.XML_HEADER + "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<sitemap><loc>/site/de.sitemap.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "<sitemap><loc>/site/de.sitemap.news-sitemap.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "</sitemapindex>",
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testSitemapIndexContainsMultiFileSitemaps() throws IOException, ServletException {
- // given
- storage.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
- storage.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 2, 0, 0);
- storage.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 3, 0, 0);
-
- MockSlingHttpServletRequest request = newSitemapIndexReq(root);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- SitemapImplTest.XML_HEADER + "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<sitemap><loc>/site/de.sitemap.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "<sitemap><loc>/site/de.sitemap.sitemap-2.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "<sitemap><loc>/site/de.sitemap.sitemap-3.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "</sitemapindex>",
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testSitemapIndexContainsOnDemandSitemapsAndSitemapsFromStorage() throws ServletException, IOException {
- // given
- storage.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
-
- MockSlingHttpServletRequest request = newSitemapIndexReq(root);
- MockSlingHttpServletResponse response = context.response();
- newsGenerator.setServeOnDemand(true);
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- SitemapImplTest.XML_HEADER + "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<sitemap><loc>/site/de.sitemap.news-sitemap.xml</loc></sitemap>"
- + "<sitemap><loc>/site/de.sitemap.xml</loc><lastmod>" + pointInTimeAtUtc + "</lastmod></sitemap>"
- + "</sitemapindex>",
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testSitemapServedOnDemand() throws ServletException, IOException {
- // given
- MockSlingHttpServletRequest request = newSitemapReq("news-sitemap", root);
- MockSlingHttpServletResponse response = context.response();
- newsGenerator.setServeOnDemand(true);
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/de</loc></url>"
- + "</urlset>",
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testSitemapServedFromStorage() throws ServletException, IOException {
- // given
- String expectedOutcome = SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/en</loc></url>"
- + "</urlset>";
- byte[] expectedOutcomeBytes = expectedOutcome.getBytes(StandardCharsets.UTF_8);
-
- storage.writeSitemap(root, "foo", new ByteArrayInputStream(expectedOutcomeBytes), 1,
- expectedOutcomeBytes.length, 1);
-
- MockSlingHttpServletRequest request = newSitemapReq("foo-sitemap", root);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- expectedOutcome,
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testMultiFileSitemapServedFromStorage() throws ServletException, IOException {
- // given
- String expectedOutcome = SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>/content/site/en</loc></url>"
- + "</urlset>";
- byte[] expectedOutcomeBytes = expectedOutcome.getBytes(StandardCharsets.UTF_8);
-
- storage.writeSitemap(root, "foo", new ByteArrayInputStream(expectedOutcomeBytes), 2,
- expectedOutcomeBytes.length, 1);
-
- MockSlingHttpServletRequest request = newSitemapReq("foo-sitemap-2", root);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(200, response.getStatus());
- assertEquals("application/xml;charset=utf-8", response.getContentType());
- assertEquals("utf-8", response.getCharacterEncoding());
- assertEquals(
- expectedOutcome,
- response.getOutputAsString()
- );
- }
-
- @Test
- public void testSitemapNotServed() throws ServletException, IOException {
- // given
- MockSlingHttpServletRequest request = newSitemapReq("sitemap", root);
- MockSlingHttpServletResponse response = context.response();
-
- // when
- subject.doGet(request, response);
-
- // then
- assertEquals(404, response.getStatus());
- }
-
- private MockSlingHttpServletRequest newSitemapReq(String sitemapFileName, Resource resource) {
- return newReq("sitemap." + sitemapFileName, resource);
- }
-
- private MockSlingHttpServletRequest newSitemapIndexReq(Resource resource) {
- return newReq("sitemap-index", resource);
- }
-
- private MockSlingHttpServletRequest newReq(String selectors, Resource resource) {
- MockSlingHttpServletRequest req = new MockSlingHttpServletRequest(context.resourceResolver(), context.bundleContext()) {
- @Override
- protected @NotNull MockRequestPathInfo newMockRequestPathInfo() {
- MockRequestPathInfo pathInfo = super.newMockRequestPathInfo();
- pathInfo.setSelectorString(selectors);
- return pathInfo;
- }
- };
- req.setResource(resource);
-
- return req;
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapStorageTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapStorageTest.java
deleted file mode 100644
index 486534d..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapStorageTest.java
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl;
-
-import com.google.common.collect.ImmutableMap;
-import org.apache.commons.io.IOUtils;
-import org.apache.sling.api.resource.ModifiableValueMap;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.generator.SitemapGenerator;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.hamcrest.CustomMatcher;
-import org.hamcrest.Matcher;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.junit.platform.commons.util.StringUtils;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventHandler;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.hasSize;
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapStorageTest {
-
- public final SlingContext context = new SlingContext();
-
- private final SitemapStorage subject = new SitemapStorage();
- private final SitemapGeneratorManagerImpl generatorManager = new SitemapGeneratorManagerImpl();
- private final SitemapServiceConfiguration configuration = new SitemapServiceConfiguration();
-
- @Mock(lenient = true)
- private SitemapGenerator generator;
- @Mock
- private ServiceUserMapped serviceUser;
-
- @BeforeEach
- public void setup() {
- context.registerService(SitemapGenerator.class, generator);
- context.registerService(ServiceUserMapped.class, serviceUser, "subServiceName", "sitemap-writer");
- context.registerInjectActivateService(configuration);
- context.registerInjectActivateService(generatorManager);
- context.registerInjectActivateService(subject,
- "stateMaxAge", 100
- );
-
- when(generator.getNames(any())).thenReturn(Collections.singleton(SitemapService.DEFAULT_SITEMAP_NAME));
- }
-
- // Read/Write
-
- @Test
- public void testConsecutiveWriteOfStateUpdatesContent() throws IOException {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeState(root, "foo", ImmutableMap.of("i", 1));
- subject.writeState(root, "foo", ImmutableMap.of("i", 2));
-
- // then
- ValueMap properties = subject.getState(root, "foo");
- assertEquals(2, properties.get("i", Integer.class));
- }
-
- @Test
- public void testConsecutiveWriteOfSitemapUpdatesContent() throws IOException {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- "sitemapRoot", Boolean.TRUE
- ));
-
- // when
- subject.writeSitemap(root, "foo", new ByteArrayInputStream(new byte[]{0x01}), 1, 1, 0);
- subject.writeSitemap(root, "foo", new ByteArrayInputStream(new byte[]{0x01, 0x02}), 1, 2, 0);
-
- // then
- assertResourceDataEquals(
- new String(new byte[]{0x01, 0x02}, StandardCharsets.US_ASCII),
- context.resourceResolver().getResource("/var/sitemaps/content/site/de/foo-sitemap.xml")
- );
- }
-
- @Test
- public void testListSitemapsReturnsOnlySitemaps() throws IOException {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeState(root, "foo", ImmutableMap.of("i", 1));
- subject.writeState(root, "bar", ImmutableMap.of("i", 1));
- subject.writeSitemap(root, "foobar", new ByteArrayInputStream(new byte[0]), 1, 0, 0);
- subject.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
-
- // then
- Collection<SitemapStorageInfo> sitemapNames = subject.getSitemaps(root);
- assertThat(sitemapNames, hasSize(2));
- assertThat(sitemapNames, hasItems(storageInfo("foobar-sitemap"), storageInfo("sitemap")));
- }
-
- // Cleanup
-
- @Test
- public void testStateExpires() throws InterruptedException, IOException {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeState(root, "foo", ImmutableMap.of("i", 1));
- Thread.sleep(100);
-
- // then
- ValueMap properties = subject.getState(root, "foo");
- assertNull(properties.get("i", Integer.class));
- }
-
-
- @Test
- public void testCleanupExpiredStates() throws Exception {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeState(root, SitemapService.DEFAULT_SITEMAP_NAME, ImmutableMap.of("i", 1));
-
- // then
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/sitemap.part"));
-
- // and when
- Thread.sleep(100);
- subject.run();
-
- // then
- assertNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/sitemap.part"));
- }
-
- @Test
- public void testCleanupObsoleteSitemapsAfterTopLevelChanged() throws Exception {
- // given
- Resource newRoot = context.create().resource("/content/site/ch");
- Resource initialRoot = context.create().resource("/content/site/ch/de-ch", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeSitemap(initialRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1,
- 0, 0);
- subject.writeSitemap(initialRoot, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 2,
- 0, 0);
-
- // then
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/ch/de-ch/sitemap.xml"));
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/ch/de-ch/sitemap-2.xml"));
-
- // and when
- newRoot.adaptTo(ModifiableValueMap.class).put(SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE);
- context.resourceResolver().commit();
- subject.run();
-
- // then
- assertNull(context.resourceResolver().getResource("/var/sitemaps/content/site/ch/de-ch/sitemap.xml"));
- assertNull(context.resourceResolver().getResource("/var/sitemaps/content/site/ch/de-ch/sitemap-2.xml"));
- }
-
- @Test
- public void testCleanupObsoleteSitemapsAfterNestedSitemapRootChanged() throws Exception {
- // given
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- Resource news = context.create().resource("/content/site/de/news", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
-
- // when
- subject.writeSitemap(root, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
- subject.writeSitemap(news, SitemapService.DEFAULT_SITEMAP_NAME, new ByteArrayInputStream(new byte[0]), 1, 0, 0);
-
- // then
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/sitemap.xml"));
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/news-sitemap.xml"));
-
- // and when
- news.adaptTo(ModifiableValueMap.class).put(SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.FALSE);
- context.resourceResolver().commit();
- subject.run();
-
- // then
- assertNotNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/sitemap.xml"));
- assertNull(context.resourceResolver().getResource("/var/sitemaps/content/site/de/news-sitemap.xml"));
- }
-
- // Eventing
-
- @Test
- public void testNoPurgeEventSentOnStateCleanup() throws IOException, InterruptedException {
- // given
- List<Event> capturedEvents = new ArrayList<>();
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- context.registerService(EventHandler.class, capturedEvents::add, EventConstants.EVENT_TOPIC, new String[]{
- SitemapGenerator.EVENT_TOPIC_SITEMAP_UPDATED,
- SitemapGenerator.EVENT_TOPIC_SITEMAP_PURGED
- });
-
- // when
- subject.writeState(root, "foo", Collections.emptyMap());
- Thread.sleep(100);
- subject.run();
-
- // then
- assertThat(capturedEvents, hasSize(0));
- }
-
- @Test
- public void testUpdatedAndPurgeEventSentOnSitemapWriteCleanup() throws IOException, InterruptedException {
- // given
- List<Event> capturedEvents = new ArrayList<>();
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- context.registerService(EventHandler.class, capturedEvents::add, EventConstants.EVENT_TOPIC, new String[]{
- SitemapGenerator.EVENT_TOPIC_SITEMAP_UPDATED,
- SitemapGenerator.EVENT_TOPIC_SITEMAP_PURGED
- });
-
- // when
- String storagePath = subject.writeSitemap(root, "foo", new ByteArrayInputStream(new byte[]{0x00}), 1, 0, 0);
- context.resourceResolver().delete(root);
- context.resourceResolver().commit();
- Thread.sleep(100);
- subject.run();
-
- // then
- System.out.println(capturedEvents.stream().map(Event::toString).collect(Collectors.joining(",")));
- assertThat(capturedEvents, hasSize(2));
- assertThat(capturedEvents, hasItems(
- sitemapEvent(SitemapGenerator.EVENT_TOPIC_SITEMAP_UPDATED, storagePath),
- sitemapEvent(SitemapGenerator.EVENT_TOPIC_SITEMAP_PURGED, storagePath)
- ));
- }
-
- @Test
- public void testUpdatedAndPurgeEventSentOnSitemapWriteDelete() throws IOException, InterruptedException {
- // given
- List<Event> capturedEvents = new ArrayList<>();
- Resource root = context.create().resource("/content/site/de", ImmutableMap.of(
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE
- ));
- context.registerService(EventHandler.class, capturedEvents::add, EventConstants.EVENT_TOPIC, new String[]{
- SitemapGenerator.EVENT_TOPIC_SITEMAP_UPDATED,
- SitemapGenerator.EVENT_TOPIC_SITEMAP_PURGED
- });
-
- // when
- Thread.sleep(100);
- String storagePath = subject.writeSitemap(root, "foo", new ByteArrayInputStream(new byte[]{0x00}), 1, 0, 0);
- subject.deleteSitemaps(root, "foo", info -> true);
- Thread.sleep(100);
-
- // then
- assertThat(capturedEvents, hasSize(2));
- assertThat(capturedEvents, hasItems(
- sitemapEvent(SitemapGenerator.EVENT_TOPIC_SITEMAP_UPDATED, storagePath),
- sitemapEvent(SitemapGenerator.EVENT_TOPIC_SITEMAP_PURGED, storagePath)
- ));
- }
-
- static void assertResourceDataEquals(String expectedValue, Resource resource) throws IOException {
- assertNotNull(resource);
- InputStream inputStream = resource.adaptTo(InputStream.class);
- if (inputStream == null) {
- inputStream = resource.getValueMap().get("jcr:data", InputStream.class);
- if (inputStream == null) {
- Resource content = resource.getChild("jcr:content");
- inputStream = content != null ? content.getValueMap().get("jcr:data", InputStream.class) : null;
- }
- }
- assertNotNull(inputStream);
- StringWriter sitemap = new StringWriter();
- IOUtils.copy(inputStream, sitemap, StandardCharsets.UTF_8);
- assertEquals(expectedValue, sitemap.toString());
- }
-
- private static Matcher<Event> sitemapEvent(String topic, String storagePath) {
- return new CustomMatcher<Event>("Event with storagePath property set to " + storagePath) {
- @Override
- public boolean matches(Object o) {
- return o instanceof Event
- && storagePath.equals(
- ((Event) o).getProperty(SitemapGenerator.EVENT_PROPERTY_SITEMAP_STORAGE_PATH));
- }
- };
- }
-
- private static Matcher<SitemapStorageInfo> storageInfo(String name) {
- return new CustomMatcher<SitemapStorageInfo>("SitemapStorageInfo with name='" + name + "'") {
- @Override
- public boolean matches(Object o) {
- return o instanceof SitemapStorageInfo && ((SitemapStorageInfo) o).getSitemapSelector().equals(name);
- }
- };
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/SitemapImplTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/SitemapImplTest.java
deleted file mode 100644
index 8a3dcd5..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/SitemapImplTest.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-@ExtendWith({SlingContextExtension.class})
-public class SitemapImplTest {
-
- public static final String XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
-
- public final SlingContext context = new SlingContext();
-
- private ExtensionProviderManager extensionManager;
-
- @BeforeEach
- public void setup() {
- extensionManager = context.registerInjectActivateService(new ExtensionProviderManager());
- }
-
- @Test
- public void testEmptySitemap() throws IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- //when
- subject.close();
- String sitemap = writer.toString();
-
- //then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"></urlset>",
- sitemap
- );
- }
-
- @Test
- public void testSitemapWithSingleLocation() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- //when
- subject.addUrl("http://example.com");
- subject.close();
- String sitemap = writer.toString();
-
- //then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com</loc></url>"
- + "</urlset>",
- sitemap
- );
- }
-
- @Test
- public void testWriteAfterRead() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- //when
- subject.addUrl("http://example.com");
- subject.writePendingUrl();
- String sitemap1 = writer.toString();
- subject.addUrl("http://example.de");
- subject.close();
- String sitemap2 = writer.toString();
-
- //then
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com</loc></url>",
- sitemap1
- );
- assertEquals(
- XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
- + "<url><loc>http://example.com</loc></url>"
- + "<url><loc>http://example.de</loc></url>"
- + "</urlset>",
- sitemap2
- );
- }
-
- @Test
- public void testIOExceptionWrappedInSitemapException() throws IOException {
- // given
- Writer throwingWriter = new Writer() {
- @Override
- public void write(@NotNull char[] cbuf, int off, int len) throws IOException {
- throw new IOException();
- }
-
- @Override
- public void flush() throws IOException {
- throw new IOException();
- }
-
- @Override
- public void close() throws IOException {
- throw new IOException();
- }
- };
- SitemapImpl sitemap = new SitemapImpl(throwingWriter, extensionManager, false);
-
- SitemapException ex = assertThrows(SitemapException.class, () -> {
- sitemap.addUrl("http://localhost:4502");
- sitemap.addUrl("http://localhost:4503"); // throws
- });
- assertThat(ex.getCause(), instanceOf(IOException.class));
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/UrlImplTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/UrlImplTest.java
deleted file mode 100644
index d1fcb0f..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/UrlImplTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.builder.Extension;
-import org.apache.sling.sitemap.builder.Url;
-import org.apache.sling.sitemap.builder.extensions.AbstractExtension;
-import org.apache.sling.sitemap.builder.extensions.ExtensionProvider;
-import org.apache.sling.sitemap.impl.builder.extensions.ExtensionProviderManager;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.jetbrains.annotations.NotNull;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.time.Instant;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-public class UrlImplTest {
-
- private static final Instant DATETIME = Instant.ofEpochMilli(1622122594000L);
-
- public final SlingContext context = new SlingContext();
-
- private ExtensionProviderManager extensionManager;
-
- @BeforeEach
- public void setup() {
- context.registerService(
- ExtensionProvider.class, new TestExtensionProvider(),
- "extension.interface", TestExtension.class.getName(),
- "extension.prefix", TestExtensionProvider.PREFIX,
- "extension.namespace", TestExtensionProvider.NAMESPACE,
- "extension.localName", TestExtensionProvider.LOCAL_NAME
- );
- extensionManager = context.registerInjectActivateService(new ExtensionProviderManager());
- }
-
- @Test
- public void testAddFullUrl() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- // when
- subject.addUrl("http://example.com")
- .setChangeFrequency(Url.ChangeFrequency.DAILY)
- .setPriority(0.6)
- .setLastModified(DATETIME)
- ;
-
- subject.close();
-
- // then
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
- "xmlns:tst=\"http://localhost/schema/test/1.0\">"
- + "<url>"
- + "<loc>http://example.com</loc>"
- + "<lastmod>2021-05-27T13:36:34Z</lastmod>"
- + "<changefreq>daily</changefreq>"
- + "<priority>0.6</priority>"
- + "</url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testPriorityNormalization() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- // when
- subject.addUrl("http://example.com/page1.html").setPriority(-1.0);
- subject.addUrl("http://example.com/page2.html").setPriority(5.0);
- subject.close();
-
- // then
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
- "xmlns:tst=\"http://localhost/schema/test/1.0\">"
- + "<url>"
- + "<loc>http://example.com/page1.html</loc>"
- + "<priority>0.0</priority>"
- + "</url>"
- + "<url>"
- + "<loc>http://example.com/page2.html</loc>"
- + "<priority>1.0</priority>"
- + "</url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testWithExtensions() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- // when
- Url url = subject.addUrl("http://example.com/page1.html");
- TestExtension extension = url.addExtension(TestExtension.class);
- extension.setValue("foobar");
- TestExtension2 extension2 = url.addExtension(TestExtension2.class);
- assertNull(extension2);
- subject.close();
-
- // then
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
- "xmlns:tst=\"http://localhost/schema/test/1.0\">"
- + "<url>"
- + "<loc>http://example.com/page1.html</loc>"
- + "<tst:test>"
- + "<tst:value>foobar</tst:value>"
- + "</tst:test>"
- + "</url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testWritingUrlOrExtensionTwiceFails() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl subject = new SitemapImpl(writer, extensionManager);
-
- // when
- Url url = subject.addUrl("http://example.com/page1.html");
- TestExtension extension = url.addExtension(TestExtension.class);
- extension.setValue("foobar");
- subject.close();
-
- assertThrows(IllegalStateException.class, () -> url.setPriority(0.0));
- assertThrows(IllegalStateException.class, () -> url.setChangeFrequency(Url.ChangeFrequency.ALWAYS));
- assertThrows(IllegalStateException.class, () -> url.setLastModified(Instant.now()));
- assertThrows(IllegalStateException.class, () -> url.addExtension(TestExtension.class));
- assertThrows(IllegalStateException.class, () -> subject.addUrl("http://foo.bar"));
- assertThrows(IllegalStateException.class, subject::close);
- }
-
- interface TestExtension2 extends Extension {
- TestExtension setValue(String value);
- }
-
- interface TestExtension extends Extension {
- TestExtension setValue(String value);
- }
-
- static class TestExtensionProvider implements ExtensionProvider {
-
- static String NAMESPACE = "http://localhost/schema/test/1.0";
- static String PREFIX = "tst";
- static String LOCAL_NAME = "test";
-
- @Override
- public @NotNull ExtensionImpl newInstance() {
- return new ExtensionImpl();
- }
-
- static class ExtensionImpl extends AbstractExtension implements TestExtension {
-
- private String value;
-
- @Override
- public TestExtension setValue(String value) {
- this.value = value;
- return this;
- }
-
- @Override
- public void writeTo(@NotNull XMLStreamWriter writer) throws XMLStreamException {
- writer.writeStartElement("value");
- writer.writeCharacters(value);
- writer.writeEndElement();
- }
- }
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java
deleted file mode 100644
index 5f616e9..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/builder/extensions/AlternateLanguageExtensionTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.builder.extensions;
-
-import org.apache.sling.sitemap.SitemapException;
-import org.apache.sling.sitemap.builder.Url;
-import org.apache.sling.sitemap.builder.extensions.AlternateLanguageExtension;
-import org.apache.sling.sitemap.impl.builder.SitemapImpl;
-import org.apache.sling.sitemap.impl.builder.SitemapImplTest;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.util.Locale;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@ExtendWith({SlingContextExtension.class})
-public class AlternateLanguageExtensionTest {
-
- public final SlingContext context = new SlingContext();
-
- private ExtensionProviderManager extensionProviderManager;
-
- @BeforeEach
- public void setup() {
- context.registerInjectActivateService(new AlternateLanguageExtensionProvider());
- extensionProviderManager = context.registerInjectActivateService(new ExtensionProviderManager());
- }
-
- @Test
- public void testAlternateLanguageCombinations() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
-
- // when
- Url url = sitemap.addUrl("http://example.ch/de.html");
- url.addExtension(AlternateLanguageExtension.class)
- .setLocale(Locale.forLanguageTag("fr-ch"))
- .setHref("http://example.ch/fr.html");
- url.addExtension(AlternateLanguageExtension.class)
- .setLocale(Locale.forLanguageTag("it-ch"))
- .setHref("http://example.ch/it.html");
- url.addExtension(AlternateLanguageExtension.class)
- .setDefaultLocale()
- .setHref("http://example.ch/language-chooser.html");
- sitemap.close();
-
- // then
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
- "xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">"
- + "<url>"
- + "<loc>http://example.ch/de.html</loc>"
- + "<xhtml:link rel=\"alternate\" hreflang=\"fr-CH\" href=\"http://example.ch/fr.html\"/>"
- + "<xhtml:link rel=\"alternate\" hreflang=\"it-CH\" href=\"http://example.ch/it.html\"/>"
- + "<xhtml:link rel=\"alternate\" hreflang=\"x-default\" href=\"http://example.ch/language-chooser.html\"/>"
- + "</url>"
- + "</urlset>",
- writer.toString()
- );
- }
-
- @Test
- public void testNothingWrittenWhenExtensionMissesMandatoryProperties() throws SitemapException, IOException {
- // given
- StringWriter writer = new StringWriter();
- SitemapImpl sitemap = new SitemapImpl(writer, extensionProviderManager);
-
- // when
- Url url = sitemap.addUrl("http://example.ch/de.html");
- url.addExtension(AlternateLanguageExtension.class)
- .setLocale(Locale.forLanguageTag("fr-ch"));
- url.addExtension(AlternateLanguageExtension.class)
- .setHref("http://example.ch/it.html");
- sitemap.close();
-
- // then
- assertEquals(
- SitemapImplTest.XML_HEADER + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" " +
- "xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">"
- + "<url>"
- + "<loc>http://example.ch/de.html</loc>"
- + "</url>"
- + "</urlset>",
- writer.toString()
- );
- }
-}
diff --git a/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java b/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java
deleted file mode 100644
index 2503fc3..0000000
--- a/sitemap/src/test/java/org/apache/sling/sitemap/impl/console/SitemapInventoryPluginTest.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sling.sitemap.impl.console;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
-import com.google.common.collect.ImmutableMap;
-import org.apache.commons.io.IOUtils;
-import org.apache.felix.inventory.Format;
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.commons.scheduler.Scheduler;
-import org.apache.sling.sitemap.SitemapInfo;
-import org.apache.sling.sitemap.SitemapService;
-import org.apache.sling.sitemap.impl.SitemapServiceConfiguration;
-import org.apache.sling.testing.mock.jcr.MockJcr;
-import org.apache.sling.testing.mock.sling.ResourceResolverType;
-import org.apache.sling.testing.mock.sling.junit5.SlingContext;
-import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Map;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.fail;
-import static org.mockito.Mockito.*;
-
-@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
-public class SitemapInventoryPluginTest {
-
- public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
- private final SitemapInventoryPlugin subject = new SitemapInventoryPlugin();
- private final SitemapServiceConfiguration configuration = new SitemapServiceConfiguration();
-
- // some terms are different in text then in json for better readability
- private static Map<String, String> YAML_TO_JSON = ImmutableMap.of(
- "within limits", "inlimits"
- );
-
- @Mock
- private SitemapService sitemapService;
- @Mock
- private SitemapInfo deInfo;
- @Mock
- private SitemapInfo enInfo1;
- @Mock
- private SitemapInfo enInfo2;
- @Mock
- private ServiceReference<Runnable> schedulerReference1;
- @Mock
- private ServiceReference<Runnable> schedulerReference2;
- @Mock
- private ServiceReference<Runnable> unknownReference;
- // we have to use a mock bundle as the osgi-mock one does not implement Bundle#getRegisteredServices()
- @Mock
- private Bundle bundle;
- @Mock
- private BundleContext bundleContext;
- // we have to use mock rrf in order to provide the context#resourceResolver() rr to the implementation with the
- // mocked jcr query responses
- @Mock
- private ResourceResolverFactory resourceResolverFactory;
-
- @BeforeEach
- public void setup() throws LoginException {
- ResourceResolver resourceResolver = spy(context.resourceResolver());
-
- context.registerService(SitemapService.class, sitemapService);
- context.registerInjectActivateService(configuration, "maxEntries", 999);
- context.registerService(ResourceResolverFactory.class, resourceResolverFactory, "service.ranking", 100);
- context.registerInjectActivateService(subject);
- subject.activate(bundleContext);
-
- Resource deRoot = context.create().resource("/content/site/de",
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE);
- Resource enRoot = context.create().resource("/content/site/en",
- SitemapService.PROPERTY_SITEMAP_ROOT, Boolean.TRUE);
-
- MockJcr.setQueryResult(
- context.resourceResolver().adaptTo(Session.class),
- "/jcr:root//*[@sling:sitemapRoot=true] option(index tag slingSitemaps)",
- Query.XPATH,
- Arrays.asList(deRoot.adaptTo(Node.class), enRoot.adaptTo(Node.class))
- );
-
- when(deInfo.getName()).thenReturn(SitemapService.DEFAULT_SITEMAP_NAME);
- when(deInfo.getUrl()).thenReturn("/site/de.sitemap.xml");
- when(deInfo.getStatus()).thenReturn(SitemapInfo.Status.STORAGE);
- when(deInfo.getSize()).thenReturn(1000);
- when(deInfo.getEntries()).thenReturn(10);
- when(deInfo.getStoragePath()).thenReturn("/var/sitemaps/content/site/de/sitemap.xml");
- when(enInfo1.getName()).thenReturn(SitemapService.SITEMAP_INDEX_NAME);
- when(enInfo1.getUrl()).thenReturn("/site/en.sitemap-index.xml");
- when(enInfo1.getStatus()).thenReturn(SitemapInfo.Status.ON_DEMAND);
- when(enInfo2.getName()).thenReturn(SitemapService.DEFAULT_SITEMAP_NAME);
- when(enInfo2.getUrl()).thenReturn("/site/en.sitemap.xml");
- when(enInfo2.getStatus()).thenReturn(SitemapInfo.Status.STORAGE);
- when(enInfo2.getSize()).thenReturn(10000);
- when(enInfo2.getEntries()).thenReturn(1000);
- when(enInfo2.getStoragePath()).thenReturn("/var/sitemaps/content/site/en/sitemap.xml");
-
- when(bundleContext.getBundle()).thenReturn(bundle);
- when(bundle.getRegisteredServices()).thenReturn(new ServiceReference[]{
- schedulerReference1, schedulerReference2, unknownReference
- });
- when(schedulerReference1.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME)).thenReturn("sitemap-default");
- when(schedulerReference1.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION)).thenReturn("0 0 0 * * * ?");
- when(schedulerReference2.getProperty(Scheduler.PROPERTY_SCHEDULER_NAME)).thenReturn("sitemap-news");
- when(schedulerReference2.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION)).thenReturn("0 */30 * * * * ?");
- when(resourceResolverFactory.getServiceResourceResolver(any())).thenReturn(resourceResolver);
-
- doNothing().when(resourceResolver).close();
- doReturn(Collections.singleton(deInfo))
- .when(sitemapService).getSitemapInfo(argThat(resourceWithPath(deRoot.getPath())));
- doReturn(Arrays.asList(enInfo1, enInfo2))
- .when(sitemapService).getSitemapInfo(argThat(resourceWithPath(enRoot.getPath())));
- }
-
- @Test
- public void testJson() {
- // given
- StringWriter writer = new StringWriter();
-
- // when
- subject.print(new PrintWriter(writer), Format.JSON, false);
-
- // then
- assertJson("SitemapInventoryPluginTest/inventory.json", writer.toString());
- }
-
- @Test
- public void testText() {
- // given
- StringWriter writer = new StringWriter();
-
- // when
- subject.print(new PrintWriter(writer), Format.TEXT, false);
-
- // then
- assertYaml("SitemapInventoryPluginTest/inventory.json", writer.toString());
- }
-
- private static void assertYaml(String expected, String given) {
- assertJson(new YAMLMapper(), expected, given);
- }
-
- private static void assertJson(String expected, String given) {
- assertJson(new ObjectMapper(), expected, given);
- }
-
- private static void assertJson(ObjectMapper objectMapper, String expected, String given) {
- try {
- InputStream expectedResource = SitemapInventoryPluginTest.class.getClassLoader()
- .getResourceAsStream(expected);
- StringWriter expectedContent = new StringWriter();
- IOUtils.copy(expectedResource, expectedContent, StandardCharsets.UTF_8);
- JsonNode expectedJson = objectMapper.readTree(normalizeJson(expectedContent.toString()));
- JsonNode givenJson = objectMapper.readTree(normalizeJson(given));
- assertEquals(expectedJson, givenJson);
- } catch (IOException ex) {
- fail(ex.getMessage());
- }
- }
-
- private static String normalizeJson(String json) {
- String lowercase = json.toLowerCase(Locale.ROOT);
- for (Map.Entry<String, String> entry : YAML_TO_JSON.entrySet()) {
- lowercase = lowercase.replace(entry.getKey(), entry.getValue());
- }
- return lowercase;
- }
-
- private static ArgumentMatcher<Resource> resourceWithPath(String path) {
- return r -> r.getPath().equals(path);
- }
-}
diff --git a/sitemap/src/test/resources/ResourceTreeSitemapGeneratorTest/sitetree.json b/sitemap/src/test/resources/ResourceTreeSitemapGeneratorTest/sitetree.json
deleted file mode 100644
index ecf0d1c..0000000
--- a/sitemap/src/test/resources/ResourceTreeSitemapGeneratorTest/sitetree.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "jcr:primaryType": "nt:unstructured",
- "jcr:content": {
- "jcr:primaryType": "nt:unstructured",
- "sling:sitemapRoot": true
- },
- "child1": {
- "jcr:primaryType": "nt:unstructured",
- "jcr:content": {
- "jcr:primaryType": "nt:unstructured",
- "notSupposedToBeInSitemap": {
- "jcr:primaryType": "nt:unstructured"
- }
- },
- "grandchild11": {
- "jcr:primaryType": "nt:unstructured",
- "jcr:content": {
- "jcr:primaryType": "nt:unstructured"
- }
- }
- },
- "child2": {
- "jcr:primaryType": "nt:unstructured",
- "jcr:content": {
- "jcr:primaryType": "nt:unstructured"
- }
- }
-}
\ No newline at end of file
diff --git a/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json b/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json
deleted file mode 100644
index bf93196..0000000
--- a/sitemap/src/test/resources/SitemapInventoryPluginTest/inventory.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "schedulers": [
- {
- "name": "sitemap-default",
- "expression": "0 0 0 * * * ?"
- },
- {
- "name": "sitemap-news",
- "expression": "0 */30 * * * * ?"
- }
- ],
- "roots": {
- "/content/site/de": [
- {
- "name": "<default>",
- "url": "/site/de.sitemap.xml",
- "status": "STORAGE",
- "path": "/var/sitemaps/content/site/de/sitemap.xml",
- "size": 1000,
- "urls": 10,
- "inLimits": true
- }
- ],
- "/content/site/en": [
- {
- "name": "<sitemap-index>",
- "url": "/site/en.sitemap-index.xml",
- "status": "ON_DEMAND"
- },
- {
- "name": "<default>",
- "url": "/site/en.sitemap.xml",
- "status": "STORAGE",
- "path": "/var/sitemaps/content/site/en/sitemap.xml",
- "size": 10000,
- "urls": 1000,
- "inLimits": false
- }
- ]
- }
-}
\ No newline at end of file
diff --git a/sitemap/src/test/resources/logback.xml b/sitemap/src/test/resources/logback.xml
deleted file mode 100644
index 6fed111..0000000
--- a/sitemap/src/test/resources/logback.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
--->
-<configuration>
- <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- <!-- encoders are assigned the type
- ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
- <encoder>
- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
- </encoder>
- </appender>
-
-
- <root level="WARN">
- <appender-ref ref="STDOUT" />
- </root>
-
- <logger name="org.apache.sling.sitemap" level="DEBUG"/>
-</configuration>
\ No newline at end of file