From: Mathieu Baudier Date: Wed, 25 Nov 2009 21:51:18 +0000 (+0000) Subject: Introduce JcrResource adapter X-Git-Tag: argeo-commons-2.1.30~1688 X-Git-Url: http://git.argeo.org/?p=lgpl%2Fargeo-commons.git;a=commitdiff_plain;h=5f097724cb2f64045c3e636db5804a5909db096f Introduce JcrResource adapter git-svn-id: https://svn.argeo.org/commons/trunk@3167 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- diff --git a/sandbox/runtime/org.argeo.sandbox.jackrabbit/src/main/resources/log4j.properties b/sandbox/runtime/org.argeo.sandbox.jackrabbit/src/main/resources/log4j.properties index c2edab75e..94ceb6ebb 100644 --- a/sandbox/runtime/org.argeo.sandbox.jackrabbit/src/main/resources/log4j.properties +++ b/sandbox/runtime/org.argeo.sandbox.jackrabbit/src/main/resources/log4j.properties @@ -1,10 +1,13 @@ -# ***** Set root logger level to DEBUG and its only appender to A. -log4j.rootLogger=WARN, A +log4j.rootLogger=WARN, console +## Levels log4j.logger.org.argeo=DEBUG +log4j.logger.org.apache.jackrabbit=INFO -# ***** A is set to be a ConsoleAppender. -log4j.appender.A=org.apache.log4j.ConsoleAppender -# ***** A uses PatternLayout. -log4j.appender.A.layout=org.apache.log4j.PatternLayout -log4j.appender.A.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n diff --git a/server/runtime/org.argeo.server.jackrabbit/.classpath b/server/runtime/org.argeo.server.jackrabbit/.classpath index 16f01e2ee..d3bdab048 100644 --- a/server/runtime/org.argeo.server.jackrabbit/.classpath +++ b/server/runtime/org.argeo.server.jackrabbit/.classpath @@ -1,6 +1,8 @@ + + diff --git a/server/runtime/org.argeo.server.jackrabbit/pom.xml b/server/runtime/org.argeo.server.jackrabbit/pom.xml index d48451e01..a00612998 100644 --- a/server/runtime/org.argeo.server.jackrabbit/pom.xml +++ b/server/runtime/org.argeo.server.jackrabbit/pom.xml @@ -30,7 +30,8 @@ - org.argeo.server.jackrabbit.* + org.argeo.server.jackrabbit.*, + org.argeo.server.jcr.* @@ -76,5 +77,21 @@ org.slf4j com.springsource.slf4j.org.apache.commons.logging + + + + org.argeo.commons.basic + org.argeo.support.junit + 0.1.2-SNAPSHOT + test + + + org.argeo.commons.basic + org.argeo.basic.dep.log4j + 0.1.2-SNAPSHOT + pom + test + + diff --git a/server/runtime/org.argeo.server.jackrabbit/repository.xml b/server/runtime/org.argeo.server.jackrabbit/repository.xml new file mode 100644 index 000000000..666919d28 --- /dev/null +++ b/server/runtime/org.argeo.server.jackrabbit/repository.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jcr/JcrResourceAdapter.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jcr/JcrResourceAdapter.java new file mode 100644 index 000000000..b770a89a8 --- /dev/null +++ b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jcr/JcrResourceAdapter.java @@ -0,0 +1,291 @@ +package org.argeo.server.jcr; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.StringTokenizer; + +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.Repository; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.Value; +import javax.jcr.version.Version; +import javax.jcr.version.VersionHistory; +import javax.jcr.version.VersionIterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.argeo.ArgeoException; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.Resource; + +public class JcrResourceAdapter implements InitializingBean, DisposableBean { + private final static Log log = LogFactory.getLog(JcrResourceAdapter.class); + + private Repository repository; + + private String username; + private String password; + + private Session session; + + private Boolean versioning = true; + private String defaultEncoding = "UTF-8"; + + // private String restoreBase = "/.restore"; + + public void mkdirs(String path) { + try { + StringTokenizer st = new StringTokenizer(path, "/"); + StringBuffer current = new StringBuffer("/"); + Node currentNode = session().getRootNode(); + while (st.hasMoreTokens()) { + String part = st.nextToken(); + current.append(part).append('/'); + if (!session().itemExists(current.toString())) { + currentNode = currentNode.addNode(part, "nt:folder"); + if (versioning) + currentNode.addMixin("mix:versionable"); + if (log.isTraceEnabled()) + log.debug("Added folder " + part + " as " + current); + } else { + currentNode = (Node) session().getItem(current.toString()); + } + } + session().save(); + } catch (RepositoryException e) { + throw new ArgeoException("Cannot mkdirs " + path, e); + } + } + + public void create(String path, Resource file, String mimeType) { + try { + if (session().itemExists(path)) + throw new ArgeoException("Node " + path + " already exists."); + + int index = path.lastIndexOf('/'); + String parentPath = path.substring(0, index); + String fileName = path.substring(index + 1); + if (!session().itemExists(parentPath)) + throw new ArgeoException("Parent folder of node " + path + + " does not exist: " + parentPath); + + Node folderNode = (Node) session().getItem(parentPath); + Node fileNode = folderNode.addNode(fileName, "nt:file"); + + Node contentNode = fileNode.addNode("jcr:content", "nt:resource"); + contentNode.setProperty("jcr:mimeType", mimeType); + contentNode.setProperty("jcr:encoding", defaultEncoding); + contentNode.setProperty("jcr:data", file.getInputStream()); + Calendar lastModified = Calendar.getInstance(); + lastModified.setTimeInMillis(file.lastModified()); + contentNode.setProperty("jcr:lastModified", lastModified); + // resNode.addMixin("mix:referenceable"); + + if (versioning) + fileNode.addMixin("mix:versionable"); + + session().save(); + + if (versioning) + fileNode.checkin(); + + if (log.isDebugEnabled()) + log.debug("Created " + path + " from " + file); + } catch (Exception e) { + throw new ArgeoException("Cannot create node from resource " + file + + " under " + path, e); + } + + } + + public void update(String path, Resource file) { + try { + Node fileNode = (Node) session().getItem(path); + Node contentNode = fileNode.getNode("jcr:content"); + fileNode.checkout(); + contentNode.setProperty("jcr:data", file.getInputStream()); + Calendar lastModified = Calendar.getInstance(); + lastModified.setTimeInMillis(file.lastModified()); + contentNode.setProperty("jcr:lastModified", lastModified); + + session().save(); + fileNode.checkin(); + + if (log.isDebugEnabled()) + log.debug("Updated " + path + " from " + file); + } catch (Exception e) { + throw new ArgeoException("Cannot update node " + path + + " from resource" + file, e); + } + } + + public List listVersions(String path) { + if (!versioning) + throw new ArgeoException("Versioning is not activated"); + + try { + List versions = new ArrayList(); + Node fileNode = (Node) session().getItem(path); + VersionHistory history = fileNode.getVersionHistory(); + for (VersionIterator it = history.getAllVersions(); it.hasNext();) { + Version version = (Version) it.next(); + versions.add(version.getCreated()); + if (log.isTraceEnabled()) { + log.debug(version); + // debug(version); + } + } + return versions; + } catch (Exception e) { + throw new ArgeoException("Cannot list version of node " + path, e); + } + } + + public InputStream retrieve(String path) { + try { + Node node = (Node) session().getItem(path + "/jcr:content"); + Property property = node.getProperty("jcr:data"); + return property.getStream(); + } catch (Exception e) { + throw new ArgeoException("Cannot retrieve " + path, e); + } + } + + public synchronized InputStream retrieve(String path, Integer revision) { + if (!versioning) + throw new ArgeoException("Versioning is not activated"); + + try { + Node fileNode = (Node) session().getItem(path); + + // if (revision == 0) { + // InputStream in = fromVersion(fileNode.getBaseVersion()); + // if (log.isDebugEnabled()) + // log.debug("Retrieved " + path + " at base revision "); + // return in; + // } + + VersionHistory history = fileNode.getVersionHistory(); + int count = 0; + Version version = null; + for (VersionIterator it = history.getAllVersions(); it.hasNext();) { + version = (Version) it.next(); + if (count == revision + 1) { + InputStream in = fromVersion(version); + if (log.isDebugEnabled()) + log.debug("Retrieved " + path + " at revision " + + revision); + return in; + } + count++; + } + } catch (Exception e) { + throw new ArgeoException("Cannot retrieve version " + revision + + " of " + path, e); + } + + throw new ArgeoException("Version " + revision + + " does not exist for node " + path); + } + + protected InputStream fromVersion(Version version) + throws RepositoryException { + Node frozenNode = version.getNode("jcr:frozenNode"); + InputStream in = frozenNode.getNode("jcr:content").getProperty( + "jcr:data").getStream(); + return in; + } + + // protected InputStream restoreOrGetRevision(Node fileNode, Version + // version, + // Integer revision) throws RepositoryException { + // Node parentFolder = (Node) fileNode + // .getAncestor(fileNode.getDepth() - 1); + // String restoreFolderPath = restoreBase + parentFolder.getPath(); + // mkdirs(restoreFolderPath); + // String subNodeName = fileNode.getName() + "__" + fill(revision); + // Node restoreFolder = (Node) session().getItem(restoreFolderPath); + // if (!restoreFolder.hasNode(subNodeName)) { + // parentFolder.restore(version, subNodeName, true); + // } + // return parentFolder.getNode(subNodeName + "/jcr:content").getProperty( + // "jcr:data").getStream(); + // } + + protected Session session() { + return session; + } + + public void afterPropertiesSet() throws Exception { + session = repository.login(new SimpleCredentials(username, password + .toCharArray())); + } + + public void destroy() throws Exception { + session.logout(); + } + + public void setRepository(Repository repository) { + this.repository = repository; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setVersioning(Boolean versioning) { + this.versioning = versioning; + } + + public void setDefaultEncoding(String defaultEncoding) { + this.defaultEncoding = defaultEncoding; + } + + /** Recursively outputs the contents of the given node. */ + public static void debug(Node node) throws RepositoryException { + // First output the node path + log.debug(node.getPath()); + // Skip the virtual (and large!) jcr:system subtree + if (node.getName().equals("jcr:system")) { + return; + } + + // Then output the properties + PropertyIterator properties = node.getProperties(); + while (properties.hasNext()) { + Property property = properties.nextProperty(); + if (property.getDefinition().isMultiple()) { + // A multi-valued property, print all values + Value[] values = property.getValues(); + for (int i = 0; i < values.length; i++) { + log.debug(property.getPath() + " = " + + values[i].getString()); + } + } else { + // A single-valued property + log.debug(property.getPath() + " = " + property.getString()); + } + } + + } + + protected String fill(Integer number) { + int size = 4; + String str = number.toString(); + for (int i = str.length(); i < size; i++) { + str = "0" + str; + } + return str; + } +} diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java b/server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java new file mode 100644 index 000000000..9c72a1e15 --- /dev/null +++ b/server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java @@ -0,0 +1,93 @@ +package org.argeo.server.jcr; + +import java.io.File; +import java.io.InputStream; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.List; + +import junit.framework.TestCase; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.jackrabbit.core.TransientRepository; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +public class JcrResourceAdapterTest extends TestCase { + private static SimpleDateFormat sdf = new SimpleDateFormat( + "yyyyMMdd:hhmmss.SSS"); + + private final static Log log = LogFactory + .getLog(JcrResourceAdapterTest.class); + + private JcrResourceAdapter jra; + private TransientRepository repository; + + public void testCreate() throws Exception { + String basePath = "/test/subdir"; + jra.mkdirs(basePath); + Resource res = new ClassPathResource("org/argeo/server/jcr/dummy00.xls"); + String filePath = basePath + "/dummy.xml"; + jra.create(filePath, res, "application/vnd.ms-excel"); + InputStream in = jra.retrieve(filePath); + assertTrue(IOUtils.contentEquals(res.getInputStream(), in)); + } + + public void testVersioning() throws Exception { + String basePath = "/test/versions"; + jra.mkdirs(basePath); + String filePath = basePath + "/dummy.xml"; + Resource res00 = new ClassPathResource( + "org/argeo/server/jcr/dummy00.xls"); + jra.create(filePath, res00, "application/vnd.ms-excel"); + Resource res01 = new ClassPathResource( + "org/argeo/server/jcr/dummy01.xls"); + jra.update(filePath, res01); + Resource res02 = new ClassPathResource( + "org/argeo/server/jcr/dummy02.xls"); + jra.update(filePath, res02); + + List versions = jra.listVersions(filePath); + for (Calendar version : versions) + log.debug(sdf.format(version.getTime())); + assertEquals(4, versions.size()); + + InputStream in = jra.retrieve(filePath, 1); + assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); + in = jra.retrieve(filePath, 0); + assertTrue(IOUtils.contentEquals(res00.getInputStream(), in)); + in = jra.retrieve(filePath, 2); + assertTrue(IOUtils.contentEquals(res02.getInputStream(), in)); + Resource res03 = new ClassPathResource( + "org/argeo/server/jcr/dummy03.xls"); + jra.update(filePath, res03); + in = jra.retrieve(filePath, 1); + assertTrue(IOUtils.contentEquals(res01.getInputStream(), in)); + } + + @Override + protected void setUp() throws Exception { + File homeDir = new File(System.getProperty("java.io.tmpdir"), + JcrResourceAdapterTest.class.getSimpleName()); + FileUtils.deleteDirectory(homeDir); + Resource res = new ClassPathResource( + "org/argeo/server/jcr/repository.xml"); + repository = new TransientRepository(res.getFile(), homeDir); + + jra = new JcrResourceAdapter(); + jra.setRepository(repository); + jra.setUsername("demo"); + jra.setPassword("demo"); + jra.afterPropertiesSet(); + } + + @Override + protected void tearDown() throws Exception { + jra.destroy(); + // repository.shutdown(); + } + +} diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/log4j.properties b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/log4j.properties new file mode 100644 index 000000000..ca995af8e --- /dev/null +++ b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/log4j.properties @@ -0,0 +1,13 @@ +log4j.rootLogger=WARN, console + +## Levels +log4j.logger.org.argeo=DEBUG +log4j.logger.org.apache.jackrabbit=OFF + +## Appenders +# console is set to be a ConsoleAppender. +log4j.appender.console=org.apache.log4j.ConsoleAppender + +# console uses PatternLayout. +log4j.appender.console.layout=org.apache.log4j.PatternLayout +log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy00.xls b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy00.xls new file mode 100644 index 000000000..e5846fef7 Binary files /dev/null and b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy00.xls differ diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy01.xls b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy01.xls new file mode 100644 index 000000000..b5c6b5539 Binary files /dev/null and b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy01.xls differ diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy02.xls b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy02.xls new file mode 100644 index 000000000..d73bc6605 Binary files /dev/null and b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy02.xls differ diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy03.xls b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy03.xls new file mode 100644 index 000000000..0759cb927 Binary files /dev/null and b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/dummy03.xls differ diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/repository.xml b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/repository.xml new file mode 100644 index 000000000..f05192337 --- /dev/null +++ b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/server/jcr/repository.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java b/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java index 76ac7dd55..e8bb7107f 100644 --- a/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java +++ b/server/runtime/org.argeo.server.jxl/src/main/java/org/argeo/server/jxl/dao/JxlDaoSupport.java @@ -20,13 +20,12 @@ import jxl.WorkbookSettings; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.argeo.server.ArgeoServerException; +import org.argeo.ArgeoException; import org.argeo.server.dao.AbstractTabularDaoSupport; import org.argeo.server.dao.LightDaoSupport; import org.springframework.beans.BeanWrapper; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContextAware; -import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; public class JxlDaoSupport extends AbstractTabularDaoSupport implements @@ -47,7 +46,7 @@ public class JxlDaoSupport extends AbstractTabularDaoSupport implements loadSheet(sheet, references); } } catch (Exception e) { - throw new ArgeoServerException("Cannot load workbook", e); + throw new ArgeoException("Cannot load workbook", e); } } @@ -79,7 +78,7 @@ public class JxlDaoSupport extends AbstractTabularDaoSupport implements if (keyProperty == null) keyProperty = fieldName; if (!espSt.hasMoreTokens()) - throw new ArgeoServerException("Badly formatted sheetname " + throw new ArgeoException("Badly formatted sheetname " + sheetName); String fieldValue = espSt.nextToken(); bw.setPropertyValue(fieldName, fieldValue); @@ -272,7 +271,7 @@ public class JxlDaoSupport extends AbstractTabularDaoSupport implements String formula = ((FormulaCell) cell).getFormula(); int index = formula.indexOf('!'); if (index < 0) - throw new ArgeoServerException("Cannot interpret formula " + throw new ArgeoException("Cannot interpret formula " + formula); ; String targetSheet = formula.substring(0, index);