You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2017/02/21 13:54:38 UTC
svn commit: r1783888 [1/3] - in /sling/trunk/bundles/extensions/fsresource:
./ src/main/java/org/apache/sling/fsprovider/internal/
src/main/java/org/apache/sling/fsprovider/internal/mapper/
src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/...
Author: sseifert
Date: Tue Feb 21 13:54:37 2017
New Revision: 1783888
URL: http://svn.apache.org/viewvc?rev=1783888&view=rev
Log:
SLING-6440 Filesystem Resource Provider: Support "mounting" content resources from JSON files
Added:
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsProperty.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsPropertyIterator.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsValue.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/parser/
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParser.java (with props)
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/parser/JsonFileParser.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/InvalidRootFolderTest.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/mapper/
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/mapper/ContentFileTest.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtilTest.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/parser/
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserTest.java (with props)
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/content/
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/content.json (with props)
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/content/content2.json (with props)
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/folder21/
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/folder21/file21a.txt (with props)
Removed:
sling/trunk/bundles/extensions/fsresource/src/test/resources/fs-test/folder2/file2a.txt
Modified:
sling/trunk/bundles/extensions/fsresource/pom.xml
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java
sling/trunk/bundles/extensions/fsresource/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
Modified: sling/trunk/bundles/extensions/fsresource/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/pom.xml?rev=1783888&r1=1783887&r2=1783888&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/pom.xml (original)
+++ sling/trunk/bundles/extensions/fsresource/pom.xml Tue Feb 21 13:54:37 2017
@@ -76,6 +76,12 @@
<configuration>
<!-- Export SCR metadata to classpath to have them available in unit tests -->
<exportScr>true</exportScr>
+ <instructions>
+ <Embed-Dependency>
+ johnzon-core;scope=compile;inline=false,
+ geronimo-json_1.0_spec;scope=compile;inline=false
+ </Embed-Dependency>
+ </instructions>
</configuration>
</plugin>
<plugin>
@@ -113,8 +119,34 @@
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.3.2</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.johnzon</groupId>
+ <artifactId>johnzon-core</artifactId>
+ <version>1.0.0</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-json_1.0_spec</artifactId>
+ <version>1.0-alpha-1</version>
+ <scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.sling</groupId>
@@ -123,6 +155,11 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.testing.sling-mock</artifactId>
<version>2.2.4</version>
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Matches file names for content file extensions.
+ */
+public final class ContentFileExtensions {
+
+ private final List<String> contentFileSuffixes;
+
+ public ContentFileExtensions(List<String> contentFileSuffixes) {
+ this.contentFileSuffixes = contentFileSuffixes;
+ }
+
+ /**
+ * Get suffix from file name.
+ * @param file File
+ * @return Content file name suffix or null if not a context file.
+ */
+ public String getSuffix(File file) {
+ for (String suffix : contentFileSuffixes) {
+ if (StringUtils.endsWith(file.getName(), suffix)) {
+ return suffix;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks suffix from file name.
+ * @param file File
+ * @return true if content file
+ */
+ public boolean matchesSuffix(File file) {
+ return getSuffix(file) != null;
+ }
+
+ /**
+ * @return Content file suffixes.
+ */
+ public Collection<String> getSuffixes() {
+ return contentFileSuffixes;
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java?rev=1783888&r1=1783887&r2=1783888&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java Tue Feb 21 13:54:37 2017
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Timer;
import java.util.TimerTask;
+import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
import org.apache.sling.spi.resource.provider.ObservationReporter;
@@ -34,7 +35,7 @@ import org.slf4j.LoggerFactory;
* This class is a monitor for the file system
* that periodically checks for changes.
*/
-public class FileMonitor extends TimerTask {
+public final class FileMonitor extends TimerTask {
/** The logger. */
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@@ -46,16 +47,19 @@ public class FileMonitor extends TimerTa
private final Monitorable root;
private final FsResourceProvider provider;
+
+ private final ContentFileExtensions contentFileExtensions;
/**
* Creates a new instance of this class.
* @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, final ContentFileExtensions contentFileExtensions) {
this.provider = provider;
- this.root = new Monitorable(this.provider.getProviderRoot(), this.provider.getRootFile());
- createStatus(this.root);
+ this.contentFileExtensions = contentFileExtensions;
+ this.root = new Monitorable(this.provider.getProviderRoot(), this.provider.getRootFile(), null);
+ createStatus(this.root, contentFileExtensions);
logger.debug("Starting file monitor for {} with an interval of {}ms", this.root.file, interval);
timer.schedule(this, 0, interval);
}
@@ -130,7 +134,7 @@ public class FileMonitor extends TimerTa
if ( monitorable.status instanceof NonExistingStatus ) {
if ( monitorable.file.exists() ) {
// new file and reset status
- createStatus(monitorable);
+ createStatus(monitorable, contentFileExtensions);
sendEvents(monitorable,
ChangeType.ADDED,
reporter);
@@ -176,9 +180,8 @@ public class FileMonitor extends TimerTa
}
}
if (children[i] == null) {
- children[i] = new Monitorable(
- monitorable.path + '/'
- + files[i].getName(), files[i]);
+ children[i] = new Monitorable(monitorable.path + '/' + files[i].getName(), files[i],
+ contentFileExtensions.getSuffix(files[i]));
children[i].status = NonExistingStatus.SINGLETON;
check(children[i], reporter);
}
@@ -212,25 +215,29 @@ public class FileMonitor extends TimerTa
/**
* Create a status object for the monitorable
*/
- private static void createStatus(final Monitorable monitorable) {
+ private static void createStatus(final Monitorable monitorable, ContentFileExtensions contentFileExtensions) {
if ( !monitorable.file.exists() ) {
monitorable.status = NonExistingStatus.SINGLETON;
} else if ( monitorable.file.isFile() ) {
monitorable.status = new FileStatus(monitorable.file);
} else {
- monitorable.status = new DirStatus(monitorable.file, monitorable.path);
+ monitorable.status = new DirStatus(monitorable.file, monitorable.path, contentFileExtensions);
}
}
/** The monitorable to hold the resource path, the file and the status. */
private static final class Monitorable {
public final String path;
- public final File file;
+ public final File file;
public Object status;
-
- public Monitorable(final String path, final File file) {
- this.path = path;
+ public Monitorable(final String path, final File file, String contentFileSuffix) {
this.file = file;
+ if (contentFileSuffix != null) {
+ this.path = StringUtils.substringBeforeLast(path, contentFileSuffix);
+ }
+ else {
+ this.path = path;
+ }
}
}
@@ -246,15 +253,15 @@ public class FileMonitor extends TimerTa
private static final class DirStatus extends FileStatus {
public Monitorable[] children;
- public DirStatus(final File dir, final String path) {
+ public DirStatus(final File dir, final String path, final ContentFileExtensions contentFileExtensions) {
super(dir);
final File[] files = dir.listFiles();
if (files != null) {
this.children = new Monitorable[files.length];
for (int i = 0; i < files.length; i++) {
- this.children[i] = new Monitorable(path + '/'
- + files[i].getName(), files[i]);
- FileMonitor.createStatus(this.children[i]);
+ this.children[i] = new Monitorable(path + '/' + files[i].getName(), files[i],
+ contentFileExtensions.getSuffix(files[i]));
+ FileMonitor.createStatus(this.children[i], contentFileExtensions);
}
} else {
this.children = new Monitorable[0];
Modified: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java?rev=1783888&r1=1783887&r2=1783888&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java Tue Feb 21 13:54:37 2017
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.sling.fsprovider.internal;
+package org.apache.sling.fsprovider.internal.mapper;
import java.io.File;
import java.io.FileInputStream;
@@ -28,6 +28,8 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.sling.adapter.annotations.Adaptable;
import org.apache.sling.adapter.annotations.Adapter;
import org.apache.sling.api.resource.AbstractResource;
@@ -36,6 +38,7 @@ import org.apache.sling.api.resource.Res
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.fsprovider.internal.FsResourceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,7 +50,7 @@ import org.slf4j.LoggerFactory;
@Adapter({File.class, URL.class}),
@Adapter(condition="If the resource is an FsResource and is a readable file.", value=InputStream.class)
})
-public class FsResource extends AbstractResource {
+public final class FileResource extends AbstractResource {
/**
* The resource type for file system files mapped into the resource tree by
@@ -61,9 +64,6 @@ public class FsResource extends Abstract
*/
static final String RESOURCE_TYPE_FOLDER = "nt:folder";
- // default log, assigned on demand
- private Logger log;
-
// the owning resource resolver
private final ResourceResolver resolver;
@@ -79,6 +79,8 @@ public class FsResource extends Abstract
// the resource metadata, assigned on demand
private ResourceMetadata metaData;
+ private static final Logger log = LoggerFactory.getLogger(FileResource.class);
+
/**
* Creates an instance of this Filesystem resource.
*
@@ -86,7 +88,7 @@ public class FsResource extends Abstract
* @param resourcePath The resource path in the resource tree
* @param file The wrapped file
*/
- FsResource(ResourceResolver resolver, String resourcePath, File file) {
+ FileResource(ResourceResolver resolver, String resourcePath, File file) {
this.resolver = resolver;
this.resourcePath = resourcePath;
this.file = file;
@@ -139,11 +141,8 @@ public class FsResource extends Abstract
*/
public String getResourceType() {
if (resourceType == null) {
- resourceType = file.isFile()
- ? RESOURCE_TYPE_FILE
- : RESOURCE_TYPE_FOLDER;
+ resourceType = file.isFile() ? RESOURCE_TYPE_FILE : RESOURCE_TYPE_FOLDER;
}
-
return resourceType;
}
@@ -156,39 +155,31 @@ public class FsResource extends Abstract
@SuppressWarnings("unchecked")
public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
if (type == File.class) {
-
return (AdapterType) file;
-
- } else if (type == InputStream.class) {
-
+ }
+ else if (type == InputStream.class) {
if (!file.isDirectory() && file.canRead()) {
-
try {
return (AdapterType) new FileInputStream(file);
- } catch (IOException ioe) {
- getLog().info(
- "adaptTo: Cannot open a stream on the file " + file,
- ioe);
}
-
- } else {
-
- getLog().debug("adaptTo: File {} is not a readable file", file);
-
+ catch (IOException ioe) {
+ log.info("adaptTo: Cannot open a stream on the file " + file, ioe);
+ }
}
-
- } else if (type == URL.class) {
-
+ else {
+ log.debug("adaptTo: File {} is not a readable file", file);
+ }
+ }
+ else if (type == URL.class) {
try {
return (AdapterType) file.toURI().toURL();
- } catch (MalformedURLException mue) {
- getLog().info(
- "adaptTo: Cannot convert the file path " + file
- + " to an URL", mue);
+ }
+ catch (MalformedURLException mue) {
+ log.info("adaptTo: Cannot convert the file path " + file + " to an URL", mue);
}
- } else if (type == ValueMap.class) {
-
+ }
+ else if (type == ValueMap.class) {
// this resource simulates nt:file/nt:folder behavior by returning it as resource type
// we should simulate the corresponding JCR properties in a value map as well
if (file.exists() && file.canRead()) {
@@ -200,18 +191,17 @@ public class FsResource extends Abstract
props.put("jcr:created", lastModifed);
return (AdapterType) new ValueMapDecorator(props);
}
-
}
-
return super.adaptTo(type);
}
- // ---------- internal
-
- private Logger getLog() {
- if (log == null) {
- log = LoggerFactory.getLogger(getClass());
- }
- return log;
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("path", resourcePath)
+ .append("file", file.getPath())
+ .append("resourceType", getResourceType())
+ .build();
}
+
}
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal;
+
+import java.util.Iterator;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * Maps files to resources.
+ */
+public interface FsResourceMapper {
+
+ /**
+ * Get single resource.
+ * @param resolver Resource resolver
+ * @param resourcePath Resource path
+ * @return Resource or null if not exists
+ */
+ Resource getResource(ResourceResolver resolver, String resourcePath);
+
+ /**
+ * Get children of resource.
+ * @param resolver Resource resolver.
+ * @param parent Parent resource.
+ * @return Child resources or null if no children exist
+ */
+ Iterator<Resource> getChildren(ResourceResolver resolver, Resource parent);
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceMapper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java?rev=1783888&r1=1783887&r2=1783888&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java Tue Feb 21 13:54:37 2017
@@ -19,14 +19,19 @@
package org.apache.sling.fsprovider.internal;
import java.io.File;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.NoSuchElementException;
+import java.util.List;
import java.util.Set;
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Predicate;
import org.apache.sling.api.resource.Resource;
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.parser.ContentFileParser;
import org.apache.sling.spi.resource.provider.ObservationReporter;
import org.apache.sling.spi.resource.provider.ProviderContext;
import org.apache.sling.spi.resource.provider.ResolveContext;
@@ -61,12 +66,12 @@ import org.osgi.service.metatype.annotat
Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
})
@Designate(ocd=FsResourceProvider.Config.class, factory=true)
-public class FsResourceProvider extends ResourceProvider<Object> {
+public final class FsResourceProvider extends ResourceProvider<Object> {
/**
* Resource metadata property set by {@link FsResource} if the underlying file reference is a directory.
*/
- static final String RESOURCE_METADATA_FILE_DIRECTORY = ":org.apache.sling.fsprovider.file.directory";
+ public static final String RESOURCE_METADATA_FILE_DIRECTORY = ":org.apache.sling.fsprovider.file.directory";
@ObjectClassDefinition(name = "Apache Sling Filesystem Resource Provider",
description = "Configure an instance of the filesystem " +
@@ -100,6 +105,10 @@ public class FsResourceProvider extends
"filesystem resources are mapped in. This property must not be an empty string.")
String provider_root();
+ @AttributeDefinition(name = "Mount JSON",
+ description = "Mount .json files as content in the resource hierarchy.")
+ boolean provider_json_content();
+
/**
* Internal Name hint for web console.
*/
@@ -109,14 +118,18 @@ public class FsResourceProvider extends
// The location in the resource tree where the resources are mapped
private String providerRoot;
- // providerRoot + "/" to be used for prefix matching of paths
- private String providerRootPrefix;
-
// The "root" file or folder in the file system
private File providerFile;
- /** The monitor to detect file changes. */
+ // The monitor to detect file changes.
private FileMonitor monitor;
+
+ // maps filesystem to resources
+ private FsResourceMapper fileMapper;
+ private FsResourceMapper contentFileMapper;
+
+ // if true resources from filesystem are only "overlayed" to JCR resources, serving JCR as fallback within the same path
+ private boolean overlayParentResourceProvider;
/**
* Returns a resource wrapping a file system file or folder for the given
@@ -133,21 +146,30 @@ public class FsResourceProvider extends
final String path,
final ResourceContext resourceContext,
final Resource parent) {
- Resource rsrc = getResource(ctx.getResourceResolver(), path, getFile(path));
- // make sure directory resources from parent resource provider have higher precedence than from this provider
- // this allows properties like sling:resourceSuperType to take effect
- if ( rsrc == null || rsrc.getResourceMetadata().containsKey(RESOURCE_METADATA_FILE_DIRECTORY) ) {
- // get resource from shadowed provider
- final ResourceProvider rp = ctx.getParentResourceProvider();
- if ( rp != null ) {
- Resource resourceFromParentResourceProvider = rp.getResource((ResolveContext)ctx.getParentResolveContext(),
- path,
- resourceContext, parent);
- if (resourceFromParentResourceProvider != null) {
- rsrc = resourceFromParentResourceProvider;
- }
- }
+
+ ResourceResolver resolver = ctx.getResourceResolver();
+ Resource rsrc = contentFileMapper.getResource(resolver, path);
+ if (rsrc == null) {
+ rsrc = fileMapper.getResource(resolver, path);
+ }
+
+ if (this.overlayParentResourceProvider) {
+ // make sure directory resources from parent resource provider have higher precedence than from this provider
+ // this allows properties like sling:resourceSuperType to take effect
+ if ( rsrc == null || rsrc.getResourceMetadata().containsKey(RESOURCE_METADATA_FILE_DIRECTORY) ) {
+ // get resource from shadowed provider
+ final ResourceProvider rp = ctx.getParentResourceProvider();
+ if ( rp != null ) {
+ Resource resourceFromParentResourceProvider = rp.getResource((ResolveContext)ctx.getParentResolveContext(),
+ path,
+ resourceContext, parent);
+ if (resourceFromParentResourceProvider != null) {
+ rsrc = resourceFromParentResourceProvider;
+ }
+ }
+ }
}
+
return rsrc;
}
@@ -157,102 +179,49 @@ public class FsResourceProvider extends
@SuppressWarnings("unchecked")
@Override
public Iterator<Resource> listChildren(final ResolveContext<Object> ctx, final Resource parent) {
- File parentFile = parent.adaptTo(File.class);
-
- // not a FsResource, try to create one from the resource
- if (parentFile == null) {
- // if the parent path is at or below the provider root, get
- // the respective file
- parentFile = getFile(parent.getPath());
-
- // if the parent path is actually the parent of the provider
- // root, return a single element iterator just containing the
- // provider file, unless the provider file is a directory and
- // a repository item with the same path actually exists
- if (parentFile == null) {
-
- String parentPath = parent.getPath().concat("/");
- if (providerRoot.startsWith(parentPath)) {
- String relPath = providerRoot.substring(parentPath.length());
- if (relPath.indexOf('/') < 0) {
- Resource res = getResource(
- parent.getResourceResolver(), providerRoot,
- providerFile);
- if (res != null) {
- return Collections.singletonList(res).iterator();
- }
- }
- }
-
- // no children here
- return null;
- }
+ ResourceResolver resolver = ctx.getResourceResolver();
+
+ List<Iterator<Resource>> allChildren = new ArrayList<>();
+ Iterator<Resource> children;
+
+ children = contentFileMapper.getChildren(resolver, parent);
+ if (children != null) {
+ allChildren.add(children);
}
-
+
+ children = fileMapper.getChildren(resolver, parent);
+ if (children != null) {
+ allChildren.add(children);
+ }
+
// get children from from shadowed provider
- final ResourceProvider rp = ctx.getParentResourceProvider();
- final Iterator<Resource> parentChildrenIterator;
- if ( rp != null ) {
- parentChildrenIterator = rp.listChildren(ctx.getParentResolveContext(), parent);
- } else {
- parentChildrenIterator = null;
- }
- final File[] children = parentFile.listFiles();
-
- final ResourceResolver resolver = ctx.getResourceResolver();
- final String parentPath = parent.getPath();
- return new Iterator<Resource>() {
-
- final Set<String> names = new HashSet<>();
-
- int index = 0;
-
- Resource next = seek();
-
- @Override
- public boolean hasNext() {
- return next != null;
- }
-
- @Override
- public Resource next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
+ if (this.overlayParentResourceProvider) {
+ final ResourceProvider parentResourceProvider = ctx.getParentResourceProvider();
+ if (parentResourceProvider != null) {
+ children = parentResourceProvider.listChildren(ctx.getParentResolveContext(), parent);
+ if (children != null) {
+ allChildren.add(children);
}
+ }
+ }
- Resource result = next;
- next = seek();
- return result;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
-
- private Resource seek() {
- while (children != null && index < children.length) {
- File file = children[index++];
- String path = parentPath + "/" + file.getName();
- Resource result = getResource(resolver, path, file);
- if (result != null) {
- names.add(file.getName());
- return result;
- }
- }
- if ( parentChildrenIterator != null ) {
- while ( parentChildrenIterator.hasNext() ) {
- final Resource result = parentChildrenIterator.next();
- if ( !names.contains(result.getName()) ) {
- names.add(result.getName());
- return result;
- }
- }
+ if (allChildren.isEmpty()) {
+ return null;
+ }
+ else if (allChildren.size() == 1) {
+ return allChildren.get(0);
+ }
+ else {
+ // merge all children from the different iterators, but filter out potential duplicates with same resource name
+ return IteratorUtils.filteredIterator(IteratorUtils.chainedIterator(allChildren), new Predicate() {
+ private Set<String> names = new HashSet<>();
+ @Override
+ public boolean evaluate(Object object) {
+ Resource resource = (Resource)object;
+ return names.add(resource.getName());
}
- // nothing found any more
- return null;
- }
- };
+ });
+ }
}
// ---------- SCR Integration
@@ -269,11 +238,22 @@ public class FsResourceProvider extends
}
this.providerRoot = providerRoot;
- this.providerRootPrefix = providerRoot.concat("/");
this.providerFile = getProviderFile(providerFileName, bundleContext);
+ this.overlayParentResourceProvider = true;
+
+ List<String> contentFileSuffixes = new ArrayList<>();
+ if (config.provider_json_content()) {
+ contentFileSuffixes.add(ContentFileParser.JSON_SUFFIX);
+ this.overlayParentResourceProvider = false;
+ }
+ ContentFileExtensions contentFileExtensions = new ContentFileExtensions(contentFileSuffixes);
+
+ this.fileMapper = new FileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions);
+ this.contentFileMapper = new ContentFileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions);
+
// start background monitor if check interval is higher than 100
if ( config.provider_checkinterval() > 100 ) {
- this.monitor = new FileMonitor(this, config.provider_checkinterval());
+ this.monitor = new FileMonitor(this, config.provider_checkinterval(), contentFileExtensions);
}
}
@@ -284,8 +264,10 @@ public class FsResourceProvider extends
this.monitor = null;
}
this.providerRoot = null;
- this.providerRootPrefix = null;
this.providerFile = null;
+ this.overlayParentResourceProvider = false;
+ this.fileMapper = null;
+ this.contentFileMapper = null;
}
File getRootFile() {
@@ -325,45 +307,6 @@ public class FsResourceProvider extends
return providerFile;
}
- /**
- * Returns a file corresponding to the given absolute resource tree path. If
- * the path equals the configured provider root, the provider root file is
- * returned. If the path starts with the configured provider root, a file is
- * returned relative to the provider root file whose relative path is the
- * remains of the resource tree path without the provider root path.
- * Otherwise <code>null</code> is returned.
- */
- private File getFile(String path) {
- if (path.equals(providerRoot)) {
- return providerFile;
- }
-
- if (path.startsWith(providerRootPrefix)) {
- String relPath = path.substring(providerRootPrefix.length());
- return new File(providerFile, relPath);
- }
-
- return null;
- }
-
- private Resource getResource(final ResourceResolver resolver,
- final String resourcePath,
- final File file) {
-
- if (file != null) {
-
- // if the file exists, but is not a directory or no repository entry
- // exists, return it as a resource
- if (file.exists()) {
- return new FsResource(resolver, resourcePath, file);
- }
-
- }
-
- // not applicable or not an existing file path
- return null;
- }
-
public ObservationReporter getObservationReporter() {
final ProviderContext ctx = this.getProviderContext();
if ( ctx != null ) {
@@ -371,4 +314,5 @@ public class FsResourceProvider extends
}
return null;
}
+
}
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java Tue Feb 21 13:54:37 2017
@@ -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.mapper;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.fsprovider.internal.parser.ContentFileParser;
+
+/**
+ * Reference to a file that contains a content fragment (e.g. JSON, JCR XML).
+ */
+public final class ContentFile {
+
+ private final File file;
+ private final String subPath;
+ private boolean contentInitialized;
+ private Object content;
+ private ValueMap valueMap;
+
+ /**
+ * @param file File with content fragment
+ * @param subPath Relative path addressing content fragment inside file
+ */
+ public ContentFile(File file, String subPath) {
+ this.file = file;
+ this.subPath = subPath;
+ }
+
+ /**
+ * @param file File with content fragment
+ * @param subPath Relative path addressing content fragment inside file
+ * @param content Content
+ */
+ public ContentFile(File file, String subPath, Object content) {
+ this.file = file;
+ this.subPath = subPath;
+ this.contentInitialized = true;
+ this.content = content;
+ }
+
+ /**
+ * @return File with content fragment
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * @return Relative path addressing content fragment inside file
+ */
+ public String getSubPath() {
+ return subPath;
+ }
+
+ /**
+ * Content object referenced by sub path.
+ * @return Map if resource, property value if property.
+ */
+ public Object getContent() {
+ if (!contentInitialized) {
+ Map<String,Object> rootContent = ContentFileParser.parse(file);
+ content = getDeepContent(rootContent, subPath);
+ contentInitialized = true;
+ }
+ return content;
+ }
+
+ /**
+ * @return true if any content was found.
+ */
+ public boolean hasContent() {
+ return getContent() != null;
+ }
+
+ /**
+ * @return true if content references resource map.
+ */
+ public boolean isResource() {
+ return (getContent() instanceof Map);
+ }
+
+ /**
+ * @return ValueMap for resource. Never null.
+ */
+ @SuppressWarnings("unchecked")
+ public ValueMap getValueMap() {
+ if (valueMap == null) {
+ Object currentContent = getContent();
+ if (currentContent instanceof Map) {
+ valueMap = ValueMapUtil.toValueMap((Map<String,Object>)currentContent);
+ }
+ else {
+ valueMap = ValueMap.EMPTY;
+ }
+ }
+ return valueMap;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Object getDeepContent(Object object, String subPath) {
+ if (object == null) {
+ return null;
+ }
+ if (subPath == null) {
+ return object;
+ }
+ if (!(object instanceof Map)) {
+ return null;
+ }
+ String name;
+ String remainingSubPath;
+ int slashIndex = subPath.indexOf('/');
+ if (slashIndex >= 0) {
+ name = subPath.substring(0, slashIndex);
+ remainingSubPath = subPath.substring(slashIndex + 1);
+ }
+ else {
+ name = subPath;
+ remainingSubPath = null;
+ }
+ Object subObject = ((Map<String,Object>)object).get(name);
+ return getDeepContent(subObject, remainingSubPath);
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,125 @@
+/*
+ * 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 javax.jcr.Node;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.sling.api.resource.AbstractResource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.fsprovider.internal.mapper.jcr.FsNode;
+
+/**
+ * Represents a JSON File with resource content.
+ */
+public final class ContentFileResource extends AbstractResource {
+
+ // the owning resource resolver
+ private final ResourceResolver resolver;
+
+ // the path of this resource in the resource tree
+ private final String resourcePath;
+
+ // the file wrapped by this instance
+ private final ContentFile contentFile;
+
+ // the resource type, assigned on demand
+ private String resourceType;
+ private String resourceSuperType;
+
+ // the resource metadata, assigned on demand
+ private ResourceMetadata metaData;
+
+ /**
+ * @param resolver The owning resource resolver
+ * @param resourcePath The resource path in the resource tree
+ * @param contentFile Content file with sub path
+ */
+ ContentFileResource(ResourceResolver resolver, String resourcePath, ContentFile contentFile) {
+ this.resolver = resolver;
+ this.resourcePath = resourcePath;
+ this.contentFile = contentFile;
+ }
+
+ public String getPath() {
+ return resourcePath;
+ }
+
+ public ResourceMetadata getResourceMetadata() {
+ if (metaData == null) {
+ metaData = new ResourceMetadata();
+ metaData.setModificationTime(contentFile.getFile().lastModified());
+ metaData.setResolutionPath(resourcePath);
+ }
+ return metaData;
+ }
+
+ public ResourceResolver getResourceResolver() {
+ return resolver;
+ }
+
+ public String getResourceSuperType() {
+ if (resourceSuperType == null) {
+ resourceSuperType = contentFile.getValueMap().get("sling:resourceSuperType", String.class);
+ }
+ return resourceSuperType;
+ }
+
+ public String getResourceType() {
+ if (resourceType == null) {
+ ValueMap props = getValueMap();
+ resourceType = props.get("sling:resourceType", String.class);
+ if (resourceType == null) {
+ // fallback to jcr:primaryType when resource type not set
+ resourceType = props.get("jcr:primaryType", String.class);
+ }
+ }
+ return resourceType;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ if (type == ContentFile.class) {
+ return (AdapterType)this.contentFile;
+ }
+ else if (type == ValueMap.class) {
+ return (AdapterType)contentFile.getValueMap();
+ }
+ else if (type == Node.class && contentFile.isResource()) {
+ // support a subset of JCR API for content file resources
+ return (AdapterType)new FsNode(this);
+ }
+ return super.adaptTo(type);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("path", resourcePath)
+ .append("file", contentFile.getFile().getPath())
+ .append("subPath", contentFile.getSubPath())
+ .append("resourceType", getResourceType())
+ .build();
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,146 @@
+/*
+ * 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 java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang3.StringUtils;
+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.ContentFileExtensions;
+import org.apache.sling.fsprovider.internal.FsResourceMapper;
+
+public final class ContentFileResourceMapper implements FsResourceMapper {
+
+ // providerRoot + "/" to be used for prefix matching of paths
+ private final String providerRootPrefix;
+
+ // The "root" file or folder in the file system
+ private final File providerFile;
+
+ private final ContentFileExtensions contentFileExtensions;
+
+ public ContentFileResourceMapper(String providerRoot, File providerFile, ContentFileExtensions contentFileExtensions) {
+ this.providerRootPrefix = providerRoot.concat("/");
+ this.providerFile = providerFile;
+ this.contentFileExtensions = contentFileExtensions;
+ }
+
+ @Override
+ public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
+ ContentFile contentFile = getFile(resourcePath, null);
+ if (contentFile != null && contentFile.hasContent()) {
+ return new ContentFileResource(resolver, resourcePath, contentFile);
+ }
+ else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<Resource> getChildren(final ResourceResolver resolver, final Resource parent) {
+ final String parentPath = parent.getPath();
+ ContentFile parentContentFile = parent.adaptTo(ContentFile.class);
+
+ // not a FsResource, try to create one from the resource
+ if (parentContentFile == null) {
+ parentContentFile = getFile(parentPath, null);
+ if (parentContentFile == null) {
+
+ // check if parent is a file resource that contains a file content resource
+ File parentFile = parent.adaptTo(File.class);
+ if (parentFile != null && parentFile.isDirectory()) {
+ List<Resource> childResources = new ArrayList<>();
+ for (File file : parentFile.listFiles()) {
+ String filenameSuffix = contentFileExtensions.getSuffix(file);
+ if (filenameSuffix != null) {
+ ContentFile contentFile = new ContentFile(file, null);
+ String path = parentPath + "/" + StringUtils.substringBeforeLast(file.getName(), filenameSuffix);
+ childResources.add(new ContentFileResource(resolver, path, contentFile));
+ }
+ }
+ if (!childResources.isEmpty()) {
+ return childResources.iterator();
+ }
+ }
+
+ // no children here
+ return null;
+ }
+ }
+
+ // 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(), subPath, entry.getValue()));
+ }
+ }
+ }
+ if (children.isEmpty()) {
+ return null;
+ }
+ else {
+ return IteratorUtils.transformedIterator(children.iterator(), new Transformer() {
+ @Override
+ public Object transform(Object input) {
+ ContentFile contentFile = (ContentFile)input;
+ String path = parentPath + "/" + ResourceUtil.getName(contentFile.getSubPath());
+ return new ContentFileResource(resolver, path, contentFile);
+ }
+ });
+ }
+ }
+
+ private ContentFile getFile(String path, String subPath) {
+ if (!StringUtils.startsWith(path, providerRootPrefix)) {
+ return null;
+ }
+ String relPath = path.substring(providerRootPrefix.length());
+ for (String filenameSuffix : contentFileExtensions.getSuffixes()) {
+ File file = new File(providerFile, relPath + filenameSuffix);
+ if (file.exists()) {
+ return new ContentFile(file, subPath);
+ }
+ }
+ // try to find in parent path which contains content fragment
+ String parentPath = ResourceUtil.getParent(path);
+ String nextSubPath = path.substring(parentPath.length() + 1)
+ + (subPath != null ? "/" + subPath : "");
+ return getFile(parentPath, nextSubPath);
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,145 @@
+/*
+ * 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 java.io.File;
+import java.util.Iterator;
+
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Predicate;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.fsprovider.internal.ContentFileExtensions;
+import org.apache.sling.fsprovider.internal.FsResourceMapper;
+
+public final class FileResourceMapper implements FsResourceMapper {
+
+ // The location in the resource tree where the resources are mapped
+ private final String providerRoot;
+
+ // providerRoot + "/" to be used for prefix matching of paths
+ private final String providerRootPrefix;
+
+ // The "root" file or folder in the file system
+ private final File providerFile;
+
+ private final ContentFileExtensions contentFileExtensions;
+
+ public FileResourceMapper(String providerRoot, File providerFile, ContentFileExtensions contentFileExtensions) {
+ this.providerRoot = providerRoot;
+ this.providerRootPrefix = providerRoot.concat("/");
+ this.providerFile = providerFile;
+ this.contentFileExtensions = contentFileExtensions;
+ }
+
+ @Override
+ public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
+ File file = getFile(resourcePath);
+ if (file != null) {
+ return new FileResource(resolver, resourcePath, file);
+ }
+ else {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator<Resource> getChildren(final ResourceResolver resolver, final Resource parent) {
+ final String parentPath = parent.getPath();
+ File parentFile = parent.adaptTo(File.class);
+
+ // not a FsResource, try to create one from the resource
+ if (parentFile == null) {
+ // if the parent path is at or below the provider root, get
+ // the respective file
+ parentFile = getFile(parentPath);
+
+ // if the parent path is actually the parent of the provider
+ // root, return a single element iterator just containing the
+ // provider file, unless the provider file is a directory and
+ // a repository item with the same path actually exists
+ if (parentFile == null) {
+
+ if (providerFile.exists() && !StringUtils.startsWith(parentPath, providerRoot)) {
+ String parentPathPrefix = parentPath.concat("/");
+ if (providerRoot.startsWith(parentPathPrefix)) {
+ String relPath = providerRoot.substring(parentPathPrefix.length());
+ if (relPath.indexOf('/') < 0) {
+ Resource res = new FileResource(resolver, providerRoot, providerFile);
+ return IteratorUtils.singletonIterator(res);
+ }
+ }
+ }
+
+ // no children here
+ return null;
+ }
+ }
+
+ // ensure parent is a directory
+ if (!parentFile.isDirectory()) {
+ return null;
+ }
+
+ Iterator<File> children = IteratorUtils.filteredIterator(IteratorUtils.arrayIterator(parentFile.listFiles()), new Predicate() {
+ @Override
+ public boolean evaluate(Object object) {
+ File file = (File)object;
+ return !contentFileExtensions.matchesSuffix(file);
+ }
+ });
+ if (!children.hasNext()) {
+ return null;
+ }
+ return IteratorUtils.transformedIterator(children, new Transformer() {
+ @Override
+ public Object transform(Object input) {
+ File file = (File)input;
+ String path = parentPath + "/" + file.getName();
+ return new FileResource(resolver, path, file);
+ }
+ });
+ }
+
+ /**
+ * Returns a file corresponding to the given absolute resource tree path. If
+ * the path equals the configured provider root, the provider root file is
+ * returned. If the path starts with the configured provider root, a file is
+ * returned relative to the provider root file whose relative path is the
+ * remains of the resource tree path without the provider root path.
+ * Otherwise <code>null</code> is returned.
+ */
+ private File getFile(String path) {
+ if (path.equals(providerRoot)) {
+ return providerFile;
+ }
+ if (path.startsWith(providerRootPrefix)) {
+ String relPath = path.substring(providerRootPrefix.length());
+ File file = new File(providerFile, relPath);
+ if (file.exists() && !contentFileExtensions.matchesSuffix(file)) {
+ return file;
+ }
+ }
+ return null;
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,57 @@
+/*
+ * 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 java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+final class ValueMapUtil {
+
+ private ValueMapUtil() {
+ // static methods only
+ }
+
+ /**
+ * Convert map to value map.
+ * @param content Content map.
+ * @return Value map.
+ */
+ public static ValueMap toValueMap(Map<String,Object> content) {
+ Map<String,Object> props = new HashMap<>();
+ for (Map.Entry<String, Object> entry : ((Map<String,Object>)content).entrySet()) {
+ if (entry.getValue() instanceof Map) {
+ // skip child resources
+ continue;
+ }
+ else if (entry.getValue() instanceof Collection) {
+ // convert lists to arrays
+ props.put(entry.getKey(), ((Collection)entry.getValue()).toArray());
+ }
+ else {
+ props.put(entry.getKey(), entry.getValue());
+ }
+ }
+ return new ValueMapDecorator(props);
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/ValueMapUtil.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java?rev=1783888&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java Tue Feb 21 13:54:37 2017
@@ -0,0 +1,165 @@
+/*
+ * 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.jcr;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+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.api.resource.ValueMap;
+
+/**
+ * Simplified implementation of read-only content access via the JCR API.
+ */
+abstract class FsItem implements Item {
+
+ protected final Resource resource;
+ protected final ValueMap props;
+ protected final ResourceResolver resolver;
+
+ public FsItem(Resource resource) {
+ this.resource = resource;
+ this.props = resource.getValueMap();
+ this.resolver = resource.getResourceResolver();
+ }
+
+ @Override
+ public String getPath() throws RepositoryException {
+ return resource.getPath();
+ }
+
+ @Override
+ public String getName() throws RepositoryException {
+ return resource.getName();
+ }
+
+ @Override
+ public FsItem getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+ String path = ResourceUtil.getParent(resource.getPath(), getDepth() - depth - 1);
+ if (path != null) {
+ Resource ancestor = resolver.getResource(path);
+ if (ancestor != null) {
+ return new FsNode(ancestor);
+ }
+ }
+ throw new ItemNotFoundException(path);
+ }
+
+ @Override
+ public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+ Resource parent = resource.getParent();
+ if (parent != null) {
+ Node parentNode = parent.adaptTo(Node.class);
+ if (parentNode != null) {
+ return parentNode;
+ }
+ }
+ throw new ItemNotFoundException();
+ }
+
+ @Override
+ public int getDepth() throws RepositoryException {
+ if (StringUtils.equals("/", getPath())) {
+ return 0;
+ } else {
+ return StringUtils.countMatches(getPath(), "/");
+ }
+ }
+
+ @Override
+ public Session getSession() throws RepositoryException {
+ return resolver.adaptTo(Session.class);
+ }
+
+ @Override
+ public boolean isNode() {
+ return (this instanceof Node);
+ }
+
+ @Override
+ public boolean isNew() {
+ return false;
+ }
+
+ @Override
+ public boolean isModified() {
+ return false;
+ }
+
+ @Override
+ public boolean isSame(Item otherItem) throws RepositoryException {
+ return StringUtils.equals(getPath(), otherItem.getPath());
+ }
+
+ @Override
+ public void accept(ItemVisitor visitor) throws RepositoryException {
+ // do nothing
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("path", getPath())
+ .build();
+ }
+ catch (RepositoryException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+
+ // --- unsupported methods ---
+
+ @Override
+ public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException,
+ InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException,
+ NoSuchNodeTypeException, RepositoryException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void remove() throws VersionException, LockException, ConstraintViolationException, AccessDeniedException,
+ RepositoryException {
+ throw new UnsupportedOperationException();
+ }
+
+}
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Feb 21 13:54:37 2017
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author
Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
------------------------------------------------------------------------------
svn:mime-type = text/plain