You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2013/08/23 14:48:06 UTC

svn commit: r1516830 [2/3] - in /chemistry/opencmis/trunk: chemistry-opencmis-server/chemistry-opencmis-server-archetype/src/main/resources/archetype-resources/src/main/webapp/ chemistry-opencmis-server/chemistry-opencmis-server-bindings/src/main/java/...

Modified: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java?rev=1516830&r1=1516829&r2=1516830&view=diff
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java (original)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java Fri Aug 23 12:48:06 2013
@@ -39,7 +39,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TimeZone;
 
 import javax.xml.stream.XMLStreamReader;
 import javax.xml.stream.XMLStreamWriter;
@@ -48,6 +47,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.data.Ace;
 import org.apache.chemistry.opencmis.commons.data.Acl;
 import org.apache.chemistry.opencmis.commons.data.AllowableActions;
+import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken;
 import org.apache.chemistry.opencmis.commons.data.ContentStream;
 import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
 import org.apache.chemistry.opencmis.commons.data.ObjectData;
@@ -59,7 +59,6 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.data.Properties;
 import org.apache.chemistry.opencmis.commons.data.PropertyData;
 import org.apache.chemistry.opencmis.commons.data.PropertyDateTime;
-import org.apache.chemistry.opencmis.commons.data.PropertyId;
 import org.apache.chemistry.opencmis.commons.data.PropertyString;
 import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
 import org.apache.chemistry.opencmis.commons.definitions.PermissionDefinition;
@@ -74,6 +73,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.enums.CapabilityChanges;
 import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
 import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin;
+import org.apache.chemistry.opencmis.commons.enums.CapabilityOrderBy;
 import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery;
 import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions;
 import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
@@ -92,6 +92,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
 import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
 import org.apache.chemistry.opencmis.commons.impl.Base64;
+import org.apache.chemistry.opencmis.commons.impl.IOUtils;
 import org.apache.chemistry.opencmis.commons.impl.MimeTypes;
 import org.apache.chemistry.opencmis.commons.impl.XMLConstants;
 import org.apache.chemistry.opencmis.commons.impl.XMLConverter;
@@ -101,7 +102,9 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AclCapabilitiesDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AllowableActionsImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateObjectIdAndChangeTokenImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.CreatablePropertyTypesImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.NewTypeSettableAttributesImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
@@ -109,6 +112,7 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.PartialContentStreamImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionDefinitionDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PermissionMappingDataImpl;
 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
@@ -126,14 +130,17 @@ import org.apache.chemistry.opencmis.com
 import org.apache.chemistry.opencmis.commons.server.CallContext;
 import org.apache.chemistry.opencmis.commons.server.ObjectInfoHandler;
 import org.apache.chemistry.opencmis.commons.spi.Holder;
