First complete node mapper
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 21 Feb 2010 18:06:11 +0000 (18:06 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 21 Feb 2010 18:06:11 +0000 (18:06 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@3372 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

13 files changed:
server/runtime/org.argeo.server.jackrabbit/.classpath
server/runtime/org.argeo.server.jackrabbit/.settings/org.eclipse.jdt.core.prefs
server/runtime/org.argeo.server.jackrabbit/build.properties
server/runtime/org.argeo.server.jackrabbit/pom.xml
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/BeanNodeMapper.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jcr/JcrUtils.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jackrabbit/JackrabbitContainer.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jackrabbit/unit/AbstractJcrTestCase.java [new file with mode: 0644]
server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/server/jackrabbit/repository-inMemory.xml [new file with mode: 0644]
server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/jcr/AbstractJcrTestCase.java [deleted file]
server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/jcr/MapperTest.java
server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/server/jcr/JcrResourceAdapterTest.java
server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/jcr/repository.xml [deleted file]

index f53d405d86395453458a208ae26206c9cc5e0259..3e6ecf767d5519108c40bc9c9ea27c164872ca49 100644 (file)
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
        <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
        <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
        <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
index 956d9b7a76b3ec0adbd2fb6c2bc854c66c570128..79eb91636c5adc5b03a21f21741a21d0a2337485 100644 (file)
@@ -1,8 +1,14 @@
-#Sun Feb 21 11:15:10 CET 2010
+#Sun Feb 21 12:36:03 CET 2010
 eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
 org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
 org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
 org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
 org.eclipse.jdt.core.compiler.problem.deadCode=warning
@@ -11,6 +17,7 @@ org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
 org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
 org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore
 org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled
 org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore
index d5d2601d0e585d6cf585bebc1a889d974bbef126..a90395047ea752c7c1c0cd38ad38e5c225f440e8 100644 (file)
@@ -1,4 +1,11 @@
-additional.bundles = com.springsource.junit
+additional.bundles = com.springsource.slf4j.api,\
+                     com.springsource.slf4j.log4j,\
+                     com.springsource.org.apache.log4j,\
+                     com.springsource.org.apache.commons.collections,\
+                     com.springsource.edu.oswego.cs.dl.util.concurrent,\
+                     com.springsource.org.apache.derby,\
+                     com.springsource.org.apache.lucene
 source.. = src/main/java/,\
            src/test/java/,\
-           src/test/resources/
+           src/test/resources/,\
+           src/main/resources/
index c0758af9c6da2361818d4b5ea7dc2f7b65b7d7fc..4b3c3254591a40b3dd9468386946a9a383d0e84a 100644 (file)
@@ -1,4 +1,5 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
                <groupId>org.argeo.commons.server</groupId>
                                <configuration>
                                        <instructions>
                                                <Export-Package>
+                                                       org.argeo.jcr.*,
                                                        org.argeo.server.jackrabbit.*,
                                                        org.argeo.server.jcr.*
                                                </Export-Package>
+                                               <Import-Package>
+                                                       *,
+                                                       junit.framework;resolution:="optional"
+                                               </Import-Package>
                                        </instructions>
                                </configuration>
                        </plugin>
                        <artifactId>com.springsource.org.apache.commons.fileupload</artifactId>
                </dependency>
 
+               <dependency>
+                       <groupId>org.junit</groupId>
+                       <artifactId>com.springsource.junit</artifactId>
+                       <optional>true</optional>
+               </dependency>
+
                <!-- TEST -->
                <dependency>
                        <groupId>org.argeo.commons.basic</groupId>
index 931b11f5ca612e0b7688352137adecb1c136ade2..165547ae0a5fb6da120cfa7dcf22ae00c47bdd38 100644 (file)
@@ -1,9 +1,12 @@
 package org.argeo.jcr;
 
 import java.beans.PropertyDescriptor;
-import java.beans.PropertyEditor;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.StringTokenizer;
@@ -11,6 +14,10 @@ import java.util.StringTokenizer;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.Value;
@@ -22,12 +29,18 @@ import org.argeo.ArgeoException;
 import org.springframework.beans.BeanWrapper;
 import org.springframework.beans.BeanWrapperImpl;
 
+//import org.springframework.beans.BeanWrapperImpl;
+
 public class BeanNodeMapper {
        private final static Log log = LogFactory.getLog(BeanNodeMapper.class);
 
-       private Boolean versioning = false;
+       private final static String NODE_VALUE = "value";
+
+       // private String keyNode = "bean:key";
        private String uuidProperty = "uuid";
        private String classProperty = "class";
+
+       private Boolean versioning = false;
        private Boolean strictUuidReference = false;
        private String primaryNodeType = null;
 
@@ -48,7 +61,7 @@ public class BeanNodeMapper {
 
        public Node saveOrUpdate(Session session, String path, Object obj) {
                try {
-                       BeanWrapper beanWrapper = new BeanWrapperImpl(obj);
+                       BeanWrapper beanWrapper = createBeanWrapper(obj);
                        final Node node;
                        if (session.itemExists(path)) {
                                Item item = session.getItem(path);
@@ -82,6 +95,139 @@ public class BeanNodeMapper {
                }
        }
 
+       @SuppressWarnings("unchecked")
+       public Object nodeToBean(Node node) throws RepositoryException {
+
+               String clssName = node.getProperty(classProperty).getValue()
+                               .getString();
+
+               if (log.isDebugEnabled())
+                       log.debug("Map node " + node.getPath() + " to bean " + clssName);
+
+               BeanWrapper beanWrapper;
+               try {
+                       // FIXME: use OSGi compatible class loading
+                       Class clss = Class.forName(clssName);
+                       beanWrapper = new BeanWrapperImpl(clss);
+               } catch (ClassNotFoundException e1) {
+                       throw new ArgeoException("Cannot initialize been wrapper for node "
+                                       + node.getPath(), e1);
+               }
+
+               // process properties
+               PropertyIterator propIt = node.getProperties();
+               props: while (propIt.hasNext()) {
+                       Property prop = propIt.nextProperty();
+                       if (!beanWrapper.isWritableProperty(prop.getName()))
+                               continue props;
+
+                       PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(prop
+                                       .getName());
+                       Class propClass = pd.getPropertyType();
+
+                       // list
+                       if (propClass != null && List.class.isAssignableFrom(propClass)) {
+                               List<Object> lst = new ArrayList<Object>();
+                               Class<?> valuesClass = classFromProperty(prop);
+                               if (valuesClass != null)
+                                       for (Value value : prop.getValues()) {
+                                               lst.add(asObject(value, valuesClass));
+                                       }
+                               continue props;
+                       }
+
+                       Object value = asObject(prop.getValue(), pd.getPropertyType());
+                       if (value != null)
+                               beanWrapper.setPropertyValue(prop.getName(), value);
+               }
+
+               // process children nodes
+               NodeIterator nodeIt = node.getNodes();
+               nodes: while (nodeIt.hasNext()) {
+                       Node childNode = nodeIt.nextNode();
+                       String name = childNode.getName();
+                       if (!beanWrapper.isWritableProperty(name))
+                               continue nodes;
+
+                       PropertyDescriptor pd = beanWrapper.getPropertyDescriptor(name);
+                       Class propClass = pd.getPropertyType();
+
+                       log.debug(childNode.getName() + "=" + propClass);
+
+                       if (propClass != null && List.class.isAssignableFrom(propClass)) {
+                               String lstClass = childNode.getProperty(classProperty)
+                                               .getString();
+                               // FIXME: use OSGi compatible class loading
+                               List<Object> lst;
+                               try {
+                                       lst = (List<Object>) Class.forName(lstClass).newInstance();
+                               } catch (Exception e) {
+                                       lst = new ArrayList<Object>();
+                               }
+
+                               NodeIterator valuesIt = childNode.getNodes();
+                               while (valuesIt.hasNext()) {
+                                       Node lstValueNode = valuesIt.nextNode();
+                                       Object lstValue = nodeToBean(lstValueNode);
+                                       lst.add(lstValue);
+                               }
+
+                               beanWrapper.setPropertyValue(name, lst);
+                               continue nodes;
+                       }
+
+                       if (propClass != null && Map.class.isAssignableFrom(propClass)) {
+                               String mapClass = childNode.getProperty(classProperty)
+                                               .getString();
+                               // FIXME: use OSGi compatible class loading
+                               Map<Object, Object> map;
+                               try {
+                                       map = (Map<Object, Object>) Class.forName(mapClass)
+                                                       .newInstance();
+                               } catch (Exception e) {
+                                       map = new HashMap<Object, Object>();
+                               }
+
+                               // properties
+                               PropertyIterator keysPropIt = childNode.getProperties();
+                               keyProps: while (keysPropIt.hasNext()) {
+                                       Property keyProp = keysPropIt.nextProperty();
+                                       // FIXME: use property editor
+                                       String key = keyProp.getName();
+                                       if (classProperty.equals(key))
+                                               continue keyProps;
+
+                                       Class keyPropClass = classFromProperty(keyProp);
+                                       if (keyPropClass != null) {
+                                               Object mapValue = asObject(keyProp.getValue(),
+                                                               keyPropClass);
+                                               map.put(key, mapValue);
+                                       }
+                               }
+
+                               // node
+                               NodeIterator keysIt = childNode.getNodes();
+                               while (keysIt.hasNext()) {
+                                       Node mapValueNode = keysIt.nextNode();
+                                       // FIXME: use property editor
+                                       Object key = mapValueNode.getName();
+
+                                       Object mapValue = nodeToBean(mapValueNode);
+
+                                       map.put(key, mapValue);
+                               }
+                               beanWrapper.setPropertyValue(name, map);
+                               continue nodes;
+                       }
+
+                       // default
+                       Object value = nodeToBean(childNode);
+                       beanWrapper.setPropertyValue(name, value);
+
+               }
+               return beanWrapper.getWrappedInstance();
+       }
+
        protected void beanToNode(Session session, BeanWrapper beanWrapper,
                        Node node) throws RepositoryException {
                if (log.isDebugEnabled())
@@ -91,6 +237,9 @@ public class BeanNodeMapper {
                                .getPropertyDescriptors()) {
                        String name = pd.getName();
 
+                       if (!beanWrapper.isReadableProperty(name))
+                               continue properties;// skip
+
                        Object value = beanWrapper.getPropertyValue(name);
                        if (value == null)
                                continue properties;// skip
@@ -110,6 +259,11 @@ public class BeanNodeMapper {
                                continue properties;
                        }
 
+                       if (value instanceof Class<?>) {
+                               node.setProperty(name, ((Class<?>) value).getName());
+                               continue properties;
+                       }
+
                        Value val = asValue(session, value);
                        if (val != null) {
                                node.setProperty(name, val);
@@ -118,52 +272,17 @@ public class BeanNodeMapper {
 
                        if (value instanceof List<?>) {
                                List<?> lst = (List<?>) value;
-                               Value[] values = new Value[lst.size()];
-                               boolean atLeastOneSet = false;
-                               for (int i = 0; i < lst.size(); i++) {
-                                       Object lstValue = lst.get(i);
-                                       values[i] = asValue(session, lstValue);
-                                       if (values[i] != null) {
-                                               atLeastOneSet = true;
-                                       } else {
-                                               Node childNode = findChildReference(session,
-                                                               new BeanWrapperImpl(lstValue));
-                                               if (childNode != null) {
-                                                       values[i] = session.getValueFactory().createValue(
-                                                                       childNode);
-                                                       atLeastOneSet = true;
-                                               }
-                                       }
-                               }
-
-                               if (!atLeastOneSet && lst.size() != 0)
-                                       throw new ArgeoException(
-                                                       "This type of list is not supported "
-                                                                       + lst.getClass());
-
-                               node.setProperty(name, values);
+                               addList(session, node, name, lst);
                                continue properties;
                        }
 
                        if (value instanceof Map<?, ?>) {
                                Map<?, ?> map = (Map<?, ?>) value;
-                               // TODO: add map specific type
-                               Node mapNode = node.addNode(name);
-                               for (Object key : map.keySet()) {
-                                       Object mapValue = map.get(key);
-                                       PropertyEditor pe = beanWrapper.findCustomEditor(key
-                                                       .getClass(), null);
-                                       String keyStr = pe.getAsText();
-                                       // TODO: check string format
-                                       Node entryNode = mapNode.addNode(keyStr);
-                                       beanToNode(session, new BeanWrapperImpl(mapValue),
-                                                       entryNode);
-                               }
-
+                               addMap(session, node, name, map);
                                continue properties;
                        }
 
-                       BeanWrapper child = new BeanWrapperImpl(value);
+                       BeanWrapper child = createBeanWrapper(value);
                        // TODO: delegate to another mapper
 
                        Node childNode = findChildReference(session, child);
@@ -182,6 +301,78 @@ public class BeanNodeMapper {
                }
        }
 
+       protected void addList(Session session, Node node, String name, List<?> lst)
+                       throws RepositoryException {
+               Node listNode = node.addNode(name);
+               listNode.setProperty(classProperty, lst.getClass().getName());
+               Value[] values = new Value[lst.size()];
+               boolean atLeastOneSet = false;
+               for (int i = 0; i < lst.size(); i++) {
+                       Object lstValue = lst.get(i);
+                       values[i] = asValue(session, lstValue);
+                       if (values[i] != null) {
+                               atLeastOneSet = true;
+                       } else {
+                               Node childNode = findChildReference(session,
+                                               createBeanWrapper(lstValue));
+                               if (childNode != null) {
+                                       values[i] = session.getValueFactory()
+                                                       .createValue(childNode);
+                                       atLeastOneSet = true;
+                               }
+                       }
+               }
+
+               // will be either properties or nodes, not both
+               if (!atLeastOneSet && lst.size() != 0) {
+                       for (Object lstValue : lst) {
+                               Node childNode = listNode.addNode(NODE_VALUE);
+                               beanToNode(session, createBeanWrapper(lstValue), childNode);
+                       }
+               } else {
+                       listNode.setProperty(name, values);
+               }
+       }
+
+       protected void addMap(Session session, Node node, String name, Map<?, ?> map)
+                       throws RepositoryException {
+               // TODO: add map specific type
+               Node mapNode = node.addNode(name);
+               mapNode.setProperty(classProperty, map.getClass().getName());
+               for (Object key : map.keySet()) {
+                       Object mapValue = map.get(key);
+                       // PropertyEditor pe = beanWrapper.findCustomEditor(key.getClass(),
+                       // null);
+                       String keyStr;
+                       // if (pe == null) {
+                       if (key instanceof CharSequence)
+                               keyStr = key.toString();
+                       else
+                               throw new ArgeoException(
+                                               "Cannot find property editor for class "
+                                                               + key.getClass());
+                       // } else {
+                       // pe.setValue(key);
+                       // keyStr = pe.getAsText();
+                       // }
+                       // TODO: check string format
+
+                       Value mapVal = asValue(session, mapValue);
+                       if (mapVal != null)
+                               mapNode.setProperty(keyStr, mapVal);
+                       else {
+                               Node entryNode = mapNode.addNode(keyStr);
+                               beanToNode(session, createBeanWrapper(mapValue), entryNode);
+                       }
+
+               }
+
+       }
+
+       protected BeanWrapper createBeanWrapper(Object obj) {
+               return new BeanWrapperImpl(obj);
+       }
+
        /** Returns null if value cannot be found */
        protected Value asValue(Session session, Object value)
                        throws RepositoryException {
@@ -198,7 +389,11 @@ public class BeanNodeMapper {
                        return valueFactory.createValue((Boolean) value);
                else if (value instanceof Calendar)
                        return valueFactory.createValue((Calendar) value);
-               else if (value instanceof CharSequence)
+               else if (value instanceof Date) {
+                       Calendar cal = new GregorianCalendar();
+                       cal.setTime((Date) value);
+                       return valueFactory.createValue(cal);
+               } else if (value instanceof CharSequence)
                        return valueFactory.createValue(value.toString());
                else if (value instanceof InputStream)
                        return valueFactory.createValue((InputStream) value);
@@ -206,6 +401,52 @@ public class BeanNodeMapper {
                        return null;
        }
 
+       protected Class<?> classFromProperty(Property property)
+                       throws RepositoryException {
+               switch (property.getType()) {
+               case PropertyType.LONG:
+                       return Long.class;
+               case PropertyType.DOUBLE:
+                       return Double.class;
+               case PropertyType.STRING:
+                       return String.class;
+               case PropertyType.BOOLEAN:
+                       return Boolean.class;
+               case PropertyType.DATE:
+                       return Calendar.class;
+               case PropertyType.NAME:
+                       return null;
+               default:
+                       throw new ArgeoException("Cannot find class for property "
+                                       + property + ", type="
+                                       + PropertyType.nameFromValue(property.getType()));
+               }
+       }
+
+       protected Object asObject(Value value, Class<?> propClass)
+                       throws RepositoryException {
+               if (propClass.equals(Integer.class))
+                       return (int) value.getLong();
+               else if (propClass.equals(Long.class))
+                       return value.getLong();
+               else if (propClass.equals(Float.class))
+                       return (float) value.getDouble();
+               else if (propClass.equals(Double.class))
+                       return value.getDouble();
+               else if (propClass.equals(Boolean.class))
+                       return value.getBoolean();
+               else if (CharSequence.class.isAssignableFrom(propClass))
+                       return value.getString();
+               else if (InputStream.class.isAssignableFrom(propClass))
+                       return value.getStream();
+               else if (Calendar.class.isAssignableFrom(propClass))
+                       return value.getDate();
+               else if (Date.class.isAssignableFrom(propClass))
+                       return value.getDate().getTime();
+               else
+                       return null;
+       }
+
        protected Node findChildReference(Session session, BeanWrapper child)
                        throws RepositoryException {
                if (child.isReadableProperty(uuidProperty)) {
index 5a46a763792d3db39c55432c4e83b78de8e141cf..274d149dcd8c3d5dc1a39f843bf73423f2981e90 100644 (file)
@@ -1,5 +1,6 @@
 package org.argeo.jcr;
 
+import java.util.Calendar;
 import java.util.StringTokenizer;
 
 import javax.jcr.Node;
@@ -30,6 +31,24 @@ public class JcrUtils {
                return pathT.substring(0, index);
        }
 
+       public static String dateAsPath(Calendar cal) {
+               StringBuffer buf = new StringBuffer(11);
+               buf.append(cal.get(Calendar.YEAR));// 4
+               buf.append('/');// 1
+               int month = cal.get(Calendar.MONTH) + 1;
+               if (month < 10)
+                       buf.append(0);
+               buf.append(month);// 2
+               buf.append('/');// 1
+               int day = cal.get(Calendar.DAY_OF_MONTH);
+               if (day < 10)
+                       buf.append(0);
+               buf.append(day);// 2
+               buf.append('/');// 1
+               return buf.toString();
+
+       }
+
        public static String lastPathElement(String path) {
                if (path.charAt(path.length() - 1) == '/')
                        throw new ArgeoException("Path " + path + " cannot end with '/'");
@@ -40,6 +59,10 @@ public class JcrUtils {
                return path.substring(index + 1);
        }
 
+       public static Node mkdirs(Session session, String path) {
+               return mkdirs(session, path, null, false);
+       }
+
        public static Node mkdirs(Session session, String path, String type,
                        Boolean versioning) {
                try {
index a81451dc1f0ff54b8d683a553cdf7965c1a4b180..2d22caae6658a1ddce8409d27413be49d01e127c 100644 (file)
@@ -10,20 +10,23 @@ import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.TransientRepository;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.core.io.Resource;
 
-@SuppressWarnings("restriction")
 public class JackrabbitContainer implements InitializingBean, DisposableBean,
                Repository {
        private Resource configuration;
        private File homeDirectory;
 
-       private RepositoryImpl repository;
+       private Boolean inMemory = false;
+
+       private Repository repository;
 
        public void afterPropertiesSet() throws Exception {
                RepositoryConfig config;
@@ -37,12 +40,21 @@ public class JackrabbitContainer implements InitializingBean, DisposableBean,
                        IOUtils.closeQuietly(in);
                }
 
-               repository = RepositoryImpl.create(config);
+               if (inMemory)
+                       repository = new TransientRepository(config);
+               else
+                       repository = RepositoryImpl.create(config);
        }
 
        public void destroy() throws Exception {
-               if (repository != null)
-                       repository.shutdown();
+               if (repository != null) {
+                       if (repository instanceof RepositoryImpl)
+                               ((RepositoryImpl) repository).shutdown();
+               }
+
+               if (inMemory)
+                       if (homeDirectory.exists())
+                               FileUtils.deleteDirectory(homeDirectory);
        }
 
        // JCR REPOSITORY (delegated)
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jackrabbit/unit/AbstractJcrTestCase.java b/server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/server/jackrabbit/unit/AbstractJcrTestCase.java
new file mode 100644 (file)
index 0000000..a70c6e9
--- /dev/null
@@ -0,0 +1,56 @@
+package org.argeo.server.jackrabbit.unit;
+
+import java.io.File;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.core.TransientRepository;
+import org.argeo.ArgeoException;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+
+public abstract class AbstractJcrTestCase extends TestCase {
+       private TransientRepository repository;
+       private Session session = null;
+
+       @Override
+       protected void setUp() throws Exception {
+               File homeDir = new File(System.getProperty("java.io.tmpdir"),
+                               AbstractJcrTestCase.class.getSimpleName());
+               FileUtils.deleteDirectory(homeDir);
+               repository = new TransientRepository(getRepositoryFile(), homeDir);
+       }
+
+       @Override
+       protected void tearDown() throws Exception {
+               if (session != null)
+                       session.logout();
+       }
+
+       protected Session session() {
+               if (session == null) {
+                       try {
+                               session = getRepository().login(
+                                               new SimpleCredentials("demo", "demo".toCharArray()));
+                       } catch (Exception e) {
+                               throw new ArgeoException("Cannot login to repository", e);
+                       }
+               }
+               return session;
+       }
+
+       protected File getRepositoryFile() throws Exception {
+               Resource res = new ClassPathResource(
+                               "org/argeo/server/jackrabbit/repository-inMemory.xml");
+               return res.getFile();
+       }
+
+       protected Repository getRepository() {
+               return repository;
+       }
+}
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/server/jackrabbit/repository-inMemory.xml b/server/runtime/org.argeo.server.jackrabbit/src/main/resources/org/argeo/server/jackrabbit/repository-inMemory.xml
new file mode 100644 (file)
index 0000000..f051923
--- /dev/null
@@ -0,0 +1,139 @@
+<?xml version="1.0"?>
+<!--
+   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.
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
+                            "http://jackrabbit.apache.org/dtd/repository-1.6.dtd">
+<!-- Example Repository Configuration File
+     Used by
+     - org.apache.jackrabbit.core.config.RepositoryConfigTest.java
+     -
+-->
+<Repository>
+    <!--
+        virtual file system where the repository stores global state
+        (e.g. registered namespaces, custom node types, etc.)
+    -->
+    <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+        <param name="path" value="${rep.home}/repository"/>
+    </FileSystem>
+
+    <!--
+        security configuration
+    -->
+    <Security appName="Jackrabbit">
+        <!--
+            security manager:
+            class: FQN of class implementing the JackrabbitSecurityManager interface
+        -->
+        <SecurityManager class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager" workspaceName="security">
+            <!--
+            workspace access:
+            class: FQN of class implementing the WorkspaceAccessManager interface
+            -->
+            <!-- <WorkspaceAccessManager class="..."/> -->
+            <!-- <param name="config" value="${rep.home}/security.xml"/> -->
+        </SecurityManager>
+
+        <!--
+            access manager:
+            class: FQN of class implementing the AccessManager interface
+        -->
+        <AccessManager class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager">
+            <!-- <param name="config" value="${rep.home}/access.xml"/> -->
+        </AccessManager>
+
+        <LoginModule class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
+           <!-- 
+              anonymous user name ('anonymous' is the default value)
+            -->
+           <param name="anonymousId" value="anonymous"/>
+           <!--
+              administrator user id (default value if param is missing is 'admin')
+            -->
+           <param name="adminId" value="admin"/>
+        </LoginModule>
+    </Security>
+
+    <!--
+        location of workspaces root directory and name of default workspace
+    -->
+    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
+    <!--
+        workspace configuration template:
+        used to create the initial workspace if there's no workspace yet
+    -->
+    <Workspace name="${wsp.name}">
+        <!--
+            virtual file system of the workspace:
+            class: FQN of class implementing the FileSystem interface
+        -->
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${wsp.home}"/>
+        </FileSystem>
+        <!--
+            persistence manager of the workspace:
+            class: FQN of class implementing the PersistenceManager interface
+        -->
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.DerbyPersistenceManager">
+          <param name="url" value="jdbc:derby:memory:db;create=true"/>
+          <param name="schemaObjectPrefix" value="${wsp.name}_"/>
+        </PersistenceManager>
+        <!--
+            Search index and the file system it uses.
+            class: FQN of class implementing the QueryHandler interface
+        -->
+        <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+            <param name="path" value="${wsp.home}/index"/>
+            <param name="extractorPoolSize" value="2"/>
+            <param name="supportHighlighting" value="true"/>
+        </SearchIndex>
+    </Workspace>
+
+    <!--
+        Configures the versioning
+    -->
+    <Versioning rootPath="${rep.home}/version">
+        <!--
+            Configures the filesystem to use for versioning for the respective
+            persistence manager
+        -->
+        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+            <param name="path" value="${rep.home}/version" />
+        </FileSystem>
+
+        <!--
+            Configures the persistence manager to be used for persisting version state.
+            Please note that the current versioning implementation is based on
+            a 'normal' persistence manager, but this could change in future
+            implementations.
+        -->
+        <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.DerbyPersistenceManager">
+          <param name="url" value="jdbc:derby:memory:version;create=true"/>
+          <param name="schemaObjectPrefix" value="version_"/>
+        </PersistenceManager>
+    </Versioning>
+
+    <!--
+        Search index for content that is shared repository wide
+        (/jcr:system tree, contains mainly versions)
+    -->
+    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+        <param name="path" value="${rep.home}/repository/index"/>
+        <param name="extractorPoolSize" value="2"/>
+        <param name="supportHighlighting" value="true"/>
+    </SearchIndex>
+</Repository>
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/jcr/AbstractJcrTestCase.java b/server/runtime/org.argeo.server.jackrabbit/src/test/java/org/argeo/jcr/AbstractJcrTestCase.java
deleted file mode 100644 (file)
index feee17e..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.argeo.jcr;
-
-import java.io.File;
-
-import javax.jcr.Repository;
-
-import junit.framework.TestCase;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.jackrabbit.core.TransientRepository;
-import org.argeo.server.jcr.JcrResourceAdapterTest;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.Resource;
-
-public abstract class AbstractJcrTestCase extends TestCase {
-       private TransientRepository repository;
-
-       @Override
-       protected void setUp() throws Exception {
-               File homeDir = new File(System.getProperty("java.io.tmpdir"),
-                               JcrResourceAdapterTest.class.getSimpleName());
-               FileUtils.deleteDirectory(homeDir);
-               repository = new TransientRepository(getRepositoryFile(), homeDir);
-       }
-
-       @Override
-       protected void tearDown() throws Exception {
-       }
-
-       protected File getRepositoryFile() throws Exception {
-               Resource res = new ClassPathResource("org/argeo/jcr/repository.xml");
-               return res.getFile();
-       }
-
-       protected Repository getRepository() {
-               return repository;
-       }
-}
index 4a14a277e22b6965a06ad8b8f2015111408912e4..b5d3113f4a5347264d8b13346933299d0be25b78 100644 (file)
@@ -1,8 +1,8 @@
 package org.argeo.jcr;
 
 import javax.jcr.Node;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
+
+import org.argeo.server.jackrabbit.unit.AbstractJcrTestCase;
 
 public class MapperTest extends AbstractJcrTestCase {
        public void testSimpleObject() throws Exception {
@@ -20,12 +20,10 @@ public class MapperTest extends AbstractJcrTestCase {
                oo2.setValue(new SimpleObject());
                mySo.setAnotherObject(oo2);
 
-               Session session = getRepository().login(
-                               new SimpleCredentials("demo", "demo".toCharArray()));
                BeanNodeMapper bnm = new BeanNodeMapper();
 
-               Node node = bnm.saveOrUpdate(session, mySo);
-               session.save();
+               Node node = bnm.saveOrUpdate(session(), mySo);
+               session().save();
                JcrUtils.debug(node);
        }
 }
index 606b065b58ec8595d0154a952d633e14a4846028..f7e22b964cd3e65a58d82777311606b707518ebc 100644 (file)
@@ -8,7 +8,7 @@ import java.util.List;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.AbstractJcrTestCase;
+import org.argeo.server.jackrabbit.unit.AbstractJcrTestCase;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.Resource;
 
@@ -81,8 +81,8 @@ public class JcrResourceAdapterTest extends AbstractJcrTestCase {
 
        @Override
        protected void tearDown() throws Exception {
-               super.tearDown();
                jra.destroy();
+               super.tearDown();
        }
 
 }
diff --git a/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/jcr/repository.xml b/server/runtime/org.argeo.server.jackrabbit/src/test/resources/org/argeo/jcr/repository.xml
deleted file mode 100644 (file)
index f051923..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-<?xml version="1.0"?>
-<!--
-   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.
--->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 1.6//EN"
-                            "http://jackrabbit.apache.org/dtd/repository-1.6.dtd">
-<!-- Example Repository Configuration File
-     Used by
-     - org.apache.jackrabbit.core.config.RepositoryConfigTest.java
-     -
--->
-<Repository>
-    <!--
-        virtual file system where the repository stores global state
-        (e.g. registered namespaces, custom node types, etc.)
-    -->
-    <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-        <param name="path" value="${rep.home}/repository"/>
-    </FileSystem>
-
-    <!--
-        security configuration
-    -->
-    <Security appName="Jackrabbit">
-        <!--
-            security manager:
-            class: FQN of class implementing the JackrabbitSecurityManager interface
-        -->
-        <SecurityManager class="org.apache.jackrabbit.core.security.simple.SimpleSecurityManager" workspaceName="security">
-            <!--
-            workspace access:
-            class: FQN of class implementing the WorkspaceAccessManager interface
-            -->
-            <!-- <WorkspaceAccessManager class="..."/> -->
-            <!-- <param name="config" value="${rep.home}/security.xml"/> -->
-        </SecurityManager>
-
-        <!--
-            access manager:
-            class: FQN of class implementing the AccessManager interface
-        -->
-        <AccessManager class="org.apache.jackrabbit.core.security.simple.SimpleAccessManager">
-            <!-- <param name="config" value="${rep.home}/access.xml"/> -->
-        </AccessManager>
-
-        <LoginModule class="org.apache.jackrabbit.core.security.simple.SimpleLoginModule">
-           <!-- 
-              anonymous user name ('anonymous' is the default value)
-            -->
-           <param name="anonymousId" value="anonymous"/>
-           <!--
-              administrator user id (default value if param is missing is 'admin')
-            -->
-           <param name="adminId" value="admin"/>
-        </LoginModule>
-    </Security>
-
-    <!--
-        location of workspaces root directory and name of default workspace
-    -->
-    <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
-    <!--
-        workspace configuration template:
-        used to create the initial workspace if there's no workspace yet
-    -->
-    <Workspace name="${wsp.name}">
-        <!--
-            virtual file system of the workspace:
-            class: FQN of class implementing the FileSystem interface
-        -->
-        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-            <param name="path" value="${wsp.home}"/>
-        </FileSystem>
-        <!--
-            persistence manager of the workspace:
-            class: FQN of class implementing the PersistenceManager interface
-        -->
-        <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.DerbyPersistenceManager">
-          <param name="url" value="jdbc:derby:memory:db;create=true"/>
-          <param name="schemaObjectPrefix" value="${wsp.name}_"/>
-        </PersistenceManager>
-        <!--
-            Search index and the file system it uses.
-            class: FQN of class implementing the QueryHandler interface
-        -->
-        <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-            <param name="path" value="${wsp.home}/index"/>
-            <param name="extractorPoolSize" value="2"/>
-            <param name="supportHighlighting" value="true"/>
-        </SearchIndex>
-    </Workspace>
-
-    <!--
-        Configures the versioning
-    -->
-    <Versioning rootPath="${rep.home}/version">
-        <!--
-            Configures the filesystem to use for versioning for the respective
-            persistence manager
-        -->
-        <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
-            <param name="path" value="${rep.home}/version" />
-        </FileSystem>
-
-        <!--
-            Configures the persistence manager to be used for persisting version state.
-            Please note that the current versioning implementation is based on
-            a 'normal' persistence manager, but this could change in future
-            implementations.
-        -->
-        <PersistenceManager class="org.apache.jackrabbit.core.persistence.bundle.DerbyPersistenceManager">
-          <param name="url" value="jdbc:derby:memory:version;create=true"/>
-          <param name="schemaObjectPrefix" value="version_"/>
-        </PersistenceManager>
-    </Versioning>
-
-    <!--
-        Search index for content that is shared repository wide
-        (/jcr:system tree, contains mainly versions)
-    -->
-    <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
-        <param name="path" value="${rep.home}/repository/index"/>
-        <param name="extractorPoolSize" value="2"/>
-        <param name="supportHighlighting" value="true"/>
-    </SearchIndex>
-</Repository>