Author: cziegeler
Date: Mon Sep 16 11:27:33 2013
New Revision: 1523593
URL: http://svn.apache.org/r1523593
Log:
SLING-3036 : Feedback on SLING-2707: Support of chunked file upload. Apply
patch from Shashank Gupta
Modified:
sling/trunk/bundles/jcr/resource/src/main/resources/SLING-INF/nodetypes/resource.cnd
sling/trunk/bundles/servlets/post/pom.xml
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingFileUploadHandler.java
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
Modified:
sling/trunk/bundles/jcr/resource/src/main/resources/SLING-INF/nodetypes/resource.cnd
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/resources/SLING-INF/nodetypes/resource.cnd?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
---
sling/trunk/bundles/jcr/resource/src/main/resources/SLING-INF/nodetypes/resource.cnd
(original)
+++
sling/trunk/bundles/jcr/resource/src/main/resources/SLING-INF/nodetypes/resource.cnd
Mon Sep 16 11:27:33 2013
@@ -32,21 +32,3 @@
mixin
- sling:resourceSuperType (string)
-//-----------------------------------------------------------------------------
-// node type to store chunk
-// offset: offset of chunk in file
-// jcr:data: binary of chunk
-[sling:chunk] > nt:hierarchyNode
- primaryitem jcr:data
- - sling:offset (long) mandatory
- - jcr:data (binary) mandatory
-
-
//-----------------------------------------------------------------------------
- // Mixin type to identify that a node has chunks
- // sling:fileLength : length of complete file
- // sling:length: cumulative length of all uploaded chunks
-[sling:chunks]
- mixin
- - sling:fileLength (long)
- - sling:length (long)
- + * (sling:chunk) multiple
Modified: sling/trunk/bundles/servlets/post/pom.xml
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/pom.xml?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
--- sling/trunk/bundles/servlets/post/pom.xml (original)
+++ sling/trunk/bundles/servlets/post/pom.xml Mon Sep 16 11:27:33 2013
@@ -70,6 +70,12 @@
<Embed-Dependency>
jackrabbit-jcr-commons;inline=org/apache/jackrabbit/util/ISO8601.class|org/apache/jackrabbit/util/Text.class
</Embed-Dependency>
+ <Sling-Namespaces>
+ sling=http://sling.apache.org/jcr/sling/1.0
+ </Sling-Namespaces>
+ <Sling-Nodetypes>
+ SLING-INF/nodetypes/chunk.cnd,
+ </Sling-Nodetypes>
</instructions>
</configuration>
</plugin>
@@ -131,6 +137,12 @@
<version>2.0.2-incubator</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.4</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.commons.json</artifactId>
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
(original)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/SlingPostConstants.java
Mon Sep 16 11:27:33 2013
@@ -552,5 +552,11 @@ public interface SlingPostConstants {
* @since 2.3.4
*/
public static final String NT_SLING_CHUNK_OFFSET = "sling:offset";
+
+ /**
+ * Constant for prefix for sling:chunk node name.
+ * @since 2.3.4
+ */
+ public static final String CHUNK_NODE_NAME = "chunk";
}
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
(original)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/RequestProperty.java
Mon Sep 16 11:27:33 2013
@@ -65,13 +65,7 @@ public class RequestProperty {
private boolean patch = false;
- private long offset;
-
- private long length;
-
- private boolean completed;
-
- private boolean chunkUpload;
+ private Chunk chunk;
public RequestProperty(String path) {
assert path.startsWith("/");
@@ -309,55 +303,17 @@ public class RequestProperty {
}
/**
- * Return offset of the chunk.
- */
- public long getOffset() {
- return offset;
- }
-
- /**
- * Set offset value.
- *
- */
- public void setOffsetValue(long offset) {
- this.offset = offset;
- this.chunkUpload = true;
- }
-
- /**
- * Return length of the file parameter.
- */
- public long getLength() {
- return length;
- }
-
- /**
- * Set length of file parameter.
- */
- public void setLength(long length) {
- this.length = length;
- }
-
- /**
- * Return true if request contains last chunk as a result
- * upload should be finished. It is useful in scenarios where
- * file streaming where file size is not known in advance.
+ * Return true if request is chunk upload.
*/
- public boolean isCompleted() {
- return completed;
+ public boolean isChunkUpload() {
+ return chunk != null;
}
- /**
- * Set complete flag
- */
- public void setCompleted(boolean complete) {
- this.completed = complete;
+ public Chunk getChunk() {
+ return chunk;
}
- /**
- * Return true if request is chunk upload.
- */
- public boolean isChunkUpload() {
- return chunkUpload;
+ public void setChunk(Chunk chunk) {
+ this.chunk = chunk;
}
}
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingFileUploadHandler.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingFileUploadHandler.java?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingFileUploadHandler.java
(original)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/SlingFileUploadHandler.java
Mon Sep 16 11:27:33 2013
@@ -17,7 +17,6 @@
package org.apache.sling.servlets.post.impl.helper;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -25,10 +24,14 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.SequenceInputStream;
import java.util.Calendar;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
@@ -37,6 +40,7 @@ import javax.jcr.nodetype.NodeType;
import javax.jcr.nodetype.NodeTypeManager;
import javax.servlet.ServletContext;
+import org.apache.commons.io.IOUtils;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.request.RequestParameter;
import org.apache.sling.api.resource.ModifiableValueMap;
@@ -44,6 +48,8 @@ import org.apache.sling.api.resource.Per
import org.apache.sling.api.resource.Resource;
import org.apache.sling.servlets.post.Modification;
import org.apache.sling.servlets.post.SlingPostConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Handles file uploads.
@@ -97,8 +103,8 @@ public class SlingFileUploadHandler {
public static final String JCR_MIMETYPE = "jcr:mimeType";
public static final String JCR_ENCODING = "jcr:encoding";
public static final String JCR_DATA = "jcr:data";
-
- private static final String CHUNK_NODE_NAME = "chunk";
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
/**
* The servlet context.
@@ -237,19 +243,20 @@ public class SlingFileUploadHandler {
* Process chunk upload. For first and intermediate chunks request persists
* chunks at jcr:content/chunk_start_end/jcr:data or
* nt:resource/chunk_start_end/jcr:data. For last last chunk,
- * merge all previous chunks and current chunk and replace binary at
+ * merge all previous chunks and last chunk and replace binary at
* destination.
*/
private void processChunk(final Resource resParent, final Node res,
final RequestProperty prop, RequestParameter value,
final List<Modification> changes) throws RepositoryException {
try {
- long chunkOffset = prop.getOffset();
+ long chunkOffset = prop.getChunk().getOffset();
if (chunkOffset == 0) {
// first chunk
// check if another chunk upload is already in progress. throw
// exception
- NodeIterator itr = res.getNodes(CHUNK_NODE_NAME + "*");
+ NodeIterator itr =
res.getNodes(SlingPostConstants.CHUNK_NODE_NAME
+ + "*");
if (itr.hasNext()) {
throw new RepositoryException(
"Chunk upload already in progress at {" + res.getPath()
@@ -270,7 +277,7 @@ public class SlingFileUploadHandler {
}
long currentLength = res.getProperty(
SlingPostConstants.NT_SLING_CHUNKS_LENGTH).getLong();
- long totalLength = prop.getLength();
+ long totalLength = prop.getChunk().getLength();
if (chunkOffset != currentLength) {
throw new RepositoryException("Chunk's offset {"
+ chunkOffset
@@ -293,17 +300,17 @@ public class SlingFileUploadHandler {
totalLength);
}
}
- NodeIterator itr = res.getNodes(CHUNK_NODE_NAME + "_"
- + String.valueOf(chunkOffset) + "*");
+ NodeIterator itr = res.getNodes(SlingPostConstants.CHUNK_NODE_NAME
+ + "_" + String.valueOf(chunkOffset) + "*");
if (itr.hasNext()) {
throw new RepositoryException("Chunk already present at {"
+ itr.nextNode().getPath() + "}");
}
- String nodeName = CHUNK_NODE_NAME + "_"
+ String nodeName = SlingPostConstants.CHUNK_NODE_NAME + "_"
+ String.valueOf(chunkOffset) + "_"
+ String.valueOf(chunkOffset + value.getSize() - 1);
if (totalLength == (currentLength + value.getSize())
- || prop.isCompleted()) {
+ || prop.getChunk().isCompleted()) {
File file = null;
InputStream fileIns = null;
try {
@@ -311,7 +318,8 @@ public class SlingFileUploadHandler {
fileIns = new FileInputStream(file);
changes.add(Modification.onModified(res.setProperty(
JCR_DATA, fileIns).getPath()));
- NodeIterator nodeItr = res.getNodes(CHUNK_NODE_NAME + "*");
+ NodeIterator nodeItr =
res.getNodes(SlingPostConstants.CHUNK_NODE_NAME
+ + "*");
while (nodeItr.hasNext()) {
Node nodeRange = nodeItr.nextNode();
changes.add(Modification.onDeleted(nodeRange.getPath()));
@@ -363,15 +371,17 @@ public class SlingFileUploadHandler {
final InputStream lastChunkStream) throws PersistenceException,
RepositoryException {
OutputStream out = null;
+ SequenceInputStream mergeStrm = null;
File file = null;
try {
file = File.createTempFile("tmp-", "-mergechunk");
- out = new BufferedOutputStream(new FileOutputStream(file),
- 16 * 1024);
- String startPattern = CHUNK_NODE_NAME + "_" + "0_*";
+ out = new FileOutputStream(file);
+ String startPattern = SlingPostConstants.CHUNK_NODE_NAME + "_"
+ + "0_*";
NodeIterator nodeItr = parentNode.getNodes(startPattern);
InputStream ins = null;
int i = 0;
+ Set<InputStream> inpStrmSet = new LinkedHashSet<InputStream>();
while (nodeItr.hasNext()) {
if (nodeItr.getSize() > 1) {
throw new RepositoryException(
@@ -379,48 +389,26 @@ public class SlingFileUploadHandler {
}
Node rangeNode = nodeItr.nextNode();
- try {
- InputStream in = rangeNode.getProperty(
- javax.jcr.Property.JCR_DATA).getBinary().getStream();
- ins = new BufferedInputStream(in, 16 * 1024);
- byte[] buf = new byte[16 * 1024];
- while ((i = ins.read(buf)) != -1) {
- out.write(buf, 0, i);
- out.flush();
- }
-
- } finally {
- if (ins != null) {
- try {
- ins.close();
- } catch (IOException ignore) {
-
- }
- }
- }
+ inpStrmSet.add(rangeNode.getProperty(
+ javax.jcr.Property.JCR_DATA).getBinary().getStream());
+ log.debug("added chunk {} to merge stream",
rangeNode.getName());
String[] indexBounds = rangeNode.getName().substring(
- (CHUNK_NODE_NAME + "_").length()).split("_");
- startPattern = CHUNK_NODE_NAME + "_"
+ (SlingPostConstants.CHUNK_NODE_NAME + "_").length()).split(
+ "_");
+ startPattern = SlingPostConstants.CHUNK_NODE_NAME + "_"
+ String.valueOf(Long.valueOf(indexBounds[1]) + 1) + "_*";
nodeItr = parentNode.getNodes(startPattern);
}
- ins = new BufferedInputStream(lastChunkStream, 16 * 1024);
- byte[] buf = new byte[16 * 1024];
- while ((i = ins.read(buf)) != -1) {
- out.write(buf, 0, i);
- out.flush();
- }
+ inpStrmSet.add(lastChunkStream);
+ mergeStrm = new SequenceInputStream(
+ Collections.enumeration(inpStrmSet));
+ IOUtils.copyLarge(mergeStrm, out);
} catch (IOException e) {
throw new PersistenceException("excepiton occured", e);
} finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException ignore) {
-
- }
- }
+ IOUtils.closeQuietly(out);
+ IOUtils.closeQuietly(mergeStrm);
}
return file;
@@ -430,30 +418,75 @@ public class SlingFileUploadHandler {
* Delete all chunks saved within a node. If no chunks exist, it is no-op.
*/
public void deleteChunks(final Node node) throws RepositoryException {
- Node chunkNode = null;
+ // parent node containing all chunks and has mixin sling:chunks applied
+ // on it.
+ Node chunkParent = null;
Node jcrContentNode = null;
if (hasChunks(node)) {
- chunkNode = node;
+ chunkParent = node;
} else if (node.hasNode(JCR_CONTENT)
&& hasChunks((jcrContentNode = node.getNode(JCR_CONTENT)))) {
- chunkNode = jcrContentNode;
+ chunkParent = jcrContentNode;
}
- if (chunkNode != null) {
- NodeIterator nodeItr = chunkNode.getNodes(CHUNK_NODE_NAME + "*");
+ if (chunkParent != null) {
+ NodeIterator nodeItr =
chunkParent.getNodes(SlingPostConstants.CHUNK_NODE_NAME
+ + "*");
while (nodeItr.hasNext()) {
Node rangeNode = nodeItr.nextNode();
rangeNode.remove();
}
- if
(chunkNode.hasProperty(SlingPostConstants.NT_SLING_FILE_LENGTH)) {
-
chunkNode.getProperty(SlingPostConstants.NT_SLING_FILE_LENGTH).remove();
+ if
(chunkParent.hasProperty(SlingPostConstants.NT_SLING_FILE_LENGTH)) {
+
chunkParent.getProperty(SlingPostConstants.NT_SLING_FILE_LENGTH).remove();
}
- if
(chunkNode.hasProperty(SlingPostConstants.NT_SLING_CHUNKS_LENGTH)) {
- chunkNode.getProperty(
+ if
(chunkParent.hasProperty(SlingPostConstants.NT_SLING_CHUNKS_LENGTH)) {
+ chunkParent.getProperty(
SlingPostConstants.NT_SLING_CHUNKS_LENGTH).remove();
}
- chunkNode.removeMixin(SlingPostConstants.NT_SLING_CHUNK_MIXIN);
+ chunkParent.removeMixin(SlingPostConstants.NT_SLING_CHUNK_MIXIN);
+ }
+ }
+
+ /**
+ * Get the last {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE}
+ * {@link Node}.
+ *
+ * @param node {@link Node} containing
+ * {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE}
+ * {@link Node}s
+ * @return the {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE} chunk
+ * node.
+ * @throws RepositoryException
+ */
+ public Node getLastChunk(Node node) throws RepositoryException {
+ // parent node containing all chunks and has mixin sling:chunks applied
+ // on it.
+ Node chunkParent = null;
+ Node jcrContentNode = null;
+ if (hasChunks(node)) {
+ chunkParent = node;
+ } else if (node.hasNode(JCR_CONTENT)
+ && hasChunks((jcrContentNode = node.getNode(JCR_CONTENT)))) {
+ chunkParent = jcrContentNode;
+
+ }
+ String startPattern = SlingPostConstants.CHUNK_NODE_NAME + "_" + "0_*";
+ NodeIterator nodeItr = chunkParent.getNodes(startPattern);
+ Node chunkNode = null;
+ while (nodeItr.hasNext()) {
+ if (nodeItr.getSize() > 1) {
+ throw new RepositoryException(
+ "more than one node found for pattern: " + startPattern);
+ }
+ chunkNode = nodeItr.nextNode();
+
+ String[] indexBounds = chunkNode.getName().substring(
+ (SlingPostConstants.CHUNK_NODE_NAME +
"_").length()).split("_");
+ startPattern = SlingPostConstants.CHUNK_NODE_NAME + "_"
+ + String.valueOf(Long.valueOf(indexBounds[1]) + 1) + "_*";
+ nodeItr = chunkParent.getNodes(startPattern);
}
+ return chunkNode;
}
/**
Modified:
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java?rev=1523593&r1=1523592&r2=1523593&view=diff
==============================================================================
---
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
(original)
+++
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/operations/AbstractCreateOperation.java
Mon Sep 16 11:27:33 2013
@@ -46,6 +46,7 @@ import org.apache.sling.servlets.post.No
import org.apache.sling.servlets.post.PostResponse;
import org.apache.sling.servlets.post.SlingPostConstants;
import org.apache.sling.servlets.post.VersioningConfiguration;
+import org.apache.sling.servlets.post.impl.helper.Chunk;
import org.apache.sling.servlets.post.impl.helper.DefaultNodeNameGenerator;
import org.apache.sling.servlets.post.impl.helper.RequestProperty;
@@ -372,7 +373,12 @@ abstract class AbstractCreateOperation e
reqProperties, propPath,
SlingPostConstants.SUFFIX_OFFSET);
if (e.getValue().length == 1) {
-
prop.setOffsetValue(Long.parseLong(e.getValue()[0].toString()));
+ Chunk chunk = prop.getChunk();
+ if(chunk == null){
+ chunk = new Chunk();
+ }
+
chunk.setOffsetValue(Long.parseLong(e.getValue()[0].toString()));
+ prop.setChunk(chunk);
}
continue;
}
@@ -382,7 +388,12 @@ abstract class AbstractCreateOperation e
reqProperties, propPath,
SlingPostConstants.SUFFIX_COMPLETED);
if (e.getValue().length == 1) {
-
prop.setCompleted(Boolean.parseBoolean((e.getValue()[0].toString())));
+ Chunk chunk = prop.getChunk();
+ if(chunk == null){
+ chunk = new Chunk();
+ }
+
chunk.setCompleted(Boolean.parseBoolean((e.getValue()[0].toString())));
+ prop.setChunk(chunk);
}
continue;
}
@@ -392,7 +403,12 @@ abstract class AbstractCreateOperation e
reqProperties, propPath,
SlingPostConstants.SUFFIX_LENGTH);
if (e.getValue().length == 1) {
- prop.setLength(Long.parseLong(e.getValue()[0].toString()));
+ Chunk chunk = prop.getChunk();
+ if(chunk == null){
+ chunk = new Chunk();
+ }
+
chunk.setLength(Long.parseLong(e.getValue()[0].toString()));
+ prop.setChunk(chunk);
}
continue;
}