Author: cziegeler
Date: Mon Sep 16 11:28:12 2013
New Revision: 1523594

URL: http://svn.apache.org/r1523594
Log:
SLING-3036 :  Feedback on SLING-2707: Support of chunked file upload. Apply 
patch from Shashank Gupta

Added:
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
   (with props)
    
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
   (with props)
    sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/
    sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/
    
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
   (with props)

Added: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java?rev=1523594&view=auto
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
 (added)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
 Mon Sep 16 11:28:12 2013
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.servlets.post.impl.helper;
+
+/**
+ * <code>Chunk</code> enscapsulates all chunk upload attributes.
+ * 
+ * @since 2.3.4
+ */
+public class Chunk {
+
+    private long offset;
+
+    private long length;
+
+    private boolean completed;
+
+    /**
+     * Return offset of the chunk.
+     */
+    public long getOffset() {
+        return offset;
+    }
+
+    /**
+     * Set offset value.
+     */
+    public void setOffsetValue(long offset) {
+        this.offset = offset;
+    }
+
+    /**
+     * 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.
+     */
+    public boolean isCompleted() {
+        return completed;
+    }
+
+    /**
+     * Set complete flag
+     */
+    public void setCompleted(boolean complete) {
+        this.completed = complete;
+    }
+
+}

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/Chunk.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java?rev=1523594&view=auto
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
 (added)
+++ 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
 Mon Sep 16 11:28:12 2013
