From 2d4dd736ab07b1ef3aaec0a9e5d29f30c551de9c Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Mon, 26 Sep 2011 20:44:33 +0000 Subject: [PATCH] Introduce JCR proxys git-svn-id: https://svn.argeo.org/commons/trunk@4754 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../repository-h2.xml | 3 + .../runtime/org.argeo.server.jcr.mvc/pom.xml | 5 + .../argeo/jcr/mvc/ResourceProxyServlet.java | 105 ++++++++++++++ .../src/main/java/org/argeo/jcr/JcrUtils.java | 5 +- .../org/argeo/jcr/proxy/AbstractUrlProxy.java | 133 ++++++++++++++++++ .../org/argeo/jcr/proxy/ResourceProxy.java | 19 +++ 6 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java create mode 100644 server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java create mode 100644 server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java diff --git a/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml b/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml index 5e63e5de8..9e7886d69 100644 --- a/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml +++ b/server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml @@ -20,10 +20,13 @@ + + 0.3.4-SNAPSHOT + + org.apache.commons + com.springsource.org.apache.commons.io + + org.springframework 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 index 000000000..8735e869e --- /dev/null +++ b/server/runtime/org.argeo.server.jcr.mvc/src/main/java/org/argeo/jcr/mvc/ResourceProxyServlet.java @@ -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; + } + +} diff --git a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java index 350ed4543..6dfb4a017 100644 --- a/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java @@ -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, 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 index 000000000..7bec5ee7a --- /dev/null +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/AbstractUrlProxy.java @@ -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 index 000000000..265ee9352 --- /dev/null +++ b/server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/proxy/ResourceProxy.java @@ -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, null if + * the resource was not found (e.g. HTPP 404) + */ + public Node proxy(Session session,String relativePath); +} -- 2.30.2