You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2023/10/25 13:27:23 UTC

[jackrabbit-filevault] 01/01: JCRVLT-722 Prevent UnsupportedTemporalTypeException in SyncLog

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

kwin pushed a commit to branch feature/vlt-sync-it
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit b460e9fc0f8a3de88fecfdfd8dd44f911c684f1c
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed Oct 25 15:27:16 2023 +0200

    JCRVLT-722 Prevent UnsupportedTemporalTypeException in SyncLog
    
    Add ITs for vlt-sync
    Clarify vlt-sync documentation
    Refactor ITs to ease execution from IDE
---
 src/site/markdown/usage.md                         |  12 +-
 vault-core-it/vault-core-integration-tests/pom.xml |  27 +++++
 .../packaging/integration/IntegrationTestBase.java |   5 +-
 .../packaging/integration/NamespaceImportIT.java   |   2 +-
 .../vault/sync/impl/VaultSyncServiceImplIT.java    | 130 +++++++++++++++++++++
 .../impl/.vlt-sync-config-jcr2fs-once.properties   |  49 ++++++++
 .../apache/jackrabbit/vault/sync/impl/filter.xml   |   4 +
 .../jackrabbit/vault/sync/impl/testfile1.txt       |   1 +
 .../jackrabbit/vault/sync/impl/testfile2.txt       |   1 +
 vault-core-it/vault-core-it-execution/pom.xml      |   5 +-
 .../vault-core-it-execution-oak-min/pom.xml        |  22 ++--
 vault-core-it/vault-core-it-support/pom.xml        |  22 +---
 .../pom.xml                                        |  25 ++--
 .../integration/support}/RepositoryProvider.java   |   4 +-
 .../support/RepositoryProviderHelper.java          |  33 ++++++
 .../vault-core-it-support-jackrabbit/pom.xml       |  19 +++
 .../vault/it/JackrabbitRepositoryProvider.java     |  11 +-
 .../vault-core-it-support-oak/pom.xml              |  19 +++
 .../jackrabbit/vault/it/OakRepositoryProvider.java |   8 +-
 .../apache/jackrabbit/vault/sync/impl/SyncLog.java |   6 +-
 .../jackrabbit/vault/sync/impl/TreeSync.java       |   5 +-
 .../vault/sync/impl/VaultSyncServiceImpl.java      |  38 +++---
 22 files changed, 365 insertions(+), 83 deletions(-)

diff --git a/src/site/markdown/usage.md b/src/site/markdown/usage.md
index 6b1e23e9..34db3971 100644
--- a/src/site/markdown/usage.md
+++ b/src/site/markdown/usage.md
@@ -102,13 +102,13 @@ Vault Sync
 ----------
 The vault sync service is used to synchronize repository content with a local filesystem representation and vice versa.
 This is achieved by installing an OSGi service that will listen for repository changes and scans the filesystem contents
-periodically. It uses the same serialization format as vault for mapping the repository contents to disk.
+periodically. It uses the same serialization format as vault for mapping the repository contents to disk. For accessing the repository it uses an Apache Sling service (therefore can only be started when Sling JCR API and an according implementation bundle are available).
 
 The vault sync service is a development tool and it is highly discouraged to use it on a productive system.
 Also note that the service can only sync with the local filesystem and cannot be used for remote development.
 
