This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.resourcebuilder-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourcebuilder.git
commit 9669c4c5578b8a5df64715900650ab03d1d5343c Author: Bertrand Delacretaz <[email protected]> AuthorDate: Thu Dec 10 15:36:52 2015 +0000 SLING-5356 - add file support git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/resourcebuilder@1719100 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 6 ++ .../sling/resourcebuilder/api/ResourceBuilder.java | 19 +++++ .../resourcebuilder/impl/ResourceBuilderImpl.java | 80 ++++++++++++++++++++-- .../impl/ResourceBuilderImplTest.java | 68 +++++++++++++++++- src/test/resources/models.js | 1 + src/test/resources/myapp.json | 4 ++ src/test/resources/text.html | 3 + 7 files changed, 174 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index b51e328..ca8feba 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,12 @@ <scope>provided</scope> </dependency> <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.testing.sling-mock</artifactId> <version>1.6.0</version> diff --git a/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java index 7d43ec3..cd54db4 100644 --- a/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java +++ b/src/main/java/org/apache/sling/resourcebuilder/api/ResourceBuilder.java @@ -18,6 +18,8 @@ */ package org.apache.sling.resourcebuilder.api; +import java.io.InputStream; + import org.apache.sling.api.resource.Resource; /** Builds Sling Resources using a simple fluent API */ @@ -33,6 +35,23 @@ public interface ResourceBuilder { * @return this builder */ ResourceBuilder resource(String relativePath, Object ... properties); + + /** Create a file under the current parent resource + * @param filename The name of the created file + * @param data The file data + * @param mimeType If null, use the Sling MimeTypeService to set the mime type + * @param lastModified if < 0, current time is used + * @return this builder + */ + ResourceBuilder file(String filename, InputStream data, String mimeType, long lastModified); + + /** Create a file under the current parent resource. Mime type is set using the + * Sling MimeTypeService, and last modified is set to current time. + * @param filename The name of the created file + * @param data The file data + * @return this builder + */ + ResourceBuilder file(String filename, InputStream data); /** Commit created resources */ ResourceBuilder commit(); diff --git a/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java b/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java index 4e8899a..bb278d1 100644 --- a/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java +++ b/src/main/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImpl.java @@ -18,6 +18,11 @@ */ package org.apache.sling.resourcebuilder.impl; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.sling.api.resource.ModifiableValueMap; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; @@ -33,6 +38,11 @@ public class ResourceBuilderImpl implements ResourceBuilder { private boolean hierarchyMode; public static final String JCR_PRIMARYTYPE = "jcr:primaryType"; + public static final String JCR_MIMETYPE = "jcr:mimeType"; + public static final String JCR_LASTMODIFIED = "jcr:lastModified"; + public static final String JCR_DATA = "jcr:data"; + public static final String JCR_CONTENT = "jcr:content"; + public static final String NT_RESOURCE = "nt:resource"; public ResourceBuilderImpl(Resource parent) { if(parent == null) { @@ -58,7 +68,7 @@ public class ResourceBuilderImpl implements ResourceBuilder { @Override public ResourceBuilder resource(String relativePath, Object... properties) { - Resource created = null; + Resource r = null; if(relativePath.startsWith("/")) { throw new IllegalArgumentException("Path is not relative:" + relativePath); } @@ -68,19 +78,32 @@ public class ResourceBuilderImpl implements ResourceBuilder { final Resource myParent = ensureResourceExists(parentPath); try { - created = currentParent.getResourceResolver().create(myParent, - ResourceUtil.getName(relativePath), MapArgsConverter.toMap(properties)); + r = currentParent.getResourceResolver().getResource(fullPath); + final Map<String, Object> props = MapArgsConverter.toMap(properties); + if(r == null) { + r = currentParent.getResourceResolver().create(myParent, + ResourceUtil.getName(relativePath), props); + } else { + // Resource exists, set our properties + final ModifiableValueMap mvm = r.adaptTo(ModifiableValueMap.class); + if(mvm == null) { + throw new IllegalStateException("Cannot modify properties of " + r.getPath()); + } + for(Map.Entry <String, Object> e : props.entrySet()) { + mvm.put(e.getKey(), e.getValue()); + } + } } catch(PersistenceException pex) { throw new RuntimeException( "PersistenceException while creating Resource " + relativePath + " under " + currentParent.getPath(), pex); } - if(created == null) { - throw new RuntimeException("Failed to created resource " + relativePath + if(r == null) { + throw new RuntimeException("Failed to get or create resource " + relativePath + " under " + currentParent.getPath()); } else if(hierarchyMode) { - currentParent = created; + currentParent = r; } return this; } @@ -112,6 +135,51 @@ public class ResourceBuilderImpl implements ResourceBuilder { throw new RuntimeException("Unable to create intermediate resource at " + path, ex); } } + + @Override + public ResourceBuilder file(String filename, InputStream data, String mimeType, long lastModified) { + Resource file = null; + final ResourceResolver resolver = currentParent.getResourceResolver(); + final String name = ResourceUtil.getName(filename); + + if(!filename.equals(name)) { + throw new IllegalArgumentException("Filename must not be a path:" + filename + " -> " + name); + } + if(data == null) { + throw new IllegalArgumentException("Data is null for file " + filename); + } + + try { + final String fullPath = currentParent.getPath() + "/" + name; + if(resolver.getResource(fullPath) != null) { + throw new IllegalStateException("Resource already exists:" + fullPath); + } + file = resolver.create(currentParent, name, null); + final Map<String, Object> props = new HashMap<String, Object>(); + props.put(JCR_PRIMARYTYPE, NT_RESOURCE); + // TODO get mime type from MimeTypeService + props.put(JCR_MIMETYPE, mimeType); + props.put(JCR_LASTMODIFIED, lastModified >= 0 ? lastModified : System.currentTimeMillis()); + props.put(JCR_DATA, data); + resolver.create(file, JCR_CONTENT, props); + } catch(PersistenceException pex) { + throw new RuntimeException("Unable to create file under " + currentParent.getPath(), pex); + } + + if(file == null) { + throw new RuntimeException("Unable to get or created file resource " + filename + " under " + currentParent.getPath()); + } + if(hierarchyMode) { + currentParent = file; + } + + return this; + } + + @Override + public ResourceBuilder file(String filename, InputStream data) { + return file(filename, data, null, -1); + } @Override public ResourceBuilder withIntermediatePrimaryType(String primaryType) { diff --git a/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java b/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java index 08e2364..dbf0253 100644 --- a/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java +++ b/src/test/java/org/apache/sling/resourcebuilder/impl/ResourceBuilderImplTest.java @@ -18,12 +18,17 @@ */ package org.apache.sling.resourcebuilder.impl; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.Map; import java.util.UUID; +import org.apache.commons.io.IOUtils; import org.apache.sling.api.resource.PersistenceException; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; @@ -61,6 +66,34 @@ public class ResourceBuilderImplTest { return result; } + /** Assert that a file exists and verify its properties. */ + private void assertFile(String path, String mimeType, String expectedContent, Long lastModified) throws IOException { + final Resource r = assertResource(fullPath(path)); + assertNotNull("Expecting resource to exist:" + path, r); + + // Files are stored according to the standard JCR structure + final Resource jcrContent = r.getChild(ResourceBuilderImpl.JCR_CONTENT); + assertNotNull("Expecting subresource:" + ResourceBuilderImpl.JCR_CONTENT, jcrContent); + final ValueMap vm = jcrContent.adaptTo(ValueMap.class); + assertNotNull("Expecting ValueMap for " + jcrContent.getPath(), vm); + assertEquals("Expecting nt:Resource type for " + jcrContent.getPath(), + ResourceBuilderImpl.NT_RESOURCE, vm.get(ResourceBuilderImpl.JCR_PRIMARYTYPE)); + assertEquals("Expecting the correct mime-type", mimeType, vm.get(ResourceBuilderImpl.JCR_MIMETYPE)); + assertEquals("Expecting the correct last modified", lastModified, vm.get(ResourceBuilderImpl.JCR_LASTMODIFIED)); + + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final InputStream is = vm.get(ResourceBuilderImpl.JCR_DATA, InputStream.class); + assertNotNull("Expecting InputStream property on nt:resource:" + ResourceBuilderImpl.JCR_DATA, is); + IOUtils.copy(is, bos); + try { + final String content = new String(bos.toByteArray()); + assertTrue("Expecting content to contain " + expectedContent, content.contains(expectedContent)); + } finally { + bos.close(); + is.close(); + } + } + private String fullPath(String path) { return path.startsWith("/") ? path : testRootPath + "/" + path; } @@ -176,7 +209,7 @@ public class ResourceBuilderImplTest { } @Test - public void buildATree() throws PersistenceException { + public void simpleTree() throws PersistenceException { getBuilder(testRootPath) .resource("a/b/c", "title", "foo", "count", 21) .siblingsMode() @@ -196,4 +229,37 @@ public class ResourceBuilderImplTest { assertResource("a/b/c/2"); assertResource("a/b/c/3"); } + + @Test + public void treeWithFiles() throws PersistenceException, IOException { + getBuilder(testRootPath) + .resource("apps/myapp/components/resource") + .siblingsMode() + .file("models.js", getClass().getResourceAsStream("/models.js"), "MT1", 42) + .file("text.html", getClass().getResourceAsStream("/text.html"), "MT2", 43) + .resetParent() + .hierarchyMode() + .resource("apps") + .file("myapp.json", getClass().getResourceAsStream("/myapp.json"), "MT3", 44) + .resetParent() + .resource("apps/content/myapp/resource") + .resetParent() + .resource("apps/content", "title", "foo") + .file("myapp.json", getClass().getResourceAsStream("/myapp.json"), "MT4", 45) + .commit() + ; + + assertResource("apps/content/myapp/resource"); + assertResource("apps/myapp/components/resource"); + assertProperties("apps/content", "title", "foo"); + + assertFile("apps/myapp/components/resource/models.js", + "MT1", "function someJavascriptFunction()", 42L); + assertFile("apps/myapp/components/resource/text.html", + "MT2", "This is an html file", 43L); + assertFile("apps/myapp.json", + "MT3", "\"sling:resourceType\":\"its/resource/type\"", 44L); + assertFile("apps/content/myapp.json", + "MT4", "\"sling:resourceType\":\"its/resource/type\"", 45L); + } } diff --git a/src/test/resources/models.js b/src/test/resources/models.js new file mode 100644 index 0000000..142f8d3 --- /dev/null +++ b/src/test/resources/models.js @@ -0,0 +1 @@ +function someJavascriptFunction() { return "yes, it worked." } \ No newline at end of file diff --git a/src/test/resources/myapp.json b/src/test/resources/myapp.json new file mode 100644 index 0000000..bd3edd2 --- /dev/null +++ b/src/test/resources/myapp.json @@ -0,0 +1,4 @@ +{ + "jcr:primaryType":"some:NodeType", + "sling:resourceType":"its/resource/type" +} \ No newline at end of file diff --git a/src/test/resources/text.html b/src/test/resources/text.html new file mode 100644 index 0000000..57dfc39 --- /dev/null +++ b/src/test/resources/text.html @@ -0,0 +1,3 @@ +<html> +This is an html file +</html> \ No newline at end of file -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
