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