@@ -0,0 +1,186 @@
+/*
+ * 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.servlets.post.impl.helper;
+
+import java.util.Map;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.commons.osgi.OsgiUtil;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.servlets.post.SlingPostConstants;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>ChunkCleanUpTask</code> implements a job run at regular intervals
+ * to find incomplete chunk uploads and remove them from the repository to
+ * prevent littering the repository with incomplete chunks.
+ * <p>
+ * This task is configured with OSGi configuration for the PID
+ * <code>org.apache.sling.servlets.post.impl.helper.ChunkCleanUpTask</code> 
with
+ * property <code>scheduler.expression</code> being the schedule to execute the
+ * task. The schedule is a cron job expression as described at <a
+ * href="http://www.docjar.com/docs/api/org/quartz/CronTrigger.html";>Cron
+ * Trigger</a> with the default value configured to run twice a day at 0h41m31s
+ * and 12h4131s.
+ * <p>
+ * The property <code>chunk.cleanup.age</code> specifies chunk's age in minutes
+ * before it is considered for clean up.
+ * <p>
+ * Currently the cleanup tasks connects as the administrative user to the
+ * default workspace assuming users are stored in that workspace and the
+ * administrative user has full access.
+ */
+@Component(metatype = true, label = "Sling Post Chunk Upload : Cleanup Task", 
description = "Task to regularly purge incomplete chunks from the repository")
+@Service(value = Runnable.class)
+@Properties({
+    @Property(name = "scheduler.expression", value = "31 41 0/12 * * ?", label 
= "Schedule", description = "Cron expression scheudling this job. Default is 
hourly 17m23s after the hour. "
+        + "See http://www.docjar.com/docs/api/org/quartz/CronTrigger.html for 
a description "
+        + "of the format for this value."),
+    @Property(name = "service.description", value = "Periodic Chunk Cleanup 
Job", propertyPrivate = true),
+    @Property(name = "service.vendor", value = "The Apache Software 
Foundation", propertyPrivate = true) })
+public class ChunkCleanUpTask implements Runnable {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference
+    private SlingRepository repository;
+
+    @Property(intValue = 360, description = "The chunk's age in minutes before 
it is considered for clean up.")
+    private static final String CHUNK_CLEANUP_AGE = "chunk.cleanup.age";
+
+    private SlingFileUploadHandler uploadhandler = new 
SlingFileUploadHandler();
+
+    /**
+     * Clean up age criterion in millisec.
+     */
+    private long chunkCleanUpAge;
+
+    /**
+     * Executes the job. Is called for each triggered schedule point.
+     */
+    public void run() {
+        log.debug("ChunkCleanUpTask: Starting cleanup");
+        cleanup();
+    }
+
+    /**
+     * This method deletes chunks which are {@link #isEligibleForCleanUp(Node)}
+     * for cleanup. It queries all
+     * {@link SlingPostConstants#NT_SLING_CHUNK_MIXIN} nodes and filter nodes
+     * which are {@link #isEligibleForCleanUp(Node)} for cleanup. It then
+     * deletes old chunks upload.
+     */
+    private void cleanup() {
+
+        long start = System.currentTimeMillis();
+
+        int numCleaned = 0;
+        int numLive = 0;
+
+        Session admin = null;
+        try {
+            // assume chunks are stored in the default workspace
+            admin = repository.loginAdministrative(null);
+            QueryManager qm = admin.getWorkspace().getQueryManager();
+
+            QueryResult queryres = qm.createQuery(
+                "SELECT * FROM [sling:chunks] ", Query.JCR_SQL2).execute();
+            NodeIterator nodeItr = queryres.getNodes();
+            while (nodeItr.hasNext()) {
+                Node node = nodeItr.nextNode();
+                if (isEligibleForCleanUp(node)) {
+                    numCleaned++;
+                    uploadhandler.deleteChunks(node);
+                } else {
+                    numLive++;
+                }
+            }
+            if (admin.hasPendingChanges()) {
+                try {
+                    admin.refresh(true);
+                    admin.save();
+                } catch (InvalidItemStateException iise) {
+                    log.info("ChunkCleanUpTask: Concurrent modification to one 
or more of the chunk to be removed. Retrying later");
+                } catch (RepositoryException re) {
+                    log.info("ChunkCleanUpTask: Failed persisting chunk 
removal. Retrying later");
+                }
+            }
+
+        } catch (Throwable t) {
+            log.error(
+                "ChunkCleanUpTask: General failure while trying to cleanup 
chunks",
+                t);
+        } finally {
+            if (admin != null) {
+                admin.logout();
+            }
+        }
+        long end = System.currentTimeMillis();
+        log.info(
+            "ChunkCleanUpTask finished: Removed {} chunk upload(s) in {}ms ({} 
chunk upload(s) still active)",
+            new Object[] { numCleaned, (end - start), numLive });
+    }
+
+    /**
+     * Check if {@link Node} is eligible of
+     * {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE} cleanup. To be
+     * eligible the age of last
+     * {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE} uploaded should be
+     * greater than @link {@link #chunkCleanUpAge}
+     * 
+     * @param node {@link Node} containing
+     *            {@link SlingPostConstants#NT_SLING_CHUNK_NODETYPE}
+     *            {@link Node}s
+     * @return true if eligible else false.
+     * @throws RepositoryException
+     */
+    private boolean isEligibleForCleanUp(Node node) throws RepositoryException 
{
+        Node lastChunkNode = uploadhandler.getLastChunk(node);
+        return lastChunkNode != null
+            && (System.currentTimeMillis() - lastChunkNode.getProperty(
+                javax.jcr.Property.JCR_CREATED).getDate().getTimeInMillis()) > 
chunkCleanUpAge;
+    }
+
+    @Activate
+    protected void activate(final ComponentContext context,
+            final Map<String, Object> configuration) {
+        chunkCleanUpAge = OsgiUtil.toInteger(
+            configuration.get(CHUNK_CLEANUP_AGE), 1) * 60 * 1000;
+        log.info("scheduler config [{}], chunkGarbageTime  [{}] ms",
+            OsgiUtil.toString(configuration.get("scheduler.expression"), ""),
+            chunkCleanUpAge);
+
+    }
+
+}
\ No newline at end of file

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: 
sling/trunk/bundles/servlets/post/src/main/java/org/apache/sling/servlets/post/impl/helper/ChunkCleanUpTask.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: 
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd?rev=1523594&view=auto
==============================================================================
--- 
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
 (added)
+++ 
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
 Mon Sep 16 11:28:12 2013
@@ -0,0 +1,39 @@
+//
+//  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.
+//
+
+<sling = 'http://sling.apache.org/jcr/sling/1.0'>
+
+//-----------------------------------------------------------------------------
+// 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  

Propchange: 
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sling/trunk/bundles/servlets/post/src/main/resources/SLING-INF/nodetypes/chunk.cnd
------------------------------------------------------------------------------
    svn:keywords = Id


Reply via email to