Introduce JCR proxys
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Sep 2011 20:44:33 +0000 (20:44 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Sep 2011 20:44:33 +0000 (20:44 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@4754 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml
server/runtime/org.argeo.server.jcr.mvc/pom.xml
server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java [new file with mode: 0644]
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java [new file with mode: 0644]
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java [new file with mode: 0644]

index 5e63e5de82ded50b0a1244e017c1ff63c3092dec..9e7886d69c9f609aa2cb4e321aedcad49068d03a 100644 (file)
                <param name="schema" value="default" />
                <param name="schemaObjectPrefix" value="fs_" />
        </FileSystem>
                <param name="schema" value="default" />
                <param name="schemaObjectPrefix" value="fs_" />
        </FileSystem>
+       <DataStore class="org.apache.jackrabbit.core.data.FileDataStore"/>
+       <!-- 
        <DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
                <param name="dataSourceName" value="dataSource" />
                <param name="schemaObjectPrefix" value="ds_" />
        </DataStore>
        <DataStore class="org.apache.jackrabbit.core.data.db.DbDataStore">
                <param name="dataSourceName" value="dataSource" />
                <param name="schemaObjectPrefix" value="ds_" />
        </DataStore>
+       -->
 
        <!-- Workspace templates -->
        <Workspaces rootPath="${rep.home}/workspaces"
 
        <!-- Workspace templates -->
        <Workspaces rootPath="${rep.home}/workspaces"
index 128b0996d07eac5d17db1823ec7fc04fc1987d57..64870ffeae78156860c9a0c10f42c87041725c39 100644 (file)
                        <version>0.3.4-SNAPSHOT</version>
                </dependency>
 
                        <version>0.3.4-SNAPSHOT</version>
                </dependency>
 
+               <dependency>
+                       <groupId>org.apache.commons</groupId>
+                       <artifactId>com.springsource.org.apache.commons.io</artifactId>
+               </dependency>
+
                <!-- Spring -->
                <dependency>
                        <groupId>org.springframework</groupId>
                <!-- Spring -->
                <dependency>
                        <groupId>org.springframework</groupId>
diff --git a/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java b/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java
new file mode 100644 (file)
index 0000000..8735e86
--- /dev/null
@@ -0,0 +1,105 @@
+package org.argeo.jcr.mvc;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Session;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.proxy.ResourceProxy;
+
+/** Wraps a proxy via HTTP */
+public class ResourceProxyServlet extends HttpServlet implements ArgeoNames {
+       private static final long serialVersionUID = -8886549549223155801L;
+
+       private final static Log log = LogFactory
+                       .getLog(ResourceProxyServlet.class);
+
+       private ResourceProxy proxy;
+
+       private Session jcrSession;
+       private String contentTypeCharset = "UTF-8";
+
+       @Override
+       protected void doGet(HttpServletRequest request,
+                       HttpServletResponse response) throws ServletException, IOException {
+               String path = request.getPathInfo();
+
+               String nodePath = proxy.getNodePath(path);
+               if (log.isTraceEnabled())
+                       log.trace("path=" + path + ", nodePath=" + nodePath);
+
+               Node node = proxy.proxy(jcrSession, path);
+               if (node == null)
+                       response.sendError(404);
+               else
+                       processResponse(nodePath, node, response);
+       }
+
+       /** Retrieve the content of the node. */
+       protected void processResponse(String path, Node node,
+                       HttpServletResponse response) {
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       String fileName = node.getName();
+                       String ext = FilenameUtils.getExtension(fileName);
+
+                       // TODO use a more generic / standard approach
+                       // see http://svn.apache.org/viewvc/tomcat/trunk/conf/web.xml
+                       String contentType;
+                       if ("xml".equals(ext))
+                               contentType = "text/xml;charset=" + contentTypeCharset;
+                       else if ("jar".equals(ext))
+                               contentType = "application/java-archive";
+                       else if ("zip".equals(ext))
+                               contentType = "application/zip";
+                       else if ("gz".equals(ext))
+                               contentType = "application/x-gzip";
+                       else if ("tar".equals(ext))
+                               contentType = "application/x-tar";
+                       else
+                               contentType = "application/octet-stream";
+                       contentType = contentType + ";name=\"" + fileName + "\"";
+                       response.setHeader("Content-Disposition", "attachment; filename=\""
+                                       + fileName + "\"");
+                       response.setHeader("Expires", "0");
+                       response.setHeader("Cache-Control", "no-cache, must-revalidate");
+                       response.setHeader("Pragma", "no-cache");
+
+                       response.setContentType(contentType);
+
+                       binary = node.getNode(Property.JCR_CONTENT)
+                                       .getProperty(Property.JCR_DATA).getBinary();
+                       in = binary.getStream();
+                       IOUtils.copy(in, response.getOutputStream());
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot download " + node, e);
+               } finally {
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       public void setJcrSession(Session jcrSession) {
+               this.jcrSession = jcrSession;
+       }
+
+       public void setProxy(ResourceProxy resourceProxy) {
+               this.proxy = resourceProxy;
+       }
+
+}
index 350ed4543f6df8b8a7917ab394f2ca060c532556..6dfb4a017f57da6870d9ad0a59c81ea11d3aae76 100644 (file)
@@ -225,8 +225,9 @@ public class JcrUtils implements ArgeoJcrConstants {
        }
 
        /**
        }
 
        /**
-        * @deprecated use {@link #mkdirs(Session, String, String, String, Boolean)}
-        *             instead.
+        * use {@link #mkdirs(Session, String, String, String, Boolean)} instead.
+        * 
+        * @deprecated
         */
        @Deprecated
        public static Node mkdirs(Session session, String path, String type,
         */
        @Deprecated
        public static Node mkdirs(Session session, String path, String type,
diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java
new file mode 100644 (file)
index 0000000..7bec5ee
--- /dev/null
@@ -0,0 +1,133 @@
+package org.argeo.jcr.proxy;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+
+/** Base class for URL based proxys. */
+public abstract class AbstractUrlProxy implements ResourceProxy {
+       private final static Log log = LogFactory.getLog(AbstractUrlProxy.class);
+
+       private Repository jcrRepository;
+       private Session jcrAdminSession;
+
+       protected abstract String retrieve(String relativePath);
+
+       void init() {
+               try {
+                       jcrAdminSession = jcrRepository.login();
+                       beforeInitSessionSave();
+                       if (jcrAdminSession.hasPendingChanges())
+                               jcrAdminSession.save();
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(jcrAdminSession);
+                       throw new ArgeoException("Cannot initialize Maven proxy", e);
+               }
+       }
+
+       /**
+        * Called before the (admin) session is saved at the end of the
+        * initialization. Does nothing by default, to be overridden.
+        */
+       protected void beforeInitSessionSave() throws RepositoryException {
+       }
+
+       void destroy() {
+               JcrUtils.logoutQuietly(jcrAdminSession);
+       }
+
+       /**
+        * Called before the (admin) session is logged out when resources are
+        * released. Does nothing by default, to be overridden.
+        */
+       protected void beforeDestroySessionLogout() throws RepositoryException {
+       }
+
+       public Node proxy(Session jcrSession, String path) {
+               Node node;
+               try {
+                       String nodePath = getNodePath(path);
+                       if (!jcrSession.itemExists(nodePath)) {
+                               String nodeIdentifier = retrieve(path);
+                               if (nodeIdentifier == null) {
+                                       // log.warn("Could not proxy " + path);
+                                       return null;
+                               } else {
+                                       node = jcrSession.getNodeByIdentifier(nodeIdentifier);
+                               }
+                       } else {
+                               node = jcrSession.getNode(nodePath);
+                       }
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot proxy " + path, e);
+               }
+               return node;
+       }
+
+       protected Node proxyUrl(String baseUrl, String path) {
+               Node node = null;
+               String remoteUrl = baseUrl + path;
+               if (log.isTraceEnabled())
+                       log.trace("baseUrl=" + remoteUrl);
+               InputStream in = null;
+               try {
+                       URL u = new URL(remoteUrl);
+                       in = u.openStream();
+                       node = importFile(getNodePath(path), in);
+                       if (log.isDebugEnabled())
+                               log.debug("Imported " + remoteUrl + " to " + node);
+               } catch (Exception e) {
+                       if (log.isTraceEnabled())
+                               log.trace("Cannot read " + remoteUrl + ", skipping... "
+                                               + e.getMessage());
+                       if (log.isTraceEnabled()) {
+                               log.trace("Cannot read because of ", e);
+                       }
+               } finally {
+                       IOUtils.closeQuietly(in);
+               }
+
+               return node;
+       }
+
+       protected synchronized Node importFile(String nodePath, InputStream in) {
+               // FIXME allow parallel proxying
+               Binary binary = null;
+               try {
+                       Node node = JcrUtils.mkdirs(jcrAdminSession, nodePath,
+                                       NodeType.NT_FILE, NodeType.NT_FOLDER, false);
+                       Node content = node.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+                       binary = jcrAdminSession.getValueFactory().createBinary(in);
+                       content.setProperty(Property.JCR_DATA, binary);
+                       jcrAdminSession.save();
+                       return node;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(jcrAdminSession);
+                       throw new ArgeoException("Cannot initialize Maven proxy", e);
+               } finally {
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       protected Session getJcrAdminSession() {
+               return jcrAdminSession;
+       }
+
+       public void setJcrRepository(Repository jcrRepository) {
+               this.jcrRepository = jcrRepository;
+       }
+
+}
diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java
new file mode 100644 (file)
index 0000000..265ee93
--- /dev/null
@@ -0,0 +1,19 @@
+package org.argeo.jcr.proxy;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+/** A proxy which nows how to resolve and synchronize relative URLs */
+public interface ResourceProxy {
+       /** Path to the proxied node (which may not already exist) */
+       public String getNodePath(String relativePath);
+
+       /**
+        * Proxy the file referenced by this relative path in the underlying
+        * repository
+        * 
+        * @return the unique identifier of the proxied Node, <code>null</code> if
+        *         the resource was not found (e.g. HTPP 404)
+        */
+       public Node proxy(Session session,String relativePath);
+}