+import org.apache.chemistry.opencmis.server.impl.ServerVersion;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * File system back-end for CMIS server.
+ * Implements all repository operations.
  */
 public class FileShareRepository {
 
+    private static final Logger LOG = LoggerFactory.getLogger(FileShareRepository.class);
+
     private static final String ROOT_ID = "@root@";
     private static final String SHADOW_EXT = ".cmis.xml";
     private static final String SHADOW_FOLDER = "cmis.xml";
@@ -146,39 +153,30 @@ public class FileShareRepository {
 
     private static final int BUFFER_SIZE = 64 * 1024;
 
-    private static final Logger LOG = LoggerFactory.getLogger(FileShareRepository.class);
-
-    /** Repository id */
+    /** Repository id. */
     private final String repositoryId;
-    /** Root directory */
+    /** Root directory. */
     private final File root;
-    /** Types */
-    private final TypeManager types;
-    /** User table */
-    private final Map<String, Boolean> userMap;
-    /** Repository info */
-    private final RepositoryInfoImpl repositoryInfo;
+    /** Types. */
+    private final FileShareTypeManager typeManager;
+    /** Users. */
+    private final Map<String, Boolean> readWriteUserMap;
+
+    /** CMIS 1.0 repository info. */
+    private final RepositoryInfo repositoryInfo10;
+    /** CMIS 1.1 repository info. */
+    private final RepositoryInfo repositoryInfo11;
 
-    /**
-     * Constructor.
-     * 
-     * @param repId
-     *            CMIS repository id
-     * @param rootPath
-     *            root folder
-     * @param types
-     *            type manager object
-     */
-    public FileShareRepository(String repId, String rootPath, TypeManager types) {
+    public FileShareRepository(final String repositoryId, final String rootPath, final FileShareTypeManager typeManager) {
         // check repository id
-        if ((repId == null) || (repId.trim().length() == 0)) {
+        if (repositoryId == null || repositoryId.trim().length() == 0) {
             throw new IllegalArgumentException("Invalid repository id!");
         }
 
-        repositoryId = repId;
+        this.repositoryId = repositoryId;
 
         // check root folder
-        if ((rootPath == null) || (rootPath.trim().length() == 0)) {
+        if (rootPath == null || rootPath.trim().length() == 0) {
             throw new IllegalArgumentException("Invalid root folder!");
         }
 
@@ -187,28 +185,36 @@ public class FileShareRepository {
             throw new IllegalArgumentException("Root is not a directory!");
         }
 
-        // set types
-        this.types = types;
+        // set type manager objects
+        this.typeManager = typeManager;
+
+        // set up read-write user map
+        readWriteUserMap = new HashMap<String, Boolean>();
 
-        // set up user table
-        userMap = new HashMap<String, Boolean>();
+        // set up repository infos
+        repositoryInfo10 = createRepositoryInfo(CmisVersion.CMIS_1_0);
+        repositoryInfo11 = createRepositoryInfo(CmisVersion.CMIS_1_1);
+    }
+
+    private RepositoryInfo createRepositoryInfo(CmisVersion cmisVersion) {
+        assert cmisVersion != null;
 
-        // compile repository info
-        repositoryInfo = new RepositoryInfoImpl();
+        RepositoryInfoImpl repositoryInfo = new RepositoryInfoImpl();
 
         repositoryInfo.setId(repositoryId);
         repositoryInfo.setName(repositoryId);
         repositoryInfo.setDescription(repositoryId);
 
-        repositoryInfo.setCmisVersionSupported("1.0");
+        repositoryInfo.setCmisVersionSupported(cmisVersion.value());
 
         repositoryInfo.setProductName("OpenCMIS FileShare");
-        repositoryInfo.setProductVersion("0.1");
+        repositoryInfo.setProductVersion(ServerVersion.OPENCMIS_VERSION);
         repositoryInfo.setVendorName("OpenCMIS");
 
         repositoryInfo.setRootFolder(ROOT_ID);
 
         repositoryInfo.setThinClientUri("");
+        repositoryInfo.setChangesIncomplete(true);
 
         RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
         capabilities.setCapabilityAcl(CapabilityAcl.DISCOVER);
@@ -226,22 +232,29 @@ public class FileShareRepository {
         capabilities.setSupportsGetFolderTree(true);
         capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
 
-        NewTypeSettableAttributesImpl typeSetAttributes = new NewTypeSettableAttributesImpl();
-        typeSetAttributes.setCanSetControllableAcl(false);
-        typeSetAttributes.setCanSetControllablePolicy(false);
-        typeSetAttributes.setCanSetCreatable(false);
-        typeSetAttributes.setCanSetDescription(false);
-        typeSetAttributes.setCanSetDisplayName(false);
-        typeSetAttributes.setCanSetFileable(false);
-        typeSetAttributes.setCanSetFulltextIndexed(false);
-        typeSetAttributes.setCanSetId(false);
-        typeSetAttributes.setCanSetIncludedInSupertypeQuery(false);
-        typeSetAttributes.setCanSetLocalName(false);
-        typeSetAttributes.setCanSetLocalNamespace(false);
-        typeSetAttributes.setCanSetQueryable(false);
-        typeSetAttributes.setCanSetQueryName(false);
+        if (cmisVersion != CmisVersion.CMIS_1_0) {
+            capabilities.setOrderByCapability(CapabilityOrderBy.NONE);
+
+            NewTypeSettableAttributesImpl typeSetAttributes = new NewTypeSettableAttributesImpl();
+            typeSetAttributes.setCanSetControllableAcl(false);
+            typeSetAttributes.setCanSetControllablePolicy(false);
+            typeSetAttributes.setCanSetCreatable(false);
+            typeSetAttributes.setCanSetDescription(false);
+            typeSetAttributes.setCanSetDisplayName(false);
+            typeSetAttributes.setCanSetFileable(false);
+            typeSetAttributes.setCanSetFulltextIndexed(false);
+            typeSetAttributes.setCanSetId(false);
+            typeSetAttributes.setCanSetIncludedInSupertypeQuery(false);
+            typeSetAttributes.setCanSetLocalName(false);
+            typeSetAttributes.setCanSetLocalNamespace(false);
+            typeSetAttributes.setCanSetQueryable(false);
+            typeSetAttributes.setCanSetQueryName(false);
+
+            capabilities.setNewTypeSettableAttributes(typeSetAttributes);
 
-        capabilities.setNewTypeSettableAttributes(typeSetAttributes);
+            CreatablePropertyTypesImpl creatablePropertyTypes = new CreatablePropertyTypesImpl();
+            capabilities.setCreatablePropertyTypes(creatablePropertyTypes);
+        }
 
         repositoryInfo.setCapabilities(capabilities);
 
@@ -283,9 +296,11 @@ public class FileShareRepository {
         aclCapability.setPermissionMappingData(map);
 
         repositoryInfo.setAclCapabilities(aclCapability);
+
+        return repositoryInfo;
     }
 
-    private static PermissionDefinition createPermission(String permission, String description) {
+    private PermissionDefinition createPermission(String permission, String description) {
         PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
         pd.setId(permission);
         pd.setDescription(description);
@@ -293,7 +308,7 @@ public class FileShareRepository {
         return pd;
     }
 
-    private static PermissionMapping createMapping(String key, String permission) {
+    private PermissionMapping createMapping(String key, String permission) {
         PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
         pm.setKey(key);
         pm.setPermissions(Collections.singletonList(permission));
@@ -302,65 +317,88 @@ public class FileShareRepository {
     }
 
     /**
-     * Adds a user to the repository.
+     * Returns the id of this repository.
+     */
+    public String getRepositoryId() {
+        return repositoryId;
+    }
+
+    /**
+     * Returns the root directory of this repository
+     */
+    public File getRootDirectory() {
+        return root;
+    }
+
+    /**
+     * Sets read-only flag for the given user.
      */
-    public void addUser(String user, boolean readOnly) {
-        if ((user == null) || (user.length() == 0)) {
+    public void setUserReadOnly(String user) {
+        if (user == null || user.length() == 0) {
             return;
         }
 
-        userMap.put(user, readOnly);
+        readWriteUserMap.put(user, true);
     }
 
-    // --- the public stuff ---
-
     /**
-     * Returns the repository id.
+     * Sets read-write flag for the given user.
      */
-    public String getRepositoryId() {
-        return repositoryId;
+    public void setUserReadWrite(String user) {
+        if (user == null || user.length() == 0) {
+            return;
+        }
+
+        readWriteUserMap.put(user, false);
     }
 
+    // --- CMIS operations ---
+
     /**
      * CMIS getRepositoryInfo.
      */
     public RepositoryInfo getRepositoryInfo(CallContext context) {
         debug("getRepositoryInfo");
+
         checkUser(context, false);
 
-        return repositoryInfo;
+        if (context.getCmisVersion() == CmisVersion.CMIS_1_0) {
+            return repositoryInfo10;
+        } else {
+            return repositoryInfo11;
+        }
     }
 
     /**
      * CMIS getTypesChildren.
      */
-    public TypeDefinitionList getTypesChildren(CallContext context, String typeId, boolean includePropertyDefinitions,
+    public TypeDefinitionList getTypeChildren(CallContext context, String typeId, Boolean includePropertyDefinitions,
             BigInteger maxItems, BigInteger skipCount) {
         debug("getTypesChildren");
         checkUser(context, false);
 
-        return types.getTypesChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
+        return typeManager.getTypeChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
     }
 
     /**
-     * CMIS getTypeDefinition.
+     * CMIS getTypesDescendants.
      */
-    public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
-        debug("getTypeDefinition");
+    public List<TypeDefinitionContainer> getTypeDescendants(CallContext context, String typeId, BigInteger depth,
+            Boolean includePropertyDefinitions) {
+        debug("getTypesDescendants");
         checkUser(context, false);
 
-        return types.getTypeDefinition(context, typeId);
+        return typeManager.getTypeDescendants(context, typeId, depth, includePropertyDefinitions);
     }
 
     /**
-     * CMIS getTypesDescendants.
+     * CMIS getTypeDefinition.
      */
-    public List<TypeDefinitionContainer> getTypeDescendants(CallContext context, String typeId, BigInteger depth,
-            Boolean includePropertyDefinitions) {
-        debug("getTypesDescendants");
+    public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
+        debug("getTypeDefinition");
         checkUser(context, false);
 
-        return types.getTypeDescendants(context, typeId, depth, includePropertyDefinitions);
+        return typeManager.getTypeDefinition(context, typeId);
     }
 
     /**
@@ -371,8 +409,8 @@ public class FileShareRepository {
         debug("create");
         boolean userReadOnly = checkUser(context, true);
 
-        String typeId = getTypeId(properties);
-        TypeDefinition type = types.getType(typeId);
+        String typeId = FileShareUtils.getObjectTypeId(properties);
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
         if (type == null) {
             throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
         }
@@ -386,7 +424,7 @@ public class FileShareRepository {
             throw new CmisObjectNotFoundException("Cannot create object of type '" + typeId + "'!");
         }
 
-        return compileObjectType(context, getFile(objectId), null, false, false, userReadOnly, objectInfos);
+        return compileObjectData(context, getFile(objectId), null, false, false, userReadOnly, objectInfos);
     }
 
     /**
@@ -398,7 +436,7 @@ public class FileShareRepository {
         checkUser(context, true);
 
         // check properties
-        if ((properties == null) || (properties.getProperties() == null)) {
+        if (properties == null || properties.getProperties() == null) {
             throw new CmisInvalidArgumentException("Properties must be set!");
         }
 
@@ -408,18 +446,17 @@ public class FileShareRepository {
         }
 
         // check type
-        String typeId = getTypeId(properties);
-        TypeDefinition type = types.getType(typeId);
+        String typeId = FileShareUtils.getObjectTypeId(properties);
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
         if (type == null) {
             throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
         }
 
         // compile the properties
-        Properties props = compileProperties(typeId, context.getUsername(),
-                millisToCalendar(System.currentTimeMillis()), context.getUsername(), properties);
+        PropertiesImpl props = compileWriteProperties(typeId, context.getUsername(), context.getUsername(), properties);
 
         // check the name
-        String name = getStringProperty(properties, PropertyIds.NAME);
+        String name = FileShareUtils.getStringProperty(properties, PropertyIds.NAME);
         if (!isValidName(name)) {
             throw new CmisNameConstraintViolationException("Name is not valid!");
         }
@@ -444,25 +481,14 @@ public class FileShareRepository {
         }
 
         // write content, if available
-        if ((contentStream != null) && (contentStream.getStream() != null)) {
-            try {
-                OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile), BUFFER_SIZE);
-                InputStream in = new BufferedInputStream(contentStream.getStream(), BUFFER_SIZE);
-
-                byte[] buffer = new byte[BUFFER_SIZE];
-                int b;
-                while ((b = in.read(buffer)) > -1) {
-                    out.write(buffer, 0, b);
-                }
-
-                out.flush();
-                out.close();
-                in.close();
-            } catch (Exception e) {
-                throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
-            }
+        if (contentStream != null && contentStream.getStream() != null) {
+            writeContent(newFile, contentStream.getStream());
         }
 
+        // set creation date
+        addPropertyDateTime(props, typeId, null, PropertyIds.CREATION_DATE,
+                FileShareUtils.millisToCalendar(newFile.lastModified()));
+
         // write properties
         writePropertiesFile(newFile, props);
 
@@ -500,17 +526,17 @@ public class FileShareRepository {
         readCustomProperties(source, sourceProperties, null, new ObjectInfoImpl());
 
         // get the type id
-        String typeId = getIdProperty(sourceProperties, PropertyIds.OBJECT_TYPE_ID);
+        String typeId = FileShareUtils.getIdProperty(sourceProperties, PropertyIds.OBJECT_TYPE_ID);
         if (typeId == null) {
-            typeId = TypeManager.DOCUMENT_TYPE_ID;
+            typeId = BaseTypeId.CMIS_DOCUMENT.value();
         }
 
         // copy properties
         PropertiesImpl newProperties = new PropertiesImpl();
         for (PropertyData<?> prop : sourceProperties.getProperties().values()) {
-            if ((prop.getId().equals(PropertyIds.OBJECT_TYPE_ID)) || (prop.getId().equals(PropertyIds.CREATED_BY))
-                    || (prop.getId().equals(PropertyIds.CREATION_DATE))
-                    || (prop.getId().equals(PropertyIds.LAST_MODIFIED_BY))) {
+            if (prop.getId().equals(PropertyIds.OBJECT_TYPE_ID) || prop.getId().equals(PropertyIds.CREATED_BY)
+                    || prop.getId().equals(PropertyIds.CREATION_DATE)
+                    || prop.getId().equals(PropertyIds.LAST_MODIFIED_BY)) {
                 continue;
             }
 
@@ -520,7 +546,7 @@ public class FileShareRepository {
         // replace properties
         if (properties != null) {
             // find new name
-            String newName = getStringProperty(properties, PropertyIds.NAME);
+            String newName = FileShareUtils.getStringProperty(properties, PropertyIds.NAME);
             if (newName != null) {
                 if (!isValidName(newName)) {
                     throw new CmisNameConstraintViolationException("Name is not valid!");
@@ -529,7 +555,7 @@ public class FileShareRepository {
             }
 
             // get the property definitions
-            TypeDefinition type = types.getType(typeId);
+            TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
             if (type == null) {
                 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
             }
@@ -560,7 +586,7 @@ public class FileShareRepository {
         addPropertyId(newProperties, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
         addPropertyString(newProperties, typeId, null, PropertyIds.CREATED_BY, context.getUsername());
         addPropertyDateTime(newProperties, typeId, null, PropertyIds.CREATION_DATE,
-                millisToCalendar(System.currentTimeMillis()));
+                FileShareUtils.millisToCalendar(System.currentTimeMillis()));
         addPropertyString(newProperties, typeId, null, PropertyIds.LAST_MODIFIED_BY, context.getUsername());
 
         // check the file
@@ -578,8 +604,26 @@ public class FileShareRepository {
 
         // copy content
         try {
-            OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
-            InputStream in = new BufferedInputStream(new FileInputStream(source));
+            writeContent(newFile, new FileInputStream(source));
+        } catch (IOException e) {
+            throw new CmisStorageException("Could not roead or write content: " + e.getMessage(), e);
+        }
+
+        // write properties
+        writePropertiesFile(newFile, newProperties);
+
+        return getId(newFile);
+    }
+
+    /**
+     * Writes the content to disc.
+     */
+    private void writeContent(File newFile, InputStream stream) {
+        OutputStream out = null;
+        InputStream in = null;
+        try {
+            out = new BufferedOutputStream(new FileOutputStream(newFile), BUFFER_SIZE);
+            in = new BufferedInputStream(stream, BUFFER_SIZE);
 
             byte[] buffer = new byte[BUFFER_SIZE];
             int b;
@@ -588,16 +632,12 @@ public class FileShareRepository {
             }
 
             out.flush();
-            out.close();
-            in.close();
-        } catch (Exception e) {
-            throw new CmisStorageException("Could not roead or write content: " + e.getMessage(), e);
+        } catch (IOException e) {
+            throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(in);
         }
-
-        // write properties
-        writePropertiesFile(newFile, newProperties);
-
-        return getId(newFile);
     }
 
     /**
@@ -608,23 +648,22 @@ public class FileShareRepository {
         checkUser(context, true);
 
         // check properties
-        if ((properties == null) || (properties.getProperties() == null)) {
+        if (properties == null || properties.getProperties() == null) {
             throw new CmisInvalidArgumentException("Properties must be set!");
         }
 
         // check type
-        String typeId = getTypeId(properties);
-        TypeDefinition type = types.getType(typeId);
+        String typeId = FileShareUtils.getObjectTypeId(properties);
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
         if (type == null) {
             throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
         }
 
         // compile the properties
-        Properties props = compileProperties(typeId, context.getUsername(),
-                millisToCalendar(System.currentTimeMillis()), context.getUsername(), properties);
+        PropertiesImpl props = compileWriteProperties(typeId, context.getUsername(), context.getUsername(), properties);
 
         // check the name
-        String name = getStringProperty(properties, PropertyIds.NAME);
+        String name = FileShareUtils.getStringProperty(properties, PropertyIds.NAME);
         if (!isValidName(name)) {
             throw new CmisNameConstraintViolationException("Name is not valid.");
         }
@@ -641,6 +680,10 @@ public class FileShareRepository {
             throw new CmisStorageException("Could not create folder!");
         }
 
+        // set creation date
+        addPropertyDateTime(props, typeId, null, PropertyIds.CREATION_DATE,
+                FileShareUtils.millisToCalendar(newFolder.lastModified()));
+
         // write properties
         writePropertiesFile(newFolder, props);
 
@@ -686,15 +729,15 @@ public class FileShareRepository {
             }
         }
 
-        return compileObjectType(context, newFile, null, false, false, userReadOnly, objectInfos);
+        return compileObjectData(context, newFile, null, false, false, userReadOnly, objectInfos);
     }
 
     /**
-     * CMIS setContentStream and deleteContentStream.
+     * CMIS setContentStream, deleteContentStream, and appendContentStream.
      */
-    public void setContentStream(CallContext context, Holder<String> objectId, Boolean overwriteFlag,
-            ContentStream contentStream) {
-        debug("setContentStream or deleteContentStream");
+    public void changeContentStream(CallContext context, Holder<String> objectId, Boolean overwriteFlag,
+            ContentStream contentStream, boolean append) {
+        debug("setContentStream or deleteContentStream or appendContentStream");
         checkUser(context, true);
 
         if (objectId == null) {
@@ -708,33 +751,34 @@ public class FileShareRepository {
         }
 
         // check overwrite
-        boolean owf = (overwriteFlag == null ? true : overwriteFlag.booleanValue());
+        boolean owf = FileShareUtils.getBooleanParameter(overwriteFlag, true);
         if (!owf && file.length() > 0) {
             throw new CmisContentAlreadyExistsException("Content already exists!");
         }
 
+        OutputStream out = null;
+        InputStream in = null;
         try {
-            OutputStream out = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
+            out = new BufferedOutputStream(new FileOutputStream(file, append), BUFFER_SIZE);
 
-            if ((contentStream == null) || (contentStream.getStream() == null)) {
+            if (contentStream == null || contentStream.getStream() == null) {
                 // delete content
                 out.write(new byte[0]);
             } else {
                 // set content
-                InputStream in = new BufferedInputStream(contentStream.getStream(), BUFFER_SIZE);
+                in = new BufferedInputStream(contentStream.getStream(), BUFFER_SIZE);
 
                 byte[] buffer = new byte[BUFFER_SIZE];
                 int b;
                 while ((b = in.read(buffer)) > -1) {
                     out.write(buffer, 0, b);
                 }
-
-                in.close();
             }
-
-            out.close();
         } catch (Exception e) {
             throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
+        } finally {
+            IOUtils.closeQuietly(out);
+            IOUtils.closeQuietly(in);
         }
     }
 
@@ -792,6 +836,39 @@ public class FileShareRepository {
     }
 
     /**
+     * Removes a folder and its content.
+     */
+    private boolean deleteFolder(File folder, boolean continueOnFailure, FailedToDeleteDataImpl ftd) {
+        boolean success = true;
+
+        for (File file : folder.listFiles()) {
+            if (file.isDirectory()) {
+                if (!deleteFolder(file, continueOnFailure, ftd)) {
+                    if (!continueOnFailure) {
+                        return false;
+                    }
+                    success = false;
+                }
+            } else {
+                if (!file.delete()) {
+                    ftd.getIds().add(getId(file));
+                    if (!continueOnFailure) {
+                        return false;
+                    }
+                    success = false;
+                }
+            }
+        }
+
+        if (!folder.delete()) {
+            ftd.getIds().add(getId(folder));
+            success = false;
+        }
+
+        return success;
+    }
+
+    /**
      * CMIS updateProperties.
      */
     public ObjectData updateProperties(CallContext context, Holder<String> objectId, Properties properties,
@@ -807,7 +884,7 @@ public class FileShareRepository {
         File file = getFile(objectId.getValue());
 
         // get and check the new name
-        String newName = getStringProperty(properties, PropertyIds.NAME);
+        String newName = FileShareUtils.getStringProperty(properties, PropertyIds.NAME);
         boolean isRename = (newName != null) && (!file.getName().equals(newName));
         if (isRename && !isValidName(newName)) {
             throw new CmisNameConstraintViolationException("Name is not valid!");
@@ -818,21 +895,21 @@ public class FileShareRepository {
         readCustomProperties(file, oldProperties, null, new ObjectInfoImpl());
 
         // get the type id
-        String typeId = getIdProperty(oldProperties, PropertyIds.OBJECT_TYPE_ID);
+        String typeId = FileShareUtils.getIdProperty(oldProperties, PropertyIds.OBJECT_TYPE_ID);
         if (typeId == null) {
-            typeId = (file.isDirectory() ? TypeManager.FOLDER_TYPE_ID : TypeManager.DOCUMENT_TYPE_ID);
+            typeId = (file.isDirectory() ? BaseTypeId.CMIS_FOLDER.value() : BaseTypeId.CMIS_DOCUMENT.value());
         }
 
         // get the creator
-        String creator = getStringProperty(oldProperties, PropertyIds.CREATED_BY);
+        String creator = FileShareUtils.getStringProperty(oldProperties, PropertyIds.CREATED_BY);
         if (creator == null) {
             creator = context.getUsername();
         }
 
         // get creation date
-        GregorianCalendar creationDate = getDateTimeProperty(oldProperties, PropertyIds.CREATION_DATE);
+        GregorianCalendar creationDate = FileShareUtils.getDateTimeProperty(oldProperties, PropertyIds.CREATION_DATE);
         if (creationDate == null) {
-            creationDate = millisToCalendar(file.lastModified());
+            creationDate = FileShareUtils.millisToCalendar(file.lastModified());
         }
 
         // compile the properties
@@ -865,7 +942,108 @@ public class FileShareRepository {
             }
         }
 
-        return compileObjectType(context, newFile, null, false, false, userReadOnly, objectInfos);
+        return compileObjectData(context, newFile, null, false, false, userReadOnly, objectInfos);
+    }
+
+    /**
+     * Checks and updates a property set that can be written to disc.
+     */
+    private Properties updateProperties(String typeId, String creator, GregorianCalendar creationDate, String modifier,
+            Properties oldProperties, Properties properties) {
+        PropertiesImpl result = new PropertiesImpl();
+
+        if (properties == null) {
+            throw new CmisConstraintException("No properties!");
+        }
+
+        // get the property definitions
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
+        if (type == null) {
+            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+        }
+
+        // copy old properties
+        for (PropertyData<?> prop : oldProperties.getProperties().values()) {
+            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
+
+            // do we know that property?
+            if (propType == null) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
+            }
+
+            // only add read/write properties
+            if (propType.getUpdatability() != Updatability.READWRITE) {
+                continue;
+            }
+
+            result.addProperty(prop);
+        }
+
+        // update properties
+        for (PropertyData<?> prop : properties.getProperties().values()) {
+            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
+
+            // do we know that property?
+            if (propType == null) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
+            }
+
+            // can it be set?
+            if (propType.getUpdatability() == Updatability.READONLY) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
+            }
+
+            if (propType.getUpdatability() == Updatability.ONCREATE) {
+                throw new CmisConstraintException("Property '" + prop.getId() + "' can only be set on create!");
+            }
+
+            // default or value
+            if (isEmptyProperty(prop)) {
+                addPropertyDefault(result, propType);
+            } else {
+                result.addProperty(prop);
+            }
+        }
+
+        addPropertyId(result, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
+        addPropertyString(result, typeId, null, PropertyIds.CREATED_BY, creator);
+        addPropertyDateTime(result, typeId, null, PropertyIds.CREATION_DATE, creationDate);
+        addPropertyString(result, typeId, null, PropertyIds.LAST_MODIFIED_BY, modifier);
+
+        return result;
+    }
+
+    /**
+     * CMIS bulkUpdateProperties.
+     */
+    public List<BulkUpdateObjectIdAndChangeToken> bulkUpdateProperties(CallContext context,
+            List<BulkUpdateObjectIdAndChangeToken> objectIdAndChangeToken, Properties properties,
+            ObjectInfoHandler objectInfos) {
+        debug("bulkUpdateProperties");
+        checkUser(context, true);
+
+        if (objectIdAndChangeToken == null) {
+            throw new CmisInvalidArgumentException("No object ids provided!");
+        }
+
+        List<BulkUpdateObjectIdAndChangeToken> result = new ArrayList<BulkUpdateObjectIdAndChangeToken>();
+
+        for (BulkUpdateObjectIdAndChangeToken oid : objectIdAndChangeToken) {
+            if (oid == null) {
+                // ignore invalid ids
+                continue;
+            }
+            try {
+                Holder<String> oidHolder = new Holder<String>(oid.getId());
+                updateProperties(context, oidHolder, properties, objectInfos);
+
+                result.add(new BulkUpdateObjectIdAndChangeTokenImpl(oid.getId(), oidHolder.getValue(), null));
+            } catch (CmisBaseException e) {
+                // ignore exceptions - see specification
+            }
+        }
+
+        return result;
     }
 
     /**
@@ -877,7 +1055,7 @@ public class FileShareRepository {
         boolean userReadOnly = checkUser(context, false);
 
         // check id
-        if ((objectId == null) && (versionServicesId == null)) {
+        if (objectId == null && versionServicesId == null) {
             throw new CmisInvalidArgumentException("Object Id must be set.");
         }
 
@@ -891,14 +1069,14 @@ public class FileShareRepository {
         File file = getFile(objectId);
 
         // set defaults if values not set
-        boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-        boolean iacl = (includeAcl == null ? false : includeAcl.booleanValue());
+        boolean iaa = FileShareUtils.getBooleanParameter(includeAllowableActions, false);
+        boolean iacl = FileShareUtils.getBooleanParameter(includeAcl, false);
 
         // split filter
-        Set<String> filterCollection = splitFilter(filter);
+        Set<String> filterCollection = FileShareUtils.splitFilter(filter);
 
         // gather properties
-        return compileObjectType(context, file, filterCollection, iaa, iacl, userReadOnly, objectInfos);
+        return compileObjectData(context, file, filterCollection, iaa, iacl, userReadOnly, objectInfos);
     }
 
     /**
@@ -939,10 +1117,6 @@ public class FileShareRepository {
         debug("getContentStream");
         checkUser(context, false);
 
-        if ((offset != null) || (length != null)) {
-            throw new CmisInvalidArgumentException("Offset and Length are not supported!");
-        }
-
         // get the file
         final File file = getFile(objectId);
         if (!file.isFile()) {
@@ -956,12 +1130,21 @@ public class FileShareRepository {
         InputStream stream = null;
         try {
             stream = new BufferedInputStream(new FileInputStream(file), 4 * 1024);
+            if (offset != null || length != null) {
+                stream = new ContentRangeInputStream(stream, offset, length);
+            }
         } catch (FileNotFoundException e) {
             throw new CmisObjectNotFoundException(e.getMessage(), e);
         }
 
         // compile data
-        ContentStreamImpl result = new ContentStreamImpl();
+        ContentStreamImpl result;
+        if ((offset != null && offset.longValue() > 0) || length != null) {
+            result = new PartialContentStreamImpl();
+        } else {
+            result = new ContentStreamImpl();
+        }
+
         result.setFileName(file.getName());
         result.setLength(BigInteger.valueOf(file.length()));
         result.setMimeType(MimeTypes.getMIMEType(file));
@@ -980,11 +1163,11 @@ public class FileShareRepository {
         boolean userReadOnly = checkUser(context, false);
 
         // split filter
-        Set<String> filterCollection = splitFilter(filter);
+        Set<String> filterCollection = FileShareUtils.splitFilter(filter);
 
         // set defaults if values not set
-        boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-        boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
+        boolean iaa = FileShareUtils.getBooleanParameter(includeAllowableActions, false);
+        boolean ips = FileShareUtils.getBooleanParameter(includePathSegment, false);
 
         // skip and max
         int skip = (skipCount == null ? 0 : skipCount.intValue());
@@ -1005,7 +1188,7 @@ public class FileShareRepository {
 
         // set object info of the the folder
         if (context.isObjectInfoRequired()) {
-            compileObjectType(context, folder, null, false, false, userReadOnly, objectInfos);
+            compileObjectData(context, folder, null, false, false, userReadOnly, objectInfos);
         }
 
         // prepare result
@@ -1035,7 +1218,7 @@ public class FileShareRepository {
 
             // build and add child object
             ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
-            objectInFolder.setObject(compileObjectType(context, child, filterCollection, iaa, false, userReadOnly,
+            objectInFolder.setObject(compileObjectData(context, child, filterCollection, iaa, false, userReadOnly,
                     objectInfos));
             if (ips) {
                 objectInFolder.setPathSegment(child.getName());
@@ -1068,11 +1251,11 @@ public class FileShareRepository {
         }
 
         // split filter
-        Set<String> filterCollection = splitFilter(filter);
+        Set<String> filterCollection = FileShareUtils.splitFilter(filter);
 
         // set defaults if values not set
-        boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-        boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
+        boolean iaa = FileShareUtils.getBooleanParameter(includeAllowableActions, false);
+        boolean ips = FileShareUtils.getBooleanParameter(includePathSegment, false);
 
         // get the folder
         File folder = getFile(folderId);
@@ -1082,7 +1265,7 @@ public class FileShareRepository {
 
         // set object info of the the folder
         if (context.isObjectInfoRequired()) {
-            compileObjectType(context, folder, null, false, false, userReadOnly, objectInfos);
+            compileObjectData(context, folder, null, false, false, userReadOnly, objectInfos);
         }
 
         // get the tree
@@ -1094,6 +1277,49 @@ public class FileShareRepository {
     }
 
     /**
+     * Gather the children of a folder.
+     */
+    private void gatherDescendants(CallContext context, File folder, List<ObjectInFolderContainer> list,
+            boolean foldersOnly, int depth, Set<String> filter, boolean includeAllowableActions,
+            boolean includePathSegments, boolean userReadOnly, ObjectInfoHandler objectInfos) {
+        assert folder != null;
+        assert list != null;
+
+        // iterate through children
+        for (File child : folder.listFiles()) {
+            // skip hidden and shadow files
+            if (child.isHidden() || child.getName().equals(SHADOW_FOLDER) || child.getPath().endsWith(SHADOW_EXT)) {
+                continue;
+            }
+
+            // folders only?
+            if (foldersOnly && !child.isDirectory()) {
+                continue;
+            }
+
+            // add to list
+            ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
+            objectInFolder.setObject(compileObjectData(context, child, filter, includeAllowableActions, false,
+                    userReadOnly, objectInfos));
+            if (includePathSegments) {
+                objectInFolder.setPathSegment(child.getName());
+            }
+
+            ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
+            container.setObject(objectInFolder);
+
+            list.add(container);
+
+            // move to next level
+            if (depth != 1 && child.isDirectory()) {
+                container.setChildren(new ArrayList<ObjectInFolderContainer>());
+                gatherDescendants(context, child, container.getChildren(), foldersOnly, depth - 1, filter,
+                        includeAllowableActions, includePathSegments, userReadOnly, objectInfos);
+            }
+        }
+    }
+
+    /**
      * CMIS getFolderParent.
      */
     public ObjectData getFolderParent(CallContext context, String folderId, String filter, ObjectInfoHandler objectInfos) {
@@ -1115,11 +1341,11 @@ public class FileShareRepository {
         boolean userReadOnly = checkUser(context, false);
 
         // split filter
-        Set<String> filterCollection = splitFilter(filter);
+        Set<String> filterCollection = FileShareUtils.splitFilter(filter);
 
         // set defaults if values not set
-        boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-        boolean irps = (includeRelativePathSegment == null ? false : includeRelativePathSegment.booleanValue());
+        boolean iaa = FileShareUtils.getBooleanParameter(includeAllowableActions, false);
+        boolean irps = FileShareUtils.getBooleanParameter(includeRelativePathSegment, false);
 
         // get the file or folder
         File file = getFile(objectId);
@@ -1131,12 +1357,12 @@ public class FileShareRepository {
 
         // set object info of the the object
         if (context.isObjectInfoRequired()) {
-            compileObjectType(context, file, null, false, false, userReadOnly, objectInfos);
+            compileObjectData(context, file, null, false, false, userReadOnly, objectInfos);
         }
 
         // get parent folder
         File parent = file.getParentFile();
-        ObjectData object = compileObjectType(context, parent, filterCollection, iaa, false, userReadOnly, objectInfos);
+        ObjectData object = compileObjectData(context, parent, filterCollection, iaa, false, userReadOnly, objectInfos);
 
         ObjectParentDataImpl result = new ObjectParentDataImpl();
         result.setObject(object);
@@ -1144,7 +1370,7 @@ public class FileShareRepository {
             result.setRelativePathSegment(file.getName());
         }
 
-        return Collections.singletonList((ObjectParentData) result);
+        return Collections.<ObjectParentData> singletonList(result);
     }
 
     /**
@@ -1156,10 +1382,10 @@ public class FileShareRepository {
         boolean userReadOnly = checkUser(context, false);
 
         // split filter
-        Set<String> filterCollection = splitFilter(filter);
+        Set<String> filterCollection = FileShareUtils.splitFilter(filter);
 
         // check path
-        if ((folderPath == null) || (!folderPath.startsWith("/"))) {
+        if (folderPath == null || folderPath.length() == 0 || folderPath.charAt(0) != '/') {
             throw new CmisInvalidArgumentException("Invalid folder path!");
         }
 
@@ -1176,141 +1402,21 @@ public class FileShareRepository {
             throw new CmisObjectNotFoundException("Path doesn't exist.");
         }
 
-        return compileObjectType(context, file, filterCollection, includeAllowableActions, includeACL, userReadOnly,
+        return compileObjectData(context, file, filterCollection, includeAllowableActions, includeACL, userReadOnly,
                 objectInfos);
     }
 
-    // --- helper methods ---
+    // --- helpers ---
 
     /**
-     * Gather the children of a folder.
+     * Compiles an object type object from a file or folder.
      */
-    private void gatherDescendants(CallContext context, File folder, List<ObjectInFolderContainer> list,
-            boolean foldersOnly, int depth, Set<String> filter, boolean includeAllowableActions,
-            boolean includePathSegments, boolean userReadOnly, ObjectInfoHandler objectInfos) {
-        // iterate through children
-        for (File child : folder.listFiles()) {
-            // skip hidden and shadow files
-            if (child.isHidden() || child.getName().equals(SHADOW_FOLDER) || child.getPath().endsWith(SHADOW_EXT)) {
-                continue;
-            }
-
-            // folders only?
-            if (foldersOnly && !child.isDirectory()) {
-                continue;
-            }
-
-            // add to list
-            ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
-            objectInFolder.setObject(compileObjectType(context, child, filter, includeAllowableActions, false,
-                    userReadOnly, objectInfos));
-            if (includePathSegments) {
-                objectInFolder.setPathSegment(child.getName());
-            }
-
-            ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
-            container.setObject(objectInFolder);
-
-            list.add(container);
-
-            // move to next level
-            if ((depth != 1) && child.isDirectory()) {
-                container.setChildren(new ArrayList<ObjectInFolderContainer>());
-                gatherDescendants(context, child, container.getChildren(), foldersOnly, depth - 1, filter,
-                        includeAllowableActions, includePathSegments, userReadOnly, objectInfos);
-            }
-        }
-    }
-
-    /**
-     * Removes a folder and its content.
-     * 
-     * @throws
-     */
-    private boolean deleteFolder(File folder, boolean continueOnFailure, FailedToDeleteDataImpl ftd) {
-        boolean success = true;
-
-        for (File file : folder.listFiles()) {
-            if (file.isDirectory()) {
-                if (!deleteFolder(file, continueOnFailure, ftd)) {
-                    if (!continueOnFailure) {
-                        return false;
-                    }
-                    success = false;
-                }
-            } else {
-                if (!file.delete()) {
-                    ftd.getIds().add(getId(file));
-                    if (!continueOnFailure) {
-                        return false;
-                    }
-                    success = false;
-                }
-            }
-        }
-
-        if (!folder.delete()) {
-            ftd.getIds().add(getId(folder));
-            success = false;
-        }
-
-        return success;
-    }
-
-    /**
-     * Checks if the given name is valid for a file system.
-     * 
-     * @param name
-     *            the name to check
-     * 
-     * @return <code>true</code> if the name is valid, <code>false</code>
-     *         otherwise
-     */
-    private static boolean isValidName(String name) {
-        if ((name == null) || (name.length() == 0) || (name.indexOf(File.separatorChar) != -1)
-                || (name.indexOf(File.pathSeparatorChar) != -1)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Checks if a folder is empty. A folder is considered as empty if no files
-     * or only the shadow file reside in the folder.
-     * 
-     * @param folder
-     *            the folder
-     * 
-     * @return <code>true</code> if the folder is empty.
-     */
-    private static boolean isFolderEmpty(File folder) {
-        if (!folder.isDirectory()) {
-            return true;
-        }
-
-        String[] fileNames = folder.list();
-
-        if ((fileNames == null) || (fileNames.length == 0)) {
-            return true;
-        }
-
-        if ((fileNames.length == 1) && (fileNames[0].equals(SHADOW_FOLDER))) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
-     * Compiles an object type object from a file or folder.
-     */
-    private ObjectData compileObjectType(CallContext context, File file, Set<String> filter,
+    private ObjectData compileObjectData(CallContext context, File file, Set<String> filter,
             boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly, ObjectInfoHandler objectInfos) {
         ObjectDataImpl result = new ObjectDataImpl();
         ObjectInfoImpl objectInfo = new ObjectInfoImpl();
 
-        result.setProperties(compileProperties(file, filter, objectInfo));
+        result.setProperties(compileProperties(context, file, filter, objectInfo));
 
         if (includeAllowableActions) {
             result.setAllowableActions(compileAllowableActions(file, userReadOnly));
@@ -1332,12 +1438,13 @@ public class FileShareRepository {
     /**
      * Gathers all base properties of a file or folder.
      */
-    private Properties compileProperties(File file, Set<String> orgfilter, ObjectInfoImpl objectInfo) {
+    private Properties compileProperties(CallContext context, File file, Set<String> orgfilter,
+            ObjectInfoImpl objectInfo) {
         if (file == null) {
             throw new IllegalArgumentException("File must not be null!");
         }
 
-        // we can gather properties if the file or folder doesn't exist
+        // we can't gather properties if the file or folder doesn't exist
         if (!file.exists()) {
             throw new CmisObjectNotFoundException("Object not found!");
         }
@@ -1349,7 +1456,7 @@ public class FileShareRepository {
         String typeId = null;
 
         if (file.isDirectory()) {
-            typeId = TypeManager.FOLDER_TYPE_ID;
+            typeId = BaseTypeId.CMIS_FOLDER.value();
             objectInfo.setBaseType(BaseTypeId.CMIS_FOLDER);
             objectInfo.setTypeId(typeId);
             objectInfo.setContentType(null);
@@ -1368,7 +1475,7 @@ public class FileShareRepository {
             objectInfo.setWorkingCopyId(null);
             objectInfo.setWorkingCopyOriginalId(null);
         } else {
-            typeId = TypeManager.DOCUMENT_TYPE_ID;
+            typeId = BaseTypeId.CMIS_DOCUMENT.value();
             objectInfo.setBaseType(BaseTypeId.CMIS_DOCUMENT);
             objectInfo.setTypeId(typeId);
             objectInfo.setHasAcl(true);
@@ -1407,7 +1514,7 @@ public class FileShareRepository {
             objectInfo.setCreatedBy(USER_UNKNOWN);
 
             // creation and modification date
-            GregorianCalendar lastModified = millisToCalendar(file.lastModified());
+            GregorianCalendar lastModified = FileShareUtils.millisToCalendar(file.lastModified());
             addPropertyDateTime(result, typeId, filter, PropertyIds.CREATION_DATE, lastModified);
             addPropertyDateTime(result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE, lastModified);
             objectInfo.setCreationDate(lastModified);
@@ -1417,14 +1524,16 @@ public class FileShareRepository {
             addPropertyString(result, typeId, filter, PropertyIds.CHANGE_TOKEN, null);
 
             // CMIS 1.1 properties
-            addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, null);
-            addPropertyIdList(result, typeId, filter, PropertyIds.SECONDARY_OBJECT_TYPE_IDS, null);
+            if (context.getCmisVersion() != CmisVersion.CMIS_1_0) {
+                addPropertyString(result, typeId, filter, PropertyIds.DESCRIPTION, null);
+                addPropertyIdList(result, typeId, filter, PropertyIds.SECONDARY_OBJECT_TYPE_IDS, null);
+            }
 
             // directory or file
             if (file.isDirectory()) {
                 // base type and type name
                 addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
-                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, TypeManager.FOLDER_TYPE_ID);
+                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_FOLDER.value());
                 String path = getRepositoryPath(file);
                 addPropertyString(result, typeId, filter, PropertyIds.PATH, (path.length() == 0 ? "/" : path));
 
@@ -1442,7 +1551,7 @@ public class FileShareRepository {
             } else {
                 // base type and type name
                 addPropertyId(result, typeId, filter, PropertyIds.BASE_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
-                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, TypeManager.DOCUMENT_TYPE_ID);
+                addPropertyId(result, typeId, filter, PropertyIds.OBJECT_TYPE_ID, BaseTypeId.CMIS_DOCUMENT.value());
 
                 // file properties
                 addPropertyBoolean(result, typeId, filter, PropertyIds.IS_IMMUTABLE, false);
@@ -1455,6 +1564,9 @@ public class FileShareRepository {
                 addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, null);
                 addPropertyString(result, typeId, filter, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, null);
                 addPropertyString(result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "");
+                if (context.getCmisVersion() != CmisVersion.CMIS_1_0) {
+                    addPropertyBoolean(result, typeId, filter, PropertyIds.IS_PRIVATE_WORKING_COPY, false);
+                }
 
                 if (file.length() == 0) {
                     addPropertyBigInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, null);
@@ -1483,15 +1595,14 @@ public class FileShareRepository {
 
             if (filter != null) {
                 if (!filter.isEmpty()) {
-                    debug("Unknown filter properties: " + filter.toString(), null);
+                    debug("Unknown filter properties: " + filter.toString());
                 }
             }
 
             return result;
+        } catch (CmisBaseException cbe) {
+            throw cbe;
         } catch (Exception e) {
-            if (e instanceof CmisBaseException) {
-                throw (CmisBaseException) e;
-            }
             throw new CmisRuntimeException(e.getMessage(), e);
         }
     }
@@ -1518,15 +1629,9 @@ public class FileShareRepository {
             obj = XMLConverter.convertObject(parser);
             parser.close();
         } catch (Exception e) {
-            warn("Unvalid CMIS properties: " + propFile.getAbsolutePath(), e);
+            LOG.warn("Unvalid CMIS properties: {}", propFile.getAbsolutePath(), e);
         } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException ioe) {
-                    // ignore
-                }
-            }
+            IOUtils.closeQuietly(stream);
         }
 
         if ((obj == null) || (obj.getProperties() == null)) {
@@ -1562,10 +1667,10 @@ public class FileShareRepository {
 
             // check filter
             if (filter != null) {
-                if (!filter.contains(prop.getId())) {
+                if (!filter.contains(prop.getQueryName())) {
                     continue;
                 } else {
-                    filter.remove(prop.getId());
+                    filter.remove(prop.getQueryName());
                 }
             }
 
@@ -1587,17 +1692,16 @@ public class FileShareRepository {
     /**
      * Checks and compiles a property set that can be written to disc.
      */
-    private Properties compileProperties(String typeId, String creator, GregorianCalendar creationDate,
-            String modifier, Properties properties) {
+    private PropertiesImpl compileWriteProperties(String typeId, String creator, String modifier, Properties properties) {
         PropertiesImpl result = new PropertiesImpl();
         Set<String> addedProps = new HashSet<String>();
 
-        if ((properties == null) || (properties.getProperties() == null)) {
+        if (properties == null || properties.getProperties() == null) {
             throw new CmisConstraintException("No properties!");
         }
 
         // get the property definitions
-        TypeDefinition type = types.getType(typeId);
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
         if (type == null) {
             throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
         }
@@ -1612,14 +1716,16 @@ public class FileShareRepository {
             }
 
             // can it be set?
-            if ((propType.getUpdatability() == Updatability.READONLY)) {
+            if (propType.getUpdatability() == Updatability.READONLY) {
                 throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
             }
 
             // empty properties are invalid
-            if (isEmptyProperty(prop)) {
-                throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
-            }
+            // TODO: check
+            // if (isEmptyProperty(prop)) {
+            // throw new CmisConstraintException("Property '" + prop.getId() +
+            // "' must not be empty!");
+            // }
 
             // add it
             result.addProperty(prop);
@@ -1628,7 +1734,7 @@ public class FileShareRepository {
 
         // check if required properties are missing
         for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
-            if (!addedProps.contains(propDef.getId()) && (propDef.getUpdatability() != Updatability.READONLY)) {
+            if (!addedProps.contains(propDef.getId()) && propDef.getUpdatability() != Updatability.READONLY) {
                 if (!addPropertyDefault(result, propDef) && propDef.isRequired()) {
                     throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
                 }
@@ -1637,81 +1743,43 @@ public class FileShareRepository {
 
         addPropertyId(result, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
         addPropertyString(result, typeId, null, PropertyIds.CREATED_BY, creator);
-        addPropertyDateTime(result, typeId, null, PropertyIds.CREATION_DATE, creationDate);
         addPropertyString(result, typeId, null, PropertyIds.LAST_MODIFIED_BY, modifier);
 
         return result;
     }
 
     /**
-     * Checks and updates a property set that can be written to disc.
+     * Writes the properties for a document or folder.
      */
-    private Properties updateProperties(String typeId, String creator, GregorianCalendar creationDate, String modifier,
-            Properties oldProperties, Properties properties) {
-        PropertiesImpl result = new PropertiesImpl();
-
-        if (properties == null) {
-            throw new CmisConstraintException("No properties!");
-        }
-
-        // get the property definitions
-        TypeDefinition type = types.getType(typeId);
-        if (type == null) {
-            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
-        }
-
-        // copy old properties
-        for (PropertyData<?> prop : oldProperties.getProperties().values()) {
-            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
-
-            // do we know that property?
-            if (propType == null) {
-                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
-            }
-
-            // only add read/write properties
-            if ((propType.getUpdatability() != Updatability.READWRITE)) {
-                continue;
-            }
+    private void writePropertiesFile(File file, Properties properties) {
+        File propFile = getPropertiesFile(file);
 
-            result.addProperty(prop);
+        // if no properties set delete the properties file
+        if (properties == null || properties.getProperties() == null || properties.getProperties().size() == 0) {
+            propFile.delete();
+            return;
         }
 
-        // update properties
-        for (PropertyData<?> prop : properties.getProperties().values()) {
-            PropertyDefinition<?> propType = type.getPropertyDefinitions().get(prop.getId());
-
-            // do we know that property?
-            if (propType == null) {
-                throw new CmisConstraintException("Property '" + prop.getId() + "' is unknown!");
-            }
-
-            // can it be set?
-            if ((propType.getUpdatability() == Updatability.READONLY)) {
-                throw new CmisConstraintException("Property '" + prop.getId() + "' is readonly!");
-            }
-
-            if ((propType.getUpdatability() == Updatability.ONCREATE)) {
-                throw new CmisConstraintException("Property '" + prop.getId() + "' can only be set on create!");
-            }
+        // create object
+        ObjectDataImpl object = new ObjectDataImpl();
+        object.setProperties(properties);
 
-            // default or value
-            if (isEmptyProperty(prop)) {
-                addPropertyDefault(result, propType);
-            } else {
-                result.addProperty(prop);
-            }
+        OutputStream stream = null;
+        try {
+            stream = new BufferedOutputStream(new FileOutputStream(propFile));
+            XMLStreamWriter writer = XMLUtils.createWriter(stream);
+            XMLUtils.startXmlDocument(writer);
+            XMLConverter.writeObject(writer, CmisVersion.CMIS_1_1, true, "object", XMLConstants.NAMESPACE_CMIS, object);
+            XMLUtils.endXmlDocument(writer);
+            writer.close();
+        } catch (Exception e) {
+            throw new CmisStorageException("Couldn't store properties!", e);
+        } finally {
+            IOUtils.closeQuietly(stream);
         }
-
-        addPropertyId(result, typeId, null, PropertyIds.OBJECT_TYPE_ID, typeId);
-        addPropertyString(result, typeId, null, PropertyIds.CREATED_BY, creator);
-        addPropertyDateTime(result, typeId, null, PropertyIds.CREATION_DATE, creationDate);
-        addPropertyString(result, typeId, null, PropertyIds.LAST_MODIFIED_BY, modifier);
-
-        return result;
     }
 
-    private static boolean isEmptyProperty(PropertyData<?> prop) {
+    private boolean isEmptyProperty(PropertyData<?> prop) {
         if ((prop == null) || (prop.getValues() == null)) {
             return true;
         }
@@ -1783,7 +1851,7 @@ public class FileShareRepository {
             throw new IllegalArgumentException("Id must not be null!");
         }
 
-        TypeDefinition type = types.getType(typeId);
+        TypeDefinition type = typeManager.getInternalTypeDefinition(typeId);
         if (type == null) {
             throw new IllegalArgumentException("Unknown type: " + typeId);
         }
@@ -1808,7 +1876,7 @@ public class FileShareRepository {
      * Adds the default value of property if defined.
      */
     @SuppressWarnings("unchecked")
-    private static boolean addPropertyDefault(PropertiesImpl props, PropertyDefinition<?> propDef) {
+    private boolean addPropertyDefault(PropertiesImpl props, PropertyDefinition<?> propDef) {
         if ((props == null) || (props.getProperties() == null)) {
             throw new IllegalArgumentException("Props must not be null!");
         }
@@ -1862,7 +1930,7 @@ public class FileShareRepository {
             throw new IllegalArgumentException("File must not be null!");
         }
 
-        // we can gather properties if the file or folder doesn't exist
+        // we can't gather allowable actions if the file or folder doesn't exist
         if (!file.exists()) {
             throw new CmisObjectNotFoundException("Object not found!");
         }
@@ -1901,7 +1969,7 @@ public class FileShareRepository {
         return result;
     }
 
-    private static void addAction(Set<Action> aas, Action action, boolean condition) {
+    private void addAction(Set<Action> aas, Action action, boolean condition) {
         if (condition) {
             aas.add(action);
         }
@@ -1914,7 +1982,7 @@ public class FileShareRepository {
         AccessControlListImpl result = new AccessControlListImpl();
         result.setAces(new ArrayList<Ace>());
 
-        for (Map.Entry<String, Boolean> ue : userMap.entrySet()) {
+        for (Map.Entry<String, Boolean> ue : readWriteUserMap.entrySet()) {
             // create principal
             AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl();
             principal.setPrincipalId(ue.getKey());
@@ -1939,138 +2007,48 @@ public class FileShareRepository {
     }
 
     /**
-     * Writes the properties for a document or folder.
-     */
-    private static void writePropertiesFile(File file, Properties properties) {
-        File propFile = getPropertiesFile(file);
-
-        // if no properties set delete the properties file
-        if ((properties == null) || (properties.getProperties() == null) || (properties.getProperties().size() == 0)) {
-            propFile.delete();
-            return;
-        }
-
-        // create object
-        ObjectDataImpl object = new ObjectDataImpl();
-        object.setProperties(properties);
-
-        OutputStream stream = null;
-        try {
-            stream = new BufferedOutputStream(new FileOutputStream(propFile));
-            XMLStreamWriter writer = XMLUtils.createWriter(stream);
-            XMLUtils.startXmlDocument(writer);
-            XMLConverter.writeObject(writer, CmisVersion.CMIS_1_1, true, "object", XMLConstants.NAMESPACE_CMIS, object);
-            XMLUtils.endXmlDocument(writer);
-        } catch (Exception e) {
-            throw new CmisStorageException("Couldn't store properties!", e);
-        } finally {
-            if (stream != null) {
-                try {
-                    stream.close();
-                } catch (IOException e2) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    // --- internal stuff ---
-
-    /**
-     * Converts milliseconds into a calendar object.
-     */
-    private static GregorianCalendar millisToCalendar(long millis) {
-        GregorianCalendar result = new GregorianCalendar();
-        result.setTimeZone(TimeZone.getTimeZone("GMT"));
-        result.setTimeInMillis((long) (Math.ceil((double) millis / 1000) * 1000));
-
-        return result;
-    }
-
-    /**
-     * Splits a filter statement into a collection of properties. If
-     * <code>filter</code> is <code>null</code>, empty or one of the properties
-     * is '*' , an empty collection will be returned.
-     */
-    private static Set<String> splitFilter(String filter) {
-        if (filter == null) {
-            return null;
-        }
-
-        if (filter.trim().length() == 0) {
-            return null;
-        }
-
-        Set<String> result = new HashSet<String>();
-        for (String s : filter.split(",")) {
-            s = s.trim();
-            if (s.equals("*")) {
-                return null;
-            } else if (s.length() > 0) {
-                result.add(s);
-            }
-        }
-
-        // set a few base properties
-        // query name == id (for base type properties)
-        result.add(PropertyIds.OBJECT_ID);
-        result.add(PropertyIds.OBJECT_TYPE_ID);
-        result.add(PropertyIds.BASE_TYPE_ID);
-
-        return result;
-    }
-
-    /**
-     * Gets the type id from a set of properties.
+     * Checks if the given name is valid for a file system.
+     * 
+     * @param name
+     *            the name to check
+     * 
+     * @return <code>true</code> if the name is valid, <code>false</code>
+     *         otherwise
      */
-    private static String getTypeId(Properties properties) {
-        PropertyData<?> typeProperty = properties.getProperties().get(PropertyIds.OBJECT_TYPE_ID);
-        if (!(typeProperty instanceof PropertyId)) {
-            throw new CmisInvalidArgumentException("Type Id must be set!");
-        }
-
-        String typeId = ((PropertyId) typeProperty).getFirstValue();
-        if (typeId == null) {
-            throw new CmisInvalidArgumentException("Type Id must be set!");
+    private boolean isValidName(String name) {
+        if (name == null || name.length() == 0 || name.indexOf(File.separatorChar) != -1
+                || name.indexOf(File.pathSeparatorChar) != -1) {
+            return false;
         }
 
-        return typeId;
+        return true;
     }
 
     /**
-     * Returns the first value of an id property.
+     * Checks if a folder is empty. A folder is considered as empty if no files
+     * or only the shadow file reside in the folder.
+     * 
+     * @param folder
+     *            the folder
+     * 
+     * @return <code>true</code> if the folder is empty.
      */
-    private static String getIdProperty(Properties properties, String name) {
-        PropertyData<?> property = properties.getProperties().get(name);
-        if (!(property instanceof PropertyId)) {
-            return null;
+    private boolean isFolderEmpty(File folder) {
+        if (!folder.isDirectory()) {
+            return true;
         }
 
-        return ((PropertyId) property).getFirstValue();
-    }
+        String[] fileNames = folder.list();
 
-    /**
-     * Returns the first value of a string property.
-     */
-    private static String getStringProperty(Properties properties, String name) {
-        PropertyData<?> property = properties.getProperties().get(name);
-        if (!(property instanceof PropertyString)) {
-            return null;
+        if ((fileNames == null) || (fileNames.length == 0)) {
+            return true;
         }
 
-        return ((PropertyString) property).getFirstValue();
-    }
-
-    /**
-     * Returns the first value of a datetime property.
-     */
-    private static GregorianCalendar getDateTimeProperty(Properties properties, String name) {
-        PropertyData<?> property = properties.getProperties().get(name);
-        if (!(property instanceof PropertyDateTime)) {
-            return null;
+        if (fileNames.length == 1 && fileNames[0].equals(SHADOW_FOLDER)) {
+            return true;
         }
 
-        return ((PropertyDateTime) property).getFirstValue();
+        return false;
     }
 
     /**
@@ -2082,7 +2060,7 @@ public class FileShareRepository {
             throw new CmisPermissionDeniedException("No user context!");
         }
 
-        Boolean readOnly = userMap.get(context.getUsername());
+        Boolean readOnly = readWriteUserMap.get(context.getUsername());
         if (readOnly == null) {
             throw new CmisPermissionDeniedException("Unknown user!");
         }
@@ -2097,7 +2075,7 @@ public class FileShareRepository {
     /**
      * Returns the properties file of the given file.
      */
-    private static File getPropertiesFile(File file) {
+    private File getPropertiesFile(File file) {
         if (file.isDirectory()) {
             return new File(file, SHADOW_FOLDER);
         }
@@ -2121,7 +2099,7 @@ public class FileShareRepository {
      * but good enough for now.
      */
     private File idToFile(String id) throws IOException {
-        if ((id == null) || (id.length() == 0)) {
+        if (id == null || id.length() == 0) {
             throw new CmisInvalidArgumentException("Id is not valid!");
         }
 
@@ -2166,19 +2144,9 @@ public class FileShareRepository {
         return file.getAbsolutePath().substring(root.getAbsolutePath().length()).replace(File.separatorChar, '/');
     }
 
-    private void warn(String msg, Throwable t) {
-        if (LOG.isWarnEnabled()) {
-            LOG.warn("<" + repositoryId + "> " + msg, t);
-        }
-    }
-
     private void debug(String msg) {
-        debug(msg, null);
-    }
-
-    private void debug(String msg, Throwable t) {
         if (LOG.isDebugEnabled()) {
-            LOG.debug("<" + repositoryId + "> " + msg, t);
+            LOG.debug("<{}> {}", repositoryId, msg);
         }
     }
 }

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepositoryManager.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepositoryManager.java?rev=1516830&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepositoryManager.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepositoryManager.java Fri Aug 23 12:48:06 2013
@@ -0,0 +1,82 @@
+/*
+ * 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.fileshare;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+
+/**
+ * Manages all repositories.
+ */
+public class FileShareRepositoryManager {
+
+    private final Map<String, FileShareRepository> repositories;
+
+    public FileShareRepositoryManager() {
+        repositories = new HashMap<String, FileShareRepository>();
+    }
+
+    /**
+     * Adds a repository object.
+     */
+    public void addRepository(FileShareRepository fsr) {
+        if (fsr == null || fsr.getRepositoryId() == null) {
+            return;
+        }
+
+        repositories.put(fsr.getRepositoryId(), fsr);
+    }
+
+    /**
+     * Gets a repository object by id.
+     */
+    public FileShareRepository getRepository(String repositoryId) {
+        FileShareRepository result = repositories.get(repositoryId);
+        if (result == null) {
+            throw new CmisObjectNotFoundException("Unknown repository '" + repositoryId + "'!");
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns all repository objects.
+     */
+    public Collection<FileShareRepository> getRepositories() {
+        return repositories.values();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        for (FileShareRepository repository : repositories.values()) {
+            sb.append('[');
+            sb.append(repository.getRepositoryId());
+            sb.append(" -> ");
+            sb.append(repository.getRootDirectory().getAbsolutePath());
+            sb.append(']');
+        }
+
+        return sb.toString();
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepositoryManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareTypeManager.java
URL: http://svn.apache.org/viewvc/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareTypeManager.java?rev=1516830&view=auto
==============================================================================
--- chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareTypeManager.java (added)
+++ chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareTypeManager.java Fri Aug 23 12:48:06 2013
@@ -0,0 +1,222 @@
+/*
+ * 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.fileshare;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.chemistry.opencmis.commons.definitions.MutableDocumentTypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.MutableFolderTypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.MutablePropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.MutableTypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
+import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.chemistry.opencmis.commons.impl.IOUtils;
+import org.apache.chemistry.opencmis.commons.impl.XMLConverter;
+import org.apache.chemistry.opencmis.commons.impl.XMLUtils;
+import org.apache.chemistry.opencmis.commons.server.CallContext;
+import org.apache.chemistry.opencmis.server.support.TypeDefinitionFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Manages the type definitions for all FileShare repositories.
+ */
+public class FileShareTypeManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FileShareTypeManager.class);
+
+    private static final String NAMESPACE = "http://chemistry.apache.org/opencmis/fileshare";
+
+    private final TypeDefinitionFactory typeDefinitionFactory;
+    private final Map<String, TypeDefinition> typeDefinitions;
+
+    public FileShareTypeManager() {
+        // set up TypeDefinitionFactory
+        typeDefinitionFactory = TypeDefinitionFactory.newInstance();
+        typeDefinitionFactory.setDefaultNamespace(NAMESPACE);
+        typeDefinitionFactory.setDefaultControllableAcl(false);
+        typeDefinitionFactory.setDefaultControllablePolicy(false);
+        typeDefinitionFactory.setDefaultQueryable(false);
+        typeDefinitionFactory.setDefaultFulltextIndexed(false);
+        typeDefinitionFactory.setDefaultTypeMutability(typeDefinitionFactory.createTypeMutability(false, false, false));
+
+        // set up definitions map
+        typeDefinitions = new HashMap<String, TypeDefinition>();
+
+        // add base folder type
+        MutableFolderTypeDefinition folderType = typeDefinitionFactory
+                .createBaseFolderTypeDefinition(CmisVersion.CMIS_1_1);
+        removeQueryableAndOrderableFlags(folderType);
+        typeDefinitions.put(folderType.getId(), folderType);
+
+        // add base document type
+        MutableDocumentTypeDefinition documentType = typeDefinitionFactory
+                .createBaseDocumentTypeDefinition(CmisVersion.CMIS_1_1);
+        removeQueryableAndOrderableFlags(documentType);
+        typeDefinitions.put(documentType.getId(), documentType);
+    }
+
+    /**
+     * Adds a type definition.
+     */
+    public synchronized void addTypeDefinition(TypeDefinition type) {
+        if (type == null) {
+            throw new IllegalArgumentException("Type must be set!");
+        }
+
+        if (type.getId() == null || type.getId().trim().length() == 0) {
+            throw new IllegalArgumentException("Type must have a valid id!");
+        }
+
+        if (type.getParentTypeId() == null || type.getParentTypeId().trim().length() == 0) {
+            throw new IllegalArgumentException("Type must have a valid parent id!");
+        }
+
+        TypeDefinition parentType = typeDefinitions.get(type.getParentTypeId());
+        if (parentType == null) {
+            throw new IllegalArgumentException("Parent type doesn't exist!");
+        }
+
+        MutableTypeDefinition newType = typeDefinitionFactory.copy(type, true);
+
+        // copy parent type property definitions and mark them as inherited
+        for (PropertyDefinition<?> propDef : parentType.getPropertyDefinitions().values()) {
+            MutablePropertyDefinition<?> basePropDef = typeDefinitionFactory.copy(propDef);
+            basePropDef.setIsInherited(true);
+            newType.addPropertyDefinition(basePropDef);
+        }
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Added type '{}'.", type.getId());
+        }
+    }
+
+    /**
+     * Removes the queryable and orderable flags from the property definitions
+     * of a type definition because this implementations does neither support
+     * queries nor can order objects.
+     */
+    private void removeQueryableAndOrderableFlags(MutableTypeDefinition type) {
+        for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
+            MutablePropertyDefinition<?> mutablePropDef = (MutablePropertyDefinition<?>) propDef;
+            mutablePropDef.setIsQueryable(false);
+            mutablePropDef.setIsOrderable(false);
+        }
+    }
+
+    public void loadTypeDefinitionFromFile(String filename) throws IOException, XMLStreamException {
+        loadTypeDefinitionFromStream(new BufferedInputStream(new FileInputStream(filename)));
+    }
+
+    public void loadTypeDefinitionFromResource(String name) throws IOException, XMLStreamException {
+        loadTypeDefinitionFromStream(this.getClass().getResourceAsStream(name));
+    }
+
+    public void loadTypeDefinitionFromStream(InputStream stream) throws IOException, XMLStreamException {
+        if (stream == null) {
+            throw new IllegalArgumentException("Stream is null!");
+        }
+
+        TypeDefinition type = null;
+
+        XMLStreamReader parser = null;
+        try {
+            parser = XMLUtils.createParser(stream);
+            if (!XMLUtils.findNextStartElemenet(parser)) {
+                return;
+            }
+
+            type = XMLConverter.convertTypeDefinition(parser);
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+            IOUtils.closeQuietly(stream);
+        }
+
+        addTypeDefinition(type);
+    }
+
+    /**
+     * Returns the internal type definition.
+     */
+    public synchronized TypeDefinition getInternalTypeDefinition(String typeId) {
+        return typeDefinitions.get(typeId);
+    }
+
+    /**
+     * Returns all internal type definitions.
+     */
+    public synchronized Collection<TypeDefinition> getInternalTypeDefinitions() {
+        return typeDefinitions.values();
+    }
+
+    // --- service methods ---
+
+    public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
+        TypeDefinition type = typeDefinitions.get(typeId);
+        if (type == null) {
+            throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+        }
+
+        return typeDefinitionFactory.copy(type, true, context.getCmisVersion());
+    }
+
+    public TypeDefinitionList getTypeChildren(CallContext context, String typeId, Boolean includePropertyDefinitions,
+            BigInteger maxItems, BigInteger skipCount) {
+        return typeDefinitionFactory.createTypeDefinitionList(typeDefinitions, typeId, includePropertyDefinitions,
+                maxItems, skipCount, context.getCmisVersion());
+    }
+
+    public List<TypeDefinitionContainer> getTypeDescendants(CallContext context, String typeId, BigInteger depth,
+            Boolean includePropertyDefinitions) {
+        return typeDefinitionFactory.createTypeDescendants(typeDefinitions, typeId, depth, includePropertyDefinitions,
+                context.getCmisVersion());
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        for (TypeDefinition type : typeDefinitions.values()) {
+            sb.append('[');
+            sb.append(type.getId());
+            sb.append(" (");
+            sb.append(type.getBaseTypeId().value());
+            sb.append(")]");
+        }
+
+        return sb.toString();
+    }
+}

Propchange: chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareTypeManager.java
------------------------------------------------------------------------------
    svn:eol-style = native