You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:37:22 UTC
[sling-org-apache-sling-fsresource] 07/23: SLING-6537 FileVault XML
support
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.fsresource-1.3.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-fsresource.git
commit af87b046380f8cb60220503b6d6891a1599eb9b4
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Mon Mar 6 21:48:44 2017 +0000
SLING-6537 FileVault XML support
git-svn-id: https://svn.apache.org/repos/asf/sling/branches/fsresource-1.1.x@1785772 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 2 +-
.../fsprovider/internal/ContentFileExtensions.java | 3 +-
.../sling/fsprovider/internal/FileMonitor.java | 159 ++++++++------
.../apache/sling/fsprovider/internal/FsMode.java | 41 ++++
.../fsprovider/internal/FsResourceProvider.java | 116 +++++++---
.../internal/InitialContentImportOptions.java | 76 +++++++
.../fsprovider/internal/mapper/ContentFile.java | 53 +++--
.../internal/mapper/ContentFileResourceMapper.java | 16 +-
.../internal/mapper/FileVaultResourceMapper.java | 201 +++++++++++++++++
.../fsprovider/internal/mapper/jcr/FsNode.java | 20 +-
.../internal/mapper/jcr/FsNodeIterator.java | 9 +-
.../internal/parser/ContentFileParserUtil.java | 6 +-
.../sling/fsprovider/internal/FileMonitorTest.java | 129 +++++------
.../fsprovider/internal/FileVaultContentTest.java | 143 ++++++++++++
.../internal/FileVaultFileMonitorTest.java | 239 +++++++++++++++++++++
.../internal/InitialContentImportOptionsTest.java | 59 +++++
.../fsprovider/internal/JcrXmlContentTest.java | 5 +-
.../sling/fsprovider/internal/JsonContentTest.java | 7 +-
.../sling/fsprovider/internal/TestUtils.java | 33 +++
.../vaultfs-test/META-INF/vault/filter.xml | 4 +-
.../jcr_root/content/dam/talk.png/.content.xml | 2 -
21 files changed, 1103 insertions(+), 220 deletions(-)
diff --git a/pom.xml b/pom.xml
index f7d4a3c..e530623 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
<parent>
<groupId>org.apache.sling</groupId>
<artifactId>sling</artifactId>
- <version>30-SNAPSHOT</version>
+ <version>30</version>
<relativePath />
</parent>
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java b/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
index 5097750..f9cacec 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
@@ -41,8 +41,9 @@ public final class ContentFileExtensions {
* @return Content file name suffix or null if not a context file.
*/
public String getSuffix(File file) {
+ String fileName = "/" + file.getName();
for (String suffix : contentFileSuffixes) {
- if (StringUtils.endsWith(file.getName(), suffix)) {
+ if (StringUtils.endsWith(fileName, suffix)) {
return suffix;
}
}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
index 609fe58..4ac056a 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
@@ -18,9 +18,17 @@
*/
package org.apache.sling.fsprovider.internal;
+import static org.apache.jackrabbit.vault.util.Constants.ROOT_DIR;
+import static org.apache.sling.api.SlingConstants.PROPERTY_PATH;
+import static org.apache.sling.api.SlingConstants.PROPERTY_RESOURCE_TYPE;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Dictionary;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
@@ -28,7 +36,7 @@ import java.util.Timer;
import java.util.TimerTask;
import org.apache.commons.lang3.StringUtils;
-import org.apache.sling.api.SlingConstants;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
import org.apache.sling.fsprovider.internal.mapper.ContentFile;
import org.apache.sling.fsprovider.internal.mapper.FileResource;
import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
@@ -51,7 +59,7 @@ public final class FileMonitor extends TimerTask {
private final Monitorable root;
private final FsResourceProvider provider;
-
+ private final FsMode fsMode;
private final ContentFileExtensions contentFileExtensions;
private final ContentFileCache contentFileCache;
@@ -60,12 +68,19 @@ public final class FileMonitor extends TimerTask {
* @param provider The resource provider.
* @param interval The interval between executions of the task, in milliseconds.
*/
- public FileMonitor(final FsResourceProvider provider, final long interval,
+ public FileMonitor(final FsResourceProvider provider, final long interval, FsMode fsMode,
final ContentFileExtensions contentFileExtensions, final ContentFileCache contentFileCache) {
this.provider = provider;
+ this.fsMode = fsMode;
this.contentFileExtensions = contentFileExtensions;
this.contentFileCache = contentFileCache;
- this.root = new Monitorable(this.provider.getProviderRoot(), this.provider.getRootFile(), null);
+
+ File rootFile = this.provider.getRootFile();
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ rootFile = new File(this.provider.getRootFile(), ROOT_DIR + PlatformNameFormat.getPlatformPath(this.provider.getProviderRoot()));
+ }
+ this.root = new Monitorable(this.provider.getProviderRoot(), rootFile, null);
+
createStatus(this.root, contentFileExtensions, contentFileCache);
log.debug("Starting file monitor for {} with an interval of {}ms", this.root.file, interval);
timer.schedule(this, 0, interval);
@@ -116,9 +131,9 @@ public final class FileMonitor extends TimerTask {
synchronized ( this ) {
try {
// if we don't have an event admin, we just skip the check
- final EventAdmin localEA = this.provider.getEventAdmin();
- if ( localEA != null ) {
- this.check(this.root, localEA);
+ final EventAdmin reporter = this.provider.getEventAdmin();
+ if ( reporter != null ) {
+ this.check(this.root, reporter);
}
} catch (Exception e) {
// ignore this
@@ -133,22 +148,29 @@ public final class FileMonitor extends TimerTask {
/**
* Check the monitorable
* @param monitorable The monitorable to check
- * @param localEA The event admin
+ * @param reporter The EventAdmin
*/
- private void check(final Monitorable monitorable, final EventAdmin localEA) {
+ private void check(final Monitorable monitorable, final EventAdmin reporter) {
log.trace("Checking {}", monitorable.file);
// if the file is non existing, check if it has been readded
if ( monitorable.status instanceof NonExistingStatus ) {
if ( monitorable.file.exists() ) {
// new file and reset status
createStatus(monitorable, contentFileExtensions, contentFileCache);
- sendEvents(monitorable, SlingConstants.TOPIC_RESOURCE_ADDED, localEA);
+ sendEvents(monitorable, TOPIC_RESOURCE_ADDED, reporter);
+ final FileStatus fs = (FileStatus)monitorable.status;
+ if ( fs instanceof DirStatus ) {
+ final DirStatus ds = (DirStatus)fs;
+ // remove monitorables for new folder and update folder children to send events for directory contents
+ ds.children = new Monitorable[0];
+ checkDirStatusChildren(monitorable, reporter);
+ }
}
} else {
// check if the file has been removed
if ( !monitorable.file.exists() ) {
// removed file and update status
- sendEvents(monitorable, SlingConstants.TOPIC_RESOURCE_REMOVED, localEA);
+ sendEvents(monitorable, TOPIC_RESOURCE_REMOVED, reporter);
monitorable.status = NonExistingStatus.SINGLETON;
contentFileCache.remove(monitorable.path);
} else {
@@ -158,7 +180,7 @@ public final class FileMonitor extends TimerTask {
if ( fs.lastModified < monitorable.file.lastModified() ) {
fs.lastModified = monitorable.file.lastModified();
// changed
- sendEvents(monitorable, SlingConstants.TOPIC_RESOURCE_CHANGED, localEA);
+ sendEvents(monitorable, TOPIC_RESOURCE_CHANGED, reporter);
changed = true;
contentFileCache.remove(monitorable.path);
}
@@ -166,105 +188,122 @@ public final class FileMonitor extends TimerTask {
// directory
final DirStatus ds = (DirStatus)fs;
for(int i=0; i<ds.children.length; i++) {
- check(ds.children[i], localEA);
+ check(ds.children[i], reporter);
}
// if the dir changed we have to update
if ( changed ) {
// and now update
- final File[] files = monitorable.file.listFiles();
- if (files != null) {
- final Monitorable[] children = new Monitorable[files.length];
- for (int i = 0; i < files.length; i++) {
- // search in old list
- for (int m = 0; m < ds.children.length; m++) {
- if (ds.children[m].file.equals(files[i])) {
- children[i] = ds.children[m];
- break;
- }
- }
- if (children[i] == null) {
- children[i] = new Monitorable(monitorable.path + '/' + files[i].getName(), files[i],
- contentFileExtensions.getSuffix(files[i]));
- children[i].status = NonExistingStatus.SINGLETON;
- check(children[i], localEA);
- }
- }
- ds.children = children;
- } else {
- ds.children = new Monitorable[0];
- }
+ checkDirStatusChildren(monitorable, reporter);
}
}
}
}
}
+
+ private void checkDirStatusChildren(final Monitorable dirMonitorable, final EventAdmin reporter) {
+ final DirStatus ds = (DirStatus)dirMonitorable.status;
+ final File[] files = dirMonitorable.file.listFiles();
+ if (files != null) {
+ final Monitorable[] children = new Monitorable[files.length];
+ for (int i = 0; i < files.length; i++) {
+ // search in old list
+ for (int m = 0; m < ds.children.length; m++) {
+ if (ds.children[m].file.equals(files[i])) {
+ children[i] = ds.children[m];
+ break;
+ }
+ }
+ if (children[i] == null) {
+ children[i] = new Monitorable(dirMonitorable.path + '/' + files[i].getName(), files[i],
+ contentFileExtensions.getSuffix(files[i]));
+ children[i].status = NonExistingStatus.SINGLETON;
+ check(children[i], reporter);
+ }
+ }
+ ds.children = children;
+ } else {
+ ds.children = new Monitorable[0];
+ }
+ }
/**
* Send the event async via the event admin.
*/
- private void sendEvents(final Monitorable monitorable, final String topic, final EventAdmin localEA) {
+ private void sendEvents(final Monitorable monitorable, final String changeType, final EventAdmin reporter) {
if (log.isDebugEnabled()) {
- log.debug("Detected change for resource {} : {}", monitorable.path, topic);
+ log.debug("Detected change for resource {} : {}", transformPath(monitorable.path), changeType);
}
- List<ResourceChange> changes = collectResourceChanges(monitorable, topic);
+ List<ResourceChange> changes = collectResourceChanges(monitorable, changeType);
for (ResourceChange change : changes) {
if (log.isTraceEnabled()) {
- log.debug("Send change for resource {}: {}", change.path, change.topic);
+ log.debug("Send change for resource {}: {}", transformPath(change.path), change.topic);
}
- final Dictionary<String, String> properties = new Hashtable<String, String>();
- properties.put(SlingConstants.PROPERTY_PATH, change.path);
+ final Dictionary<String, String> properties = new Hashtable<>();
+ properties.put(PROPERTY_PATH, transformPath(change.path));
if (change.resourceType != null) {
- properties.put(SlingConstants.PROPERTY_RESOURCE_TYPE, change.resourceType);
+ properties.put(PROPERTY_RESOURCE_TYPE, change.resourceType);
}
- localEA.postEvent(new org.osgi.service.event.Event(change.topic, properties));
+ reporter.postEvent(new org.osgi.service.event.Event(change.topic, properties));
}
}
+ /**
+ * Transform path for resource event.
+ * @param path Path
+ * @return Transformed path
+ */
+ private String transformPath(String path) {
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ return PlatformNameFormat.getRepositoryPath(path);
+ }
+ else {
+ return path;
+ }
+ }
+
@SuppressWarnings("unchecked")
- private List<ResourceChange> collectResourceChanges(final Monitorable monitorable, final String topic) {
+ private List<ResourceChange> collectResourceChanges(final Monitorable monitorable, final String changeType) {
List<ResourceChange> changes = new ArrayList<>();
if (monitorable.status instanceof ContentFileStatus) {
ContentFile contentFile = ((ContentFileStatus)monitorable.status).contentFile;
- if (StringUtils.equals(topic, SlingConstants.TOPIC_RESOURCE_CHANGED)) {
+ if (StringUtils.equals(changeType, TOPIC_RESOURCE_CHANGED)) {
Map<String,Object> content = (Map<String,Object>)contentFile.getContent();
// we cannot easily report the diff of resource changes between two content files
// so we simulate a removal of the toplevel node and then add all nodes contained in the current content file again.
- changes.add(buildContentResourceChange(SlingConstants.TOPIC_RESOURCE_REMOVED, content, monitorable.path));
- addContentResourceChanges(changes, SlingConstants.TOPIC_RESOURCE_ADDED, content, monitorable.path);
+ changes.add(buildContentResourceChange(TOPIC_RESOURCE_REMOVED, content, transformPath(monitorable.path)));
+ addContentResourceChanges(changes, TOPIC_RESOURCE_ADDED, content, transformPath(monitorable.path));
}
else {
- addContentResourceChanges(changes, topic, (Map<String,Object>)contentFile.getContent(), monitorable.path);
+ addContentResourceChanges(changes, changeType, (Map<String,Object>)contentFile.getContent(), transformPath(monitorable.path));
}
}
else {
- ResourceChange change = new ResourceChange();
- change.path = monitorable.path;
- change.resourceType = monitorable.status instanceof FileStatus ?
- FileResource.RESOURCE_TYPE_FILE : FileResource.RESOURCE_TYPE_FOLDER;
- change.topic = topic;
- changes.add(change);
+ Map<String,Object> content = new HashMap<>();
+ content.put("sling:resourceType", monitorable.status instanceof FileStatus ?
+ FileResource.RESOURCE_TYPE_FILE : FileResource.RESOURCE_TYPE_FOLDER);
+ changes.add(buildContentResourceChange(changeType, content, transformPath(monitorable.path)));
}
return changes;
}
@SuppressWarnings("unchecked")
- private void addContentResourceChanges(final List<ResourceChange> changes, final String topic,
+ private void addContentResourceChanges(final List<ResourceChange> changes, final String changeType,
final Map<String,Object> content, final String path) {
- changes.add(buildContentResourceChange(topic, content, path));
+ changes.add(buildContentResourceChange(changeType, content, path));
if (content != null) {
for (Map.Entry<String,Object> entry : content.entrySet()) {
if (entry.getValue() instanceof Map) {
String childPath = path + "/" + entry.getKey();
- addContentResourceChanges(changes, topic, (Map<String,Object>)entry.getValue(), childPath);
+ addContentResourceChanges(changes, changeType, (Map<String,Object>)entry.getValue(), childPath);
}
}
}
}
- private ResourceChange buildContentResourceChange(final String topic, final Map<String,Object> content, final String path) {
+ private ResourceChange buildContentResourceChange(final String changeType, final Map<String,Object> content, final String path) {
ResourceChange change = new ResourceChange();
change.path = path;
change.resourceType = content != null ? (String)content.get("sling:resourceType") : null;
- change.topic = topic;
+ change.topic = changeType;
return change;
}
@@ -351,5 +390,5 @@ public final class FileMonitor extends TimerTask {
public String resourceType;
public String topic;
}
-
+
}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java b/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java
new file mode 100644
index 0000000..15af91f
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java
@@ -0,0 +1,41 @@
+/*
+ * 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.fsprovider.internal;
+
+/**
+ * Different modes for Filesystem provider support and filesystem layouts.
+ */
+public enum FsMode {
+
+ /**
+ * Sling-Initial-Content filesystem layout, with full support for JSON and jcr.xml content files.
+ */
+ INITIAL_CONTENT,
+
+ /**
+ * Sling-Initial-Content filesystem layout, support only files and folders (classic mode).
+ */
+ INITIAL_CONTENT_FILES_FOLDERS,
+
+ /**
+ * FileVault XML format (expanded content package).
+ */
+ FILEVAULT_XML
+
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
index 12e5bf2..6a9d45f 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
@@ -18,6 +18,8 @@
*/
package org.apache.sling.fsprovider.internal;
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
+
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
@@ -35,6 +37,7 @@ import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.fsprovider.internal.mapper.ContentFileResourceMapper;
import org.apache.sling.fsprovider.internal.mapper.FileResourceMapper;
+import org.apache.sling.fsprovider.internal.mapper.FileVaultResourceMapper;
import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
import org.apache.sling.fsprovider.internal.parser.ContentFileTypes;
import org.osgi.framework.BundleContext;
@@ -50,6 +53,7 @@ import org.osgi.service.event.EventAdmin;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.service.metatype.annotations.Option;
/**
* The <code>FsResourceProvider</code> is a resource provider which maps
@@ -72,6 +76,11 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@Designate(ocd=FsResourceProvider.Config.class, factory=true)
public final class FsResourceProvider implements ResourceProvider {
+ /**
+ * Resource metadata property set by {@link FsResource} if the underlying file reference is a directory.
+ */
+ public static final String RESOURCE_METADATA_FILE_DIRECTORY = ":org.apache.sling.fsprovider.file.directory";
+
@ObjectClassDefinition(name = "Apache Sling File System Resource Provider",
description = "Configure an instance of the filesystem " +
"resource provider in terms of provider root and filesystem location")
@@ -104,17 +113,25 @@ public final class FsResourceProvider implements ResourceProvider {
"filesystem resources are mapped in. This property must contain at least one non-empty string.")
String[] provider_roots();
- @AttributeDefinition(name = "Mount json",
- description = "Mount .json files as content in the resource hierarchy.")
- boolean provider_json_content();
-
- @AttributeDefinition(name = "Mount jcr.xml",
- description = "Mount .jcr.xml files as content in the resource hierarchy.")
- boolean provider_jcrxml_content();
+ @AttributeDefinition(name = "Filesystem layout",
+ description = "Filesystem layout mode for files, folders and content.",
+ options={
+ @Option(value="INITIAL_CONTENT", label="INITIAL_CONTENT - "
+ + "Sling-Initial-Content filesystem layout, with full support for JSON and jcr.xml content files."),
+ @Option(value="INITIAL_CONTENT_FILES_FOLDERS", label="INITIAL_CONTENT_FILES_FOLDERS - "
+ + "Sling-Initial-Content filesystem layout, support only files and folders (classic mode)."),
+ @Option(value="FILEVAULT_XML", label="FILEVAULT_XML - "
+ + "FileVault XML format (expanded content package)."),
+ })
+ FsMode provider_fs_mode() default FsMode.INITIAL_CONTENT;
+
+ @AttributeDefinition(name = "Init. Content Options",
+ description = "Import options for Sling-Initial-Content filesystem layout. Supported options: overwrite, ignoreImportProviders.")
+ String provider_initial_content_import_options();
@AttributeDefinition(name = "Cache Size",
description = "Max. number of content files cached in memory.")
- int provider_cache_size() default 1000;
+ int provider_cache_size() default 10000;
/**
* Internal Name hint for web console.
@@ -132,8 +149,10 @@ public final class FsResourceProvider implements ResourceProvider {
private FileMonitor monitor;
// maps filesystem to resources
+ private FsMode fsMode;
private FsResourceMapper fileMapper;
private FsResourceMapper contentFileMapper;
+ private FileVaultResourceMapper fileVaultMapper;
// cache for parsed content files
private ContentFileCache contentFileCache;
@@ -155,34 +174,56 @@ public final class FsResourceProvider implements ResourceProvider {
* to access the file or folder. If no such file or folder exists, this
* method returns <code>null</code>.
*/
+ @SuppressWarnings("rawtypes")
@Override
public Resource getResource(ResourceResolver resolver, String path) {
- Resource rsrc = contentFileMapper.getResource(resolver, path);
- if (rsrc == null) {
- rsrc = fileMapper.getResource(resolver, path);
+
+ Resource rsrc = null;
+
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ // filevault: check if path matches, if not fallback to parent resource provider
+ if (fileVaultMapper.pathMatches(path)) {
+ rsrc = fileVaultMapper.getResource(resolver, path);
+ }
+ }
+ else {
+ // Sling-Initial-Content: mount folder/files an content files
+ rsrc = contentFileMapper.getResource(resolver, path);
+ if (rsrc == null) {
+ rsrc = fileMapper.getResource(resolver, path);
+ }
}
+
return rsrc;
}
-
+
/**
* Returns an iterator of resources.
*/
@SuppressWarnings("unchecked")
- @Override
public Iterator<Resource> listChildren(Resource parent) {
ResourceResolver resolver = parent.getResourceResolver();
List<Iterator<Resource>> allChildren = new ArrayList<>();
Iterator<Resource> children;
- children = contentFileMapper.getChildren(resolver, parent);
- if (children != null) {
- allChildren.add(children);
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ // filevault: always ask provider, it checks itself if children matches the filters
+ children = fileVaultMapper.getChildren(resolver, parent);
+ if (children != null) {
+ allChildren.add(children);
+ }
}
-
- children = fileMapper.getChildren(resolver, parent);
- if (children != null) {
- allChildren.add(children);
+ else {
+ // Sling-Initial-Content: get all matchind folders/files and content files
+ children = contentFileMapper.getChildren(resolver, parent);
+ if (children != null) {
+ allChildren.add(children);
+ }
+ children = fileMapper.getChildren(resolver, parent);
+ if (children != null) {
+ allChildren.add(children);
+ }
}
if (allChildren.isEmpty()) {
@@ -207,6 +248,7 @@ public final class FsResourceProvider implements ResourceProvider {
// ---------- SCR Integration
@Activate
protected void activate(BundleContext bundleContext, final Config config) {
+ fsMode = config.provider_fs_mode();
String[] providerRoots = config.provider_roots();
if (providerRoots == null || providerRoots.length != 1 || StringUtils.isBlank(providerRoots[0])) {
throw new IllegalArgumentException("provider.roots property must be set to exactly one entry.");
@@ -214,30 +256,42 @@ public final class FsResourceProvider implements ResourceProvider {
String providerRoot = config.provider_roots()[0];
String providerFileName = config.provider_file();
- if (providerFileName == null || providerFileName.length() == 0) {
+ if (StringUtils.isBlank(providerFileName)) {
throw new IllegalArgumentException("provider.file property must be set");
}
this.providerRoot = providerRoot;
this.providerFile = getProviderFile(providerFileName, bundleContext);
+ InitialContentImportOptions options = new InitialContentImportOptions(config.provider_initial_content_import_options());
+
List<String> contentFileSuffixes = new ArrayList<>();
- if (config.provider_json_content()) {
- contentFileSuffixes.add(ContentFileTypes.JSON_SUFFIX);
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ contentFileSuffixes.add("/" + DOT_CONTENT_XML);
}
- if (config.provider_jcrxml_content()) {
- contentFileSuffixes.add(ContentFileTypes.JCR_XML_SUFFIX);
+ else if (fsMode == FsMode.INITIAL_CONTENT) {
+ if (!options.getIgnoreImportProviders().contains("json")) {
+ contentFileSuffixes.add(ContentFileTypes.JSON_SUFFIX);
+ }
+ if (!options.getIgnoreImportProviders().contains("jcr.xml")) {
+ contentFileSuffixes.add(ContentFileTypes.JCR_XML_SUFFIX);
+ }
}
ContentFileExtensions contentFileExtensions = new ContentFileExtensions(contentFileSuffixes);
this.contentFileCache = new ContentFileCache(config.provider_cache_size());
- this.fileMapper = new FileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions);
- this.contentFileMapper = new ContentFileResourceMapper(this.providerRoot, this.providerFile,
- contentFileExtensions, this.contentFileCache);
+ if (fsMode == FsMode.FILEVAULT_XML) {
+ this.fileVaultMapper = new FileVaultResourceMapper(this.providerFile, this.contentFileCache);
+ }
+ else {
+ this.fileMapper = new FileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions);
+ this.contentFileMapper = new ContentFileResourceMapper(this.providerRoot, this.providerFile,
+ contentFileExtensions, this.contentFileCache);
+ }
// start background monitor if check interval is higher than 100
- if ( config.provider_checkinterval() > 100 ) {
- this.monitor = new FileMonitor(this, config.provider_checkinterval(),
+ if (config.provider_checkinterval() > 100) {
+ this.monitor = new FileMonitor(this, config.provider_checkinterval(), fsMode,
contentFileExtensions, this.contentFileCache);
}
}
@@ -252,10 +306,12 @@ public final class FsResourceProvider implements ResourceProvider {
this.providerFile = null;
this.fileMapper = null;
this.contentFileMapper = null;
+ this.fileVaultMapper = null;
if (this.contentFileCache != null) {
this.contentFileCache.clear();
this.contentFileCache = null;
}
+ this.fsMode = null;
}
EventAdmin getEventAdmin() {
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/InitialContentImportOptions.java b/src/main/java/org/apache/sling/fsprovider/internal/InitialContentImportOptions.java
new file mode 100644
index 0000000..5353d0f
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/InitialContentImportOptions.java
@@ -0,0 +1,76 @@
+/*
+ * 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.fsprovider.internal;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.StringUtils;
+
+class InitialContentImportOptions {
+
+ /**
+ * The overwrite directive specifying if content should be overwritten or
+ * just initially added.
+ */
+ private static final String OVERWRITE_DIRECTIVE = "overwrite";
+
+ /**
+ * The ignore content readers directive specifying whether the available ContentReaders
+ * should be used during content loading.
+ */
+ private static final String IGNORE_CONTENT_READERS_DIRECTIVE = "ignoreImportProviders";
+
+
+ private final boolean overwrite;
+ private final Set<String> ignoreImportProviders;
+
+ public InitialContentImportOptions(String optionsString) {
+ Map<String,String> options = parseOptions(optionsString);
+ overwrite = BooleanUtils.toBoolean(options.get(OVERWRITE_DIRECTIVE));
+ ignoreImportProviders = new HashSet<>(Arrays.asList(StringUtils.split(StringUtils.defaultString(options.get(IGNORE_CONTENT_READERS_DIRECTIVE)))));
+ }
+
+ private static Map<String,String> parseOptions(String optionsString) {
+ Map<String,String> options = new HashMap<>();
+ String[] optionsList = StringUtils.split(optionsString, ";");
+ if (optionsList != null) {
+ for (String keyValueString : optionsList) {
+ String[] keyValue = StringUtils.splitByWholeSeparator(keyValueString, ":=");
+ if (keyValue.length == 2) {
+ options.put(StringUtils.trim(keyValue[0]), StringUtils.trim(keyValue[1]));
+ }
+ }
+ }
+ return options;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public Set<String> getIgnoreImportProviders() {
+ return ignoreImportProviders;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
index d31a851..27e09d5 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
@@ -19,8 +19,11 @@
package org.apache.sling.fsprovider.internal.mapper;
import java.io.File;
+import java.util.Iterator;
import java.util.Map;
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Predicate;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
@@ -51,19 +54,6 @@ public final class ContentFile {
}
/**
- * @param file File with content fragment
- * @param path Root path of the content file
- * @param subPath Relative path addressing content fragment inside file
- * @param contentFileCache Content file cache
- * @param content Content
- */
- public ContentFile(File file, String path, String subPath, ContentFileCache contentFileCache, Object content) {
- this(file, path, subPath, contentFileCache);
- this.contentInitialized = true;
- this.content = content;
- }
-
- /**
* @return File with content fragment
*/
public File getFile() {
@@ -129,14 +119,47 @@ public final class ContentFile {
}
/**
+ * @return Child maps.
+ */
+ @SuppressWarnings("unchecked")
+ public Iterator<Map.Entry<String,Map<String,Object>>> getChildren() {
+ if (!isResource()) {
+ return IteratorUtils.emptyIterator();
+ }
+ return IteratorUtils.filteredIterator(((Map)getContent()).entrySet().iterator(), new Predicate() {
+ @Override
+ public boolean evaluate(Object object) {
+ Map.Entry<String,Object> entry = (Map.Entry<String,Object>)object;
+ return entry.getValue() instanceof Map;
+ }
+ });
+ }
+
+ /**
* Navigate to another sub path position in content file.
- * @param newSubPath New sub path
+ * @param newSubPath New sub path related to root path of content file
* @return Content file
*/
- public ContentFile navigateTo(String newSubPath) {
+ public ContentFile navigateToAbsolute(String newSubPath) {
return new ContentFile(file, path, newSubPath, contentFileCache);
}
+ /**
+ * Navigate to another sub path position in content file.
+ * @param newSubPath New sub path relative to current sub path in content file
+ * @return Content file
+ */
+ public ContentFile navigateToRelative(String newSubPath) {
+ String absoluteSubPath;
+ if (newSubPath == null) {
+ absoluteSubPath = this.subPath;
+ }
+ else {
+ absoluteSubPath = (this.subPath != null ? this.subPath + "/" : "") + newSubPath;
+ }
+ return new ContentFile(file, path, absoluteSubPath, contentFileCache);
+ }
+
@SuppressWarnings("unchecked")
private static Object getDeepContent(Object object, String subPath) {
if (object == null) {
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
index e6edd04..09b6ffe 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
@@ -106,18 +106,10 @@ public final class ContentFileResourceMapper implements FsResourceMapper {
// get child resources from content fragments in content file
List<ContentFile> children = new ArrayList<>();
if (parentContentFile.hasContent() && parentContentFile.isResource()) {
- Map<String,Object> content = (Map<String,Object>)parentContentFile.getContent();
- for (Map.Entry<String, Object> entry: content.entrySet()) {
- if (entry.getValue() instanceof Map) {
- String subPath;
- if (parentContentFile.getSubPath() == null) {
- subPath = entry.getKey();
- }
- else {
- subPath = parentContentFile.getSubPath() + "/" + entry.getKey();
- }
- children.add(new ContentFile(parentContentFile.getFile(), parentContentFile.getPath(), subPath, contentFileCache, entry.getValue()));
- }
+ Iterator<Map.Entry<String,Map<String,Object>>> childMaps = parentContentFile.getChildren();
+ while (childMaps.hasNext()) {
+ Map.Entry<String,Map<String,Object>> entry = childMaps.next();
+ children.add(parentContentFile.navigateToRelative(entry.getKey()));
}
}
if (children.isEmpty()) {
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java
new file mode 100644
index 0000000..88e001e
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java
@@ -0,0 +1,201 @@
+/*
+ * 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.fsprovider.internal.mapper;
+
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
+import static org.apache.jackrabbit.vault.util.Constants.FILTER_XML;
+import static org.apache.jackrabbit.vault.util.Constants.META_DIR;
+import static org.apache.jackrabbit.vault.util.Constants.ROOT_DIR;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.fsprovider.internal.FsResourceMapper;
+import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class FileVaultResourceMapper implements FsResourceMapper {
+
+ private static final String DOT_CONTENT_XML_SUFFIX = "/" + DOT_CONTENT_XML;
+ private static final String DOT_DIR_SUFFIX = "/.dir";
+
+ private final File providerFile;
+ private final ContentFileCache contentFileCache;
+ private final WorkspaceFilter workspaceFilter;
+
+ private static final Logger log = LoggerFactory.getLogger(FileVaultResourceMapper.class);
+
+ public FileVaultResourceMapper(File providerFile, ContentFileCache contentFileCache) {
+ this.providerFile = providerFile;
+ this.contentFileCache = contentFileCache;
+ this.workspaceFilter = getWorkspaceFilter();
+ }
+
+ @Override
+ public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
+
+ // direct file
+ File file = getFile(resourcePath);
+ if (file != null && file.isFile()) {
+ return new FileResource(resolver, resourcePath, file);
+ }
+
+ // content file
+ ContentFile contentFile = getContentFile(resourcePath, null);
+ if (contentFile != null) {
+ return new ContentFileResource(resolver, contentFile);
+ }
+
+ // fallback to directory resource if folder was found but nothing else
+ if (file != null && file.isDirectory()) {
+ return new FileResource(resolver, resourcePath, file);
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<Resource> getChildren(final ResourceResolver resolver, final Resource parent) {
+ String parentPath = parent.getPath();
+
+ Set<String> childPaths = new LinkedHashSet<>();
+
+ // get children from content resource of parent
+ ContentFile parentContentFile = getContentFile(parentPath, null);
+ if (parentContentFile != null) {
+ Iterator<Map.Entry<String,Map<String,Object>>> childMaps = parentContentFile.getChildren();
+ while (childMaps.hasNext()) {
+ Map.Entry<String,Map<String,Object>> entry = childMaps.next();
+ String childPath = parentPath + "/" + entry.getKey();
+ if (pathMatches(childPath)) {
+ childPaths.add(childPath);
+ }
+ }
+ }
+
+ // additional check for children in filesystem
+ File parentFile = getFile(parentPath);
+ if (parentFile != null && parentFile.isDirectory()) {
+ for (File childFile : parentFile.listFiles()) {
+ String childPath = parentPath + "/" + PlatformNameFormat.getRepositoryName(childFile.getName());
+ if (pathMatches(childPath) && !childPaths.contains(childPath)) {
+ childPaths.add(childPath);
+ }
+ }
+ }
+
+ if (childPaths.isEmpty()) {
+ return null;
+ }
+ else {
+ return IteratorUtils.transformedIterator(childPaths.iterator(), new Transformer() {
+ @Override
+ public Object transform(Object input) {
+ String path = (String)input;
+ return getResource(resolver, path);
+ }
+ });
+ }
+ }
+
+ /**
+ * @return Workspace filter or null if none found.
+ */
+ private WorkspaceFilter getWorkspaceFilter() {
+ File filter = new File(providerFile, META_DIR + "/" + FILTER_XML);
+ if (filter.exists()) {
+ try {
+ DefaultWorkspaceFilter workspaceFilter = new DefaultWorkspaceFilter();
+ workspaceFilter.load(filter);
+ return workspaceFilter;
+ } catch (IOException | ConfigurationException ex) {
+ log.error("Unable to parse workspace filter: " + filter.getPath(), ex);
+ }
+ }
+ else {
+ log.warn("Workspace filter not found: " + filter.getPath());
+ }
+ return null;
+ }
+
+ /**
+ * Checks if the given path matches the workspace filter.
+ * @param path Path
+ * @return true if path matches
+ */
+ public boolean pathMatches(String path) {
+ // ignore .dir folder
+ if (StringUtils.endsWith(path, DOT_DIR_SUFFIX) || StringUtils.endsWith(path, DOT_CONTENT_XML_SUFFIX)) {
+ return false;
+ }
+ if (workspaceFilter == null) {
+ return false;
+ }
+ else {
+ return workspaceFilter.contains(path);
+ }
+ }
+
+ private File getFile(String path) {
+ if (StringUtils.endsWith(path, DOT_CONTENT_XML_SUFFIX)) {
+ return null;
+ }
+ File file = new File(providerFile, ROOT_DIR + PlatformNameFormat.getPlatformPath(path));
+ if (file.exists()) {
+ return file;
+ }
+ return null;
+ }
+
+ private ContentFile getContentFile(String path, String subPath) {
+ File file = new File(providerFile, ROOT_DIR + PlatformNameFormat.getPlatformPath(path) + DOT_CONTENT_XML_SUFFIX);
+ if (file.exists()) {
+ ContentFile contentFile = new ContentFile(file, path, subPath, contentFileCache);
+ if (contentFile.hasContent()) {
+ return contentFile;
+ }
+ }
+
+ // try to find in parent path which contains content fragment
+ String parentPath = ResourceUtil.getParent(path);
+ if (parentPath == null) {
+ return null;
+ }
+ String nextSubPath = path.substring(parentPath.length() + 1)
+ + (subPath != null ? "/" + subPath : "");
+ return getContentFile(parentPath, nextSubPath);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
index 6f52691..23abe66 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
@@ -110,22 +110,22 @@ public final class FsNode extends FsItem implements Node {
else {
subPath = path.substring(contentFile.getPath().length() + 1);
}
- ContentFile referencedFile = contentFile.navigateTo(subPath);
+ ContentFile referencedFile = contentFile.navigateToAbsolute(subPath);
if (referencedFile.hasContent()) {
return new FsNode(referencedFile, resolver);
}
}
- else {
- // node is outside content file
- Node refNode = null;
- Resource resource = resolver.getResource(path);
- if (resource != null) {
- refNode = resource.adaptTo(Node.class);
- if (refNode != null) {
- return refNode;
- }
+
+ // check if node is outside content file
+ Node refNode = null;
+ Resource resource = resolver.getResource(path);
+ if (resource != null) {
+ refNode = resource.adaptTo(Node.class);
+ if (refNode != null) {
+ return refNode;
}
}
+
throw new PathNotFoundException(relPath);
}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
index f03b0a7..09db7d4 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
@@ -63,14 +63,7 @@ class FsNodeIterator implements NodeIterator {
@Override
public Node nextNode() {
Map.Entry<String,Map<String,Object>> nextEntry = children.next();
- String subPath;
- if (contentFile.getSubPath() == null) {
- subPath = nextEntry.getKey();
- }
- else {
- subPath = contentFile.getSubPath() + "/" + nextEntry.getKey();
- }
- return new FsNode(contentFile.navigateTo(subPath), resolver);
+ return new FsNode(contentFile.navigateToRelative(nextEntry.getKey()), resolver);
}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserUtil.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserUtil.java
index 4b46157..6ac72c5 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserUtil.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserUtil.java
@@ -18,6 +18,7 @@
*/
package org.apache.sling.fsprovider.internal.parser;
+import static org.apache.jackrabbit.vault.util.Constants.DOT_CONTENT_XML;
import static org.apache.sling.fsprovider.internal.parser.ContentFileTypes.JCR_XML_SUFFIX;
import static org.apache.sling.fsprovider.internal.parser.ContentFileTypes.JSON_SUFFIX;
@@ -62,11 +63,14 @@ class ContentFileParserUtil {
* @return Content or null if content could not be parsed.
*/
public static Map<String,Object> parse(File file) {
+ if (!file.exists()) {
+ return null;
+ }
try {
if (StringUtils.endsWith(file.getName(), JSON_SUFFIX)) {
return JSON_PARSER.parse(file);
}
- else if (StringUtils.endsWith(file.getName(), JCR_XML_SUFFIX)) {
+ else if (StringUtils.equals(file.getName(), DOT_CONTENT_XML) || StringUtils.endsWith(file.getName(), JCR_XML_SUFFIX)) {
return JCR_XML_PARSER.parse(file);
}
}
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
index 5341ced..a9c842b 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
@@ -18,35 +18,39 @@
*/
package org.apache.sling.fsprovider.internal;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
+import static org.apache.sling.fsprovider.internal.TestUtils.assertChange;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.nio.file.Files;
-import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.sling.api.SlingConstants;
import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
+import org.apache.sling.fsprovider.internal.TestUtils.ResourceListener;
import org.apache.sling.testing.mock.sling.ResourceResolverType;
import org.apache.sling.testing.mock.sling.junit.SlingContext;
import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
import org.apache.sling.testing.mock.sling.junit.SlingContextCallback;
import org.junit.Rule;
import org.junit.Test;
-import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
/**
- * Test events when changing filesystem content.
+ * Test events when changing filesystem content (Sling-Initial-Content).
*/
public class FileMonitorTest {
+ private static final int CHECK_INTERVAL = 120;
+ private static final int WAIT_INTERVAL = 250;
+
private final File tempDir;
- private final EventAdminListener eventListener = new EventAdminListener();
+ private final ResourceListener resourceListener = new ResourceListener();
public FileMonitorTest() throws Exception {
tempDir = Files.createTempDirectory(getClass().getName()).toFile();
@@ -66,15 +70,16 @@ public class FileMonitorTest {
context.registerInjectActivateService(new FsResourceProvider(),
"provider.file", tempDir.getPath(),
"provider.roots", "/fs-test",
- "provider.checkinterval", 120,
- "provider.json.content", true);
+ "provider.checkinterval", CHECK_INTERVAL,
+ "provider.fs.mode", FsMode.INITIAL_CONTENT.name(),
+ "provider.initial.content.import.options", "overwrite:=true;ignoreImportProviders:=jcr.xml");
// register resource change listener
- context.registerService(EventHandler.class, eventListener,
+ context.registerService(EventHandler.class, resourceListener,
EventConstants.EVENT_TOPIC, new String[] {
- SlingConstants.TOPIC_RESOURCE_ADDED,
- SlingConstants.TOPIC_RESOURCE_CHANGED,
- SlingConstants.TOPIC_RESOURCE_REMOVED
+ TOPIC_RESOURCE_ADDED,
+ TOPIC_RESOURCE_CHANGED,
+ TOPIC_RESOURCE_REMOVED
});
}
})
@@ -89,150 +94,122 @@ public class FileMonitorTest {
@Test
public void testUpdateFile() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1a = new File(tempDir, "folder1/file1a.txt");
FileUtils.touch(file1a);
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(1, changes.size());
- assertChange(changes, "/fs-test/folder1/file1a.txt", SlingConstants.TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder1/file1a.txt", TOPIC_RESOURCE_CHANGED);
}
@Test
public void testAddFile() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1c = new File(tempDir, "folder1/file1c.txt");
FileUtils.write(file1c, "newcontent");
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(2, changes.size());
- assertChange(changes, "/fs-test/folder1", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder1/file1c.txt", SlingConstants.TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder1/file1c.txt", TOPIC_RESOURCE_ADDED);
}
@Test
public void testRemoveFile() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1a = new File(tempDir, "folder1/file1a.txt");
file1a.delete();
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(2, changes.size());
- assertChange(changes, "/fs-test/folder1", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder1/file1a.txt", SlingConstants.TOPIC_RESOURCE_REMOVED);
+ assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder1/file1a.txt", TOPIC_RESOURCE_REMOVED);
}
@Test
public void testAddFolder() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File folder99 = new File(tempDir, "folder99");
folder99.mkdir();
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(2, changes.size());
- assertChange(changes, "/fs-test", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder99", SlingConstants.TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder99", TOPIC_RESOURCE_ADDED);
}
@Test
public void testRemoveFolder() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File folder1 = new File(tempDir, "folder1");
FileUtils.deleteDirectory(folder1);
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(2, changes.size());
- assertChange(changes, "/fs-test", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder1", SlingConstants.TOPIC_RESOURCE_REMOVED);
+ assertChange(changes, "/fs-test", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_REMOVED);
}
@Test
- public void testUpdateJsonContent() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ public void testUpdateContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1a = new File(tempDir, "folder2/content.json");
FileUtils.touch(file1a);
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
- assertTrue(changes.size() > 1);
- assertChange(changes, "/fs-test/folder2/content", SlingConstants.TOPIC_RESOURCE_REMOVED);
- assertChange(changes, "/fs-test/folder2/content", SlingConstants.TOPIC_RESOURCE_ADDED);
- assertChange(changes, "/fs-test/folder2/content/jcr:content", SlingConstants.TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_REMOVED);
+ assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test/folder2/content/jcr:content", TOPIC_RESOURCE_ADDED);
}
@Test
- public void testAddJsonContent() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ public void testAddContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1c = new File(tempDir, "folder1/file1c.json");
FileUtils.write(file1c, "{\"prop1\":\"value1\",\"child1\":{\"prop2\":\"value1\"}}");
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(3, changes.size());
- assertChange(changes, "/fs-test/folder1", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder1/file1c", SlingConstants.TOPIC_RESOURCE_ADDED);
- assertChange(changes, "/fs-test/folder1/file1c/child1", SlingConstants.TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder1/file1c", TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/fs-test/folder1/file1c/child1", TOPIC_RESOURCE_ADDED);
}
@Test
- public void testRemoveJsonContent() throws Exception {
- List<ResourceChange> changes = eventListener.getChanges();
+ public void testRemoveContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
assertTrue(changes.isEmpty());
File file1a = new File(tempDir, "folder2/content.json");
file1a.delete();
- Thread.sleep(250);
+ Thread.sleep(WAIT_INTERVAL);
assertEquals(2, changes.size());
- assertChange(changes, "/fs-test/folder2", SlingConstants.TOPIC_RESOURCE_CHANGED);
- assertChange(changes, "/fs-test/folder2/content", SlingConstants.TOPIC_RESOURCE_REMOVED);
+ assertChange(changes, "/fs-test/folder2", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_REMOVED);
}
-
- private void assertChange(List<ResourceChange> changes, String path, String topic) {
- boolean found = false;
- for (ResourceChange change : changes) {
- if (StringUtils.equals(change.path, path) && StringUtils.equals(change.topic, topic)) {
- found = true;
- break;
- }
- }
- assertTrue("Change with path=" + path + ", topic=" + topic, found);
- }
-
- static class EventAdminListener implements EventHandler {
- private final List<ResourceChange> allChanges = new ArrayList<>();
- public List<ResourceChange> getChanges() {
- return allChanges;
- }
- @Override
- public void handleEvent(Event event) {
- ResourceChange change = new ResourceChange();
- change.path = (String)event.getProperty(SlingConstants.PROPERTY_PATH);
- change.resourceType = (String)event.getProperty(SlingConstants.PROPERTY_RESOURCE_TYPE);
- change.topic = event.getTopic();
- allChanges.add(change);
- }
- }
-
}
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java
new file mode 100644
index 0000000..b6dda6a
--- /dev/null
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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.fsprovider.internal;
+
+import static org.apache.sling.fsprovider.internal.TestUtils.assertFile;
+import static org.apache.sling.fsprovider.internal.TestUtils.assertFolder;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.fsprovider.internal.TestUtils.RegisterFsResourcePlugin;
+import org.apache.sling.hamcrest.ResourceMatchers;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Test access FileFault XML files, folders and content.
+ */
+public class FileVaultContentTest {
+
+ private Resource damAsset;
+ private Resource sampleContent;
+
+ @Rule
+ public SlingContext context = new SlingContextBuilder(ResourceResolverType.JCR_MOCK)
+ .plugin(new RegisterFsResourcePlugin(
+ "provider.fs.mode", FsMode.FILEVAULT_XML.name(),
+ "provider.file", "src/test/resources/vaultfs-test",
+ "provider.roots", "/content/dam/talk.png"
+ ))
+ .plugin(new RegisterFsResourcePlugin(
+ "provider.fs.mode", FsMode.FILEVAULT_XML.name(),
+ "provider.file", "src/test/resources/vaultfs-test",
+ "provider.roots", "/content/samples"
+ ))
+ .build();
+
+ @Before
+ public void setUp() {
+ damAsset = context.resourceResolver().getResource("/content/dam/talk.png");
+ sampleContent = context.resourceResolver().getResource("/content/samples");
+ }
+
+ @Test
+ public void testDamAsset() {
+ assertNotNull(damAsset);
+ assertEquals("app:Asset", damAsset.getResourceType());
+
+ Resource content = damAsset.getChild("jcr:content");
+ assertNotNull(content);
+ assertEquals("app:AssetContent", content.getResourceType());
+
+ Resource metadata = content.getChild("metadata");
+ assertNotNull(metadata);
+ ValueMap props = ResourceUtil.getValueMap(metadata);
+ assertEquals((Integer)4, props.get("app:Bitsperpixel", Integer.class));
+
+ assertFolder(content, "renditions");
+ assertFile(content, "renditions/original", null);
+ assertFile(content, "renditions/web.1280.1280.png", null);
+ }
+
+ @Test
+ public void testSampleContent() {
+ assertNotNull(sampleContent);
+ assertEquals("sling:OrderedFolder", sampleContent.getResourceType());
+
+ Resource enContent = sampleContent.getChild("en/jcr:content");
+ assertArrayEquals(new String[] { "/etc/mobile/groups/responsive" }, ResourceUtil.getValueMap(enContent).get("app:deviceGroups", String[].class));
+ }
+
+ @Test
+ public void testListChildren() {
+ Resource en = sampleContent.getChild("en");
+ List<Resource> children = ImmutableList.copyOf(en.listChildren());
+ assertEquals(2, children.size());
+
+ Resource child1 = children.get(0);
+ assertEquals("jcr:content", child1.getName());
+ assertEquals("samples/sample-app/components/content/page/homepage", child1.getResourceType());
+
+ Resource child2 = children.get(1);
+ assertEquals("tools", child2.getName());
+ assertEquals("app:Page", child2.getResourceType());
+
+ // child3 (conference) is hidden because of filter
+ }
+
+ @Test
+ public void testJcrMixedContent() throws RepositoryException {
+ // prepare mixed JCR content
+ Node root = context.resourceResolver().adaptTo(Session.class).getNode("/");
+ Node content = root.addNode("content", "nt:folder");
+ Node samples = content.addNode("samples", "nt:folder");
+ Node en = samples.addNode("en", "nt:folder");
+ Node conference = en.addNode("conference", "nt:folder");
+ conference.addNode("page2", "nt:folder");
+ samples.addNode("it", "nt:folder");
+
+ // pass-through because of filter
+ assertNotNull(context.resourceResolver().getResource("/content/samples/en/conference"));
+ assertNotNull(sampleContent.getChild("en/conference"));
+ assertNotNull(context.resourceResolver().getResource("/content/samples/en/conference/page2"));
+ assertNotNull(sampleContent.getChild("en/conference/page2"));
+
+ // list children with mixed content
+ Resource enResource = sampleContent.getChild("en");
+ assertThat(enResource, ResourceMatchers.containsChildren("jcr:content", "tools", "conference"));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java
new file mode 100644
index 0000000..7219033
--- /dev/null
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.fsprovider.internal;
+
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
+import static org.apache.sling.fsprovider.internal.TestUtils.assertChange;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
+import org.apache.sling.fsprovider.internal.TestUtils.ResourceListener;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
+import org.apache.sling.testing.mock.sling.junit.SlingContextCallback;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
+
+/**
+ * Test events when changing filesystem content (FileVault XML).
+ */
+public class FileVaultFileMonitorTest {
+
+ private static final int CHECK_INTERVAL = 120;
+ private static final int WAIT_INTERVAL = 250;
+
+ private final File tempDir;
+ private final ResourceListener resourceListener = new ResourceListener();
+
+ public FileVaultFileMonitorTest() throws Exception {
+ tempDir = Files.createTempDirectory(getClass().getName()).toFile();
+ }
+
+ @Rule
+ public SlingContext context = new SlingContextBuilder(ResourceResolverType.JCR_MOCK)
+ .beforeSetUp(new SlingContextCallback() {
+ @Override
+ public void execute(SlingContext context) throws Exception {
+ // copy test content to temp. directory
+ tempDir.mkdirs();
+ File sourceDir = new File("src/test/resources/vaultfs-test");
+ FileUtils.copyDirectory(sourceDir, tempDir);
+
+ // mount temp. directory
+ context.registerInjectActivateService(new FsResourceProvider(),
+ "provider.file", tempDir.getPath(),
+ "provider.roots", "/content/dam/talk.png",
+ "provider.checkinterval", CHECK_INTERVAL,
+ "provider.fs.mode", FsMode.FILEVAULT_XML.name());
+ context.registerInjectActivateService(new FsResourceProvider(),
+ "provider.file", tempDir.getPath(),
+ "provider.roots", "/content/samples",
+ "provider.checkinterval", CHECK_INTERVAL,
+ "provider.fs.mode", FsMode.FILEVAULT_XML.name());
+
+ // register resource change listener
+ context.registerService(EventHandler.class, resourceListener,
+ EventConstants.EVENT_TOPIC, new String[] {
+ TOPIC_RESOURCE_ADDED,
+ TOPIC_RESOURCE_CHANGED,
+ TOPIC_RESOURCE_REMOVED
+ });
+ }
+ })
+ .afterTearDown(new SlingContextCallback() {
+ @Override
+ public void execute(SlingContext context) throws Exception {
+ // remove temp directory
+ tempDir.delete();
+ }
+ })
+ .build();
+
+ @Test
+ public void testUpdateFile() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/dam/talk.png/_jcr_content/renditions/web.1280.1280.png");
+ FileUtils.touch(file);
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(1, changes.size());
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", TOPIC_RESOURCE_CHANGED);
+ }
+
+ @Test
+ public void testAddFile() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/dam/talk.png/_jcr_content/renditions/text.txt");
+ FileUtils.write(file, "newcontent");
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/text.txt", TOPIC_RESOURCE_ADDED);
+ }
+
+ @Test
+ public void testRemoveFile() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/dam/talk.png/_jcr_content/renditions/web.1280.1280.png");
+ file.delete();
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", TOPIC_RESOURCE_REMOVED);
+ }
+
+ @Test
+ public void testAddFolder() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File folder = new File(tempDir, "jcr_root/content/dam/talk.png/_jcr_content/newfolder");
+ folder.mkdir();
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/dam/talk.png/jcr:content", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/dam/talk.png/jcr:content/newfolder", TOPIC_RESOURCE_ADDED);
+ }
+
+ @Test
+ public void testRemoveFolder() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File folder = new File(tempDir, "jcr_root/content/dam/talk.png/_jcr_content/renditions");
+ FileUtils.deleteDirectory(folder);
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/dam/talk.png/jcr:content", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_REMOVED);
+ }
+
+ @Test
+ public void testUpdateContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/samples/en/.content.xml");
+ FileUtils.touch(file);
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
+ assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/content/samples/en/jcr:content", TOPIC_RESOURCE_ADDED);
+ }
+
+ @Test
+ public void testAddContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/samples/fr/.content.xml");
+ file.getParentFile().mkdir();
+ FileUtils.write(file, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ + "<jcr:root xmlns:jcr=\"http://www.jcp.org/jcr/1.0\" xmlns:app=\"http://sample.com/jcr/app/1.0\" "
+ + "xmlns:sling=\"http://sling.apache.org/jcr/sling/1.0\" jcr:primaryType=\"app:Page\">\n"
+ + "<jcr:content jcr:primaryType=\"app:PageContent\"/>\n"
+ + "</jcr:root>");
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertChange(changes, "/content/samples", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/samples/fr", TOPIC_RESOURCE_ADDED);
+ assertChange(changes, "/content/samples/fr/jcr:content", TOPIC_RESOURCE_ADDED);
+ }
+
+ @Test
+ public void testRemoveContent() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/samples/en");
+ FileUtils.deleteDirectory(file);
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/samples", TOPIC_RESOURCE_CHANGED);
+ assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
+ }
+
+ @Test
+ public void testRemoveContentDotXmlOnly() throws Exception {
+ List<ResourceChange> changes = resourceListener.getChanges();
+ assertTrue(changes.isEmpty());
+
+ File file = new File(tempDir, "jcr_root/content/samples/en/.content.xml");
+ file.delete();
+
+ Thread.sleep(WAIT_INTERVAL);
+
+ assertEquals(2, changes.size());
+ assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_CHANGED);
+ // this second event is not fully correct, but this is a quite special case, accept it for now
+ assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/InitialContentImportOptionsTest.java b/src/test/java/org/apache/sling/fsprovider/internal/InitialContentImportOptionsTest.java
new file mode 100644
index 0000000..513c51f
--- /dev/null
+++ b/src/test/java/org/apache/sling/fsprovider/internal/InitialContentImportOptionsTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.fsprovider.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+
+public class InitialContentImportOptionsTest {
+
+ @Test
+ public void testNull() {
+ InitialContentImportOptions underTest = new InitialContentImportOptions(null);
+ assertFalse(underTest.isOverwrite());
+ assertTrue(underTest.getIgnoreImportProviders().isEmpty());
+ }
+
+ @Test
+ public void testBlank() {
+ InitialContentImportOptions underTest = new InitialContentImportOptions(" ");
+ assertFalse(underTest.isOverwrite());
+ assertTrue(underTest.getIgnoreImportProviders().isEmpty());
+ }
+
+ @Test
+ public void testOptions1() {
+ InitialContentImportOptions underTest = new InitialContentImportOptions("overwrite:=true;ignoreImportProviders:=xml,json");
+ assertTrue(underTest.isOverwrite());
+ assertEquals(ImmutableSet.of("xml,json"), underTest.getIgnoreImportProviders());
+ }
+
+ @Test
+ public void testOptions2() {
+ InitialContentImportOptions underTest = new InitialContentImportOptions(" overwrite := false ; ignoreImportProviders := xml ");
+ assertFalse(underTest.isOverwrite());
+ assertEquals(ImmutableSet.of("xml"), underTest.getIgnoreImportProviders());
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
index d928205..2a677ba 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
@@ -57,7 +57,10 @@ public class JcrXmlContentTest {
@Rule
public SlingContext context = new SlingContextBuilder(ResourceResolverType.JCR_MOCK)
- .plugin(new RegisterFsResourcePlugin("provider.jcrxml.content", true))
+ .plugin(new RegisterFsResourcePlugin(
+ "provider.fs.mode", FsMode.INITIAL_CONTENT.name(),
+ "provider.initial.content.import.options", "overwrite:=true;ignoreImportProviders:=json"
+ ))
.build();
@Before
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
index 04a9418..5ea4ea5 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
@@ -58,7 +58,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
- * Test access to files and folders from filesystem.
+ * Test access to files and folders and JSON content from filesystem.
*/
public class JsonContentTest {
@@ -67,7 +67,10 @@ public class JsonContentTest {
@Rule
public SlingContext context = new SlingContextBuilder(ResourceResolverType.JCR_MOCK)
- .plugin(new RegisterFsResourcePlugin("provider.json.content", true))
+ .plugin(new RegisterFsResourcePlugin(
+ "provider.fs.mode", FsMode.INITIAL_CONTENT.name(),
+ "provider.initial.content.import.options", "overwrite:=true;ignoreImportProviders:=jcr.xml"
+ ))
.build();
@Before
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
index 4d62076..0d12a06 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
@@ -28,17 +28,23 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharEncoding;
import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.resource.Resource;
+import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
import org.apache.sling.hamcrest.ResourceMatchers;
import org.apache.sling.testing.mock.osgi.MapUtil;
import org.apache.sling.testing.mock.osgi.context.AbstractContextPlugin;
import org.apache.sling.testing.mock.sling.context.SlingContextImpl;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
class TestUtils {
@@ -53,6 +59,7 @@ class TestUtils {
config.put("provider.file", "src/test/resources/fs-test");
config.put("provider.roots", "/fs-test");
config.put("provider.checkinterval", 0);
+ config.put("provider.fs.mode", FsMode.INITIAL_CONTENT_FILES_FOLDERS.name());
config.putAll(props);
context.registerInjectActivateService(new FsResourceProvider(), config);
}
@@ -94,4 +101,30 @@ class TestUtils {
}
}
+ public static void assertChange(List<ResourceChange> changes, String path, String topic) {
+ boolean found = false;
+ for (ResourceChange change : changes) {
+ if (StringUtils.equals(change.path, path) && StringUtils.equals(change.topic, topic)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("Change with path=" + path + ", topic=" + topic + " expected", found);
+ }
+
+ public static class ResourceListener implements EventHandler {
+ private final List<ResourceChange> allChanges = new ArrayList<>();
+ public List<ResourceChange> getChanges() {
+ return allChanges;
+ }
+ @Override
+ public void handleEvent(Event event) {
+ ResourceChange change = new ResourceChange();
+ change.path = (String)event.getProperty(SlingConstants.PROPERTY_PATH);
+ change.resourceType = (String)event.getProperty(SlingConstants.PROPERTY_RESOURCE_TYPE);
+ change.topic = event.getTopic();
+ allChanges.add(change);
+ }
+ }
+
}
diff --git a/src/test/resources/vaultfs-test/META-INF/vault/filter.xml b/src/test/resources/vaultfs-test/META-INF/vault/filter.xml
index 20be2d8..a56ee24 100644
--- a/src/test/resources/vaultfs-test/META-INF/vault/filter.xml
+++ b/src/test/resources/vaultfs-test/META-INF/vault/filter.xml
@@ -19,5 +19,7 @@
-->
<workspaceFilter version="1.0">
<filter root="/content/dam/talk.png" />
- <filter root="/content/samples" />
+ <filter root="/content/samples">
+ <exclude pattern="/content/samples/en/conference(/.*)?"/>
+ </filter>
</workspaceFilter>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml
index 4f8312a..e207a1e 100644
--- a/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml
+++ b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml
@@ -39,8 +39,6 @@
dc:format="image/png"
dc:modified="{Date}2014-09-19T21:20:26.812+02:00"
jcr:primaryType="nt:unstructured"
- tiff:ImageLength="{Long}270"
- tiff:ImageWidth="{Long}480"
writebackEnable="{Boolean}true" />
</jcr:content>
</jcr:root>
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.