-The initial version (2.3.22 / 2.4.24) only supports simple files and folders but detects special vault serialized files
-(`.content.xml`, `dialog.xml`, etc) and ignores them silently. Thus it is possible to use vault sync on a default vlt
+Currently it only supports simple files (`nt:file`) and folders (`nt:hierachyNode`) but detects special vault serialized files
+(`.content.xml`, `dialog.xml`, etc) and ignores them silently ([JCRVLT-125](https://issues.apache.org/jira/browse/JCRVLT-125)). Thus it is possible to use vault sync on a default vlt
 checkout.
 
 ### Installation and Configuration
@@ -135,14 +135,14 @@ initialize the sync root with the following files:
 : log file that contains information about the operations performed during syncing
 
 `.vlt-sync-filter.xml`
-: filter that is neede to configure what portions of the repository are synced.
+: filter that is needed to configure what portions of the repository are synced.
 
 The vault sync service can also be installed using the vlt command line tool. See _Vlt Integration_ below.
 
 ### .vlt-sync-filter.xml
 The sync filter has the format of a normal vault workspace filter. If the sync root lies on a vlt checkout, specifically
-points to a jcr_root directory of a such, then no `.vlt-sync-filter.xml` is initialized in the sync root, but the one
-defined by the respective vlt checkout is used. this is usually `META-INF/vault/filter.xml`.
+points to a `jcr_root` directory of a such, then no `.vlt-sync-filter.xml` is initialized in the sync root, but the one
+defined by the respective vlt checkout is used. This is usually `META-INF/vault/filter.xml`.
 
 ### .vlt-sync-config.properties
 This config has the following default content:
diff --git a/vault-core-it/vault-core-integration-tests/pom.xml b/vault-core-it/vault-core-integration-tests/pom.xml
index b9e85861..bb774d49 100644
--- a/vault-core-it/vault-core-integration-tests/pom.xml
+++ b/vault-core-it/vault-core-integration-tests/pom.xml
@@ -44,6 +44,19 @@
             <version>${project.version}</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit.vault</groupId>
+            <artifactId>vault-sync</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <!-- transitive dependency of vault-sync -->
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.jcr.api</artifactId>
+            <version>2.0.6</version>
+            <scope>compile</scope>
+        </dependency>
         <!-- transitive dependencies of org.apache.jackrabbit.vault but must be declared due to https://issues.apache.org/jira/browse/JCRVLT-394 -->
         <dependency>
             <groupId>org.jetbrains</groupId>
@@ -66,6 +79,12 @@
             <scope>compile</scope>
         </dependency>
         <!-- end transitive dependencies -->
+        <dependency>
+            <groupId>org.apache.jackrabbit.vault</groupId>
+            <artifactId>vault-core-it-support-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-jcr-commons</artifactId>
@@ -144,6 +163,14 @@
             <version>1.0.9</version>
             <scope>compile</scope>
         </dependency>
+        
+        <!-- default test execution providers (only used inside IDE) -->
+        <dependency>
+            <groupId>org.apache.jackrabbit.vault</groupId>
+            <artifactId>vault-core-it-support-oak</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     
diff --git a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
index e5be2971..6f57704b 100644
--- a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
+++ b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/IntegrationTestBase.java
@@ -36,7 +36,6 @@ import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.attribute.BasicFileAttributes;
@@ -70,7 +69,6 @@ import javax.jcr.security.AccessControlPolicyIterator;
 import javax.jcr.security.Privilege;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.file.PathUtils;
 import org.apache.commons.lang3.SystemUtils;
 import org.apache.jackrabbit.api.JackrabbitSession;
 import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry;
@@ -85,6 +83,8 @@ import org.apache.jackrabbit.vault.fs.io.FileArchive;
 import org.apache.jackrabbit.vault.fs.io.ImportOptions;
 import org.apache.jackrabbit.vault.fs.io.ZipArchive;
 import org.apache.jackrabbit.vault.fs.io.ZipStreamArchive;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProvider;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProvider.RepositoryWithMetadata;
 import org.apache.jackrabbit.vault.packaging.JcrPackage;
 import org.apache.jackrabbit.vault.packaging.PackageException;
 import org.apache.jackrabbit.vault.packaging.PackageId;
@@ -93,7 +93,6 @@ import org.apache.jackrabbit.vault.packaging.events.impl.PackageEventDispatcherI
 import org.apache.jackrabbit.vault.packaging.impl.ActivityLog;
 import org.apache.jackrabbit.vault.packaging.impl.JcrPackageManagerImpl;
 import org.apache.jackrabbit.vault.packaging.impl.ZipVaultPackage;
-import org.apache.jackrabbit.vault.packaging.integration.RepositoryProvider.RepositoryWithMetadata;
 import org.apache.jackrabbit.vault.packaging.registry.impl.JcrPackageRegistry;
 import org.junit.After;
 import org.junit.AfterClass;
diff --git a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
index e05096ae..b44b44e3 100644
--- a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
+++ b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/NamespaceImportIT.java
@@ -40,11 +40,11 @@ import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
 import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.io.Archive;
 import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProvider.RepositoryWithMetadata;
 import org.apache.jackrabbit.vault.packaging.ExportOptions;
 import org.apache.jackrabbit.vault.packaging.PackageException;
 import org.apache.jackrabbit.vault.packaging.VaultPackage;
 import org.apache.jackrabbit.vault.packaging.impl.JcrPackageManagerImpl;
-import org.apache.jackrabbit.vault.packaging.integration.RepositoryProvider.RepositoryWithMetadata;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
diff --git a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImplIT.java b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImplIT.java
new file mode 100644
index 00000000..7fc17a4d
--- /dev/null
+++ b/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImplIT.java
@@ -0,0 +1,130 @@
+/*
+ * 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.jackrabbit.vault.sync.impl;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.vault.packaging.integration.IntegrationTestBase;
+import org.apache.jackrabbit.vault.util.MimeTypes;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class VaultSyncServiceImplIT extends IntegrationTestBase {
+
+    @Rule
+    public TemporaryFolder tmpFolder = new TemporaryFolder();
+
+    @Test
+    public void testAddRemoveFileFromNonVltCheckoutFolder() throws RepositoryException, IOException, InterruptedException {
+        Path syncRootDirectory1 = tmpFolder.newFolder().toPath();
+        Session newAdminSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
+        // create new session
+        VaultSyncServiceImpl service = new VaultSyncServiceImpl(newAdminSession, true, 250, Collections.singleton(syncRootDirectory1.toFile()));
+        try {
+            // verify filter is created automatically
+            Path filterFile = syncRootDirectory1.resolve(".vlt-sync-filter.xml");
+            assertTrue(Files.exists(filterFile));
+            // check for file not yet being there in repo
+            assertNodeMissing("/testroot/testfile1.txt");
+            // modify filter
+            try (InputStream input = this.getClass().getResourceAsStream("filter.xml")) {
+                Files.copy(input, filterFile, StandardCopyOption.REPLACE_EXISTING);
+            }
+            Path syncDirectory = syncRootDirectory1.resolve("testroot");
+            try (InputStream input = this.getClass().getResourceAsStream("testfile1.txt")) {
+                Files.createDirectories(syncDirectory);
+                Files.copy(input, syncDirectory.resolve("testfile.txt"));
+            }
+            // wait a bit
+            Thread.sleep(500);
+            admin.refresh(false);
+            // check for file being added to repo
+            assertNodeExists("/testroot/testfile.txt");
+            // now change file
+            try (InputStream input = this.getClass().getResourceAsStream("testfile2.txt")) {
+                Files.copy(input, syncDirectory.resolve("testfile.txt"), StandardCopyOption.REPLACE_EXISTING);
+            }
+            // wait a bit
+            Thread.sleep(500);
+            admin.refresh(false);
+            //admin.getNode("/testroot/testfile.txt").getProperty(JCR)
+            // check for file being added to repo again
+            assertNodeExists("/testroot/testfile.txt");
+        } finally {
+            service.deactivate();
+        }
+    }
+
+    @Test
+    public void testSyncOnceFromRepository() throws RepositoryException, IOException, InterruptedException {
+        Node testRootNode = admin.getNode("/").addNode("testroot", NodeType.NT_FOLDER);
+        try (InputStream input = this.getClass().getResourceAsStream("testfile1.txt")) {
+            JcrUtils.putFile(testRootNode, "testfile", MimeTypes.APPLICATION_OCTET_STREAM, input);
+        }
+        admin.save();
+        
+        Path syncRootDirectory1 = tmpFolder.newFolder().toPath();
+        Path filterFile = syncRootDirectory1.resolve(".vlt-sync-filter.xml");
+        try (InputStream input = this.getClass().getResourceAsStream("filter.xml")) {
+            Files.copy(input, filterFile, StandardCopyOption.REPLACE_EXISTING);
+        }
+        Path propertiesFile = syncRootDirectory1.resolve(".vlt-sync-config.properties");
+        try (InputStream input = this.getClass().getResourceAsStream(".vlt-sync-config-jcr2fs-once.properties")) {
+            Files.copy(input, propertiesFile, StandardCopyOption.REPLACE_EXISTING);
+        }
+        Session newAdminSession = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));
+        // create new session
+        VaultSyncServiceImpl service = new VaultSyncServiceImpl(newAdminSession, true, 250, Collections.singleton(syncRootDirectory1.toFile()));
+        try {
+            // wait a bit
+            Thread.sleep(1000);
+            Path syncFile = syncRootDirectory1.resolve(Paths.get("testroot", "testfile"));
+            assertTrue(Files.exists(syncFile));
+            FileTime lastModified1 = Files.getLastModifiedTime(syncFile);
+            
+            // now modify node
+            try (InputStream input = this.getClass().getResourceAsStream("testfile2.txt")) {
+                JcrUtils.putFile(testRootNode, "testfile", MimeTypes.APPLICATION_OCTET_STREAM, input);
+            }
+            admin.save();
+            Thread.sleep(1000);
+            FileTime lastModified2 = Files.getLastModifiedTime(syncFile);
+            MatcherAssert.assertThat(lastModified2, Matchers.greaterThan(lastModified1));
+        } finally {
+            service.deactivate();
+        }
+    }
+}
diff --git a/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/.vlt-sync-config-jcr2fs-once.properties b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/.vlt-sync-config-jcr2fs-once.properties
new file mode 100644
index 00000000..82e32cf6
--- /dev/null
+++ b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/.vlt-sync-config-jcr2fs-once.properties
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+#
+# Vault Sync Config File
+#
+# Note: Changes to this file are detected automatically if the Vault Sync Service is
+# running and this directory is configured as sync root.
+#
+# Supported Properties:
+# ---------------------
+#
+# disabled = ( "true" | "false" )
+#     Defines if syncing of this directory is generally disabled. It can be useful
+#     to disabled syncing temporarily if structural changes need to be done that required
+#     several modifications.
+#
+#     defaults to: false
+disabled=false
+
+# sync-once= ( "" | "JCR2FS" | "FS2JCR" )
+#     If non empty the next scan will trigger a full sync in the direction indicated.
+#
+#     JCR2FS: 'export' all content in the JCR repository and write to the local disk.
+#     FS2JCR: 'import' all content from the disk into the JCR repository.
+#
+#     defaults to: ""
+sync-once=JCR2FS
+
+# sync-log = <filename>
+#     Defines the filename of the sync log.
+#
+#     defaults to: .vlt-sync.log
+#
+#sync-log=.vlt-sync.log
diff --git a/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/filter.xml b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/filter.xml
new file mode 100644
index 00000000..bbbd616d
--- /dev/null
+++ b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/filter.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<workspaceFilter version="1.0">
+    <filter root="/testroot"/>
+</workspaceFilter>
diff --git a/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile1.txt b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile1.txt
new file mode 100644
index 00000000..19102815
--- /dev/null
+++ b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile1.txt
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile2.txt b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile2.txt
new file mode 100644
index 00000000..ba0e162e
--- /dev/null
+++ b/vault-core-it/vault-core-integration-tests/src/main/resources/org/apache/jackrabbit/vault/sync/impl/testfile2.txt
@@ -0,0 +1 @@
+bar
\ No newline at end of file
diff --git a/vault-core-it/vault-core-it-execution/pom.xml b/vault-core-it/vault-core-it-execution/pom.xml
index 7bbd880b..314fd6f2 100644
--- a/vault-core-it/vault-core-it-execution/pom.xml
+++ b/vault-core-it/vault-core-it-execution/pom.xml
@@ -47,6 +47,7 @@
     <build>
         <pluginManagement>
             <plugins>
+                <!--
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-deploy-plugin</artifactId>
@@ -58,9 +59,9 @@
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-install-plugin</artifactId>
                     <configuration>
-                        <skip>true</skip>
+                        <skip>false</skip>
                     </configuration>
-                </plugin>
+                </plugin>-->
                 <plugin>
                     <groupId>org.owasp</groupId>
                     <artifactId>dependency-check-maven</artifactId>
diff --git a/vault-core-it/vault-core-it-execution/vault-core-it-execution-oak-min/pom.xml b/vault-core-it/vault-core-it-execution/vault-core-it-execution-oak-min/pom.xml
index 46df73c8..00e32bab 100644
--- a/vault-core-it/vault-core-it-execution/vault-core-it-execution-oak-min/pom.xml
+++ b/vault-core-it/vault-core-it-execution/vault-core-it-execution-oak-min/pom.xml
@@ -52,51 +52,57 @@
             <groupId>org.apache.jackrabbit.vault</groupId>
             <artifactId>vault-core-it-support-oak</artifactId>
             <version>${project.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-core</artifactId>
             <version>${oak.min.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-core</artifactId>
             <version>${oak.min.version}</version>
             <classifier>tests</classifier>
-            <scope>test</scope>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-data</artifactId>
+            <version>2.20.1</version>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-jcr</artifactId>
             <version>${oak.min.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-segment-tar</artifactId>
             <version>${oak.min.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <!-- transitive artifactItem of oak-segment-tar, necessary for filedatastore (https://issues.apache.org/jira/browse/OAK-6542) -->
         <dependency>
             <groupId>io.dropwizard.metrics</groupId>
             <artifactId>metrics-core</artifactId>
             <version>3.2.3</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-authorization-principalbased</artifactId>
             <version>${oak.min.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-authorization-cug</artifactId>
             <version>${oak.min.version}</version>
-            <scope>test</scope>
+            <scope>runtime</scope>
         </dependency>
     </dependencies>
 
diff --git a/vault-core-it/vault-core-it-support/pom.xml b/vault-core-it/vault-core-it-support/pom.xml
index 81eac8a1..a4c10b0f 100644
--- a/vault-core-it/vault-core-it-support/pom.xml
+++ b/vault-core-it/vault-core-it-support/pom.xml
@@ -37,29 +37,9 @@
     <!-- M O D U L E S                                                          -->
     <!-- ====================================================================== -->
     <modules>
+        <module>vault-core-it-support-api</module>
         <module>vault-core-it-support-oak</module>
         <module>vault-core-it-support-jackrabbit</module>
     </modules>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.jetbrains</groupId>
-            <artifactId>annotations</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <!-- http://metainf-services.kohsuke.org/index.html -->
-        <dependency>
-            <groupId>org.kohsuke.metainf-services</groupId>
-            <artifactId>metainf-services</artifactId>
-            <version>1.8</version>
-            <optional>true</optional>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.jackrabbit.vault</groupId>
-            <artifactId>vault-core-integration-tests</artifactId>
-            <version>${project.version}</version>
-            <scope>compile</scope>
-        </dependency>
-    </dependencies>
 </project>
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml b/vault-core-it/vault-core-it-support/vault-core-it-support-api/pom.xml
similarity index 73%
copy from vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml
copy to vault-core-it/vault-core-it-support/vault-core-it-support-api/pom.xml
index 34e27adf..fa946501 100644
--- a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-api/pom.xml
@@ -28,21 +28,26 @@
     <!-- ====================================================================== -->
     <!-- P R O J E C T  D E S C R I P T I O N                                   -->
     <!-- ====================================================================== -->
-    <artifactId>vault-core-it-support-jackrabbit</artifactId>
+    <artifactId>vault-core-it-support-api</artifactId>
 
-    <name>Apache Jackrabbit FileVault Core IT Support Classes for Jackrabbit 2 Repositories</name>
-    <description>Provides repository provider for Jackrabbit</description>
+    <name>Apache Jackrabbit FileVault Core IT Support API</name>
+    <description>Defines repository provider API which is used to decouple repository implementation from integration test code</description>
 
-    <!-- ====================================================================== -->
-    <!-- D E P E N D E N C I E S                                                -->
-    <!-- ====================================================================== -->
     <dependencies>
         <dependency>
-            <groupId>org.apache.jackrabbit</groupId>
-            <artifactId>jackrabbit-core</artifactId>
-            <version>${jackrabbit.min.version}</version>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
             <scope>compile</scope>
         </dependency>
     </dependencies>
-
 </project>
diff --git a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/RepositoryProvider.java b/vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProvider.java
similarity index 94%
rename from vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/RepositoryProvider.java
rename to vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProvider.java
index a4f13471..b35fe005 100644
--- a/vault-core-it/vault-core-integration-tests/src/main/java/org/apache/jackrabbit/vault/packaging/integration/RepositoryProvider.java
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProvider.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.jackrabbit.vault.packaging.integration;
+package org.apache.jackrabbit.vault.integration.support;
 
 import java.io.IOException;
 import java.util.Map;
@@ -24,7 +24,7 @@ import javax.jcr.RepositoryException;
 
 public interface RepositoryProvider {
 
-    public final static class RepositoryWithMetadata {
+    public static final class RepositoryWithMetadata {
         private final Repository repository;
         private final Map<String, Object> metadata;
         public RepositoryWithMetadata(Repository repository, Map<String, Object> metadata) {
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProviderHelper.java b/vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProviderHelper.java
new file mode 100644
index 00000000..737eb0e6
--- /dev/null
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-api/src/main/java/org/apache/jackrabbit/vault/integration/support/RepositoryProviderHelper.java
@@ -0,0 +1,33 @@
+package org.apache.jackrabbit.vault.integration.support;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.SystemUtils;
+
+public class RepositoryProviderHelper {
+    private RepositoryProviderHelper() {
+        // only static methods
+    }
+    
+    public static void deleteDirectory(File directory) throws IOException {
+        try {
+            FileUtils.deleteDirectory(directory);
+        } catch (IOException ioe) {
+            // retry after wait on Windows, as it may release file locks in a deferred manner
+            if (SystemUtils.IS_OS_WINDOWS) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException ie) {
+                    Thread.currentThread().interrupt();
+                    IOException wrappedIOException = new IOException("Initially failed with IOException and waiting was interrupted", ioe);
+                    wrappedIOException.addSuppressed(ie);
+                }
+                FileUtils.deleteDirectory(directory);
+            } else {
+                throw ioe;
+            }
+        }
+    }
+}
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml b/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml
index 34e27adf..58e8814e 100644
--- a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/pom.xml
@@ -37,12 +37,31 @@
     <!-- D E P E N D E N C I E S                                                -->
     <!-- ====================================================================== -->
     <dependencies>
+        <dependency>
+            <groupId>org.apache.jackrabbit.vault</groupId>
+            <artifactId>vault-core-it-support-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-core</artifactId>
             <version>${jackrabbit.min.version}</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- http://metainf-services.kohsuke.org/index.html -->
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+            <version>1.8</version>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/src/main/java/org/apache/jackrabbit/vault/it/JackrabbitRepositoryProvider.java b/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/src/main/java/org/apache/jackrabbit/vault/it/JackrabbitRepositoryProvider.java
index 5a6c0f9d..83457112 100644
--- a/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/src/main/java/org/apache/jackrabbit/vault/it/JackrabbitRepositoryProvider.java
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-jackrabbit/src/main/java/org/apache/jackrabbit/vault/it/JackrabbitRepositoryProvider.java
@@ -25,8 +25,8 @@ import javax.jcr.RepositoryException;
 
 import org.apache.jackrabbit.core.RepositoryImpl;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
-import org.apache.jackrabbit.vault.packaging.integration.IntegrationTestBase;
-import org.apache.jackrabbit.vault.packaging.integration.RepositoryProvider;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProvider;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProviderHelper;
 import org.kohsuke.MetaInfServices;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -37,14 +37,14 @@ public class JackrabbitRepositoryProvider implements RepositoryProvider {
     /**
      * default logger
      */
-    private static final Logger log = LoggerFactory.getLogger(IntegrationTestBase.class);
+    private static final Logger log = LoggerFactory.getLogger(JackrabbitRepositoryProvider.class);
 
     private static final File DIR_JR2_REPO_HOME = new File("target", "repository-jr2-" + System.getProperty("repoSuffix", "fork1"));
 
     @Override
     public RepositoryWithMetadata createRepository(boolean arg0, boolean arg1, String... arg2) throws RepositoryException, IOException {
         Repository repository;
-        try (InputStream in = IntegrationTestBase.class.getResourceAsStream("/repository.xml")) {
+        try (InputStream in = JackrabbitRepositoryProvider.class.getResourceAsStream("/repository.xml")) {
             RepositoryConfig cfg = RepositoryConfig.create(in, DIR_JR2_REPO_HOME.getPath());
             repository = RepositoryImpl.create(cfg);
         }
@@ -54,7 +54,7 @@ public class JackrabbitRepositoryProvider implements RepositoryProvider {
     @Override
     public void closeRepository(RepositoryWithMetadata repositoryWithMetadata) throws IOException {
         ((RepositoryImpl) repositoryWithMetadata.getRepository()).shutdown();
-        IntegrationTestBase.deleteDirectory(DIR_JR2_REPO_HOME);
+        RepositoryProviderHelper.deleteDirectory(DIR_JR2_REPO_HOME);
     }
 
     @Override
@@ -66,4 +66,5 @@ public class JackrabbitRepositoryProvider implements RepositoryProvider {
     public boolean isOak() {
         return false;
     }
+
 }
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-oak/pom.xml b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/pom.xml
index d7a88e7c..dbcdd036 100644
--- a/vault-core-it/vault-core-it-support/vault-core-it-support-oak/pom.xml
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/pom.xml
@@ -37,6 +37,12 @@
     <!-- D E P E N D E N C I E S                                                -->
     <!-- ====================================================================== -->
     <dependencies>
+        <dependency>
+            <groupId>org.apache.jackrabbit.vault</groupId>
+            <artifactId>vault-core-it-support-api</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>oak-core</artifactId>
@@ -80,6 +86,19 @@
             <version>1.0.14</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <!-- http://metainf-services.kohsuke.org/index.html -->
+        <dependency>
+            <groupId>org.kohsuke.metainf-services</groupId>
+            <artifactId>metainf-services</artifactId>
+            <version>1.8</version>
+            <optional>true</optional>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/vault-core-it/vault-core-it-support/vault-core-it-support-oak/src/main/java/org/apache/jackrabbit/vault/it/OakRepositoryProvider.java b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/src/main/java/org/apache/jackrabbit/vault/it/OakRepositoryProvider.java
index 1fcda0be..7d797fee 100644
--- a/vault-core-it/vault-core-it-support/vault-core-it-support-oak/src/main/java/org/apache/jackrabbit/vault/it/OakRepositoryProvider.java
+++ b/vault-core-it/vault-core-it-support/vault-core-it-support-oak/src/main/java/org/apache/jackrabbit/vault/it/OakRepositoryProvider.java
@@ -58,8 +58,8 @@ import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.action.AccessControlAction;
 import org.apache.jackrabbit.oak.spi.xml.ImportBehavior;
 import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter;
-import org.apache.jackrabbit.vault.packaging.integration.IntegrationTestBase;
-import org.apache.jackrabbit.vault.packaging.integration.RepositoryProvider;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProvider;
+import org.apache.jackrabbit.vault.integration.support.RepositoryProviderHelper;
 import org.kohsuke.MetaInfServices;
 import org.osgi.util.converter.Converters;
 import org.slf4j.Logger;
@@ -76,7 +76,7 @@ public class OakRepositoryProvider implements RepositoryProvider {
     /**
      * default logger
      */
-    private static final Logger log = LoggerFactory.getLogger(IntegrationTestBase.class);
+    private static final Logger log = LoggerFactory.getLogger(OakRepositoryProvider.class);
 
     
     private static final File DIR_OAK_REPO_HOME = new File("target", "repository-oak-" + System.getProperty("repoSuffix", "fork1"));
@@ -186,7 +186,7 @@ public class OakRepositoryProvider implements RepositoryProvider {
         if (fileStore != null) {
             fileStore.close();
         }
-        IntegrationTestBase.deleteDirectory(DIR_OAK_REPO_HOME);
+        RepositoryProviderHelper.deleteDirectory(DIR_OAK_REPO_HOME);
     }
 
     @Override
diff --git a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/SyncLog.java b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/SyncLog.java
index edb5d5a6..22f749a6 100644
--- a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/SyncLog.java
+++ b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/SyncLog.java
@@ -19,7 +19,7 @@ package org.apache.jackrabbit.vault.sync.impl;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.time.Instant;
+import java.time.OffsetDateTime;
 import java.time.format.DateTimeFormatter;
 
 import org.slf4j.Logger;
@@ -35,8 +35,6 @@ public class SyncLog {
      */
     private static final Logger log = LoggerFactory.getLogger(SyncLog.class);
 
-    private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss ");
-
     private final File logFile;
 
     public SyncLog(File logFile) {
@@ -48,7 +46,7 @@ public class SyncLog {
         log.info("{}", msg);
 
         StringBuilder line = new StringBuilder();
-        line.append(DATE_FMT.format(Instant.now()));
+        line.append(DateTimeFormatter.ISO_DATE_TIME.format(OffsetDateTime.now()));
         line.append(msg);
         line.append("\n");
         try (FileWriter writer = new FileWriter(logFile, true)) {
diff --git a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/TreeSync.java b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/TreeSync.java
index 1d84cf31..60c1d162 100644
--- a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/TreeSync.java
+++ b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/TreeSync.java
@@ -34,10 +34,12 @@ import javax.jcr.nodetype.NodeType;
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.vault.fs.api.SerializationType;
 import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
 import org.apache.jackrabbit.vault.util.FileInputSource;
 import org.apache.jackrabbit.vault.util.MimeTypes;
+import org.apache.jackrabbit.vault.util.PathUtil;
 import org.apache.jackrabbit.vault.util.PlatformNameFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -485,8 +487,9 @@ public class TreeSync {
             if (parentNode == null && node == null) {
                 return null;
             }
+            
             return node == null
-                    ? parentNode.getPath() + "/" + jcrName
+                    ? PathUtil.append(parentNode.getPath(), jcrName)
                     : node.getPath();
 
         }
diff --git a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImpl.java b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImpl.java
index b3d01528..c176c360 100644
--- a/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImpl.java
+++ b/vault-sync/src/main/java/org/apache/jackrabbit/vault/sync/impl/VaultSyncServiceImpl.java
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.vault.sync.impl;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -26,6 +28,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
 
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -64,9 +67,6 @@ public class VaultSyncServiceImpl implements EventListener, Runnable {
 
     public static final String SYNC_ENABLED = "vault.sync.enabled";
 
-    @Reference
-    private SlingRepository repository;
-
     private Session session;
 
     private SyncHandler[] syncHandlers = new SyncHandler[0];
@@ -105,22 +105,27 @@ public class VaultSyncServiceImpl implements EventListener, Runnable {
     }
 
     @Activate
-    protected void activate(Config config) throws RepositoryException {
-        List<SyncHandler> newSyncSpecs = new LinkedList<SyncHandler>();
-        for (String def : config.vault_sync_syncroots()) {
-            SyncHandler spec = new SyncHandler(new File(def));
+    public VaultSyncServiceImpl(@Reference SlingRepository slingRepository, Config config) throws RepositoryException {
+        this(slingRepository.loginAdministrative(null), 
+                config.vault_sync_enabled(), 
+                config.vault_sync_fscheckinterval() * 1000l, 
+                Arrays.stream(config.vault_sync_syncroots()).map(File::new).collect(Collectors.toList()));
+    }
+
+    VaultSyncServiceImpl(Session session, boolean isEnabled, long fsCheckIntervalMilliseconds, Collection<File> syncRoots) throws RepositoryException {
+        List<SyncHandler> newSyncSpecs = new LinkedList<>();
+        for (File syncRoot : syncRoots) {
+            SyncHandler spec = new SyncHandler(syncRoot);
             newSyncSpecs.add(spec);
             log.info("Added sync specification: {}", spec);
         }
+        this.session = session;
         syncHandlers = newSyncSpecs.toArray(new SyncHandler[newSyncSpecs.size()]);
-        enabled = config.vault_sync_enabled();
-        checkDelay = config.vault_sync_fscheckinterval() * 1000;
+        enabled = isEnabled;
+        checkDelay = fsCheckIntervalMilliseconds;
 
         log.info("Vault Sync service is {}", enabled ? "enabled" : "disabled");
         if (enabled) {
-            // setup session
-            session = repository.loginAdministrative(null);
-
             // set up observation listener
             session.getWorkspace().getObservationManager().addEventListener(
                     this,
@@ -142,10 +147,6 @@ public class VaultSyncServiceImpl implements EventListener, Runnable {
     protected void deactivate() {
         waitLock.lock();
         try {
-            if (session != null) {
-                session.logout();
-                session = null;
-            }
             enabled = false;
             waitCondition.signalAll();
         } finally {
@@ -160,6 +161,11 @@ public class VaultSyncServiceImpl implements EventListener, Runnable {
             }
             fsCheckThread = null;
         }
+        // session is still accessed in fsCheckThread, therefore close it last
+        if (session != null) {
+            session.logout();
+            session = null;
+        }
     }
 
     public void run() {