chemistry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r1082139 [1/3] - in /chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr: ./ util/
Date Wed, 16 Mar 2011 12:46:36 GMT
Author: jukka
Date: Wed Mar 16 12:46:36 2011
New Revision: 1082139

URL: http://svn.apache.org/viewvc?rev=1082139&view=rev
Log:
CMIS-329: Implement support for versioning in opencmis-server-jcr module

Patch by Michael Dürig

Added:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrDocument.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrFolder.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNode.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNodeFactory.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrPrivateWorkingCopy.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrUnversionedDocument.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrVersion.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrVersionBase.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/PathManager.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/PropertyHelper.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/util/
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/util/FilterIterator.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/util/Predicate.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/util/Util.java
Modified:
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrBinary.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrConverter.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrRepository.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrService.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrServiceFactory.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/TypeManager.java
    chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/Util.java

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrBinary.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrBinary.java?rev=1082139&r1=1082138&r2=1082139&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrBinary.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrBinary.java Wed Mar 16 12:46:36 2011
@@ -32,8 +32,8 @@ import java.io.OutputStream;
 import java.io.RandomAccessFile;
 
 /**
- * <code>BinaryImpl</code> implements the <code>Binary</code> interface.
- * This is mostly a copied from org.apache.jackrabbit.value.BinaryImpl in
+ * <code>JcrBinary</code> implements the JCR <code>Binary</code> interface.
+ * This is mostly a copy from org.apache.jackrabbit.value.BinaryImpl in
  * Apache Jackrabbit's jcr-commons module. 
  */
 public class JcrBinary implements Binary {
@@ -61,14 +61,14 @@ public class JcrBinary implements Binary
     public static final Binary EMPTY = new JcrBinary(EMPTY_BYTE_ARRAY);
 
     /**
-     * Creates a new <code>BinaryImpl</code> instance from an
-     * <code>InputStream</code>. The contents of the stream is spooled
-     * to a temporary file or to a byte buffer if its size is smaller than
-     * {@link #MAX_BUFFER_SIZE}. The input stream is closed by this implementation.
-     * <p/>
-     * @param in stream to be represented as a <code>BLOBFileValue</code> instance
+     * Creates a new <code>JcrBinary</code> instance from an <code>InputStream</code>.
+     * The contents of the stream is spooled to a temporary file or to a byte buffer
+     * if its size is smaller than {@link #MAX_BUFFER_SIZE}.
+     * The input stream is closed by this implementation.
+     *
+     * @param in  stream to be represented as a <code>JcrBinary</code> instance
      * @throws IOException if an error occurs while reading from the stream or
-     *                     writing to the temporary file
+     *      writing to the temporary file
      */
     public JcrBinary(InputStream in) throws IOException {
         byte[] spoolBuffer = new byte[0x2000];
@@ -116,11 +116,9 @@ public class JcrBinary implements Binary
     }
 
     /**
-     * Creates a new <code>BinaryImpl</code> instance from a
-     * <code>byte[]</code> array.
-     *
-     * @param buffer byte array to be represented as a <code>BinaryImpl</code>
-     *               instance
+     * Creates a new <code>JcrBinary</code> instance from a <code>byte[]</code> array.
+     * 
+     * @param buffer byte array to be represented as a <code>JcrBinary</code> instance
      */
     public JcrBinary(byte[] buffer) {
         if (buffer == null) {

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrConverter.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrConverter.java?rev=1082139&r1=1082138&r2=1082139&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrConverter.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrConverter.java Wed Mar 16 12:46:36 2011
@@ -37,6 +37,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
+import org.apache.chemistry.opencmis.jcr.util.Util;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
@@ -45,15 +46,40 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.NodeType;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.GregorianCalendar;
 import java.util.List;
 
+/**
+ * Utility class providing methods for converting various entities from/to their respective representation
+ * in JCR/CMIS.
+ */
 public final class JcrConverter {
     private JcrConverter() {}
 
+    /**
+     * Escapes all illegal JCR name characters of a string.
+     * The encoding is loosely modeled after URI encoding, but only encodes
+     * the characters it absolutely needs to in order to make the resulting
+     * string a valid JCR name.
+     * <p/>
+     * QName EBNF:<br>
+     * <pre>
+     * simplename ::= onecharsimplename | twocharsimplename | threeormorecharname
+     * onecharsimplename ::= (* Any Unicode character except: '.', '/', ':', '[', ']', '*', '|' or any whitespace character *)
+     * twocharsimplename ::= '.' onecharsimplename | onecharsimplename '.' | onecharsimplename onecharsimplename
+     * threeormorecharname ::= nonspace string nonspace
+     * string ::= char | string char
+     * char ::= nonspace | ' '
+     * nonspace ::= (* Any Unicode character except: '/', ':', '[', ']', '*', '|' or any whitespace character *)
+     * </pre>
+     *
+     * @param cmisName the name to escape
+     * @return the escaped name
+     */    
     public static String toJcrName(String cmisName) {
         StringBuffer buffer = new StringBuffer(cmisName.length() * 2);
         for (int i = 0; i < cmisName.length(); i++) {
@@ -73,10 +99,6 @@ public final class JcrConverter {
         return buffer.toString();
     }
 
-    public static String toCmisQueryName(String name) {
-        return name; // fixme implement toQueryName
-    }
-
     /**
      * Checks if the given name is valid a valid JCR name
      *
@@ -101,6 +123,13 @@ public final class JcrConverter {
         return true;
     }
 
+    /**
+     * Convert a JCR <code>Property</code> to a CMIS <code>PropertyData</code>.
+     * 
+     * @param jcrProperty
+     * @return  
+     * @throws RepositoryException
+     */
     public static PropertyData<?> convert(Property jcrProperty) throws RepositoryException {
         AbstractPropertyData<?> propertyData;
 
@@ -158,14 +187,21 @@ public final class JcrConverter {
 
         propertyData.setDisplayName(jcrProperty.getName());
         propertyData.setLocalName(jcrProperty.getName());
-        propertyData.setQueryName(toCmisQueryName(jcrProperty.getName()));
+        propertyData.setQueryName(jcrProperty.getName());
         
         return propertyData;
     }
 
+    /**
+     * Set a property on a JCR node. 
+     *
+     * @param node  the node to set the property
+     * @param propertyData  the property to set
+     * @throws RepositoryException
+     */
     public static void setProperty(Node node, PropertyData<?> propertyData) throws RepositoryException {
-        int propertyType;
         Value[] values;
+        int propertyType;
 
         if (propertyData instanceof PropertyBoolean) {
             values = toValue((PropertyBoolean) propertyData, node.getSession().getValueFactory());
@@ -189,7 +225,7 @@ public final class JcrConverter {
         }
         else if (propertyData instanceof PropertyInteger) {
             values = toValue((PropertyInteger) propertyData, node.getSession().getValueFactory());
-            propertyType = PropertyType.LONG;
+            propertyType = PropertyType.DECIMAL;
         }
         else if (propertyData instanceof PropertyString) {
             values = toValue((PropertyString) propertyData, node.getSession().getValueFactory());
@@ -206,11 +242,11 @@ public final class JcrConverter {
         String id = propertyData.getId();
         String name;
         if (PropertyIds.NAME.equals(id)) {
-            node.addMixin("mix:title");
-            name = "jcr:title";
+            node.addMixin(NodeType.MIX_TITLE);
+            name = Property.JCR_TITLE;
         }
         else if (PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(id)) {
-            name = "jcr:mimeType";
+            name = Property.JCR_MIMETYPE;
         }
         else {
             name = toJcrName(propertyData.getId());
@@ -224,19 +260,29 @@ public final class JcrConverter {
         }
     }
 
+    /**
+     * Remove a property from a JCR node
+     *
+     * @param node  the node from which to remove the property
+     * @param propertyData  the property to remove
+     * @throws RepositoryException
+     */
     public static void removeProperty(Node node, PropertyData<?> propertyData) throws RepositoryException {
         String id = propertyData.getId();
         String name = PropertyIds.NAME.equals(id)
-                ? "jcr:title" :
+                ? Property.JCR_TITLE :
                 toJcrName(propertyData.getId());
 
         if (node.hasProperty(name)) {
-            node.getProperty(name).remove();;
+            node.getProperty(name).remove();
         }
     }
 
     //------------------------------------------< private >---
 
+    /**
+     * Convert an array of <code>Value</code>s to a list of <code>String</code>s.
+     */
     private static List<String> toStrings(Value[] values) throws RepositoryException {
         ArrayList<String> strings = new ArrayList<String>(values.length);
 
@@ -247,6 +293,9 @@ public final class JcrConverter {
         return strings;
     }
 
+    /**
+     * Convert an array of <code>Value</code>s to a list of <code>BigInteger</code>s.
+     */
     private static List<BigInteger> toInts(Value[] values) throws RepositoryException {
         ArrayList<BigInteger> ints = new ArrayList<BigInteger>(values.length);
 
@@ -257,6 +306,9 @@ public final class JcrConverter {
         return ints;
     }
 
+    /**
+     * Convert an array of <code>Value</code>s to a list of <code>BigDecimal</code>s.
+     */
     private static List<BigDecimal> toDecs(Value[] values) throws RepositoryException {
         ArrayList<BigDecimal> decs = new ArrayList<BigDecimal>(values.length);
 
@@ -267,6 +319,9 @@ public final class JcrConverter {
         return decs;
     }
 
+    /**
+     * Convert an array of double <code>Value</code>s to a list of <code>BigInteger</code>s.
+     */
     private static List<BigDecimal> doublesToDecs(Value[] values) throws RepositoryException {
         ArrayList<BigDecimal> decs = new ArrayList<BigDecimal>(values.length);
 
@@ -277,6 +332,9 @@ public final class JcrConverter {
         return decs;
     }
 
+    /**
+     * Convert an array of <code>Value</code>s to a list of <code>Booleans</code>s.
+     */
     private static List<Boolean> toBools(Value[] values) throws RepositoryException {
         ArrayList<Boolean> bools = new ArrayList<Boolean>(values.length);
 
@@ -287,6 +345,9 @@ public final class JcrConverter {
         return bools;
     }
 
+    /**
+     * Convert an array of <code>Value</code>s to a list of <code>GregorianCalendar</code>s.
+     */
     private static List<GregorianCalendar> toDates(Value[] values) throws RepositoryException {
         ArrayList<GregorianCalendar> dates = new ArrayList<GregorianCalendar>(values.length);
 
@@ -297,6 +358,9 @@ public final class JcrConverter {
         return dates;
     }
 
+    /**
+     * Convert a <code>PropertyBoolean</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyBoolean propertyData, ValueFactory valueFactory) {
         List<Boolean> values = propertyData.getValues();
         if (values == null) {
@@ -312,6 +376,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyDateTime</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyDateTime propertyData, ValueFactory valueFactory) {
         List<GregorianCalendar> values = propertyData.getValues();
         if (values == null) {
@@ -327,6 +394,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyDecimal</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyDecimal propertyData, ValueFactory valueFactory) {
         List<BigDecimal> values = propertyData.getValues();
         if (values == null) {
@@ -342,6 +412,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyHtml</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyHtml propertyData, ValueFactory valueFactory) {
         List<String> values = propertyData.getValues();
         if (values == null) {
@@ -357,6 +430,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyId</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyId propertyData, ValueFactory valueFactory) {
         List<String> values = propertyData.getValues();
         if (values == null) {
@@ -372,6 +448,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyInteger</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyInteger propertyData, ValueFactory valueFactory) {
         List<BigInteger> values = propertyData.getValues();
         if (values == null) {
@@ -387,6 +466,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyString</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyString propertyData, ValueFactory valueFactory) {
         List<String> values = propertyData.getValues();
         if (values == null) {
@@ -402,6 +484,9 @@ public final class JcrConverter {
         return result;
     }
 
+    /**
+     * Convert a <code>PropertyUri</code> to an array of JCR <code>Values</code>.
+     */
     private static Value[] toValue(PropertyUri propertyData, ValueFactory valueFactory) throws ValueFormatException {
         List<String> values = propertyData.getValues();
         if (values == null) {

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrDocument.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrDocument.java?rev=1082139&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrDocument.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrDocument.java Wed Mar 16 12:46:36 2011
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.jcr;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.enums.Action;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
+import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.util.Set;
+
+/**
+ * Instances of this class represent a cmis:document backed by an underlying JCR <code>Node</code>. 
+ */
+public abstract class JcrDocument extends JcrNode {
+    private static final Log log = LogFactory.getLog(JcrDocument.class);
+
+    public static final String MIME_UNKNOWN = "application/octet-stream";
+
+    public JcrDocument(Node node, TypeManager typeManager, PathManager pathManager, JcrNodeFactory nodeFactory) {
+        super(node, typeManager, pathManager, nodeFactory);
+    }
+
+    /**
+     * @return  <code>true</code> iff the document is checked out
+     */
+    public boolean isDocumentCheckedOut() {
+        try {
+            return getNode().isCheckedOut();
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.10 getContentStream
+     *
+     * @throws CmisObjectNotFoundException
+     * @throws CmisRuntimeException
+     */
+    public ContentStream getContentStream() {
+        try {
+            Node contentNode = getContextNode();
+            Property data = contentNode.getProperty(Property.JCR_DATA);
+
+            // compile data
+            ContentStreamImpl result = new ContentStreamImpl();
+            result.setFileName(getNodeName());
+            result.setLength(BigInteger.valueOf(data.getLength()));
+            result.setMimeType(getPropertyOrElse(contentNode, Property.JCR_MIMETYPE, MIME_UNKNOWN));
+            result.setStream(new BufferedInputStream(data.getBinary().getStream()));  // stream closed by consumer
+
+            return result;
+        }
+        catch (PathNotFoundException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisObjectNotFoundException(e.getMessage(), e);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.16 setContentStream
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode setContentStream(ContentStream contentStream, boolean overwriteFlag) {
+        try {
+            // get content node
+            Node contentNode = getNode().getNode(Node.JCR_CONTENT);
+            Property data = contentNode.getProperty(Property.JCR_DATA);
+
+            // check overwrite
+            if (!overwriteFlag && data.getLength() != 0) {
+                throw new CmisContentAlreadyExistsException("Content already exists!");
+            }
+
+            JcrVersionBase jcrVersion = isVersionable()
+                    ? asVersion()
+                    : null;
+
+            boolean autoCheckout = jcrVersion != null && !jcrVersion.isCheckedOut();
+            if (autoCheckout) {
+                jcrVersion.checkout();
+            }
+
+            // write content, if available
+            Binary binary = contentStream == null || contentStream.getStream() == null
+                    ? JcrBinary.EMPTY
+                    : new JcrBinary(new BufferedInputStream(contentStream.getStream()));
+            try {
+                contentNode.setProperty(Property.JCR_DATA, binary);
+                if (contentStream != null && contentStream.getMimeType() != null) {
+                    contentNode.setProperty(Property.JCR_MIMETYPE, contentStream.getMimeType());
+                }
+            }
+            finally {
+                binary.dispose();
+            }
+
+            contentNode.getSession().save();
+
+            if (autoCheckout) {
+                // auto versioning -> return new version created by checkin
+                return jcrVersion.checkin(null, null, "auto checkout");
+            }
+            else if (jcrVersion != null) {
+                // the node is checked out -> return pwc.
+                return jcrVersion.getPwc();
+            }
+            else {
+                // non versionable -> return this
+                return this;
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+        catch (IOException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+
+    }
+
+    //------------------------------------------< protected >---
+
+    /**
+     * @return  the value of the <code>cmis:isLatestVersion</code> property
+     * @throws RepositoryException
+     */
+    protected abstract boolean isLatestVersion() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:isMajorVersion</code> property
+     * @throws RepositoryException
+     */
+    protected abstract boolean isMajorVersion() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:isLatestMajorVersion</code> property
+     * @throws RepositoryException
+     */
+    protected abstract boolean isLatestMajorVersion() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:versionLabel</code> property
+     * @throws RepositoryException
+     */
+    protected abstract String getVersionLabel() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:isVersionSeriesCheckedOut</code> property
+     * @throws RepositoryException
+     */
+    protected abstract boolean isCheckedOut() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:versionSeriesCheckedOutId</code> property
+     * @throws RepositoryException
+     */
+    protected abstract String getCheckedOutId() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:versionSeriesCheckedOutBy</code> property
+     * @throws RepositoryException
+     */
+    protected abstract String getCheckedOutBy() throws RepositoryException;
+
+
+    /**
+     * @return  the value of the <code>cmis:checkinComment</code> property
+     * @throws RepositoryException
+     */
+    protected abstract String getCheckInComment() throws RepositoryException;
+
+    protected boolean getIsImmutable() {
+        return false;
+    }
+    
+    @Override
+    protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
+            throws RepositoryException {
+
+        super.compileProperties(properties, filter, objectInfo);
+
+        objectInfo.setHasContent(true);
+        objectInfo.setHasParent(true);
+        objectInfo.setSupportsDescendants(false);
+        objectInfo.setSupportsFolderTree(false);
+
+        String typeId = getTypeIdInternal();
+        Node contextNode = getContextNode();
+
+        // mutability
+        addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_IMMUTABLE, getIsImmutable());
+
+        // content stream
+        long length = getPropertyLength(contextNode, Property.JCR_DATA);
+        addPropertyInteger(properties, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, length);
+
+        // mime type
+        String mimeType = getPropertyOrElse(contextNode, Property.JCR_MIMETYPE, MIME_UNKNOWN);
+        addPropertyString(properties, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE, mimeType);
+        objectInfo.setContentType(mimeType);
+
+        // file name
+        String fileName = getNodeName();
+        addPropertyString(properties, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, fileName);
+        objectInfo.setFileName(fileName);
+
+        addPropertyId(properties, typeId, filter, PropertyIds.CONTENT_STREAM_ID, getObjectId() + "/stream");
+
+        // versioning
+        addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_LATEST_VERSION, isLatestVersion());
+        addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_MAJOR_VERSION, isMajorVersion());
+        addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, isLatestMajorVersion());
+        addPropertyString(properties, typeId, filter, PropertyIds.VERSION_LABEL, getVersionLabel());
+        addPropertyId(properties, typeId, filter, PropertyIds.VERSION_SERIES_ID, getVersionSeriesId());
+        addPropertyString(properties, typeId, filter, PropertyIds.CHECKIN_COMMENT, getCheckInComment());
+
+        boolean isCheckedOut = isCheckedOut();
+        addPropertyBoolean(properties, typeId, filter, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, isCheckedOut);
+
+        if (isCheckedOut) {
+            addPropertyId(properties, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, getCheckedOutId());
+            addPropertyString(properties, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, getCheckedOutBy());
+        }
+    }
+
+    @Override
+    protected Set<Action> compileAllowableActions(Set<Action> aas) {
+        Set<Action> result = super.compileAllowableActions(aas);
+        setAction(result, Action.CAN_GET_CONTENT_STREAM, true);
+        setAction(result, Action.CAN_SET_CONTENT_STREAM, true);
+        setAction(result, Action.CAN_DELETE_CONTENT_STREAM, true);
+        setAction(result, Action.CAN_GET_RENDITIONS, false);
+        return result;
+    }
+
+    @Override
+    protected BaseTypeId getBaseTypeId() {
+        return BaseTypeId.CMIS_DOCUMENT;
+    }
+
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrFolder.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrFolder.java?rev=1082139&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrFolder.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrFolder.java Wed Mar 16 12:46:36 2011
@@ -0,0 +1,397 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.jcr;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.data.Properties;
+import org.apache.chemistry.opencmis.commons.data.PropertyData;
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.enums.Action;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.Updatability;
+import org.apache.chemistry.opencmis.commons.enums.VersioningState;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
+import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
+import org.apache.chemistry.opencmis.jcr.util.FilterIterator;
+import org.apache.chemistry.opencmis.jcr.util.Predicate;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+
+/**
+ * Instances of this class represent a cmis:folder backed by an underlying JCR <code>Node</code>. 
+ */
+public class JcrFolder extends JcrNode {
+    private static final Log log = LogFactory.getLog(JcrFolder.class);
+
+    public JcrFolder(Node node, TypeManager typeManager, PathManager pathManager, JcrNodeFactory nodeFactory) {
+        super(node, typeManager, pathManager, nodeFactory);
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.3.1 getChildren
+     * 
+     * @return  Iterator of <code>JcrNode</code>. Children which are created in the checked out
+     *      state are left out from the iterator.
+     * @throws CmisRuntimeException
+     */
+    public Iterator<JcrNode> getNodes() {
+        try {
+            final NodeIterator nodes = getNode().getNodes();
+
+            Iterator<JcrNode> jcrNodes = new Iterator<JcrNode>() {
+                public boolean hasNext() {
+                    return nodes.hasNext();
+                }
+
+                public JcrNode next() {
+                    return create(nodes.nextNode());
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+            };
+
+            // Filter out nodes which are checked out and do not have a version history (i.e. only a root version)
+            // These are created with VersioningState checkedout and not yet checked in.
+            return new FilterIterator<JcrNode>(jcrNodes, new Predicate<JcrNode>() {
+                public boolean evaluate(JcrNode node) {
+                    try {
+                        if (node.isVersionable()) {
+                            Version baseVersion = getBaseVersion(node.getNode());
+                            return baseVersion.getPredecessors().length > 0;
+                        }
+                        else {
+                            return true;
+                        }
+                    }
+                    catch (RepositoryException e) {
+                        log.debug(e.getMessage(), e);
+                        throw new CmisRuntimeException(e.getMessage(), e);
+                    }
+                }
+            });
+
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.1 createDocument
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode addNode(String name, String typeId, Properties properties, ContentStream contentStream,
+            VersioningState versioningState) {
+        
+        try {
+            Node fileNode = getNode().addNode(name, NodeType.NT_FILE);
+            if (versioningState != VersioningState.NONE) {
+                fileNode.addMixin(NodeType.MIX_SIMPLE_VERSIONABLE);
+            }
+
+            Node contentNode = fileNode.addNode(Node.JCR_CONTENT, NodeType.NT_RESOURCE);
+            contentNode.addMixin(NodeType.MIX_CREATED);
+
+            // compile the properties
+            setProperties(contentNode, typeId, properties);
+
+            // write content, if available
+            Binary binary = contentStream == null || contentStream.getStream() == null
+                    ? JcrBinary.EMPTY
+                    : new JcrBinary(new BufferedInputStream(contentStream.getStream()));
+            try {
+                contentNode.setProperty(Property.JCR_DATA, binary);
+                if (contentStream != null && contentStream.getMimeType() != null) {
+                    contentNode.setProperty(Property.JCR_MIMETYPE, contentStream.getMimeType());
+                }
+            }
+            finally {
+                binary.dispose();
+            }
+
+            fileNode.getSession().save();
+            JcrNode jcrFileNode = create(fileNode);
+            if (versioningState == VersioningState.NONE) {
+                return jcrFileNode;
+            }
+
+            JcrVersionBase jcrVersion = jcrFileNode.asVersion();
+            if (versioningState == VersioningState.MINOR || versioningState == VersioningState.MAJOR) {
+                return jcrVersion.checkin(null, null, "auto checkin");
+            }
+            else {
+                return jcrVersion.getPwc();
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+        catch (IOException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.2 createDocumentFromSource
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode addNodeFromSource(JcrDocument source, Properties properties) {
+        try {
+            String destPath = PathManager.createCmisPath(getNode().getPath(), source.getName());
+            Session session = getNode().getSession();
+
+            session.getWorkspace().copy(source.getNode().getPath(), destPath);  
+            JcrNode jcrNode = create(session.getNode(destPath));
+
+            // overlay new properties
+            if (properties != null && properties.getProperties() != null) {
+                updateProperties(jcrNode.getNode(), jcrNode.getTypeId(), properties);
+            }
+
+            session.save();
+            return jcrNode;
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.3 createFolder
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode addFolder(String name, String typeId, Properties properties) {
+        try {
+            Node node = getNode().addNode(name, NodeType.NT_FOLDER);
+            node.addMixin(NodeType.MIX_CREATED);
+            node.addMixin(NodeType.MIX_LAST_MODIFIED);
+
+            // compile the properties
+            setProperties(node, typeId, properties);
+
+            node.getSession().save();
+            return create(node);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.14 deleteObject
+     *
+     * @throws CmisRuntimeException
+     */
+    @Override
+    public void delete(boolean allVersions, boolean isPwc) {
+        try {
+            if (getNode().hasNodes()) {
+                throw new CmisConstraintException("Folder is not empty!");
+            }
+            else {
+                super.delete(allVersions, isPwc);
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.15 deleteTree
+     */
+    public FailedToDeleteDataImpl deleteTree() {
+        FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
+
+        String id = getId();
+        try {
+            Session session = getNode().getSession();
+            getNode().remove();
+            session.save();
+            result.setIds(Collections.<String>emptyList());
+        }
+        catch (RepositoryException e) {
+            result.setIds(Collections.singletonList(id));
+        }
+
+        return result;
+    }
+
+    //------------------------------------------< protected >---
+
+    @Override
+    protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
+            throws RepositoryException {
+
+        super.compileProperties(properties, filter, objectInfo);
+
+        objectInfo.setHasContent(false);
+        objectInfo.setSupportsDescendants(true);
+        objectInfo.setSupportsFolderTree(true);
+
+        String typeId = getTypeIdInternal();
+
+        addPropertyString(properties, typeId, filter, PropertyIds.PATH, pathManager.getPath(getNode()));
+
+        // folder properties
+        if (pathManager.isRoot(getNode())) {
+            objectInfo.setHasParent(false);
+        }
+        else {
+            objectInfo.setHasParent(true);
+            addPropertyId(properties, typeId, filter, PropertyIds.PARENT_ID, getParent().getObjectId());
+        }
+    }
+
+    @Override
+    protected Set<Action> compileAllowableActions(Set<Action> aas) {
+        Set<Action> result = super.compileAllowableActions(aas);
+        setAction(result, Action.CAN_GET_DESCENDANTS, true);
+        setAction(result, Action.CAN_GET_CHILDREN, true);
+        setAction(result, Action.CAN_GET_FOLDER_PARENT, !pathManager.isRoot(getNode()));
+        setAction(result, Action.CAN_GET_OBJECT_PARENTS, !pathManager.isRoot(getNode()));
+        setAction(result, Action.CAN_GET_FOLDER_TREE, true);
+        setAction(result, Action.CAN_CREATE_DOCUMENT, true);
+        setAction(result, Action.CAN_CREATE_FOLDER, true);
+        setAction(result, Action.CAN_DELETE_TREE, true);
+        return result;
+    }
+
+    @Override
+    protected Node getContextNode() {
+        return getNode();
+    }
+
+    @Override
+    protected String getObjectId() throws RepositoryException {
+        return isRoot()
+                ? PathManager.CMIS_ROOT_ID
+                : super.getObjectId();
+    }
+
+    @Override
+    protected BaseTypeId getBaseTypeId() {
+        return BaseTypeId.CMIS_FOLDER;
+    }
+
+    @Override
+    protected String getTypeIdInternal() {
+        return TypeManager.FOLDER_TYPE_ID;
+    }
+
+    //------------------------------------------< private >---
+
+    private void setProperties(Node node, String typeId, Properties properties) {
+        if (properties == null || properties.getProperties() == null) {
+            throw new CmisConstraintException("No properties!");
+        }
+
+        Set<String> addedProps = new HashSet<String>();
+
+        // get the property definitions
+        TypeDefinition type = typeManager.getType(typeId);
+        if (type == null) {
+            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+        }
+
+        try {
+            // check if all required properties are there
+            for (PropertyData<?> prop : properties.getProperties().values()) {
+                PropertyDefinition<?> propDef = type.getPropertyDefinitions().get(prop.getId());
+
+                // do we know that property?
+                if (propDef == null) {
+                    throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
+                }
+
+                // skip type id
+                if (propDef.getId().equals(PropertyIds.OBJECT_TYPE_ID)) {
+                    addedProps.add(prop.getId());
+                    continue;
+                }
+
+                // can it be set?
+                if (propDef.getUpdatability() == Updatability.READONLY) {
+                    throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
+                }
+
+                // empty properties are invalid
+                if (PropertyHelper.isPropertyEmpty(prop)) {
+                    throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
+                }
+
+                // add it
+                JcrConverter.setProperty(node, prop);
+                addedProps.add(prop.getId());
+            }
+
+            // check if required properties are missing and try to add default values if defined
+            for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
+                if (!addedProps.contains(propDef.getId()) && propDef.getUpdatability() != Updatability.READONLY) {
+                    PropertyData<?> prop = PropertyHelper.getDefaultValue(propDef);
+                    if (prop == null && propDef.isRequired()) {
+                        throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
+                    }
+                    else if (prop != null) {
+                        JcrConverter.setProperty(node, prop);
+                    }
+                }
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+    }
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNode.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNode.java?rev=1082139&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNode.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNode.java Wed Mar 16 12:46:36 2011
@@ -0,0 +1,877 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.jcr;
+
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.AllowableActions;
+import org.apache.chemistry.opencmis.commons.data.ObjectData;
+import org.apache.chemistry.opencmis.commons.data.Properties;
+import org.apache.chemistry.opencmis.commons.data.PropertyData;
+import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.enums.Action;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.Updatability;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
+import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
+import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
+import org.apache.chemistry.opencmis.jcr.util.Util;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionHistory;
+import javax.jcr.version.VersionManager;
+import java.math.BigInteger;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Common base class for all JCR <code>Node</code>s to be represented as CMIS objects. Instances of this class
+ * are responsible for mapping from CMIS to JCR and vice versa. 
+ */
+public abstract class JcrNode {
+    private static final Log log = LogFactory.getLog(JcrNode.class);
+
+    /**
+     * Default value for last cmis:createdBy and cmis:modifiedBy
+     */
+    public static final String USER_UNKNOWN = "unknown";
+
+    /**
+     * Default value for cmis:createdBy and cmis:lastModifiedDate
+     * (Thu Jan 01 01:11:59 CET 1970)
+     */
+    public static final GregorianCalendar DATE_UNKNOWN;
+
+    static {
+        DATE_UNKNOWN = new GregorianCalendar();
+        DATE_UNKNOWN.setTimeInMillis(719163);
+    }
+
+    private final Node node;
+    protected final TypeManager typeManager;
+    protected final PathManager pathManager;
+    private final JcrNodeFactory nodeFactory;
+
+    /**
+     * Create a new instance wrapping a JCR <code>node</code>.
+     * @param node  the JCR <code>node</code> to represent
+     * @param typeManager
+     * @param pathManager
+     * @param nodeFactory
+     */
+    public JcrNode(Node node, TypeManager typeManager, PathManager pathManager, JcrNodeFactory nodeFactory) {
+        this.node = node;
+        this.typeManager = typeManager;
+        this.pathManager = pathManager;
+        this.nodeFactory = nodeFactory;
+    }
+
+    /**
+     * @return  the JCR <code>node</code> represented by this instance
+     */
+    public Node getNode() {
+        return node;
+    }
+
+    /**
+     * @return  the name of the CMIS object represented by this instance
+     * @throws  CmisRuntimeException
+     */
+    public String getName() {
+        try {
+            return getNodeName();
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * @return  the id of the CMIS object represented by this instance
+     * @throws  CmisRuntimeException
+     */
+    public String getId() {
+        try {
+            return getObjectId();
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * @return  the typeId of the CMIS object represented by this instance
+     */
+    public String getTypeId() {
+        return getTypeIdInternal();
+    }
+
+    /**
+     * @return  <code>true</code> iff this instance represent the root of the CMIS folder hierarchy.
+     */
+    public boolean isRoot() {
+        return pathManager.isRoot(node);
+    }
+
+    /**
+     * @return  <code>true</code> iff this instance represents a cmis:document type
+     */
+    public boolean isDocument() {
+        return BaseTypeId.CMIS_DOCUMENT == getBaseTypeId();
+    }
+
+    /**
+     * @return  <code>true</code> iff this instance represents a cmis:folder type
+     */
+    public boolean isFolder() {
+        return BaseTypeId.CMIS_FOLDER == getBaseTypeId();
+    }
+    
+    /**
+     * @return  <code>true</code> iff this instance represents a versionable CMIS object
+     */
+    public boolean isVersionable() {
+        TypeDefinition typeDef = typeManager.getTypeDefinition(getTypeIdInternal());
+        return typeDef instanceof DocumentTypeDefinition
+                ? ((DocumentTypeDefinition) typeDef).isVersionable()
+                : false;
+    }
+
+    /**
+     * @return  this instance as a <code>JcrDocument</code>
+     * @throws CmisConstraintException if <code>this.isDocument() == false</code>
+     */
+    public JcrDocument asDocument() {
+        if (isDocument()) {
+            return (JcrDocument) this;
+        }
+        else {
+            throw new CmisConstraintException("Not a document: " + this);
+        }
+    }
+
+    /**
+     * @return  this instance as a <code>JcrFolder</code>
+     * @throws CmisConstraintException if <code>this.isFolder() == false</code>
+     */
+    public JcrFolder asFolder() {
+        if (isFolder()) {
+            return (JcrFolder) this;
+        }
+        else {
+            throw new CmisObjectNotFoundException("Not a folder: " + this);
+        }
+    }
+
+    /**
+     * @return  this instance as a <code>JcrVersionBase</code>
+     * @throws CmisConstraintException if <code>this.isVersionable() == false</code>
+     */
+    public JcrVersionBase asVersion() {
+        if (isVersionable()) {
+            return (JcrVersionBase) this;
+        }
+        else {
+            throw new CmisObjectNotFoundException("Not a version: " + this);
+        }
+    }
+
+    /**
+     * Factory method creating a new <code>JcrNode</code> from a node at a given JCR path.
+     *
+     * @param path  JCR path of the node
+     * @return  A new instance representing the JCR node at <code>path</code>
+     * @throws CmisObjectNotFoundException  if <code>path</code> does not identify a JCR node
+     * @throws CmisRuntimeException
+     */
+    public JcrNode getNode(String path) {
+        try {
+            return create(node.getNode(path));
+        }
+        catch (PathNotFoundException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisObjectNotFoundException(e.getMessage(), e);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Compile the <code>ObjectData</code> for this node
+     */
+    public ObjectData compileObjectType(Set<String> filter, Boolean includeAllowableActions,
+            ObjectInfoHandler objectInfos, boolean requiresObjectInfo) {
+
+        try {
+            ObjectDataImpl result = new ObjectDataImpl();
+            ObjectInfoImpl objectInfo = new ObjectInfoImpl();
+
+            PropertiesImpl properties = new PropertiesImpl();
+            filter = filter == null ? null : new HashSet<String>(filter);
+            compileProperties(properties, filter, objectInfo);
+            result.setProperties(properties);
+            if (filter != null && !filter.isEmpty()) {
+                log.debug("Unknown filter properties: " + filter.toString());
+            }
+
+            if (Boolean.TRUE.equals(includeAllowableActions)) {
+                result.setAllowableActions(getAllowableActions());
+            }
+
+            if (requiresObjectInfo) {
+                objectInfo.setObject(result);
+                objectInfos.addObjectInfo(objectInfo);
+            }
+
+            return result;
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.6 getAllowableActions
+     */
+    public AllowableActions getAllowableActions() {
+        AllowableActionsImpl aas = new AllowableActionsImpl();
+        aas.setAllowableActions(compileAllowableActions(new HashSet<Action>()));
+        return aas;
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.3.5 getObjectParents
+     *
+     * @return  parent of this object
+     * @throws  CmisObjectNotFoundException  if this is the root folder
+     * @throws  CmisRuntimeException
+     */
+    public JcrFolder getParent() {
+        try {
+            return create(node.getParent()).asFolder();
+        }
+        catch (ItemNotFoundException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisObjectNotFoundException(e.getMessage(), e);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.12 updateProperties
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode updateProperties(Properties properties) {
+        // get and check the new name
+        String newName = PropertyHelper.getStringProperty(properties, PropertyIds.NAME);
+        boolean rename = newName != null && !getName().equals(newName);
+        if (rename && !JcrConverter.isValidJcrName(newName)) {
+            throw new CmisNameConstraintViolationException("Name is not valid: " + newName);
+        }
+        if (rename && isRoot()) {
+            throw new CmisUpdateConflictException("Cannot rename root node");
+        }
+
+        try {
+            // rename file or folder if necessary
+            Session session = getNode().getSession();
+            Node newNode;
+            if (rename) {
+                String destPath = PathManager.createCmisPath(node.getParent().getPath(), newName);
+                session.move(node.getPath(), destPath);
+                newNode = session.getNode(destPath);
+            }
+            else {
+                newNode = node;
+            }
+
+            // Are there properties to update?
+            int propertyCount = properties.getProperties().size();
+            boolean update = rename && propertyCount > 1 || !rename && propertyCount > 0;
+
+            JcrVersionBase jcrVersion = isVersionable()
+                    ? asVersion()
+                    : null;
+
+            // Update properties. Checkout if required
+            boolean autoCheckout = false;
+            if (update) {
+                autoCheckout = jcrVersion != null && !jcrVersion.isCheckedOut();
+                if (autoCheckout) {
+                    jcrVersion.checkout();
+                }
+
+                // update the properties
+                updateProperties(node, getTypeId(), properties);
+            }
+
+            session.save();
+
+            if (autoCheckout) {
+                // auto versioning -> return new version created by checkin
+                return jcrVersion.checkin(null, null, "auto checkout");
+            }
+            else if (jcrVersion != null && jcrVersion.isCheckedOut()) {
+                // the node is checked out -> return pwc.
+                JcrVersionBase jcrNewVersion = create(newNode).asVersion();
+                return jcrNewVersion.getPwc();
+            }
+            else {
+                // non versionable or not a new node -> return this
+                return create(newNode);
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.14 deleteObject
+     *
+     * @throws CmisRuntimeException
+     */
+    public void delete(boolean allVersions, boolean isPwc) {
+        try {
+            Session session = getNode().getSession();
+            getNode().remove();
+            session.save();
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * See CMIS 1.0 section 2.2.4.13 moveObject
+     *
+     * @throws CmisStorageException
+     */
+    public JcrNode move(JcrFolder parent) {
+        try {
+            // move it if target location is not same as source location
+            String destPath = PathManager.createCmisPath(parent.getNode().getPath(), node.getName());
+            String srcPath = node.getPath();
+            Node newNode;
+            if (srcPath.equals(destPath)) {
+                newNode = node;
+            }
+            else {
+                Session session = getNode().getSession();
+                session.move(srcPath, destPath);
+                newNode = session.getNode(destPath);
+                session.save();
+            }
+
+            return create(newNode);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisStorageException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        try {
+            return node.getPath();
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            return e.getMessage();
+        }
+    }
+
+    //------------------------------------------< protected >---
+
+    /**
+     * Retrieve the context node of the CMIS object represented by this instance. The
+     * context node is the node which is used to derive the common properties from
+     * (creation date, modification date, ...)
+     *
+     * @return  the context node
+     * @throws RepositoryException
+     */
+    protected abstract Node getContextNode() throws RepositoryException;
+
+    /**
+     * @return  the value of the <code>cmis:baseTypeId</code> property
+     */
+    protected abstract BaseTypeId getBaseTypeId();
+
+    /**
+     * @return  the value of the <code>cmis:objectTypeId</code> property 
+     */
+    protected abstract String getTypeIdInternal();
+
+    /**
+     * Compile the properties of the CMIS object represented by this instance.
+     * See CMIS 1.0 section 2.2.4.7 getObject
+     *
+     * @param properties  compilation of properties
+     * @param filter
+     * @param objectInfo
+     * @throws RepositoryException
+     */
+    protected void compileProperties(PropertiesImpl properties, Set<String> filter, ObjectInfoImpl objectInfo)
+            throws RepositoryException {
+
+        String typeId = getTypeIdInternal();
+        BaseTypeId baseTypeId = getBaseTypeId();
+
+        objectInfo.setBaseType(baseTypeId);
+        objectInfo.setTypeId(typeId);
+        objectInfo.setHasAcl(false);
+        objectInfo.setVersionSeriesId(getVersionSeriesId());
+        objectInfo.setRelationshipSourceIds(null);
+        objectInfo.setRelationshipTargetIds(null);
+        objectInfo.setRenditionInfos(null);
+        objectInfo.setSupportsPolicies(false);
+        objectInfo.setSupportsRelationships(false);
+
+        // id
+        String objectId = getObjectId();
+        addPropertyId(properties, typeId, filter, PropertyIds.OBJECT_ID, objectId);
+        objectInfo.setId(objectId);
+
+        // name
+        String name = getNodeName();
+        addPropertyString(properties, typeId, filter, PropertyIds.NAME, name);
+        objectInfo.setName(name);
+
+        // base type and type name
+        addPropertyId(properties, typeId, filter, PropertyIds.BASE_TYPE_ID, baseTypeId.value());
+        addPropertyId(properties, typeId, filter, PropertyIds.OBJECT_TYPE_ID, typeId);
+
+        // created and modified by
+        String createdBy = getCreatedBy();
+        addPropertyString(properties, typeId, filter, PropertyIds.CREATED_BY, createdBy);
+        objectInfo.setCreatedBy(createdBy);
+
+        addPropertyString(properties, typeId, filter, PropertyIds.LAST_MODIFIED_BY, getLastModifiedBy());
+
+        // creation and modification date
+        GregorianCalendar created = getCreated();
+        addPropertyDateTime(properties, typeId, filter, PropertyIds.CREATION_DATE, created);
+        objectInfo.setCreationDate(created);
+
+        GregorianCalendar lastModified = getLastModified();
+        addPropertyDateTime(properties, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified);
+        objectInfo.setLastModificationDate(lastModified);
+
+        addPropertyString(properties, typeId, filter, PropertyIds.CHANGE_TOKEN, getChangeToken());
+    }
+
+    /**
+     * Compile the allowed actions on the CMIS object represented by this instance
+     * See CMIS 1.0 section 2.2.4.6 getAllowableActions
+     *
+     * @param aas  compilation of allowed actions
+     * @return
+     */
+    protected Set<Action> compileAllowableActions(Set<Action> aas) {
+        setAction(aas, Action.CAN_GET_OBJECT_PARENTS, true);
+        setAction(aas, Action.CAN_GET_PROPERTIES, true);
+        setAction(aas, Action.CAN_UPDATE_PROPERTIES, true);
+        setAction(aas, Action.CAN_MOVE_OBJECT, true);
+        setAction(aas, Action.CAN_DELETE_OBJECT, true);
+        setAction(aas, Action.CAN_GET_ACL, false);
+        setAction(aas, Action.CAN_APPLY_ACL, false);
+        setAction(aas, Action.CAN_GET_OBJECT_RELATIONSHIPS, false);
+        setAction(aas, Action.CAN_ADD_OBJECT_TO_FOLDER, false);
+        setAction(aas, Action.CAN_REMOVE_OBJECT_FROM_FOLDER, false);
+        setAction(aas, Action.CAN_APPLY_POLICY, false);
+        setAction(aas, Action.CAN_GET_APPLIED_POLICIES, false);
+        setAction(aas, Action.CAN_REMOVE_POLICY, false);
+        setAction(aas, Action.CAN_CREATE_RELATIONSHIP, false);
+        return aas;
+    }
+
+    /**
+     * @return  the change token of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected String getChangeToken() throws RepositoryException {
+        return null;
+    }
+
+    /**
+     * @return  the last modifier of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected String getLastModifiedBy() throws RepositoryException {
+        return getPropertyOrElse(getContextNode(), Property.JCR_LAST_MODIFIED_BY, USER_UNKNOWN);
+    }
+
+    /**
+     * @return  the last modification date of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected GregorianCalendar getLastModified() throws RepositoryException {
+        return getPropertyOrElse(getContextNode(), Property.JCR_LAST_MODIFIED, DATE_UNKNOWN);
+    }
+
+    /**
+     * @return  the creation date of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected GregorianCalendar getCreated() throws RepositoryException {
+        return getPropertyOrElse(getContextNode(), Property.JCR_CREATED, DATE_UNKNOWN);
+    }
+
+    /**
+     * @return  the creator of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected String getCreatedBy() throws RepositoryException {
+        return getPropertyOrElse(getContextNode(), Property.JCR_CREATED_BY, USER_UNKNOWN);
+    }
+
+    /**
+     * @return  the name of the underlying JCR <code>node</code>.
+     * @throws RepositoryException
+     */
+    protected String getNodeName() throws RepositoryException {
+        return node.getName();
+    }
+
+    /**
+     * @return  the object id of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected String getObjectId() throws RepositoryException {
+        return getVersionSeriesId();
+    }
+
+    /**
+     * @return  the versions series id of the CMIS object represented by this instance
+     * @throws RepositoryException
+     */
+    protected String getVersionSeriesId() throws RepositoryException {
+        return node.getIdentifier();
+    }
+
+    /**
+     * Factory method for creating a new <code>JcrNode</code> instance from a JCR <code>Node</code>
+     * 
+     * @param node  the JCR <code>Node</code>
+     * @return  a new <code>JcrNode</code>
+     */
+    protected final JcrNode create(Node node) {
+        return nodeFactory.create(node);
+    }
+
+    /**
+     * Add Id property to the CMIS object represented by this instance
+     */
+    protected final void addPropertyId(PropertiesImpl props, String typeId, Set<String> filter, String id, String value) {
+        if (value == null) {
+            throw new IllegalArgumentException("Value must not be null!");
+        }
+
+        if (!checkAddProperty(props, typeId, filter, id)) {
+            return;
+        }
+
+        PropertyIdImpl prop = new PropertyIdImpl(id, value);
+        prop.setQueryName(id);
+        props.addProperty(prop);
+    }
+
+    /**
+     * Add string property to the CMIS object represented by this instance
+     */
+    protected final void addPropertyString(PropertiesImpl props, String typeId, Set<String> filter, String id, String value) {
+        if (!checkAddProperty(props, typeId, filter, id)) {
+            return;
+        }
+
+        PropertyStringImpl prop = new PropertyStringImpl(id, value);
+        prop.setQueryName(id);
+        props.addProperty(prop);
+    }
+
+    /**
+     * Add integer property to the CMIS object represented by this instance
+     */
+    protected final void addPropertyInteger(PropertiesImpl props, String typeId, Set<String> filter, String id, long value) {
+        if (!checkAddProperty(props, typeId, filter, id)) {
+            return;
+        }
+
+        PropertyIntegerImpl prop = new PropertyIntegerImpl(id, BigInteger.valueOf(value));
+        prop.setQueryName(id);
+        props.addProperty(prop);
+    }
+
+    /**
+     * Add boolean property to the CMIS object represented by this instance
+     */
+    protected final void addPropertyBoolean(PropertiesImpl props, String typeId, Set<String> filter, String id, boolean value) {
+        if (!checkAddProperty(props, typeId, filter, id)) {
+            return;
+        }
+
+        PropertyBooleanImpl prop = new PropertyBooleanImpl(id, value);
+        prop.setQueryName(id);
+        props.addProperty(prop);
+    }
+
+    /**
+     * Add date-time property to the CMIS object represented by this instance
+     */
+    protected final void addPropertyDateTime(PropertiesImpl props, String typeId, Set<String> filter, String id,
+            GregorianCalendar value) {
+
+        if (!checkAddProperty(props, typeId, filter, id)) {
+            return;
+        }
+
+        PropertyDateTimeImpl prop = new PropertyDateTimeImpl(id, value);
+        prop.setQueryName(id);
+        props.addProperty(prop);
+    }
+
+    /**
+     * Validate a set of properties against a filter and its definitions
+     */
+    protected final boolean checkAddProperty(Properties properties, String typeId, Set<String> filter, String id) {
+        if (properties == null || properties.getProperties() == null) {
+            throw new IllegalArgumentException("Properties must not be null!");
+        }
+
+        if (id == null) {
+            throw new IllegalArgumentException("Id must not be null!");
+        }
+
+        TypeDefinition type = typeManager.getType(typeId);
+        if (type == null) {
+            throw new IllegalArgumentException("Unknown type: " + typeId);
+        }
+        if (!type.getPropertyDefinitions().containsKey(id)) {
+            throw new IllegalArgumentException("Unknown property: " + id);
+        }
+
+        String queryName = type.getPropertyDefinitions().get(id).getQueryName();
+
+        if (queryName != null && filter != null) {
+            if (filter.contains(queryName)) {
+                filter.remove(queryName);
+            }
+            else {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Update the properties of the CMIS object represented by this instance
+     */
+    protected final void updateProperties(Node node, String typeId, Properties properties) {
+        if (properties == null) {
+            throw new CmisConstraintException("No properties!");
+        }
+
+        // get the property definitions
+        TypeDefinition type = typeManager.getType(typeId);
+        if (type == null) {
+            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+        }
+
+        // update properties
+        for (PropertyData<?> prop : properties.getProperties().values()) {
+            PropertyDefinition<?> propDef = type.getPropertyDefinitions().get(prop.getId());
+
+            // do we know that property?
+            if (propDef == null) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
+            }
+
+            // can it be set?
+            if (propDef.getUpdatability() == Updatability.READONLY) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
+            }
+
+            if (propDef.getUpdatability() == Updatability.ONCREATE) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' can only be set on create!");
+            }
+
+            // default or value
+            PropertyData<?> newProp;
+            newProp = PropertyHelper.isPropertyEmpty(prop)
+                    ? PropertyHelper.getDefaultValue(propDef)
+                    : prop;
+
+            try {
+                if (newProp == null) {
+                    JcrConverter.removeProperty(node, prop);
+                }
+                else {
+                    JcrConverter.setProperty(node, newProp);
+                }
+            }
+            catch (RepositoryException e) {
+                log.debug(e.getMessage(), e);
+                throw new CmisStorageException(e.getMessage(), e);
+            }
+        }
+    }
+
+    /**
+     * Utility function for retrieving the version history of a JCR <code>Node</code>.
+     *
+     * @param node  the node for which to retrieve the version history
+     * @return  version history of <code>node</code>
+     * @throws RepositoryException  if <code>node</code> is not versionable 
+     */
+    protected static VersionHistory getVersionHistory(Node node) throws RepositoryException {
+        return getVersionManager(node).getVersionHistory(node.getPath());
+    }
+
+    /**
+     * Utility function for retrieving the version manager from a JCR <code>Node</code>.
+     * 
+     * @param node
+     * @return
+     * @throws RepositoryException
+     */
+    protected static VersionManager getVersionManager(Node node) throws RepositoryException {
+        return node.getSession().getWorkspace().getVersionManager();
+    }
+
+    /**
+     * Utility function for retrieving the base version of a JCR <code>Node</code>.
+     *
+     * @param node  the node for which to retrieve the base version
+     * @return  version base version of <code>node</code>
+     * @throws RepositoryException  if <code>node</code> is not versionable
+     */
+    protected static Version getBaseVersion(Node node) throws RepositoryException {
+        return getVersionManager(node).getBaseVersion(node.getPath());
+    }
+
+    /**
+     * Utility function to retrieve the length of a property of a JCR <code>Node</code>.
+     *
+     * @param node
+     * @param propertyName
+     * @return
+     * @throws RepositoryException
+     */
+    protected static long getPropertyLength(Node node, String propertyName) throws RepositoryException {
+        return node.hasProperty(propertyName)
+            ? node.getProperty(propertyName).getLength()
+            : -1;
+    }
+
+    /**
+     * Utility function for retrieving a string property from a JCR <code>Node</code> or a default 
+     * value in case of an error.
+     *
+     * @param node
+     * @param propertyName
+     * @param defaultValue
+     * @return
+     * @throws RepositoryException
+     */
+    protected static String getPropertyOrElse(Node node, String propertyName, String defaultValue)
+            throws RepositoryException {
+
+        return node.hasProperty(propertyName)
+            ? node.getProperty(propertyName).getString()
+            : defaultValue;
+    }
+
+    /**
+     * Utility function for retrieving a date property from a JCR <code>Node</code> or a default
+     * value in case of an error.
+     * 
+     * @param node
+     * @param propertyName
+     * @param defaultValue
+     * @return
+     * @throws RepositoryException
+     */
+    protected static GregorianCalendar getPropertyOrElse(Node node, String propertyName, GregorianCalendar defaultValue)
+            throws RepositoryException {
+
+        if (node.hasProperty(propertyName)) {
+            Calendar date = node.getProperty(propertyName).getDate();
+            return Util.toCalendar(date);
+        }
+        else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Add <code>action</code> to <code>actions</code> iff <code>condition</code> is true.
+     * 
+     * @param actions
+     * @param action
+     * @param condition
+     */
+    protected static void setAction(Set<Action> actions, Action action, boolean condition) {
+        if (condition) {
+            actions.add(action);
+        }
+        else {
+            actions.remove(action);
+        }
+    }
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNodeFactory.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNodeFactory.java?rev=1082139&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNodeFactory.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrNodeFactory.java Wed Mar 16 12:46:36 2011
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.jcr;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionManager;
+
+/**
+ * Factory for creating instances of sub-classes of {@link JcrNode} from JCR <code>Node</code>s.
+ */
+public class JcrNodeFactory {  
+    private static final Log log = LogFactory.getLog(JcrNodeFactory.class);
+
+    private TypeManager typeManager;
+    private PathManager pathManager;
+
+    /**
+     * Create a new {@link JcrNode} from a JCR <code>Node</code>.
+     * This implementation creates a {@link JcrUnversionedDocument} if <code>node</code> is
+     * of type nt:file but not of type mix:simpleVersionable, a {@link JcrVersion} if <code>node</code>
+     * is of type nt:file and of type mix:simpleVersionable and a {@link JcrFolder} otherwise.
+     *
+     * @param node
+     * @return
+     */
+    public JcrNode create(Node node){
+        try {
+            if (node.isNodeType(NodeType.NT_FILE)) {
+                if (node.isNodeType(NodeType.MIX_SIMPLE_VERSIONABLE)) {
+                    VersionManager versionManager = node.getSession().getWorkspace().getVersionManager();
+                    Version version = versionManager.getBaseVersion(node.getPath());
+                    return new JcrVersion(node, version, typeManager, pathManager, this);
+                }
+                else {
+                    return new JcrUnversionedDocument(node, typeManager, pathManager, this);
+                }
+            }
+            else {
+                return new JcrFolder(node, typeManager, pathManager, this);
+            }
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisRuntimeException(e.getMessage(), e);
+        }
+    }
+
+    //------------------------------------------< internal >---
+
+    protected final TypeManager getTypeManager() {
+        return typeManager;
+    }
+
+    protected final PathManager getPathManager() {
+        return pathManager;
+    }
+
+    void initialize(TypeManager typeManager, PathManager pathManager) {
+        this.typeManager = typeManager;
+        this.pathManager = pathManager;
+    }
+}

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrPrivateWorkingCopy.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrPrivateWorkingCopy.java?rev=1082139&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrPrivateWorkingCopy.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-jcr/src/main/java/org/apache/chemistry/opencmis/jcr/JcrPrivateWorkingCopy.java Wed Mar 16 12:46:36 2011
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.chemistry.opencmis.jcr;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+/**
+ * Instances of this class represent a private working copy of a cmis:document backed by an underlying
+ * JCR <code>Node</code>.
+ */
+public class JcrPrivateWorkingCopy extends JcrVersionBase {
+    private static final Log log = LogFactory.getLog(JcrPrivateWorkingCopy.class);
+
+    /**
+     * Name of a private working copy
+     */
+    public static String PWC_NAME = "pwc";
+
+    public JcrPrivateWorkingCopy(Node node, TypeManager typeManager, PathManager pathManager,
+            JcrNodeFactory nodeFactory) {
+        
+        super(node, typeManager, pathManager, nodeFactory);
+    }
+
+    /**
+     * @return <code>true</code> iff <code>versionName</code> is the name of private working copy.
+     * @see JcrPrivateWorkingCopy#PWC_NAME
+     */
+    public static boolean denotesPwc(String versionName) {
+        return PWC_NAME.equals(versionName);
+    }
+
+    /**
+     * @param objectId
+     * @return <code>true</code> iff <code>objectId</code> is the id of a private working copy.
+     * @see JcrPrivateWorkingCopy#PWC_NAME
+     */
+    public static boolean isPwc(String objectId) {
+        return objectId.endsWith('/' + PWC_NAME);   
+    }
+
+    //------------------------------------------< protected >---
+
+    @Override
+    protected Node getContextNode() {
+        try {
+            return getNode().getNode(Node.JCR_CONTENT);
+        }
+        catch (RepositoryException e) {
+            log.debug(e.getMessage(), e);
+            throw new CmisObjectNotFoundException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    protected String getPwcId() throws RepositoryException {
+        return getObjectId();
+    }
+
+    @Override
+    protected String getObjectId() throws RepositoryException {
+        return getVersionSeriesId() + '/' + PWC_NAME;
+    }
+
+    @Override
+    protected String getVersionLabel() throws RepositoryException {
+        return PWC_NAME;
+    }
+
+    @Override
+    protected boolean isLatestVersion() throws RepositoryException {
+        return false;
+    }
+
+    @Override
+    protected boolean isMajorVersion() throws RepositoryException {
+        return false;
+    }
+
+    @Override
+    protected boolean isLatestMajorVersion() throws RepositoryException {
+        return false; 
+    }
+
+    @Override
+    protected String getCheckInComment() {
+        return "";
+    }
+
+}



Mime
View raw message