You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2022/03/18 18:41:00 UTC

[sling-org-apache-sling-jcr-maintenance] branch master updated (1334686 -> d028263)

This is an automated email from the ASF dual-hosted git repository.

dklco pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git.


    from 1334686  SLING-11051 - Fixing JavaDoc badge
     new 3d1793b  SLING-11211: updated maven dependencies && fixed broken links from Readme file
     new b394668  SLING-11211: updated sling-parent to version 47
     new 934e8be  SLING-11211: reverted sling.api version
     new e125143  SLING-11211: reverted oak versions
     new 8e7e576  Update pom.xml
     new d028263  Merge pull request #2 from ashokmca07/master

The 21 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md |  8 ++++----
 pom.xml   | 21 ++++++++++-----------
 2 files changed, 14 insertions(+), 15 deletions(-)

[sling-org-apache-sling-jcr-maintenance] 13/21: SLING-10676 - add or update SECURITY.md

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 71cb82b13695e91177458cc9a5e1302c056bd0c5
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Wed Jul 28 16:24:22 2021 +0200

    SLING-10676 - add or update SECURITY.md
---
 SECURITY.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/SECURITY.md b/SECURITY.md
index e69de29..2e2f930 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy for Apache Sling modules
+
+This module is part of the [Apache Sling Project](https://sling.apache.org), a
+project of the [Apache Software Foundation](https://apache.org) (ASF).
+
+It follows the ASF's [vulnerability handling process](https://apache.org/security/#vulnerability-handling) and
+provides its own [security information page](http://sling.apache.org/project-information/security.html).
+
+## Reporting a Vulnerability
+
+To report a new vulnerability you have discovered in an Apache Sling module,
+please follow the instructions on the
+[project's security page](http://sling.apache.org/project-information/security.html) .
\ No newline at end of file

[sling-org-apache-sling-jcr-maintenance] 15/21: SLING-11051 - Fixing JavaDoc badge

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 13346865c7b659161417702adede4116fffb78e6
Author: Dan Klco <kl...@adobe.com>
AuthorDate: Tue Jan 11 08:10:22 2022 -0500

    SLING-11051 - Fixing JavaDoc badge
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 6615962..40b1f88 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 [![Apache Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org)
 
-&#32;[![Build Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)&#32;[![Test Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apac [...]
+&#32;[![Build Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)&#32;[![Test Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apac [...]
 
 
 # Apache Sling JCR Maintenance

[sling-org-apache-sling-jcr-maintenance] 02/21: Removing Jacoco to avoid conflicts with Sonarqube

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit bcb1bc3dcff9fa0d0646320bca6a787a2989e680
Author: Dan Klco <kl...@users.noreply.github.com>
AuthorDate: Mon Jan 25 15:24:13 2021 -0500

    Removing Jacoco to avoid conflicts with Sonarqube
---
 pom.xml | 20 --------------------
 1 file changed, 20 deletions(-)

diff --git a/pom.xml b/pom.xml
index 15a2dfd..ddac040 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,26 +52,6 @@
                 </configuration>
             </plugin>
             <plugin>
-                <groupId>org.jacoco</groupId>
-                <artifactId>jacoco-maven-plugin</artifactId>
-                <version>0.8.2</version>
-                <executions>
-                    <execution>
-                        <goals>
-                            <goal>prepare-agent</goal>
-                        </goals>
-                    </execution>
-                    <!-- attached to Maven test phase -->
-                    <execution>
-                        <id>report</id>
-                        <phase>test</phase>
-                        <goals>
-                            <goal>report</goal>
-                        </goals>
-                    </execution>
-                </executions>
-            </plugin>
-            <plugin>
                 <groupId>org.apache.sling</groupId>
                 <artifactId>slingfeature-maven-plugin</artifactId>
                 <version>1.4.18</version>

[sling-org-apache-sling-jcr-maintenance] 01/21: SLING-9985: Migrating JCR Maintenance code from whiteboard

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 82bb34d76a7dfa6d65e5d3546eb3c83b00cf1be1
Author: Dan Klco <da...@perficient.com>
AuthorDate: Tue Jan 19 11:16:26 2021 -0500

    SLING-9985: Migrating JCR Maintenance code from whiteboard
---
 .gitignore                                         |  26 +++
 CODE_OF_CONDUCT.md                                 |  22 ++
 CONTRIBUTING.md                                    |  30 +++
 Jenkinsfile                                        |  20 ++
 NOTICE                                             |   5 +
 README.md                                          |  30 +++
 pom.xml                                            | 221 +++++++++++++++++++
 src/main/features/base.json                        |  22 ++
 src/main/features/configuration.json               |  17 ++
 .../jcr/maintenance/DataStoreCleanupConfig.java    |  31 +++
 .../jcr/maintenance/RepositoryManagementUtil.java  |  53 +++++
 .../jcr/maintenance/RevisionCleanupConfig.java     |  31 +++
 .../apache/sling/jcr/maintenance/RunnableJob.java  |  24 +++
 .../jcr/maintenance/VersionCleanupConfig.java      |  28 +++
 .../jcr/maintenance/VersionCleanupPathConfig.java  |  37 ++++
 .../internal/DataStoreCleanupScheduler.java        |  68 ++++++
 .../internal/RepositoryMaintenanceHealthCheck.java | 104 +++++++++
 .../internal/RevisionCleanupScheduler.java         |  67 ++++++
 .../jcr/maintenance/internal/VersionCleanup.java   | 238 +++++++++++++++++++++
 .../maintenance/internal/VersionCleanupMBean.java  |  47 ++++
 .../maintenance/internal/VersionCleanupPath.java   |  91 ++++++++
 src/main/resources/OSGI-INF/l10n/bundle.properties |  56 +++++
 .../sling/jcr/maintenance/CompositeDataMock.java   |  55 +++++
 .../internal/DataStoreCleanupSchedulerTest.java    |  91 ++++++++
 .../RepositoryMaintenanceHealthCheckTest.java      | 168 +++++++++++++++
 .../internal/RevisionCleanupSchedulerTest.java     |  91 ++++++++
 .../internal/VersionCleanupPathTest.java           |  94 ++++++++
 .../maintenance/internal/VersionCleanupTest.java   | 216 +++++++++++++++++++
 src/test/resources/nodetypes.cnd                   |  89 ++++++++
 src/test/resources/version-content.json            |  44 ++++
 30 files changed, 2116 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..230229a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+*.jar
+.metadata
+RemoteSystemsTempFiles
+target
+.grunt
+.idea
+.classpath
+.project
+.settings
+bin
+.vlt
+.DS_Store
+.vlt-sync-config.properties
+.vlt-sync.log
+.brackets.json
+.metadata/
+.vagrant/
+*.iml
+node_modules/
+node/
+.vscode/
+pom.xml.releaseBackup
+pom.xml.tag
+release.properties
+pom.xml.next
+.java-version
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..0fa18e5
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,22 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Apache Software Foundation Code of Conduct
+====
+
+Being an Apache project, Apache Sling adheres to the Apache Software Foundation's [Code of Conduct](https://www.apache.org/foundation/policies/conduct.html).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..880019b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+<!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/-->
+Contributing
+====
+
+Thanks for choosing to contribute!
+
+Here's some great places to get started:
+
+ - [Backlog](https://issues.apache.org/jira/issues/?jql=project%20%3D%20SLING%20AND%20status%20%3D%20Open%20AND%20component%20in%20(%22App%20CMS%22%2C%20%22App%20CMS%20Reference%22))
+ - [Good Starting Issues](https://issues.apache.org/jira/browse/SLING-8910?jql=project%20%3D%20SLING%20AND%20status%20%3D%20Open%20AND%20component%20in%20(%22App%20CMS%22%2C%20%22App%20CMS%20Reference%22)%20AND%20labels%20%3D%20newbie)
+
+
+You will find all the necessary details about how you can do this at https://sling.apache.org/contributing.html.
diff --git a/Jenkinsfile b/Jenkinsfile
new file mode 100644
index 0000000..f582519
--- /dev/null
+++ b/Jenkinsfile
@@ -0,0 +1,20 @@
+/**
+ * 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.
+ */
+
+slingOsgiBundleBuild()
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..5de96ff
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,5 @@
+Apache Sling JCR Maintenance
+Copyright 2021 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6615962
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+[![Apache Sling](https://sling.apache.org/res/logos/sling.png)](https://sling.apache.org)
+
+&#32;[![Build Status](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/badge/icon)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)&#32;[![Test Status](https://img.shields.io/jenkins/tests.svg?jobUrl=https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apache-sling-jcr-maintenance/job/master/)](https://ci-builds.apache.org/job/Sling/job/modules/job/sling-org-apac [...]
+
+
+# Apache Sling JCR Maintenance
+
+This project provides reference implementation of Maintenance jobs for maintaining a Apache Jackrabbit OAK repository in Apache Sling.
+
+This includes the following Maintenance jobs:
+
+- [DataStoreCleanupScheduler](src/main/java/org/apache/sling/maintenance/internal/DataStoreCleanupScheduler.java) - Run the [RepositoryManagementMBean.startDataStoreGC(true)](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startDataStoreGC-boolean-) method to perform a Garbage Collection of the Data Store
+- [RevisionCleanupScheduler](src/main/java/org/apache/sling/maintenance/internal/RevisionCleanupScheduler.java) - Run the [RepositoryManagementMBean.startRevisionGC()](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startRevisionGC--) method to perform a Garbage Collection of the Revision Store
+- [VersionCleanup](src/main/java/org/apache/sling/maintenance/internal/VersionCleanup.java) - Job to traverse the JCR Version Store
+  and remove versions (oldest-first) exceeding a configurable limit
+
+As well as a [Health Check](src/main/java/org/apache/sling/maintenance/internal/RepositoryMaintenanceHealthCheck.java) to ensure the jobs are scheduled and have not failed.
+
+## Configuration
+
+To see a reference implementation, see the [Configuration Feature](src/main/features/configuration.json).
+
+## Features
+
+There are two primary features made by this project include:
+
+- **Base** - org.apache.sling:org.apache.sling.jcr.maintenance:slingosgifeature:base:${project.version} - only the bundle and service user
+- **Default** - org.apache.sling:org.apache.sling.jcr.maintenance:slingosgifeature:default:${project.version} - the bundle, service user and default configuration which keeps 5 versions and runs the jobs every night
+
+This module is part of the [Apache Sling](https://sling.apache.org) project.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..15a2dfd
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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>40</version>
+        <relativePath />
+    </parent>
+    <artifactId>org.apache.sling.jcr.maintenance</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <name>Apache Sling JCR Maintenance</name>
+    <description>Maintenance jobs for the JCR</description>
+
+    <properties>
+        <bnd.baseline.fail.on.missing>false</bnd.baseline.fail.on.missing>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>biz.aQute.bnd</groupId>
+                <artifactId>bnd-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <useSystemClassLoader>false</useSystemClassLoader>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.jacoco</groupId>
+                <artifactId>jacoco-maven-plugin</artifactId>
+                <version>0.8.2</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>prepare-agent</goal>
+                        </goals>
+                    </execution>
+                    <!-- attached to Maven test phase -->
+                    <execution>
+                        <id>report</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>report</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>slingfeature-maven-plugin</artifactId>
+                <version>1.4.18</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <framework>
+                        <groupId>org.apache.felix</groupId>
+                        <artifactId>org.apache.felix.framework</artifactId>
+                        <version>6.0.3</version>
+                    </framework>
+                    <aggregates>
+                        <aggregate>
+                            <classifier>default</classifier>
+                            <filesInclude>**/*.json</filesInclude>
+                            <title>Apache Sling JCR Maintenance - Default</title>
+                        </aggregate>
+                    </aggregates>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>attach-features</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>aggregate-features</goal>
+                            <goal>attach-features</goal>
+                        </goals>
+                        <configuration>
+                            <replacePropertyVariables>project.version</replacePropertyVariables>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <version>1.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <version>2.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Apache Dependencies -->
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.healthcheck.api</artifactId>
+            <version>2.0.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-atinject_1.0_spec</artifactId>
+            <version>1.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>oak-core</artifactId>
+            <version>1.8.8</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>oak-jcr</artifactId>
+            <version>1.8.8</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.18.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <version>20.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+
+        <!-- OSGi Dependencies -->
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.component.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.service.metatype.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.annotation</artifactId>
+            <version>6.0.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
+            <version>2.6.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.sling-mock-oak</artifactId>
+            <version>2.1.10-1.16.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>3.6.28</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/features/base.json b/src/main/features/base.json
new file mode 100644
index 0000000..e4830f1
--- /dev/null
+++ b/src/main/features/base.json
@@ -0,0 +1,22 @@
+{
+    "bundles": [
+        {
+            "id": "org.apache.sling:org.apache.sling.jcr.maintenance:${project.version}",
+            "start-order": "20"
+        }
+    ],
+    "configurations":{
+        "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling-versionmgr":{
+            "user.mapping":[
+                "org.apache.sling.jcr.maintenance:sling-versionmgr=sling-versionmgr"
+            ]
+        }
+    },
+    "repoinit:TEXT|true": [
+        "create service user sling-versionmgr",
+        "set ACL for sling-versionmgr",
+        "allow   jcr:write,jcr:nodeTypeManagement,jcr:versionManagement    on /",
+        "allow   jcr:read    on /jcr:system/jcr:versionStorage",
+        "end"
+    ]
+}
\ No newline at end of file
diff --git a/src/main/features/configuration.json b/src/main/features/configuration.json
new file mode 100644
index 0000000..9fc1eef
--- /dev/null
+++ b/src/main/features/configuration.json
@@ -0,0 +1,17 @@
+{
+    "configurations": {
+        "org.apache.sling.jcr.maintenance.internal.DataStoreCleanupScheduler": {
+            "scheduler.expression": "0 0 2 ? * *"
+        },
+        "org.apache.sling.jcr.maintenance.internal.RevisionCleanupScheduler": {
+            "scheduler.expression": "0 0 2 ? * *"
+        },
+        "org.apache.sling.jcr.maintenance.internal.VersionCleanup": {
+            "scheduler.expression": "0 0 2 ? * *"
+        },
+        "org.apache.sling.jcr.maintenance.internal.VersionCleanupPath~default": {
+            "path": "/",
+            "limit": 5
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/DataStoreCleanupConfig.java b/src/main/java/org/apache/sling/jcr/maintenance/DataStoreCleanupConfig.java
new file mode 100644
index 0000000..3374465
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/DataStoreCleanupConfig.java
@@ -0,0 +1,31 @@
+/*
+ * 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.jcr.maintenance;
+
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+
+/**
+ * Configuration for the DataStore Cleanup Service
+ */
+@ObjectClassDefinition(name = "%datastore.cleanup.name", description = "%datastore.cleanup.description", localization = "OSGI-INF/l10n/bundle")
+public @interface DataStoreCleanupConfig {
+
+    @AttributeDefinition(name = "%scheduler.expression.name", description = "%scheduler.expression.description")
+    String scheduler_expression();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/RepositoryManagementUtil.java b/src/main/java/org/apache/sling/jcr/maintenance/RepositoryManagementUtil.java
new file mode 100644
index 0000000..e6548c2
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/RepositoryManagementUtil.java
@@ -0,0 +1,53 @@
+/*
+ * 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.jcr.maintenance;
+
+import java.util.Arrays;
+
+import javax.management.openmbean.CompositeData;
+
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean.StatusCode;
+
+/**
+ * Utilities for interacting with the RepositoryManagementMBean
+ * 
+ * @see org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean
+ */
+public class RepositoryManagementUtil {
+
+    private RepositoryManagementUtil() {
+    }
+
+    public static boolean isRunning(CompositeData status) {
+        return StatusCode.RUNNING == getStatusCode(status);
+    }
+
+    public static boolean isValid(CompositeData status) {
+        StatusCode statusCode = getStatusCode(status);
+        return statusCode != StatusCode.UNAVAILABLE && statusCode != StatusCode.FAILED;
+    }
+
+    public static StatusCode getStatusCode(CompositeData status) {
+        int c = ((Integer) status.get("code"));
+        return Arrays.stream(StatusCode.values()).filter(sc -> sc.ordinal() == c).findFirst().orElse(StatusCode.NONE);
+    }
+
+    public static String getMessage(CompositeData status) {
+        return ((String) status.get("message"));
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/RevisionCleanupConfig.java b/src/main/java/org/apache/sling/jcr/maintenance/RevisionCleanupConfig.java
new file mode 100644
index 0000000..4fe54fe
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/RevisionCleanupConfig.java
@@ -0,0 +1,31 @@
+/*
+ * 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.jcr.maintenance;
+
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+
+/**
+ * Configuration for the Reference Mapping Transformer
+ */
+@ObjectClassDefinition(name = "%revision.cleanup.name", description = "%revision.cleanup.description", localization = "OSGI-INF/l10n/bundle")
+public @interface RevisionCleanupConfig {
+
+    @AttributeDefinition(name = "%scheduler.expression.name", description = "%scheduler.expression.description")
+    String scheduler_expression();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/RunnableJob.java b/src/main/java/org/apache/sling/jcr/maintenance/RunnableJob.java
new file mode 100644
index 0000000..137ad6e
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/RunnableJob.java
@@ -0,0 +1,24 @@
+/*
+ * 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.jcr.maintenance;
+
+/**
+ * Interface to add a method to get the scheduler expression for a runnable job
+ */
+public interface RunnableJob extends Runnable {
+    String getSchedulerExpression();
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupConfig.java b/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupConfig.java
new file mode 100644
index 0000000..4a0c9ae
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupConfig.java
@@ -0,0 +1,28 @@
+/*
+ * 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.jcr.maintenance;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@ObjectClassDefinition(name = "%version.cleanup.name", description = "%version.cleanup.description", localization = "OSGI-INF/l10n/bundle")
+public @interface VersionCleanupConfig {
+
+    @AttributeDefinition(name = "%scheduler.expression.name", description = "%scheduler.expression.description")
+    String scheduler_expression();
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupPathConfig.java b/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupPathConfig.java
new file mode 100644
index 0000000..8f76745
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/VersionCleanupPathConfig.java
@@ -0,0 +1,37 @@
+/*
+ * 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.jcr.maintenance;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+/**
+ * Configuration to configure how version cleanup works on a per-path basis
+ */
+@ObjectClassDefinition(name = "%version.cleanup.path.name", description = "%version.cleanup.path.description", localization = "OSGI-INF/l10n/bundle")
+public @interface VersionCleanupPathConfig {
+
+    @AttributeDefinition(name = "%version.path.name", description = "%version.path.description")
+    String path();
+
+    @AttributeDefinition(name = "%version.limit.name", description = "%version.limit.description")
+    int limit();
+
+    @AttributeDefinition(name = "%version.keepVersions.name", description = "%version.keepVersions.description")
+    boolean keepVersions();
+
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupScheduler.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupScheduler.java
new file mode 100644
index 0000000..ba59feb
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupScheduler.java
@@ -0,0 +1,68 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.sling.jcr.maintenance.DataStoreCleanupConfig;
+import org.apache.sling.jcr.maintenance.RepositoryManagementUtil;
+import org.apache.sling.jcr.maintenance.RunnableJob;
+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.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Service for running the Jackrabbit OAK Blob Store cleanup on a schedule.
+ */
+@Component(service = { Runnable.class }, property = {
+        "scheduler.concurrent:Boolean=false" }, configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true)
+@Designate(ocd = DataStoreCleanupConfig.class)
+public class DataStoreCleanupScheduler implements RunnableJob {
+
+    private static final Logger log = LoggerFactory.getLogger(DataStoreCleanupScheduler.class);
+
+    private final RepositoryManagementMBean repositoryManager;
+
+    private final String schedulerExpression;
+
+    @Activate
+    public DataStoreCleanupScheduler(final DataStoreCleanupConfig config,
+            @Reference final RepositoryManagementMBean repositoryManager) {
+        this.repositoryManager = repositoryManager;
+        this.schedulerExpression = config.scheduler_expression();
+    }
+
+    public void run() {
+        if (!RepositoryManagementUtil.isRunning(repositoryManager.getDataStoreGCStatus())) {
+            log.info("Starting DataStore Garbage Collection");
+            repositoryManager.startDataStoreGC(false);
+        } else {
+            log.warn("DataStore Garbage Collection already running!");
+        }
+    }
+
+    /**
+     * @return the schedulerExpression
+     */
+    public String getSchedulerExpression() {
+        return schedulerExpression;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheck.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheck.java
new file mode 100644
index 0000000..d6b1ad6
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheck.java
@@ -0,0 +1,104 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import javax.management.openmbean.CompositeData;
+
+import org.apache.felix.hc.api.FormattingResultLog;
+import org.apache.felix.hc.api.HealthCheck;
+import org.apache.felix.hc.api.Result;
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.sling.jcr.maintenance.RepositoryManagementUtil;
+import org.apache.sling.jcr.maintenance.RunnableJob;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+
+@Component(service = HealthCheck.class, property = { HealthCheck.TAGS + "=oak", HealthCheck.TAGS + "=system-resource",
+        HealthCheck.NAME + "=Apache Sling JCR Maintenance" }, immediate = true)
+public class RepositoryMaintenanceHealthCheck implements HealthCheck {
+
+    private DataStoreCleanupScheduler dataStoreCleanupScheduler;
+
+    private RevisionCleanupScheduler revisionCleanupScheduler;
+
+    private RepositoryManagementMBean repositoryManagementMBean;
+
+    private VersionCleanupMBean versionCleanup;
+
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY, service = Runnable.class, target = "(component.name=org.apache.sling.jcr.maintenance.internal.DataStoreCleanupScheduler)")
+    public void setDataStoreCleanupScheduler(Runnable dataStoreCleanupScheduler) {
+        this.dataStoreCleanupScheduler = (DataStoreCleanupScheduler) dataStoreCleanupScheduler;
+    }
+
+    @Reference
+    public void setRepositoryManagementMBean(RepositoryManagementMBean repositoryManagementMBean) {
+        this.repositoryManagementMBean = repositoryManagementMBean;
+    }
+
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY, service = Runnable.class, target = "(component.name=org.apache.sling.jcr.maintenance.internal.RevisionCleanupScheduler)")
+    public void setRevisionCleanupScheduler(Runnable revisionCleanupScheduler) {
+        this.revisionCleanupScheduler = (RevisionCleanupScheduler) revisionCleanupScheduler;
+    }
+
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policyOption = ReferencePolicyOption.GREEDY)
+    public void setVersionCleanup(VersionCleanupMBean versionCleanup) {
+        this.versionCleanup = versionCleanup;
+    }
+
+    private void evaluateJobStatus(FormattingResultLog log, String jobName, RunnableJob job, CompositeData status) {
+        if (job != null) {
+            log.debug("{} Schedule: {}", jobName, job.getSchedulerExpression());
+        } else {
+            log.warn("{} not registered", jobName);
+        }
+        if (RepositoryManagementUtil.isValid(status)) {
+            log.debug("{} Last Status: {}", jobName, RepositoryManagementUtil.getStatusCode(status).name());
+            log.debug("{} Last Message: {}", jobName, RepositoryManagementUtil.getMessage(status));
+        } else {
+            log.critical("{} Last Status: {}", jobName, RepositoryManagementUtil.getStatusCode(status).name());
+            log.critical("{} Last Message: {}", jobName, RepositoryManagementUtil.getMessage(status));
+        }
+    }
+
+    @Override
+    public Result execute() {
+        FormattingResultLog log = new FormattingResultLog();
+
+        evaluateJobStatus(log, "DataStoreCleanupScheduler", dataStoreCleanupScheduler,
+                repositoryManagementMBean.getDataStoreGCStatus());
+
+        evaluateJobStatus(log, "RevisionCleanupScheduler", revisionCleanupScheduler,
+                repositoryManagementMBean.getRevisionGCStatus());
+
+        if (versionCleanup != null) {
+            if (versionCleanup.isFailed()) {
+                log.critical("VersionCleanup Status: FAILED");
+                log.critical("VersionCleanup Message: {}", versionCleanup.getLastMessage());
+            } else {
+                log.debug("VersionCleanup Status: SUCCEEDED");
+            }
+            log.debug("VersionCleanup Last Cleaned: {}", versionCleanup.getLastCleanedVersionsCount());
+        } else {
+            log.warn("VersionCleanup not registered");
+        }
+
+        return new Result(log);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupScheduler.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupScheduler.java
new file mode 100644
index 0000000..0a81d0e
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupScheduler.java
@@ -0,0 +1,67 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.sling.jcr.maintenance.RepositoryManagementUtil;
+import org.apache.sling.jcr.maintenance.RevisionCleanupConfig;
+import org.apache.sling.jcr.maintenance.RunnableJob;
+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.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Service for running the Jackrabbit OAK Segment Store cleanup on a schedule.
+ */
+@Component(service = { Runnable.class }, property = {
+        "scheduler.concurrent:Boolean=false" }, configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true)
+@Designate(ocd = RevisionCleanupConfig.class)
+public class RevisionCleanupScheduler implements RunnableJob {
+
+    private static final Logger log = LoggerFactory.getLogger(RevisionCleanupScheduler.class);
+
+    private final RepositoryManagementMBean repositoryManager;
+
+    private final String schedulerExpression;
+
+    @Activate
+    public RevisionCleanupScheduler(final RevisionCleanupConfig config,
+            @Reference final RepositoryManagementMBean repositoryManager) {
+        this.repositoryManager = repositoryManager;
+        this.schedulerExpression = config.scheduler_expression();
+    }
+
+    public void run() {
+        if (!RepositoryManagementUtil.isRunning(repositoryManager.getRevisionGCStatus())) {
+            log.info("Starting Revision Garbage Collection");
+            repositoryManager.startRevisionGC();
+        } else {
+            log.warn("Revision Garbage Collection already running!");
+        }
+    }
+
+    /**
+     * @return the schedulerExpression
+     */
+    public String getSchedulerExpression() {
+        return schedulerExpression;
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java
new file mode 100644
index 0000000..31a7372
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java
@@ -0,0 +1,238 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionIterator;
+import javax.jcr.version.VersionManager;
+import javax.management.DynamicMBean;
+
+import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
+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.jcr.maintenance.VersionCleanupConfig;
+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.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ */
+@Component(service = { VersionCleanupMBean.class, Runnable.class, DynamicMBean.class }, property = {
+        "jmx.objectname=org.apache.sling.jcr.maintenance:type=VersionCleanup",
+        "scheduler.concurrent:Boolean=false" }, configurationPolicy = ConfigurationPolicy.REQUIRE, immediate = true)
+@Designate(ocd = VersionCleanupConfig.class)
+public class VersionCleanup extends AnnotatedStandardMBean implements Runnable, VersionCleanupMBean {
+
+    private static final Logger log = LoggerFactory.getLogger(VersionCleanup.class);
+
+    private Thread cleanupThread;
+    private final ResourceResolverFactory factory;
+    private long lastCleanedVersions;
+    private String lastFailureMessage;
+    private final List<VersionCleanupPath> versionCleanupConfigs;
+
+    @Activate
+    public VersionCleanup(
+            @Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE, policyOption = ReferencePolicyOption.GREEDY) final List<VersionCleanupPath> versionCleanupConfigs,
+            @Reference final ResourceResolverFactory factory) {
+        super(VersionCleanupMBean.class);
+        this.factory = factory;
+        this.versionCleanupConfigs = versionCleanupConfigs;
+        versionCleanupConfigs.sort((c1, c2) -> c1.getPath().compareTo(c2.getPath()) * -1);
+
+    }
+
+    private String getPath(final Session session, final VersionHistory versionHistory) throws RepositoryException {
+        String identifier = versionHistory.getVersionableIdentifier();
+        try {
+            Node versionableNode = session.getNodeByIdentifier(identifier);
+            return versionableNode.getPath();
+        } catch (ItemNotFoundException infe) {
+            log.debug("Unable to get versionable node by ID: {}, exception: {}", identifier, infe.getMessage());
+            return versionHistory.getProperty(session.getWorkspace().getName()).getString();
+        }
+    }
+
+    private void cleanupVersions(final Session session, final Resource history) {
+        try {
+            final VersionHistory versionHistory = (VersionHistory) session.getItem(history.getPath());
+            final String path = getPath(session, versionHistory);
+            final VersionCleanupPath config = VersionCleanupPath.getMatchingConfiguration(this.versionCleanupConfigs,
+                    path);
+            int limit = config.getLimit();
+
+            if (!isMatchingVersion(session, path, versionHistory) && !config.isKeepVersions() && limit > 0) {
+                log.debug("Deleted, removing all but last version");
+                limit = 1;
+            }
+            log.debug("Cleaning up versions for: {}", versionHistory.getPath());
+            final VersionIterator versionIterator = versionHistory.getAllVersions();
+            final List<String> versionNames = new ArrayList<>();
+            while (versionIterator.hasNext()) {
+                final Version version = versionIterator.nextVersion();
+                if (!version.getName().equals("jcr:rootVersion")) {
+                    versionNames.add(version.getName());
+                }
+            }
+            if (versionNames.size() > limit) {
+                final List<String> toCleanup = versionNames.subList(0, versionNames.size() - limit);
+                log.info("Cleaning up {} versions from {} at: {}", toCleanup.size(), path, versionHistory.getPath());
+                for (final String item : toCleanup) {
+                    versionHistory.removeVersion(item);
+                    log.trace("Cleaned up: {}", item);
+                    lastCleanedVersions++;
+                }
+            }
+        } catch (final RepositoryException re) {
+            log.warn("Failed to cleanup version history for: {}", history.getPath(), re);
+        }
+
+    }
+
+    private void findVersions(final Session session, final Resource resource)
+            throws RepositoryException, InterruptedException {
+        if (Thread.interrupted()) {
+            throw new InterruptedException("Process interrupted");
+        }
+        log.debug("Finding versions under: {}", resource.getPath());
+        if ("nt:versionHistory".equals(resource.getResourceType())) {
+            resource.getResourceResolver().refresh();
+            cleanupVersions(session, resource);
+        } else {
+            for (final Resource child : resource.getChildren()) {
+                findVersions(session, child);
+            }
+        }
+    }
+
+    private boolean isMatchingVersion(Session session, String path, VersionHistory versionHistory)
+            throws RepositoryException {
+        try {
+            VersionManager versionManager = session.getWorkspace().getVersionManager();
+            String baseVersionPath = versionManager.getBaseVersion(path).getParent().getPath();
+            String versionHistoryPath = versionHistory.getPath();
+
+            return session.nodeExists(path) && isVersionable(session.getNode(path))
+                    && baseVersionPath.equals(versionHistoryPath);
+        } catch (PathNotFoundException pnfe) {
+            log.debug("Path: {} not found: {}", path, pnfe.getMessage());
+            return false;
+        }
+    }
+
+    private boolean isVersionable(final Node node) throws RepositoryException {
+        return node != null && node.isNodeType("{http://www.jcp.org/jcr/mix/1.0}versionable");
+    }
+
+    @Override
+    public void run() {
+        if (isRunning()) {
+            log.warn("Version cleanup already running!");
+        } else {
+            cleanupThread = new Thread((this::doRun));
+            cleanupThread.setDaemon(true);
+            cleanupThread.start();
+        }
+    }
+
+    private void doRun() {
+        log.info("Running version cleanup");
+        boolean interrupted = false;
+        boolean succeeded = false;
+        String failureMessage = null;
+        lastCleanedVersions = 0;
+        try {
+            try (final ResourceResolver adminResolver = factory.getServiceResourceResolver(
+                    Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, "sling-versionmgr"))) {
+                final Resource versionRoot = adminResolver.getResource("/jcr:system/jcr:versionStorage");
+                final Session session = Optional.ofNullable(versionRoot.getResourceResolver().adaptTo(Session.class))
+                        .orElseThrow(() -> new RepositoryException("Failed to get session"));
+                for (final Resource folder : versionRoot.getChildren()) {
+                    log.info("Traversing and cleaning: {}", folder.getPath());
+                    findVersions(session, folder);
+                }
+                succeeded = true;
+            }
+        } catch (final LoginException le) {
+            log.error("Failed to run version cleanup, cannot get service user", le);
+            failureMessage = "Failed to run version cleanup, cannot get service user";
+        } catch (final RepositoryException re) {
+            log.error("Failed to run version cleanup", re);
+            failureMessage = "Failed to run version cleanup";
+        } catch (final InterruptedException e) { // no need to do anything, at this point nearly done
+            log.info("Process interrupted, quitting");
+            interrupted = true;
+        } finally {
+            if (succeeded) {
+                this.lastFailureMessage = null;
+            } else if (!interrupted) {
+                lastFailureMessage = failureMessage != null ? failureMessage
+                        : "Failed due to unexpected exception, see logs";
+            }
+        }
+    }
+
+    @Override
+    public boolean isRunning() {
+        return cleanupThread != null && cleanupThread.isAlive();
+    }
+
+    @Override
+    public boolean isFailed() {
+        return lastFailureMessage != null;
+    }
+
+    @Override
+    public String getLastMessage() {
+        return lastFailureMessage;
+    }
+
+    @Override
+    public long getLastCleanedVersionsCount() {
+        return lastCleanedVersions;
+    }
+
+    @Override
+    public void start() {
+        this.run();
+    }
+
+    @Override
+    public void stop() {
+        Optional.ofNullable(cleanupThread).ifPresent(Thread::interrupt);
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupMBean.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupMBean.java
new file mode 100644
index 0000000..343bdf5
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupMBean.java
@@ -0,0 +1,47 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import org.apache.jackrabbit.oak.api.jmx.Description;
+
+/**
+ * JMX MBean interface for the version cleanup tool to enable introspection into
+ * the state of
+ */
+
+@Description("Cleanup versions")
+public interface VersionCleanupMBean {
+
+    @Description("Whether or not the service is running")
+    boolean isRunning();
+
+    @Description("Whether or not the service is failed")
+    boolean isFailed();
+
+    @Description("The last message")
+    String getLastMessage();
+
+    @Description("The count of the last cleaned versions")
+    long getLastCleanedVersionsCount();
+
+    @Description("Start running the job, will stop any running instances")
+    void start();
+
+    @Description("Stop the running instance or do nothing")
+    void stop();
+
+}
diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPath.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPath.java
new file mode 100644
index 0000000..07bd3c3
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPath.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.jcr.maintenance.VersionCleanupPathConfig;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.metatype.annotations.Designate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = VersionCleanupPath.class, immediate = true)
+@Designate(ocd = VersionCleanupPathConfig.class, factory = true)
+public class VersionCleanupPath implements Comparable<VersionCleanupPath> {
+
+    private static final Logger log = LoggerFactory.getLogger(VersionCleanupPath.class);
+
+    private final boolean keepVersions;
+    private final int limit;
+    private final String path;
+
+    @Activate
+    public VersionCleanupPath(VersionCleanupPathConfig config) {
+        this.keepVersions = config.keepVersions();
+        this.limit = config.limit();
+        this.path = config.path();
+    }
+
+    @Override
+    public int compareTo(VersionCleanupPath o) {
+        return path.compareTo(o.path) * -1;
+    }
+
+    /**
+     * @return the keepVersions
+     */
+    public boolean isKeepVersions() {
+        return keepVersions;
+    }
+
+    /**
+     * @return the limit
+     */
+    public int getLimit() {
+        return limit;
+    }
+
+    /**
+     * @return the path
+     */
+    public String getPath() {
+        return path;
+    }
+
+    public static final VersionCleanupPath getMatchingConfiguration(
+            final List<VersionCleanupPath> versionCleanupConfigs, final String path) throws RepositoryException {
+        log.trace("Evaluating configurations {} for path {}", versionCleanupConfigs, path);
+        return versionCleanupConfigs.stream().filter(c -> path.startsWith(c.getPath())).findFirst()
+                .orElseThrow(() -> new RepositoryException("Failed to find version cleanup configuration for " + path));
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#toString()
+     */
+
+    @Override
+    public String toString() {
+        return "VersionCleanupPath [keepVersions=" + keepVersions + ", limit=" + limit + ", path=" + path + "]";
+    }
+
+}
diff --git a/src/main/resources/OSGI-INF/l10n/bundle.properties b/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..3f8f0f3
--- /dev/null
+++ b/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,56 @@
+#
+#  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 file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the Sling SCR plugin
+
+## Global Entries
+scheduler.expression.name=Quartz Scheduler Expression
+scheduler.expression.description=A quartz expression for configuring when \
+a scheduler should be triggered
+
+# DataStore Cleanup Entries
+datastore.cleanup.name=Apache Sling JCR Maintenance Oak DataStore Garbage Collection
+datastore.cleanup.description=A scheduler to initiate a Data Store garbage collection operation \
+the Jackrabbit OAK repository
+
+# Revision Cleanup Entries
+revision.cleanup.name=Apache Sling JCR Maintenance Revision Garbage Collection
+revision.cleanup.description=A scheduler to initiate a revision garbage collection operation \
+the Jackrabbit OAK repository
+
+version.cleanup.name=Apache Sling JCR Maintenance Version Cleanup
+version.cleanup.description=A scheduler and service to cleanup JCR versions
+
+version.cleanup.path.name=Apache Sling JCR Maintenance Version Cleanup Path Configuration
+version.cleanup.description=A configuration for how to perform version cleanup based on the path \
+inside the repository
+
+version.keepVersions.name=Keep Deleted Versions
+version.keepVersions.description=If true, versions will be kept even if the associated content has been deleted \
+or orphaned, if not only the latest version of deleted or orphaned content will be kept
+
+version.limit.name=Version Limit
+version.limit.description=The number of versions to keep, any additional versions beyond this number will be deleted \
+with the oldest first
+
+version.path.name=Path
+version.path.description=The path for which this configuration applies
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/CompositeDataMock.java b/src/test/java/org/apache/sling/jcr/maintenance/CompositeDataMock.java
new file mode 100644
index 0000000..abb7ca7
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/CompositeDataMock.java
@@ -0,0 +1,55 @@
+/*
+ * 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.jcr.maintenance;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.management.openmbean.CompositeData;
+
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * A mock for creating a fluent API version of a CompositeData Object.
+ */
+public class CompositeDataMock {
+
+    private Map<String, Object> data = new HashMap<>();
+
+    public static CompositeDataMock init() {
+        return new CompositeDataMock();
+    }
+
+    public CompositeDataMock put(String key, Object value) {
+        this.data.put(key, value);
+        return this;
+    }
+
+    public CompositeData build() {
+        CompositeData dc = Mockito.mock(CompositeData.class);
+        Mockito.when(dc.get(Mockito.anyString())).thenAnswer(new Answer<Object>() {
+            @Override
+            public Object answer(InvocationOnMock invocation) throws Throwable {
+                return data.get(invocation.getArguments()[0]);
+            }
+        });
+        return dc;
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupSchedulerTest.java b/src/test/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupSchedulerTest.java
new file mode 100644
index 0000000..014ae38
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupSchedulerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+
+import java.lang.annotation.Annotation;
+
+import javax.management.openmbean.CompositeData;
+
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean.StatusCode;
+import org.apache.sling.jcr.maintenance.CompositeDataMock;
+import org.apache.sling.jcr.maintenance.DataStoreCleanupConfig;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class DataStoreCleanupSchedulerTest {
+
+    @Test
+    public void testRunnable() {
+
+        Integer id = 1;
+        final RepositoryManagementMBean repositoryManager = Mockito.mock(RepositoryManagementMBean.class);
+        CompositeData startingCd = CompositeDataMock.init().put("id", id).build();
+        Mockito.when(repositoryManager.startDataStoreGC(false)).thenReturn(startingCd);
+        CompositeData doneCd = CompositeDataMock.init().put("id", id).put("code", StatusCode.SUCCEEDED.ordinal())
+                .build();
+        Mockito.when(repositoryManager.getDataStoreGCStatus()).thenReturn(doneCd);
+        final DataStoreCleanupScheduler dscs = new DataStoreCleanupScheduler(Mockito.mock(DataStoreCleanupConfig.class),
+                repositoryManager);
+        dscs.run();
+
+        Mockito.verify(repositoryManager).startDataStoreGC(Mockito.anyBoolean());
+    }
+
+    @Test
+    public void testRunCheck() {
+
+        Integer id = 1;
+        final RepositoryManagementMBean repositoryManager = Mockito.mock(RepositoryManagementMBean.class);
+        CompositeData startingCd = CompositeDataMock.init().put("id", id).build();
+        Mockito.when(repositoryManager.startDataStoreGC(false)).thenReturn(startingCd);
+        CompositeData doneCd = CompositeDataMock.init().put("id", id).put("code", StatusCode.RUNNING.ordinal()).build();
+        Mockito.when(repositoryManager.getDataStoreGCStatus()).thenReturn(doneCd);
+        final DataStoreCleanupScheduler dscs = new DataStoreCleanupScheduler(Mockito.mock(DataStoreCleanupConfig.class),
+                repositoryManager);
+
+        dscs.run();
+
+        Mockito.verify(repositoryManager, never()).startDataStoreGC(Mockito.anyBoolean());
+    }
+
+    @Test
+    public void testSheduledExpression() {
+        final String EXPECTED = "* * * * *";
+        final DataStoreCleanupScheduler dscs = new DataStoreCleanupScheduler(new DataStoreCleanupConfig() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public String scheduler_expression() {
+                return EXPECTED;
+            }
+
+        }, null);
+
+        assertEquals(EXPECTED, dscs.getSchedulerExpression());
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheckTest.java b/src/test/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheckTest.java
new file mode 100644
index 0000000..3315691
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheckTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import javax.management.openmbean.CompositeData;
+
+import org.apache.felix.hc.api.Result;
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.sling.jcr.maintenance.CompositeDataMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class RepositoryMaintenanceHealthCheckTest {
+
+    private CompositeData successCompositeData;
+    private CompositeData failedCompositeData;
+
+    @Before
+    public void init() {
+        successCompositeData = CompositeDataMock.init()
+                .put("code", RepositoryManagementMBean.StatusCode.SUCCEEDED.ordinal()).build();
+        failedCompositeData = CompositeDataMock.init()
+                .put("code", RepositoryManagementMBean.StatusCode.FAILED.ordinal()).build();
+    }
+
+    @Test
+    public void testNothingRegistered() {
+        RepositoryMaintenanceHealthCheck repositoryHealthCheck = new RepositoryMaintenanceHealthCheck();
+
+        RepositoryManagementMBean repositoryManagementMBean = Mockito.mock(RepositoryManagementMBean.class);
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(successCompositeData);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(successCompositeData);
+        repositoryHealthCheck.setRepositoryManagementMBean(repositoryManagementMBean);
+
+        Result result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+    }
+
+    @Test
+    public void testAllSuccessful() {
+        RepositoryMaintenanceHealthCheck repositoryHealthCheck = new RepositoryMaintenanceHealthCheck();
+
+        DataStoreCleanupScheduler dataStoreCleanupScheduler = Mockito.mock(DataStoreCleanupScheduler.class);
+        repositoryHealthCheck.setDataStoreCleanupScheduler(dataStoreCleanupScheduler);
+
+        RepositoryManagementMBean repositoryManagementMBean = Mockito.mock(RepositoryManagementMBean.class);
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(successCompositeData);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(successCompositeData);
+
+        repositoryHealthCheck.setRepositoryManagementMBean(repositoryManagementMBean);
+
+        RevisionCleanupScheduler revisionCleanupScheduler = Mockito.mock(RevisionCleanupScheduler.class);
+        repositoryHealthCheck.setRevisionCleanupScheduler(revisionCleanupScheduler);
+
+        VersionCleanupMBean versionCleanupMBean = Mockito.mock(VersionCleanupMBean.class);
+        Mockito.when(versionCleanupMBean.isFailed()).thenReturn(false);
+        repositoryHealthCheck.setVersionCleanup(versionCleanupMBean);
+
+        Result result = repositoryHealthCheck.execute();
+        assertTrue(result.isOk());
+    }
+
+    @Test
+    public void testDataStoreFailure() {
+        RepositoryMaintenanceHealthCheck repositoryHealthCheck = new RepositoryMaintenanceHealthCheck();
+
+        RepositoryManagementMBean repositoryManagementMBean = Mockito.mock(RepositoryManagementMBean.class);
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(successCompositeData);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(successCompositeData);
+
+        repositoryHealthCheck.setRepositoryManagementMBean(repositoryManagementMBean);
+
+        RevisionCleanupScheduler revisionCleanupScheduler = Mockito.mock(RevisionCleanupScheduler.class);
+        repositoryHealthCheck.setRevisionCleanupScheduler(revisionCleanupScheduler);
+
+        VersionCleanupMBean versionCleanupMBean = Mockito.mock(VersionCleanupMBean.class);
+        Mockito.when(versionCleanupMBean.isFailed()).thenReturn(false);
+        repositoryHealthCheck.setVersionCleanup(versionCleanupMBean);
+
+        Result result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.WARN, result.getStatus());
+
+        DataStoreCleanupScheduler dataStoreCleanupScheduler = Mockito.mock(DataStoreCleanupScheduler.class);
+        repositoryHealthCheck.setDataStoreCleanupScheduler(dataStoreCleanupScheduler);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(failedCompositeData);
+        result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.CRITICAL, result.getStatus());
+    }
+
+    @Test
+    public void testRevisionFailure() {
+        RepositoryMaintenanceHealthCheck repositoryHealthCheck = new RepositoryMaintenanceHealthCheck();
+
+        DataStoreCleanupScheduler dataStoreCleanupScheduler = Mockito.mock(DataStoreCleanupScheduler.class);
+        repositoryHealthCheck.setDataStoreCleanupScheduler(dataStoreCleanupScheduler);
+
+        RepositoryManagementMBean repositoryManagementMBean = Mockito.mock(RepositoryManagementMBean.class);
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(successCompositeData);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(successCompositeData);
+
+        repositoryHealthCheck.setRepositoryManagementMBean(repositoryManagementMBean);
+
+        VersionCleanupMBean versionCleanupMBean = Mockito.mock(VersionCleanupMBean.class);
+        Mockito.when(versionCleanupMBean.isFailed()).thenReturn(false);
+        repositoryHealthCheck.setVersionCleanup(versionCleanupMBean);
+
+        Result result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.WARN, result.getStatus());
+
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(failedCompositeData);
+        RevisionCleanupScheduler revisionCleanupScheduler = Mockito.mock(RevisionCleanupScheduler.class);
+        repositoryHealthCheck.setRevisionCleanupScheduler(revisionCleanupScheduler);
+        result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.CRITICAL, result.getStatus());
+    }
+
+    @Test
+    public void testVersionFailure() {
+        RepositoryMaintenanceHealthCheck repositoryHealthCheck = new RepositoryMaintenanceHealthCheck();
+
+        DataStoreCleanupScheduler dataStoreCleanupScheduler = Mockito.mock(DataStoreCleanupScheduler.class);
+        repositoryHealthCheck.setDataStoreCleanupScheduler(dataStoreCleanupScheduler);
+
+        RepositoryManagementMBean repositoryManagementMBean = Mockito.mock(RepositoryManagementMBean.class);
+        Mockito.when(repositoryManagementMBean.getRevisionGCStatus()).thenReturn(successCompositeData);
+        Mockito.when(repositoryManagementMBean.getDataStoreGCStatus()).thenReturn(successCompositeData);
+
+        repositoryHealthCheck.setRepositoryManagementMBean(repositoryManagementMBean);
+
+        RevisionCleanupScheduler revisionCleanupScheduler = Mockito.mock(RevisionCleanupScheduler.class);
+        repositoryHealthCheck.setRevisionCleanupScheduler(revisionCleanupScheduler);
+
+        Result result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.WARN, result.getStatus());
+
+        VersionCleanupMBean versionCleanupMBean = Mockito.mock(VersionCleanupMBean.class);
+        Mockito.when(versionCleanupMBean.isFailed()).thenReturn(true);
+        repositoryHealthCheck.setVersionCleanup(versionCleanupMBean);
+        result = repositoryHealthCheck.execute();
+        assertFalse(result.isOk());
+        assertEquals(Result.Status.CRITICAL, result.getStatus());
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupSchedulerTest.java b/src/test/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupSchedulerTest.java
new file mode 100644
index 0000000..5797226
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupSchedulerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.never;
+
+import java.lang.annotation.Annotation;
+
+import javax.management.openmbean.CompositeData;
+
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean;
+import org.apache.jackrabbit.oak.api.jmx.RepositoryManagementMBean.StatusCode;
+import org.apache.sling.jcr.maintenance.CompositeDataMock;
+import org.apache.sling.jcr.maintenance.RevisionCleanupConfig;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class RevisionCleanupSchedulerTest {
+
+    @Test
+    public void testRunnable() {
+
+        Integer id = 1;
+        final RepositoryManagementMBean repositoryManager = Mockito.mock(RepositoryManagementMBean.class);
+        CompositeData startingCd = CompositeDataMock.init().put("id", id).build();
+        Mockito.when(repositoryManager.startDataStoreGC(false)).thenReturn(startingCd);
+        CompositeData doneCd = CompositeDataMock.init().put("id", (Integer) id + 1)
+                .put("code", StatusCode.SUCCEEDED.ordinal()).build();
+        Mockito.when(repositoryManager.getRevisionGCStatus()).thenReturn(doneCd);
+        final RevisionCleanupScheduler rcs = new RevisionCleanupScheduler(Mockito.mock(RevisionCleanupConfig.class),
+                repositoryManager);
+
+        rcs.run();
+
+        Mockito.verify(repositoryManager).startRevisionGC();
+    }
+
+    @Test
+    public void testRunning() {
+
+        Integer id = 1;
+        final RepositoryManagementMBean repositoryManager = Mockito.mock(RepositoryManagementMBean.class);
+        CompositeData runningCd = CompositeDataMock.init().put("id", id).put("code", StatusCode.RUNNING.ordinal())
+                .build();
+        Mockito.when(repositoryManager.getRevisionGCStatus()).thenReturn(runningCd);
+
+        final RevisionCleanupScheduler rcs = new RevisionCleanupScheduler(Mockito.mock(RevisionCleanupConfig.class),
+                repositoryManager);
+
+        rcs.run();
+
+        Mockito.verify(repositoryManager, never()).startRevisionGC();
+    }
+
+    @Test
+    public void testSheduledExpression() {
+        final String EXPECTED = "* * * * *";
+        final RevisionCleanupScheduler rcs = new RevisionCleanupScheduler(new RevisionCleanupConfig() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public String scheduler_expression() {
+                return EXPECTED;
+            }
+
+        }, null);
+
+        assertEquals(EXPECTED, rcs.getSchedulerExpression());
+
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPathTest.java b/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPathTest.java
new file mode 100644
index 0000000..f05d726
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupPathTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.jcr.maintenance.VersionCleanupPathConfig;
+import org.junit.Test;
+
+public class VersionCleanupPathTest {
+
+    private VersionCleanupPath simpleCreate(String path) {
+        return new VersionCleanupPath(new VersionCleanupPathConfig() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return null;
+            }
+
+            @Override
+            public boolean keepVersions() {
+                return false;
+            }
+
+            @Override
+            public int limit() {
+                return 5;
+            }
+
+            @Override
+            public String path() {
+                return path;
+            }
+
+        });
+    }
+
+    @Test
+    public void testNotFound() {
+        try {
+            VersionCleanupPath.getMatchingConfiguration(Collections.emptyList(), "/");
+            fail();
+        } catch (RepositoryException re) {
+            // expected
+        }
+
+        try {
+            VersionCleanupPath.getMatchingConfiguration(Collections.singletonList(simpleCreate("/subpath")), "/");
+            fail();
+        } catch (RepositoryException re) {
+            // expected
+        }
+    }
+
+    @Test
+    public void testSorting() throws RepositoryException {
+        List<VersionCleanupPath> configs = new ArrayList<>();
+        configs.add(simpleCreate("/content"));
+        configs.add(simpleCreate("/content/content2"));
+        Collections.sort(configs);
+
+        assertEquals("/content/content2,/content",
+                configs.stream().map(VersionCleanupPath::getPath).collect(Collectors.joining(",")));
+        assertEquals("/content/content2",
+                VersionCleanupPath.getMatchingConfiguration(configs, "/content/content2/subitem").getPath());
+        assertEquals("/content",
+                VersionCleanupPath.getMatchingConfiguration(configs, "/content/content3/subitem").getPath());
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupTest.java b/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupTest.java
new file mode 100644
index 0000000..07fa774
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/maintenance/internal/VersionCleanupTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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.jcr.maintenance.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.atLeast;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.lang.annotation.Annotation;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
+import javax.jcr.nodetype.NodeTypeExistsException;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionManager;
+
+import org.apache.jackrabbit.commons.cnd.CndImporter;
+import org.apache.jackrabbit.commons.cnd.ParseException;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.jcr.maintenance.VersionCleanupPathConfig;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class VersionCleanupTest {
+
+    private VersionManager versionManager;
+
+    @Rule
+    public SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK);
+
+    private List<VersionCleanupPath> globalConfig;
+
+    private Session session;
+
+    @Before
+    public void init() throws LoginException, InvalidNodeTypeDefinitionException, NodeTypeExistsException,
+            UnsupportedRepositoryOperationException, UnsupportedEncodingException, ParseException, RepositoryException,
+            IOException {
+
+        session = context.resourceResolver().adaptTo(Session.class);
+        versionManager = session.getWorkspace().getVersionManager();
+        InputStream cnd = getClass().getResourceAsStream("/nodetypes.cnd");
+        CndImporter.registerNodeTypes(new InputStreamReader(cnd, "UTF-8"), session);
+
+        context.load().json("/version-content.json", "/content/apache/sling-apache-org");
+
+        globalConfig = Collections.singletonList(new VersionCleanupPath(new VersionCleanupPathConfig() {
+
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return null;
+            }
+
+            @Override
+            public boolean keepVersions() {
+                return false;
+            }
+
+            @Override
+            public int limit() {
+                return 5;
+            }
+
+            @Override
+            public String path() {
+                return "/";
+            }
+
+        }));
+
+    }
+
+    private void doVersions(String path, int count) throws RepositoryException {
+        Node node = session.getNode(path);
+        node.addMixin("mix:versionable");
+        session.save();
+        for (int i = 0; i < count; i++) {
+            versionManager.checkpoint(path);
+        }
+    }
+
+    @Test(timeout = 5000)
+    public void testRunnable() throws InterruptedException, VersionException, UnsupportedRepositoryOperationException,
+            InvalidItemStateException, LockException, RepositoryException {
+
+        doVersions("/content/apache/sling-apache-org/index", 10);
+
+        final VersionCleanup vcs = new VersionCleanup(globalConfig, context.getService(ResourceResolverFactory.class));
+
+        vcs.start();
+        while (vcs.isRunning()) {
+            TimeUnit.SECONDS.sleep(2);
+        }
+
+        assertFalse(vcs.isFailed());
+        assertFalse(vcs.isRunning());
+        assertNull(vcs.getLastMessage());
+        assertEquals(5L, vcs.getLastCleanedVersionsCount());
+    }
+
+    @Test(timeout = 5000)
+    public void testStop() throws InterruptedException, VersionException, UnsupportedRepositoryOperationException,
+            InvalidItemStateException, LockException, RepositoryException {
+
+        doVersions("/content/apache/sling-apache-org/index", 100);
+
+        final VersionCleanup vcs = new VersionCleanup(globalConfig, context.getService(ResourceResolverFactory.class));
+
+        vcs.start();
+        assertTrue(vcs.isRunning());
+        vcs.stop();
+        while (vcs.isRunning()) {
+            TimeUnit.SECONDS.sleep(2);
+        }
+        assertFalse(vcs.isRunning());
+        assertNull(vcs.getLastMessage());
+    }
+
+    @Test(timeout = 5000)
+    public void testReRun() throws InterruptedException, VersionException, UnsupportedRepositoryOperationException,
+            InvalidItemStateException, LockException, RepositoryException {
+
+        doVersions("/content/apache/sling-apache-org/index", 100);
+
+        final VersionCleanup vcs = Mockito
+                .spy(new VersionCleanup(globalConfig, context.getService(ResourceResolverFactory.class)));
+
+        vcs.start();
+        vcs.start();
+
+        Mockito.verify(vcs, atLeast(2)).isRunning();
+        while (vcs.isRunning()) {
+            TimeUnit.SECONDS.sleep(2);
+        }
+        assertFalse(vcs.isRunning());
+        assertNull(vcs.getLastMessage());
+
+    }
+
+    @Test(timeout = 5000)
+    public void testMissingServiceUser()
+            throws InterruptedException, VersionException, UnsupportedRepositoryOperationException,
+            InvalidItemStateException, LockException, RepositoryException, LoginException {
+
+        ResourceResolverFactory factory = Mockito.mock(ResourceResolverFactory.class);
+        Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap()))
+                .thenThrow(new LoginException("No service user"));
+        final VersionCleanup vcs = Mockito.spy(new VersionCleanup(globalConfig, factory));
+
+        vcs.start();
+
+        while (vcs.isRunning()) {
+            TimeUnit.SECONDS.sleep(2);
+        }
+        assertFalse(vcs.isRunning());
+        assertTrue(vcs.isFailed());
+        assertNotNull(vcs.getLastMessage());
+    }
+
+    @Test(timeout = 5000)
+    public void testDeleted() throws InterruptedException, VersionException, UnsupportedRepositoryOperationException,
+            InvalidItemStateException, LockException, RepositoryException, LoginException, PersistenceException {
+        doVersions("/content/apache/sling-apache-org/index", 10);
+        doVersions("/content/apache/sling-apache-org/test2", 3);
+        context.resourceResolver().delete(context.resourceResolver().getResource("/content/apache/sling-apache-org/test2"));
+        context.resourceResolver().commit();
+
+        final VersionCleanup vcs = new VersionCleanup(globalConfig, context.getService(ResourceResolverFactory.class));
+
+        vcs.start();
+        while (vcs.isRunning()) {
+            TimeUnit.SECONDS.sleep(2);
+        }
+
+        assertFalse(vcs.isFailed());
+        assertFalse(vcs.isRunning());
+        assertNull(vcs.getLastMessage());
+        assertEquals(7L, vcs.getLastCleanedVersionsCount());
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/resources/nodetypes.cnd b/src/test/resources/nodetypes.cnd
new file mode 100644
index 0000000..43edb8f
--- /dev/null
+++ b/src/test/resources/nodetypes.cnd
@@ -0,0 +1,89 @@
+//  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.
+//  
+//  You can find out more documentation on this topic 
+//  by following these links:
+//
+//    -  http://sling.apache.org/site/content-loading.html
+//    -  http://jackrabbit.apache.org/node-type-notation.html
+
+<'sling'='http://sling.apache.org/jcr/sling/1.0'>
+<'nt'='http://www.jcp.org/jcr/nt/1.0'>
+<'mix'='http://www.jcp.org/jcr/mix/1.0'>
+<'jcr'='http://www.jcp.org/jcr/1.0'>
+
+[mix:publishable] mixin
+- sling:published (boolean)
+- sling:lastPublication (date)
+- sling:lastPublicationBy (string)
+- sling:lastPublicationType (string)
+
+[sling:Component] > nt:unstructured
+    - componentType (string)
+    - jcr:title (string)
+    
+[sling:Config] > nt:hierarchyNode, mix:lastModified, mix:publishable
+    orderable
+    - sling:resourceType (string)
+    - jcr:title (string)
+    + * (nt:unstructured) = nt:unstructured version
+    - * (UNDEFINED) multiple
+    - * (UNDEFINED)
+    
+[sling:File] > nt:file, mix:publishable
+     - * (undefined) copy
+     + jcr:content (sling:FileContent) = sling:FileContent copy primary autocreated
+    
+[sling:FileContent] > nt:resource, mix:publishable
+    - * (undefined) copy
+    - * (undefined) copy multiple
+    + metadata (nt:unstructured) = nt:unstructured copy primary
+    + renditions (nt:unstructured) = nt:unstructured copy primary
+
+[sling:Page] > nt:hierarchyNode, mix:lastModified
+    orderable
+    + jcr:content (nt:unstructured) = nt:unstructured copy primary
+    + * (nt:base) = nt:base version
+
+[sling:Site] > nt:hierarchyNode, mix:lastModified
+    orderable
+    - sling:configRef (string)
+    - sling:url (string)
+    - jcr:language (string)
+    - jcr:title (string)
+    - jcr:description (string)
+    + * (nt:base) = nt:base version
+
+[sling:Taxonomy] > nt:hierarchyNode, mix:lastModified
+    orderable
+    - sling:related (string)
+    - jcr:title (string)
+    + * (sling:Taxonomy) = sling:Taxonomy version
+
+[sling:UGC] > nt:unstructured
+    - approveaction (string)
+    - contenttype (string)
+    - finalpath (string)
+    - preview (string)
+    - published (boolean)
+    - referrer (string)
+    - user (string)
+    - useragent (string)
+    - userip (string)
+    - * (UNDEFINED) multiple
+    - * (UNDEFINED)
+    
\ No newline at end of file
diff --git a/src/test/resources/version-content.json b/src/test/resources/version-content.json
new file mode 100644
index 0000000..f52ba84
--- /dev/null
+++ b/src/test/resources/version-content.json
@@ -0,0 +1,44 @@
+{
+    "jcr:primaryType": "sling:Site",
+    "jcr:title": "Apache Sling",
+    "jcr:language": "en",
+    "sling:url": "https://sling.apache.org",
+    "index": {
+        "jcr:primaryType": "sling:Page",
+        "jcr:content": {
+            "jcr:primaryType": "nt:unstructured",
+            "jcr:title": "Apache Sling - Bringing Back the Fun!",
+            "sling:template": "/conf/global/site/templates/base-page",
+            "sling:taxonomy": "/etc/taxonomy/reference/community",
+            "sling:resourceType": "reference/components/pages/base",
+            "published": true,
+            "hideInSitemap": false,
+            "container": {
+                "jcr:primaryType": "nt:unstructured",
+                "richtext": {
+                    "jcr:primaryType": "nt:unstructured",
+                    "text": "<p>Apache Sling™ is a framework for RESTful web-applications based on an extensible content tree.</p>\r\n<p>In a nutshell, Sling maps HTTP request URLs to content resources based on the request's path, extension and selectors. Using convention over configuration, requests are processed by scripts and servlets, dynamically selected based on the current resource. This fosters meaningful URLs and resource driven request processing, while the modular nature of Sl [...]
+                    "sling:resourceType": "sling-cms/components/general/richtext"
+                }
+            },
+            "menu": {
+                "jcr:primaryType": "nt:unstructured",
+                "richtext": {
+                    "jcr:primaryType": "nt:unstructured",
+                    "text": "<p>\r\n                <strong><a href=\"#\">Documentation</a></strong><br>\r\n                <a href=\"#\">Getting Started</a><br>\r\n                <a href=\"#\">The Sling Engine</a><br>\r\n                <a href=\"#\">Development</a><br>\r\n                <a href=\"#\">Bundles</a><br>\r\n                <a href=\"#\">Tutorials &amp; How-Tos</a><br>\r\n                <a href=\"http://sling.apache.org/components/\">Maven Plugins</a><br>\r\n              [...]
+                    "sling:resourceType": "sling-cms/components/general/richtext"
+                }
+            }
+        }
+    },
+    "test2": {
+        "jcr:primaryType": "sling:Page",
+        "jcr:content": {
+            "jcr:primaryType": "nt:unstructured",
+            "jcr:title": "Test 2",
+            "sling:template": "/conf/global/site/templates/base-page",
+            "sling:resourceType": "reference/components/pages/base",
+            "published": false
+        }
+    }
+}
\ No newline at end of file

[sling-org-apache-sling-jcr-maintenance] 08/21: SLING-10263 create the sling-versionmgr system user with path system/sling (#1)

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 899c8bd04f0996cc8aa929dc0b4d6bc4febbf796
Author: Eric Norman <er...@gmail.com>
AuthorDate: Tue Mar 30 08:56:22 2021 -0700

    SLING-10263 create the sling-versionmgr system user with path system/sling (#1)
    
    * create the sling-versionmgr system user with path
    system/sling
    
    * use pre-authentication for system users
---
 src/main/features/base.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/main/features/base.json b/src/main/features/base.json
index e4830f1..254ff79 100644
--- a/src/main/features/base.json
+++ b/src/main/features/base.json
@@ -8,13 +8,13 @@
     "configurations":{
         "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling-versionmgr":{
             "user.mapping":[
-                "org.apache.sling.jcr.maintenance:sling-versionmgr=sling-versionmgr"
+                "org.apache.sling.jcr.maintenance:sling-versionmgr=[sling-versionmgr]"
             ]
         }
     },
     "repoinit:TEXT|true": [
-        "create service user sling-versionmgr",
-        "set ACL for sling-versionmgr",
+        "create service user sling-versionmgr with path system/sling",
+        "set principal ACL for sling-versionmgr",
         "allow   jcr:write,jcr:nodeTypeManagement,jcr:versionManagement    on /",
         "allow   jcr:read    on /jcr:system/jcr:versionStorage",
         "end"

[sling-org-apache-sling-jcr-maintenance] 10/21: [maven-release-plugin] prepare release org.apache.sling.jcr.maintenance-1.0.2

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 09286c820a4eab39b161e2cabc25e4eb85c7b5ea
Author: Eric Norman <en...@apache.org>
AuthorDate: Thu Apr 15 11:13:39 2021 -0700

    [maven-release-plugin] prepare release org.apache.sling.jcr.maintenance-1.0.2
---
 pom.xml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pom.xml b/pom.xml
index b539c9b..0e1f3cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,12 +26,12 @@
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
-    <version>1.0.1-SNAPSHOT</version>
+    <version>1.0.2</version>
     <name>Apache Sling JCR Maintenance</name>
     <description>Maintenance jobs for the JCR</description>
 
     <properties>
-        <project.build.outputTimestamp>2021-04-15T17:29:03Z</project.build.outputTimestamp>
+        <project.build.outputTimestamp>2021-04-15T18:13:10Z</project.build.outputTimestamp>
     </properties>
 
 
@@ -39,7 +39,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-maintenance.git</url>
-        <tag>HEAD</tag>
+        <tag>org.apache.sling.jcr.maintenance-1.0.2</tag>
     </scm>
 
     <build>

[sling-org-apache-sling-jcr-maintenance] 09/21: SLING-10310 Update to Sling Bundle Parent 41

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit ba86cf81530537ff5585d55b95737bc307b12fc3
Author: Eric Norman <en...@apache.org>
AuthorDate: Thu Apr 15 10:40:37 2021 -0700

    SLING-10310 Update to Sling Bundle Parent 41
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index b0fc0b6..b539c9b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
-        <version>40</version>
+        <version>41</version>
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
@@ -31,7 +31,7 @@
     <description>Maintenance jobs for the JCR</description>
 
     <properties>
-        <bnd.baseline.fail.on.missing>false</bnd.baseline.fail.on.missing>
+        <project.build.outputTimestamp>2021-04-15T17:29:03Z</project.build.outputTimestamp>
     </properties>
 
 

[sling-org-apache-sling-jcr-maintenance] 12/21: SLING-10676 - add or update SECURITY.md

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 4cdf66dce20e498e937745a184cac5922078e8f0
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Wed Jul 28 16:12:00 2021 +0200

    SLING-10676 - add or update SECURITY.md
---
 SECURITY.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..e69de29

[sling-org-apache-sling-jcr-maintenance] 18/21: SLING-11211: reverted sling.api version

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 934e8be7c08575e21b23e709d1041196c88d8c21
Author: Ashok Pelluru <as...@gmail.com>
AuthorDate: Thu Mar 17 09:11:57 2022 +0100

    SLING-11211: reverted sling.api version
---
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 3b30a0d..525dbaa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -142,7 +142,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.24.0</version>
+            <version>2.18.4</version>
             <scope>provided</scope>
         </dependency>
         <dependency>

[sling-org-apache-sling-jcr-maintenance] 16/21: SLING-11211: updated maven dependencies && fixed broken links from Readme file

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 3d1793b2b62299e95aa89788c7c2948ca41c29e0
Author: Ashok Pelluru <as...@aldi-sued.com>
AuthorDate: Wed Mar 16 17:04:00 2022 +0100

    SLING-11211: updated maven dependencies && fixed broken links from Readme file
---
 README.md |  8 ++++----
 pom.xml   | 21 +++++++++++----------
 2 files changed, 15 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md
index 40b1f88..3972b88 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,12 @@ This project provides reference implementation of Maintenance jobs for maintaini
 
 This includes the following Maintenance jobs:
 
-- [DataStoreCleanupScheduler](src/main/java/org/apache/sling/maintenance/internal/DataStoreCleanupScheduler.java) - Run the [RepositoryManagementMBean.startDataStoreGC(true)](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startDataStoreGC-boolean-) method to perform a Garbage Collection of the Data Store
-- [RevisionCleanupScheduler](src/main/java/org/apache/sling/maintenance/internal/RevisionCleanupScheduler.java) - Run the [RepositoryManagementMBean.startRevisionGC()](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startRevisionGC--) method to perform a Garbage Collection of the Revision Store
-- [VersionCleanup](src/main/java/org/apache/sling/maintenance/internal/VersionCleanup.java) - Job to traverse the JCR Version Store
+- [DataStoreCleanupScheduler](src/main/java/org/apache/sling/jcr/maintenance/internal/DataStoreCleanupScheduler.java) - Run the [RepositoryManagementMBean.startDataStoreGC(true)](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startDataStoreGC-boolean-) method to perform a Garbage Collection of the Data Store
+- [RevisionCleanupScheduler](src/main/java/org/apache/sling/jcr/maintenance/internal/RevisionCleanupScheduler.java) - Run the [RepositoryManagementMBean.startRevisionGC()](https://jackrabbit.apache.org/oak/docs/apidocs/org/apache/jackrabbit/oak/api/jmx/RepositoryManagementMBean.html#startRevisionGC--) method to perform a Garbage Collection of the Revision Store
+- [VersionCleanup](src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java) - Job to traverse the JCR Version Store
   and remove versions (oldest-first) exceeding a configurable limit
 
-As well as a [Health Check](src/main/java/org/apache/sling/maintenance/internal/RepositoryMaintenanceHealthCheck.java) to ensure the jobs are scheduled and have not failed.
+As well as a [Health Check](src/main/java/org/apache/sling/jcr/maintenance/internal/RepositoryMaintenanceHealthCheck.java) to ensure the jobs are scheduled and have not failed.
 
 ## Configuration
 
diff --git a/pom.xml b/pom.xml
index 36b8daf..0f4bc78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
-        <version>41</version>
+        <version>46</version>
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
@@ -31,7 +31,9 @@
     <description>Maintenance jobs for the JCR</description>
 
     <properties>
+        <sling.java.version>8</sling.java.version>
         <project.build.outputTimestamp>2021-04-15T18:14:05Z</project.build.outputTimestamp>
+        <oak.version>1.40.0</oak.version>
     </properties>
 
 
@@ -62,7 +64,7 @@
             <plugin>
                 <groupId>org.apache.sling</groupId>
                 <artifactId>slingfeature-maven-plugin</artifactId>
-                <version>1.4.18</version>
+                <version>1.6.0</version>
                 <extensions>true</extensions>
                 <configuration>
                     <framework>
@@ -117,7 +119,7 @@
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.healthcheck.api</artifactId>
-            <version>2.0.0</version>
+            <version>2.0.4</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -129,25 +131,24 @@
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-core</artifactId>
-            <version>1.8.8</version>
+            <version>${oak.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-jcr</artifactId>
-            <version>1.8.8</version>
+            <version>${oak.version}</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.18.4</version>
+            <version>2.24.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.jetbrains</groupId>
             <artifactId>annotations</artifactId>
-            <version>20.1.0</version>
             <scope>provided</scope>
         </dependency>
 
@@ -185,19 +186,19 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.sling-mock.junit4</artifactId>
-            <version>2.6.2</version>
+            <version>3.2.2</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.sling-mock-oak</artifactId>
-            <version>2.1.10-1.16.0</version>
+            <version>3.1.2-1.40.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
-            <version>3.6.28</version>
+            <version>4.4.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>

[sling-org-apache-sling-jcr-maintenance] 14/21: SLING-10676 - remove SECURITY.md which is not needed

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit a7781690930faab91afb8e2e8dfd452ed8ca8a83
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Fri Jul 30 10:44:59 2021 +0200

    SLING-10676 - remove SECURITY.md which is not needed
---
 SECURITY.md | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 2e2f930..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Security Policy for Apache Sling modules
-
-This module is part of the [Apache Sling Project](https://sling.apache.org), a
-project of the [Apache Software Foundation](https://apache.org) (ASF).
-
-It follows the ASF's [vulnerability handling process](https://apache.org/security/#vulnerability-handling) and
-provides its own [security information page](http://sling.apache.org/project-information/security.html).
-
-## Reporting a Vulnerability
-
-To report a new vulnerability you have discovered in an Apache Sling module,
-please follow the instructions on the
-[project's security page](http://sling.apache.org/project-information/security.html) .
\ No newline at end of file

[sling-org-apache-sling-jcr-maintenance] 19/21: SLING-11211: reverted oak versions

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit e125143c918dc621bb44ff5dfc835583bded95e8
Author: Ashok Pelluru <as...@gmail.com>
AuthorDate: Thu Mar 17 15:32:51 2022 +0100

    SLING-11211: reverted oak versions
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 525dbaa..24abf30 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
 
     <properties>
         <project.build.outputTimestamp>2021-04-15T18:14:05Z</project.build.outputTimestamp>
-        <oak.version>1.40.0</oak.version>
+        <oak.version>1.8.8</oak.version>
     </properties>
 
 
@@ -191,7 +191,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.sling-mock-oak</artifactId>
-            <version>3.1.2-1.40.0</version>
+            <version>2.1.10-1.16.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>

[sling-org-apache-sling-jcr-maintenance] 21/21: Merge pull request #2 from ashokmca07/master

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit d028263c0dc94bbbcbd794090ac37d942033e8ab
Merge: 1334686 8e7e576
Author: Dan Klco <kl...@users.noreply.github.com>
AuthorDate: Fri Mar 18 14:40:56 2022 -0400

    Merge pull request #2 from ashokmca07/master
    
    SLING-11211: updated maven dependencies && fixed broken links

 README.md |  8 ++++----
 pom.xml   | 21 ++++++++++-----------
 2 files changed, 14 insertions(+), 15 deletions(-)

[sling-org-apache-sling-jcr-maintenance] 04/21: Merge branch 'master' of github.com:apache/sling-org-apache-sling-jcr-maintenance

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit a9e85ab72f0c8ad9ed647359401f89faa81b6ed6
Merge: 0043e5d bcb1bc3
Author: Dan Klco <da...@perficient.com>
AuthorDate: Mon Feb 1 16:58:54 2021 -0500

    Merge branch 'master' of github.com:apache/sling-org-apache-sling-jcr-maintenance

 pom.xml | 20 --------------------
 1 file changed, 20 deletions(-)

[sling-org-apache-sling-jcr-maintenance] 17/21: SLING-11211: updated sling-parent to version 47

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit b394668cee6de7c77a0a7d53d38213420d9c4f34
Author: Ashok Pelluru <as...@gmail.com>
AuthorDate: Wed Mar 16 21:18:20 2022 +0100

    SLING-11211: updated sling-parent to version 47
---
 pom.xml | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0f4bc78..3b30a0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling-bundle-parent</artifactId>
-        <version>46</version>
+        <version>47</version>
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
@@ -31,7 +31,6 @@
     <description>Maintenance jobs for the JCR</description>
 
     <properties>
-        <sling.java.version>8</sling.java.version>
         <project.build.outputTimestamp>2021-04-15T18:14:05Z</project.build.outputTimestamp>
         <oak.version>1.40.0</oak.version>
     </properties>
@@ -102,6 +101,8 @@
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+
+        <!-- Javax Dependencies -->
         <dependency>
             <groupId>javax.annotation</groupId>
             <artifactId>javax.annotation-api</artifactId>
@@ -111,8 +112,6 @@
         <dependency>
             <groupId>javax.jcr</groupId>
             <artifactId>jcr</artifactId>
-            <version>2.0</version>
-            <scope>provided</scope>
         </dependency>
 
         <!-- Apache Dependencies -->

[sling-org-apache-sling-jcr-maintenance] 11/21: [maven-release-plugin] prepare for next development iteration

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 687a9ecc270c671812f590d4c47e42ce332302e6
Author: Eric Norman <en...@apache.org>
AuthorDate: Thu Apr 15 11:14:05 2021 -0700

    [maven-release-plugin] prepare for next development iteration
---
 pom.xml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pom.xml b/pom.xml
index 0e1f3cd..36b8daf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,12 +26,12 @@
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
-    <version>1.0.2</version>
+    <version>1.0.3-SNAPSHOT</version>
     <name>Apache Sling JCR Maintenance</name>
     <description>Maintenance jobs for the JCR</description>
 
     <properties>
-        <project.build.outputTimestamp>2021-04-15T18:13:10Z</project.build.outputTimestamp>
+        <project.build.outputTimestamp>2021-04-15T18:14:05Z</project.build.outputTimestamp>
     </properties>
 
 
@@ -39,7 +39,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-maintenance.git</url>
-        <tag>org.apache.sling.jcr.maintenance-1.0.2</tag>
+        <tag>HEAD</tag>
     </scm>
 
     <build>

[sling-org-apache-sling-jcr-maintenance] 07/21: [maven-release-plugin] prepare for next development iteration

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit bb44446f8d0eec884d10a139799321e3bf10e42f
Author: Dan Klco <da...@perficient.com>
AuthorDate: Mon Feb 1 23:16:03 2021 -0500

    [maven-release-plugin] prepare for next development iteration
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 461f1b7..b0fc0b6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
-    <version>1.0.0</version>
+    <version>1.0.1-SNAPSHOT</version>
     <name>Apache Sling JCR Maintenance</name>
     <description>Maintenance jobs for the JCR</description>
 
@@ -39,7 +39,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-maintenance.git</url>
-        <tag>org.apache.sling.jcr.maintenance-1.0.0</tag>
+        <tag>HEAD</tag>
     </scm>
 
     <build>

[sling-org-apache-sling-jcr-maintenance] 20/21: Update pom.xml

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 8e7e5761623b5acd7f7c6d2e847062a4f30a1af3
Author: apelluru <37...@users.noreply.github.com>
AuthorDate: Thu Mar 17 16:38:05 2022 +0100

    Update pom.xml
    
    GitHub PR is not showing commits
---
 pom.xml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 24abf30..9d3d957 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,7 +35,6 @@
         <oak.version>1.8.8</oak.version>
     </properties>
 
-
     <scm>
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>

[sling-org-apache-sling-jcr-maintenance] 03/21: Updating to use a boolean flag to indicate interruption rather than throwing an exception

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 0043e5d578d7a76504ba79c152e4436b531b3508
Author: Dan Klco <da...@perficient.com>
AuthorDate: Mon Feb 1 16:58:46 2021 -0500

    Updating to use a boolean flag to indicate interruption rather than throwing an exception
---
 .../jcr/maintenance/internal/VersionCleanup.java   | 33 ++++++++--------------
 1 file changed, 12 insertions(+), 21 deletions(-)

diff --git a/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java
index 31a7372..ac04821 100644
--- a/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java
+++ b/src/main/java/org/apache/sling/jcr/maintenance/internal/VersionCleanup.java
@@ -123,10 +123,9 @@ public class VersionCleanup extends AnnotatedStandardMBean implements Runnable,
 
     }
 
-    private void findVersions(final Session session, final Resource resource)
-            throws RepositoryException, InterruptedException {
+    private boolean findVersions(final Session session, final Resource resource) throws RepositoryException {
         if (Thread.interrupted()) {
-            throw new InterruptedException("Process interrupted");
+            return true;
         }
         log.debug("Finding versions under: {}", resource.getPath());
         if ("nt:versionHistory".equals(resource.getResourceType())) {
@@ -134,9 +133,12 @@ public class VersionCleanup extends AnnotatedStandardMBean implements Runnable,
             cleanupVersions(session, resource);
         } else {
             for (final Resource child : resource.getChildren()) {
-                findVersions(session, child);
+                if (findVersions(session, child)) {
+                    return true;
+                }
             }
         }
+        return false;
     }
 
     private boolean isMatchingVersion(Session session, String path, VersionHistory versionHistory)
@@ -171,9 +173,6 @@ public class VersionCleanup extends AnnotatedStandardMBean implements Runnable,
 
     private void doRun() {
         log.info("Running version cleanup");
-        boolean interrupted = false;
-        boolean succeeded = false;
-        String failureMessage = null;
         lastCleanedVersions = 0;
         try {
             try (final ResourceResolver adminResolver = factory.getServiceResourceResolver(
@@ -183,26 +182,18 @@ public class VersionCleanup extends AnnotatedStandardMBean implements Runnable,
                         .orElseThrow(() -> new RepositoryException("Failed to get session"));
                 for (final Resource folder : versionRoot.getChildren()) {
                     log.info("Traversing and cleaning: {}", folder.getPath());
-                    findVersions(session, folder);
+                    if (findVersions(session, folder)) {
+                        break;
+                    }
                 }
-                succeeded = true;
+                lastFailureMessage = null;
             }
         } catch (final LoginException le) {
             log.error("Failed to run version cleanup, cannot get service user", le);
-            failureMessage = "Failed to run version cleanup, cannot get service user";
+            lastFailureMessage = "Failed to run version cleanup, cannot get service user";
         } catch (final RepositoryException re) {
             log.error("Failed to run version cleanup", re);
-            failureMessage = "Failed to run version cleanup";
-        } catch (final InterruptedException e) { // no need to do anything, at this point nearly done
-            log.info("Process interrupted, quitting");
-            interrupted = true;
-        } finally {
-            if (succeeded) {
-                this.lastFailureMessage = null;
-            } else if (!interrupted) {
-                lastFailureMessage = failureMessage != null ? failureMessage
-                        : "Failed due to unexpected exception, see logs";
-            }
+            lastFailureMessage = "Failed to run version cleanup";
         }
     }
 

[sling-org-apache-sling-jcr-maintenance] 06/21: [maven-release-plugin] prepare release org.apache.sling.jcr.maintenance-1.0.0

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 29e08ebeee964731a749c2532ecabb51229e94e7
Author: Dan Klco <da...@perficient.com>
AuthorDate: Mon Feb 1 23:15:53 2021 -0500

    [maven-release-plugin] prepare release org.apache.sling.jcr.maintenance-1.0.0
---
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pom.xml b/pom.xml
index 21c7c81..461f1b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
         <relativePath />
     </parent>
     <artifactId>org.apache.sling.jcr.maintenance</artifactId>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>1.0.0</version>
     <name>Apache Sling JCR Maintenance</name>
     <description>Maintenance jobs for the JCR</description>
 
@@ -39,7 +39,7 @@
         <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
         <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>
         <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-maintenance.git</url>
-        <tag>HEAD</tag>
+        <tag>org.apache.sling.jcr.maintenance-1.0.0</tag>
     </scm>
 
     <build>

[sling-org-apache-sling-jcr-maintenance] 05/21: Minor: Adding SCM element

Posted by dk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git

commit 70895a70603261ab5c2ec7a456936f8ba372e4ad
Author: Dan Klco <da...@perficient.com>
AuthorDate: Mon Feb 1 23:01:19 2021 -0500

    Minor: Adding SCM element
---
 pom.xml | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/pom.xml b/pom.xml
index ddac040..21c7c81 100644
--- a/pom.xml
+++ b/pom.xml
@@ -34,6 +34,14 @@
         <bnd.baseline.fail.on.missing>false</bnd.baseline.fail.on.missing>
     </properties>
 
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-maintenance.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-maintenance.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
     <build>
         <plugins>
             <plugin>