You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by je...@apache.org on 2010/04/16 16:14:02 UTC

svn commit: r934896 [7/12] - in /incubator/chemistry/opencmis/trunk/chemistry-opencmis-server: chemistry-opencmis-server-bindings/src/main/java/org/apache/chemistry/opencmis/server/impl/ chemistry-opencmis-server-bindings/src/main/java/org/apache/chemi...

Modified: incubator/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/incubator/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java?rev=934896&r1=934895&r2=934896&view=diff
==============================================================================
--- incubator/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java (original)
+++ incubator/chemistry/opencmis/trunk/chemistry-opencmis-server/chemistry-opencmis-server-fileshare/src/main/java/org/apache/chemistry/opencmis/fileshare/FileShareRepository.java Fri Apr 16 14:14:00 2010
@@ -129,2024 +129,1963 @@ import org.apache.commons.logging.LogFac
 
 /**
  * File system back-end for CMIS server.
- *
+ * 
  * @author <a href="mailto:fmueller@opentext.com">Florian M&uuml;ller</a>
- *
+ * 
  */
 public class FileShareRepository {
 
-  private static final String ROOT_ID = "@root@";
-  private static final String SHADOW_EXT = ".cmis.xml";
-  private static final String SHADOW_FOLDER = "cmis.xml";
-
-  private static final String USER_UNKNOWN = "<unknown>";
-
-  private static final String CMIS_READ = "cmis:read";
-  private static final String CMIS_WRITE = "cmis:write";
-  private static final String CMIS_ALL = "cmis:all";
-
-  private static final int BUFFER_SIZE = 64 * 1024;
-
-  private static final Log log = LogFactory.getLog(FileShareRepository.class);
-
-  /** Repository id */
-  private String fRepositoryId;
-  /** Root directory */
-  private File fRoot;
-  /** Types */
-  private TypeManager fTypes;
-  /** User table */
-  private Map<String, Boolean> fUserMap;
-  /** Repository info */
-  private RepositoryInfoImpl fRepositoryInfo;
-
-  /**
-   * Constructor.
-   *
-   * @param repId
-   *          CMIS repository id
-   * @param root
-   *          root folder
-   * @param types
-   *          type manager object
-   */
-  public FileShareRepository(String repId, String root, TypeManager types) {
-    // check repository id
-    if ((repId == null) || (repId.trim().length() == 0)) {
-      throw new IllegalArgumentException("Invalid repository id!");
-    }
-
-    fRepositoryId = repId;
-
-    // check root folder
-    if ((root == null) || (root.trim().length() == 0)) {
-      throw new IllegalArgumentException("Invalid root folder!");
-    }
-
-    fRoot = new File(root);
-    if (!fRoot.isDirectory()) {
-      throw new IllegalArgumentException("Root is not a directory!");
-    }
-
-    // set types
-    fTypes = types;
-
-    // set up user table
-    fUserMap = new HashMap<String, Boolean>();
-
-    // compile repository info
-    fRepositoryInfo = new RepositoryInfoImpl();
-
-    fRepositoryInfo.setRepositoryId(fRepositoryId);
-    fRepositoryInfo.setRepositoryName(fRepositoryId);
-    fRepositoryInfo.setRepositoryDescription(fRepositoryId);
-
-    fRepositoryInfo.setCmisVersionSupported("1.0");
-
-    fRepositoryInfo.setProductName("OpenCMIS FileShare");
-    fRepositoryInfo.setProductVersion("0.1");
-    fRepositoryInfo.setVendorName("OpenCMIS");
-
-    fRepositoryInfo.setRootFolder(ROOT_ID);
-
-    fRepositoryInfo.setThinClientUri("");
-
-    RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
-    capabilities.setCapabilityAcl(CapabilityAcl.DISCOVER);
-    capabilities.setAllVersionsSearchable(false);
-    capabilities.setCapabilityJoin(CapabilityJoin.NONE);
-    capabilities.setSupportsMultifiling(false);
-    capabilities.setSupportsUnfiling(false);
-    capabilities.setSupportsVersionSpecificFiling(false);
-    capabilities.setIsPwcSearchable(false);
-    capabilities.setIsPwcUpdatable(false);
-    capabilities.setCapabilityQuery(CapabilityQuery.NONE);
-    capabilities.setCapabilityChanges(CapabilityChanges.NONE);
-    capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
-    capabilities.setSupportsGetDescendants(true);
-    capabilities.setSupportsGetFolderTree(true);
-    capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
-
-    fRepositoryInfo.setRepositoryCapabilities(capabilities);
-
-    AclCapabilitiesDataImpl aclCapability = new AclCapabilitiesDataImpl();
-    aclCapability.setSupportedPermissions(SupportedPermissions.BASIC);
-    aclCapability.setAclPropagation(AclPropagation.OBJECTONLY);
-
-    // permissions
-    List<PermissionDefinition> permissions = new ArrayList<PermissionDefinition>();
-    permissions.add(createPermission(CMIS_READ, "Read"));
-    permissions.add(createPermission(CMIS_WRITE, "Write"));
-    permissions.add(createPermission(CMIS_ALL, "All"));
-    aclCapability.setPermissionDefinitionData(permissions);
-
-    // mapping
-    List<PermissionMapping> list = new ArrayList<PermissionMapping>();
-    list.add(createMapping(PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_CREATE_FOLDER_FOLDER, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, CMIS_WRITE));
-    list.add(createMapping(PermissionMapping.CAN_DELETE_OBJECT, CMIS_ALL));
-    list.add(createMapping(PermissionMapping.CAN_DELETE_TREE_FOLDER, CMIS_ALL));
-    list.add(createMapping(PermissionMapping.CAN_GET_ACL_OBJECT, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES,
-        CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_CHILDREN_FOLDER, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_PARENTS_FOLDER, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_GET_PROPERTIES_OBJECT, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_MOVE_OBJECT, CMIS_WRITE));
-    list.add(createMapping(PermissionMapping.CAN_MOVE_SOURCE, CMIS_READ));
-    list.add(createMapping(PermissionMapping.CAN_MOVE_TARGET, CMIS_WRITE));
-    list.add(createMapping(PermissionMapping.CAN_SET_CONTENT_DOCUMENT, CMIS_WRITE));
-    list.add(createMapping(PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, CMIS_WRITE));
-    list.add(createMapping(PermissionMapping.CAN_VIEW_CONTENT_OBJECT, CMIS_READ));
-    Map<String, PermissionMapping> map = new LinkedHashMap<String, PermissionMapping>();
-    for (PermissionMapping pm : list)  {
-        map.put(pm.getKey(), pm);
-    }
-    aclCapability.setPermissionMappingData(map);
-
-    fRepositoryInfo.setAclCapabilities(aclCapability);
-  }
-
-  private PermissionDefinition createPermission(String permission, String description) {
-    PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
-    pd.setPermission(permission);
-    pd.setDescription(description);
-
-    return pd;
-  }
-
-  private PermissionMapping createMapping(String key, String permission) {
-    PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
-    pm.setKey(key);
-    pm.setPermissions(Collections.singletonList(permission));
-
-    return pm;
-  }
-
-  /**
-   * Adds a user to the repository.
-   */
-  public void addUser(String user, boolean readOnly) {
-    if ((user == null) || (user.length() == 0)) {
-      return;
-    }
-
-    fUserMap.put(user, readOnly);
-  }
-
-  // --- the public stuff ---
-
-  /**
-   * Returns the repository id.
-   */
-  public String getRepositoryId() {
-    return fRepositoryId;
-  }
-
-  /**
-   * CMIS getRepositoryInfo.
-   */
-  public RepositoryInfo getRepositoryInfo(CallContext context) {
-    debug("getRepositoryInfo");
-    checkUser(context, false);
-
-    return fRepositoryInfo;
-  }
-
-  /**
-   * CMIS getTypesChildren.
-   */
-  public TypeDefinitionList getTypesChildren(CallContext context, String typeId,
-      boolean includePropertyDefinitions, BigInteger maxItems, BigInteger skipCount) {
-    debug("getTypesChildren");
-    checkUser(context, false);
-
-    return fTypes
-        .getTypesChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
-  }
-
-  /**
-   * CMIS getTypeDefinition.
-   */
-  public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
-    debug("getTypeDefinition");
-    checkUser(context, false);
-
-    return fTypes.getTypeDefinition(context, typeId);
-  }
-
-  /**
-   * CMIS getTypesDescendants.
-   */
-  public List<TypeDefinitionContainer> getTypesDescendants(CallContext context, String typeId,
-      BigInteger depth, Boolean includePropertyDefinitions) {
-    debug("getTypesDescendants");
-    checkUser(context, false);
-
-    return fTypes.getTypesDescendants(context, typeId, depth, includePropertyDefinitions);
-  }
-
-  /**
-   * Create* dispatch for AtomPub.
-   */
-  public ObjectData create(CallContext context, Properties properties, String folderId,
-      ContentStream contentStream, VersioningState versioningState, ObjectInfoHolder objectInfos) {
-    debug("create");
-    boolean userReadOnly = checkUser(context, true);
-
-    String typeId = getTypeId(properties);
-    TypeDefinition type = fTypes.getType(typeId);
-    if (type == null) {
-      throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
-    }
-
-    String objectId = null;
-    if (type.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) {
-      objectId = createDocument(context, properties, folderId, contentStream, versioningState);
-    }
-    else if (type.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) {
-      objectId = createFolder(context, properties, folderId);
-    }
-    else {
-      throw new CmisObjectNotFoundException("Cannot create object of type '" + typeId + "'!");
-    }
-
-    return compileObjectType(getFile(objectId), null, false, false, userReadOnly, objectInfos);
-  }
-
-  /**
-   * CMIS createDocument.
-   */
-  public String createDocument(CallContext context, Properties properties, String folderId,
-      ContentStream contentStream, VersioningState versioningState) {
-    debug("createDocument");
-    checkUser(context, true);
-
-    // check properties
-    if ((properties == null) || (properties.getProperties() == null)) {
-      throw new CmisInvalidArgumentException("Properties must be set!");
-    }
-
-    // check versioning state
-    if (VersioningState.NONE != versioningState) {
-      throw new CmisConstraintException("Versioning not supported!");
-    }
-
-    // check type
-    String typeId = getTypeId(properties);
-    TypeDefinition type = fTypes.getType(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);
-
-    // check the name
-    String name = getStringProperty(properties, PropertyIds.NAME);
-    if (!isValidName(name)) {
-      throw new CmisNameConstraintViolationException("Name is not valid!");
-    }
-
-    // get parent File
-    File parent = getFile(folderId);
-    if (!parent.isDirectory()) {
-      throw new CmisObjectNotFoundException("Parent is not a folder!");
-    }
-
-    // check the file
-    File newFile = new File(parent, name);
-    if (newFile.exists()) {
-      throw new CmisNameConstraintViolationException("Document already exists!");
-    }
-
-    // create the file
-    try {
-      newFile.createNewFile();
-    }
-    catch (IOException e) {
-      throw new CmisStorageException("Could not create file: " + e.getMessage());
-    }
-
-    // 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);
-      }
-    }
-
-    // write properties
-    writePropertiesFile(newFile, props);
-
-    return getId(newFile);
-  }
-
-  /**
-   * CMIS createDocumentFromSource.
-   */
-  public String createDocumentFromSource(CallContext context, String sourceId,
-      Properties properties, String folderId, VersioningState versioningState) {
-
-    // check versioning state
-    if (VersioningState.NONE != versioningState) {
-      throw new CmisConstraintException("Versioning not supported!");
-    }
-
-    // get parent File
-    File parent = getFile(folderId);
-    if (!parent.isDirectory()) {
-      throw new CmisObjectNotFoundException("Parent is not a folder!");
-    }
-
-    // get source File
-    File source = getFile(sourceId);
-    if (!source.isFile()) {
-      throw new CmisObjectNotFoundException("Source is not a document!");
-    }
-
-    // file name
-    String name = source.getName();
-
-    // get properties
-    PropertiesImpl sourceProperties = new PropertiesImpl();
-    readCustomProperties(source, sourceProperties, null, new ObjectInfoImpl());
-
-    // get the type id
-    String typeId = getIdProperty(sourceProperties, PropertyIds.OBJECT_TYPE_ID);
-    if (typeId == null) {
-      typeId = TypeManager.DOCUMENT_TYPE_ID;
-    }
-
-    // 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))) {
-        continue;
-      }
-
-      newProperties.addProperty(prop);
-    }
-
-    // replace properties
-    if (properties != null) {
-      // find new name
-      String newName = getStringProperty(properties, PropertyIds.NAME);
-      if (newName != null) {
-        if (!isValidName(newName)) {
-          throw new CmisNameConstraintViolationException("Name is not valid!");
-        }
-        name = newName;
-      }
-
-      // get the property definitions
-      TypeDefinition type = fTypes.getType(typeId);
-      if (type == null) {
-        throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
-      }
-
-      // replace with new values
-      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.READWRITE)) {
-          throw new CmisConstraintException("Property '" + prop.getId() + "' cannot be updated!");
-        }
-
-        // empty properties are invalid
-        if (isEmptyProperty(prop)) {
-          throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
-        }
-
-        newProperties.addProperty(prop);
-      }
-    }
-
-    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()));
-    addPropertyString(newProperties, typeId, null, PropertyIds.LAST_MODIFIED_BY, context
-        .getUsername());
-
-    // check the file
-    File newFile = new File(parent, name);
-    if (newFile.exists()) {
-      throw new CmisNameConstraintViolationException("Document already exists.");
-    }
-
-    // create the file
-    try {
-      newFile.createNewFile();
-    }
-    catch (IOException e) {
-      throw new CmisStorageException("Could not create file: " + e.getMessage(), e);
-    }
-
-    // copy content
-    try {
-      OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
-      InputStream in = new BufferedInputStream(new FileInputStream(source));
-
-      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 roead or write content: " + e.getMessage(), e);
-    }
-
-    // write properties
-    writePropertiesFile(newFile, newProperties);
-
-    return getId(newFile);
-  }
-
-  /**
-   * CMIS createFolder.
-   */
-  public String createFolder(CallContext context, Properties properties, String folderId) {
-    debug("createFolder");
-    checkUser(context, true);
-
-    // check properties
-    if ((properties == null) || (properties.getProperties() == null)) {
-      throw new CmisInvalidArgumentException("Properties must be set!");
-    }
-
-    // check type
-    String typeId = getTypeId(properties);
-    TypeDefinition type = fTypes.getType(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);
-
-    // check the name
-    String name = getStringProperty(properties, PropertyIds.NAME);
-    if (!isValidName(name)) {
-      throw new CmisNameConstraintViolationException("Name is not valid.");
-    }
-
-    // get parent File
-    File parent = getFile(folderId);
-    if (!parent.isDirectory()) {
-      throw new CmisObjectNotFoundException("Parent is not a folder!");
-    }
-
-    // create the folder
-    File newFolder = new File(parent, name);
-    if (!newFolder.mkdir()) {
-      throw new CmisStorageException("Could not create folder!");
-    }
-
-    // write properties
-    writePropertiesFile(newFolder, props);
-
-    return getId(newFolder);
-  }
-
-  /**
-   * CMIS moveObject.
-   */
-  public ObjectData moveObject(CallContext context, Holder<String> objectId, String targetFolderId,
-      ObjectInfoHolder objectInfos) {
-    debug("moveObject");
-    boolean userReadOnly = checkUser(context, true);
-
-    if (objectId == null) {
-      throw new CmisInvalidArgumentException("Id is not valid!");
-    }
-
-    // get the file and parent
-    File file = getFile(objectId.getValue());
-    File parent = getFile(targetFolderId);
-
-    // build new path
-    File newFile = new File(parent, file.getName());
-    if (newFile.exists()) {
-      throw new CmisStorageException("Object already exists!");
-    }
-
-    // move it
-    if (!file.renameTo(newFile)) {
-      throw new CmisStorageException("Move failed!");
-    }
-    else {
-      // set new id
-      objectId.setValue(getId(newFile));
-
-      // if it is a file, move properties file too
-      if (newFile.isFile()) {
-        File propFile = getPropertiesFile(file);
-        if (propFile.exists()) {
-          File newPropFile = new File(parent, propFile.getName());
-          propFile.renameTo(newPropFile);
-        }
-      }
-    }
-
-    return compileObjectType(newFile, null, false, false, userReadOnly, objectInfos);
-  }
-
-  /**
-   * CMIS setContentStream and deleteContentStream.
-   */
-  public void setContentStream(CallContext context, Holder<String> objectId, Boolean overwriteFlag,
-      ContentStream contentStream) {
-    debug("setContentStream or deleteContentStream");
-    checkUser(context, true);
-
-    if (objectId == null) {
-      throw new CmisInvalidArgumentException("Id is not valid!");
-    }
-
-    // get the file
-    File file = getFile(objectId.getValue());
-    if (!file.isFile()) {
-      throw new CmisStreamNotSupportedException("Not a file!");
-    }
-
-    // check overwrite
-    boolean owf = (overwriteFlag == null ? true : overwriteFlag.booleanValue());
-    if (!owf && file.length() > 0) {
-      throw new CmisContentAlreadyExistsException("Content already exists!");
-    }
-
-    try {
-      OutputStream out = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
-
-      if ((contentStream == null) || (contentStream.getStream() == null)) {
-        // delete content
-        out.write(new byte[0]);
-      }
-      else {
-        // set content
-        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);
-        }
-
-        in.close();
-      }
-
-      out.close();
-    }
-    catch (Exception e) {
-      throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
-    }
-  }
-
-  /**
-   * CMIS deleteObject.
-   */
-  public void deleteObject(CallContext context, String objectId) {
-    debug("deleteObject");
-    checkUser(context, true);
-
-    // get the file or folder
-    File file = getFile(objectId);
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Object not found!");
-    }
-
-    // check if it is a folder and if it is empty
-    if (!isFolderEmpty(file)) {
-      throw new CmisConstraintException("Folder is not empty!");
-    }
-
-    // delete properties and actual file
-    getPropertiesFile(file).delete();
-    if (!file.delete()) {
-      throw new CmisStorageException("Deletion failed!");
-    }
-  }
-
-  /**
-   * CMIS deleteTree.
-   */
-  public FailedToDeleteData deleteTree(CallContext context, String folderId,
-      Boolean continueOnFailure) {
-    debug("deleteTree");
-    checkUser(context, true);
-
-    boolean cof = (continueOnFailure == null ? false : continueOnFailure.booleanValue());
-
-    // get the file or folder
-    File file = getFile(folderId);
-
-    FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
-    result.setIds(new ArrayList<String>());
-
-    // if it is a folder, remove it recursively
-    if (file.isDirectory()) {
-      deleteFolder(file, cof, result);
-    }
-    else {
-      getPropertiesFile(file).delete();
-      if (!file.delete()) {
-        result.getIds().add(getId(file));
-      }
-    }
-
-    return result;
-  }
-
-  /**
-   * CMIS updateProperties.
-   */
-  public ObjectData updateProperties(CallContext context, Holder<String> objectId,
-      Properties properties, ObjectInfoHolder objectInfos) {
-    debug("updateProperties");
-    boolean userReadOnly = checkUser(context, true);
-
-    if (objectId == null) {
-      throw new CmisInvalidArgumentException("Id is not valid!");
-    }
-
-    // get the file or folder
-    File file = getFile(objectId.getValue());
-
-    // get and check the new name
-    String newName = getStringProperty(properties, PropertyIds.NAME);
-    boolean isRename = (newName != null) && (!file.getName().equals(newName));
-    if (isRename && !isValidName(newName)) {
-      throw new CmisNameConstraintViolationException("Name is not valid!");
-    }
-
-    // get old properties
-    PropertiesImpl oldProperties = new PropertiesImpl();
-    readCustomProperties(file, oldProperties, null, new ObjectInfoImpl());
-
-    // get the type id
-    String typeId = getIdProperty(oldProperties, PropertyIds.OBJECT_TYPE_ID);
-    if (typeId == null) {
-      typeId = (file.isDirectory() ? TypeManager.FOLDER_TYPE_ID : TypeManager.DOCUMENT_TYPE_ID);
-    }
-
-    // get the creator
-    String creator = getStringProperty(oldProperties, PropertyIds.CREATED_BY);
-    if (creator == null) {
-      creator = context.getUsername();
-    }
-
-    // get creation date
-    GregorianCalendar creationDate = getDateTimeProperty(oldProperties,
-        PropertyIds.CREATION_DATE);
-    if (creationDate == null) {
-      creationDate = millisToCalendar(file.lastModified());
-    }
-
-    // compile the properties
-    Properties props = updateProperties(typeId, creator, creationDate, context.getUsername(),
-        oldProperties, properties);
-
-    // write properties
-    writePropertiesFile(file, props);
-
-    // rename file or folder if necessary
-    File newFile = file;
-    if (isRename) {
-      File parent = file.getParentFile();
-      File propFile = getPropertiesFile(file);
-      newFile = new File(parent, newName);
-      if (!file.renameTo(newFile)) {
-        // if something went wrong, throw an exception
-        throw new CmisUpdateConflictException("Could not rename object!");
-      }
-      else {
-        // set new id
-        objectId.setValue(getId(newFile));
-
-        // if it is a file, rename properties file too
-        if (newFile.isFile()) {
-          if (propFile.exists()) {
-            File newPropFile = new File(parent, newName + SHADOW_EXT);
-            propFile.renameTo(newPropFile);
-          }
-        }
-      }
-    }
-
-    return compileObjectType(newFile, null, false, false, userReadOnly, objectInfos);
-  }
-
-  /**
-   * CMIS getObject.
-   */
-  public ObjectData getObject(CallContext context, String objectId, String filter,
-      Boolean includeAllowableActions, Boolean includeAcl, ObjectInfoHolder objectInfos) {
-    debug("getObject");
-    boolean userReadOnly = checkUser(context, false);
-
-    // check id
-    if (objectId == null) {
-      throw new CmisInvalidArgumentException("Object Id must be set.");
-    }
-
-    // get the file or folder
-    File file = getFile(objectId);
-
-    // set defaults if values not set
-    boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-    boolean iacl = (includeAcl == null ? false : includeAcl.booleanValue());
-
-    // split filter
-    Set<String> filterCollection = splitFilter(filter);
-
-    // gather properties
-    return compileObjectType(file, filterCollection, iaa, iacl, userReadOnly, objectInfos);
-  }
-
-  /**
-   * CMIS getAllowableActions.
-   */
-  public AllowableActions getAllowableActions(CallContext context, String objectId) {
-    debug("getAllowableActions");
-    boolean userReadOnly = checkUser(context, false);
-
-    File file = getFile(objectId);
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Object not found!");
-    }
-
-    return compileAllowableActions(file, userReadOnly);
-  }
-
-  /**
-   * CMIS getACL.
-   */
-  public Acl getAcl(CallContext context, String repositoryId, String objectId) {
-    debug("getAcl");
-    checkUser(context, false);
-
-    // get the file or folder
-    File file = getFile(objectId);
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Object not found!");
-    }
-
-    return compileAcl(file);
-  }
-
-  /**
-   * CMIS getContentStream.
-   */
-  public ContentStream getContentStream(CallContext context, String objectId,
-      BigInteger offset, BigInteger length) {
-    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()) {
-      throw new CmisStreamNotSupportedException("Not a file!");
-    }
-
-    InputStream stream = null;
-    try {
-      stream = new BufferedInputStream(new FileInputStream(file), 4 * 1024);
-    }
-    catch (FileNotFoundException e) {
-      throw new CmisObjectNotFoundException(e.getMessage(), e);
-    }
-
-    // compile data
-    ContentStreamImpl result = new ContentStreamImpl();
-    result.setFileName(file.getName());
-    result.setLength(BigInteger.valueOf(file.length()));
-    result.setMimeType(MIMETypes.getMIMEType(file));
-    result.setStream(stream);
-
-    return result;
-  }
-
-  /**
-   * CMIS getChildren.
-   */
-  public ObjectInFolderList getChildren(CallContext context, String folderId, String filter,
-      Boolean includeAllowableActions, Boolean includePathSegment, BigInteger maxItems,
-      BigInteger skipCount, ObjectInfoHolder objectInfos) {
-    debug("getChildren");
-    boolean userReadOnly = checkUser(context, false);
-
-    // split filter
-    Set<String> filterCollection = splitFilter(filter);
-
-    // set defaults if values not set
-    boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-    boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
-
-    // skip and max
-    int skip = (skipCount == null ? 0 : skipCount.intValue());
-    if (skip < 0) {
-      skip = 0;
-    }
-
-    int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
-    if (max < 0) {
-      max = Integer.MAX_VALUE;
-    }
-
-    // get the folder
-    File folder = getFile(folderId);
-    if (!folder.isDirectory()) {
-      throw new CmisObjectNotFoundException("Not a folder!");
-    }
-
-    // set object info of the the folder
-    if (objectInfos != null) {
-      compileObjectType(folder, null, false, false, userReadOnly, objectInfos);
-    }
-
-    // prepare result
-    ObjectInFolderListImpl result = new ObjectInFolderListImpl();
-    result.setObjects(new ArrayList<ObjectInFolderData>());
-    result.setHasMoreItems(false);
-    int count = 0;
-
-    // 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;
-      }
-
-      count++;
-
-      if (skip > 0) {
-        skip--;
-        continue;
-      }
-
-      if (result.getObjects().size() >= max) {
-        result.setHasMoreItems(true);
-        continue;
-      }
-
-      // build and add child object
-      ObjectInFolderDataImpl objectInFolder = new ObjectInFolderDataImpl();
-      objectInFolder.setObject(compileObjectType(child, filterCollection, iaa, false, userReadOnly,
-          objectInfos));
-      if (ips) {
-        objectInFolder.setPathSegment(child.getName());
-      }
-
-      result.getObjects().add(objectInFolder);
-    }
-
-    result.setNumItems(BigInteger.valueOf(count));
-
-    return result;
-  }
-
-  /**
-   * CMIS getDescendants.
-   */
-  public List<ObjectInFolderContainer> getDescendants(CallContext context, String folderId,
-      BigInteger depth, String filter, Boolean includeAllowableActions, Boolean includePathSegment,
-      ObjectInfoHolder objectInfos, boolean foldersOnly) {
-    debug("getDescendants or getFolderTree");
-    boolean userReadOnly = checkUser(context, false);
-
-    // check depth
-    int d = (depth == null ? 2 : depth.intValue());
-    if (d == 0) {
-      throw new CmisInvalidArgumentException("Depth must not be 0!");
-    }
-    if (d < -1) {
-      d = -1;
-    }
-
-    // split filter
-    Set<String> filterCollection = splitFilter(filter);
-
-    // set defaults if values not set
-    boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-    boolean ips = (includePathSegment == null ? false : includePathSegment.booleanValue());
-
-    // get the folder
-    File folder = getFile(folderId);
-    if (!folder.isDirectory()) {
-      throw new CmisObjectNotFoundException("Not a folder!");
-    }
-
-    // set object info of the the folder
-    if (objectInfos != null) {
-      compileObjectType(folder, null, false, false, userReadOnly, objectInfos);
-    }
-
-    // get the tree
-    List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
-    gatherDescendants(folder, result, foldersOnly, d, filterCollection, iaa, ips, userReadOnly,
-        objectInfos);
-
-    return result;
-  }
-
-  /**
-   * CMIS getFolderParent.
-   */
-  public ObjectData getFolderParent(CallContext context, String folderId, String filter,
-      ObjectInfoHolder objectInfos) {
-    List<ObjectParentData> parents = getObjectParents(context, folderId, filter, false, false,
-        objectInfos);
-
-    if (parents.size() == 0) {
-      throw new CmisInvalidArgumentException("The root folder has no parent!");
-    }
-
-    return parents.get(0).getObject();
-  }
-
-  /**
-   * CMIS getObjectParents.
-   */
-  public List<ObjectParentData> getObjectParents(CallContext context, String objectId,
-      String filter, Boolean includeAllowableActions, Boolean includeRelativePathSegment,
-      ObjectInfoHolder objectInfos) {
-    debug("getObjectParents");
-    boolean userReadOnly = checkUser(context, false);
-
-    // split filter
-    Set<String> filterCollection = splitFilter(filter);
-
-    // set defaults if values not set
-    boolean iaa = (includeAllowableActions == null ? false : includeAllowableActions.booleanValue());
-    boolean irps = (includeRelativePathSegment == null ? false : includeRelativePathSegment
-        .booleanValue());
-
-    // get the file or folder
-    File file = getFile(objectId);
-
-    // don't climb above the root folder
-    if (fRoot.equals(file)) {
-      return Collections.emptyList();
-    }
-
-    // set object info of the the object
-    if (objectInfos != null) {
-      compileObjectType(file, null, false, false, userReadOnly, objectInfos);
-    }
-
-    // get parent folder
-    File parent = file.getParentFile();
-    ObjectData object = compileObjectType(parent, filterCollection, iaa, false, userReadOnly,
-        objectInfos);
-
-    ObjectParentDataImpl result = new ObjectParentDataImpl();
-    result.setObject(object);
-    if (irps) {
-      result.setRelativePathSegment(file.getName());
-    }
-
-    return Collections.singletonList((ObjectParentData) result);
-  }
-
-  /**
-   * CMIS getObjectByPath.
-   */
-  public ObjectData getObjectByPath(CallContext context, String folderPath, String filter,
-      boolean includeAllowableActions, boolean includeACL, ObjectInfoHolder objectInfos) {
-    debug("getObjectByPath");
-    boolean userReadOnly = checkUser(context, false);
-
-    // split filter
-    Set<String> filterCollection = splitFilter(filter);
-
-    // check path
-    if ((folderPath == null) || (!folderPath.startsWith("/"))) {
-      throw new CmisInvalidArgumentException("Invalid folder path!");
-    }
-
-    // get the file or folder
-    File file = null;
-    if (folderPath.length() == 1) {
-      file = fRoot;
-    }
-    else {
-      String path = folderPath.replace('/', File.separatorChar).substring(1);
-      file = new File(fRoot, path);
-    }
-
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Path doesn't exist.");
-    }
-
-    return compileObjectType(file, filterCollection, includeAllowableActions, includeACL,
-        userReadOnly, objectInfos);
-  }
-
-  // --- helper methods ---
-
-  /**
-   * Gather the children of a folder.
-   */
-  private void gatherDescendants(File folder, List<ObjectInFolderContainer> list,
-      boolean foldersOnly, int depth, Set<String> filter, boolean includeAllowableActions,
-      boolean includePathSegments, boolean userReadOnly, ObjectInfoHolder 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(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(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 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 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(File file, Set<String> filter,
-      boolean includeAllowableActions, boolean includeAcl, boolean userReadOnly,
-      ObjectInfoHolder objectInfos) {
-    ObjectDataImpl result = new ObjectDataImpl();
-
-    result.setProperties(compileProperties(file, filter, objectInfos));
-
-    if (includeAllowableActions) {
-      result.setAllowableActions(compileAllowableActions(file, userReadOnly));
-    }
-
-    if (includeAcl) {
-      result.setAcl(compileAcl(file));
-      result.setIsExactAcl(true);
-    }
-
-    return result;
-  }
-
-  /**
-   * Gathers all base properties of a file or folder.
-   */
-  private Properties compileProperties(File file, Set<String> orgfilter,
-      ObjectInfoHolder objectInfos) {
-    if (file == null) {
-      throw new IllegalArgumentException("File must not be null!");
-    }
-
-    // we can gather properties if the file or folder doesn't exist
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Object not found!");
-    }
-
-    // copy filter
-    Set<String> filter = (orgfilter == null ? null : new HashSet<String>(orgfilter));
-
-    // find base type
-    String typeId = null;
-    ObjectInfoImpl objectInfo = null;
-
-    if (file.isDirectory()) {
-      typeId = TypeManager.FOLDER_TYPE_ID;
-      objectInfo = new FolderInfo();
-      objectInfo.setTypeId(typeId);
-    }
-    else {
-      typeId = TypeManager.DOCUMENT_TYPE_ID;
-      objectInfo = new DocumentInfo();
-      objectInfo.setTypeId(typeId);
-    }
-
-    // let's do it
-    try {
-      PropertiesImpl result = new PropertiesImpl();
-
-      // id
-      String id = fileToId(file);
-      addPropertyId(result, typeId, filter, PropertyIds.OBJECT_ID, id);
-      objectInfo.setId(id);
-
-      // name
-      String name = file.getName();
-      addPropertyString(result, typeId, filter, PropertyIds.NAME, name);
-      objectInfo.setName(name);
-
-      // created and modified by
-      addPropertyString(result, typeId, filter, PropertyIds.CREATED_BY, USER_UNKNOWN);
-      addPropertyString(result, typeId, filter, PropertyIds.LAST_MODIFIED_BY, USER_UNKNOWN);
-      objectInfo.setCreatedBy(USER_UNKNOWN);
-
-      // creation and modification date
-      GregorianCalendar lastModified = millisToCalendar(file.lastModified());
-      addPropertyDateTime(result, typeId, filter, PropertyIds.CREATION_DATE, lastModified);
-      addPropertyDateTime(result, typeId, filter, PropertyIds.LAST_MODIFICATION_DATE,
-          lastModified);
-      objectInfo.setCreationDate(lastModified);
-      objectInfo.setLastModificationDate(lastModified);
-
-      // 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);
-        String path = getRepositoryPath(file);
-        addPropertyString(result, typeId, filter, PropertyIds.PATH, (path.length() == 0 ? "/"
-            : path));
-
-        // folder properties
-        if (!fRoot.equals(file)) {
-          addPropertyId(result, typeId, filter, PropertyIds.PARENT_ID, (fRoot.equals(file
-              .getParentFile()) ? ROOT_ID : fileToId(file.getParentFile())));
-          objectInfo.setHasParent(true);
-        }
-        else {
-          objectInfo.setHasParent(false);
-        }
-      }
-      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);
-
-        // file properties
-        addPropertyBoolean(result, typeId, filter, PropertyIds.IS_IMMUTABLE, false);
-        addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_VERSION, true);
-        addPropertyBoolean(result, typeId, filter, PropertyIds.IS_MAJOR_VERSION, true);
-        addPropertyBoolean(result, typeId, filter, PropertyIds.IS_LATEST_MAJOR_VERSION, true);
-        addPropertyString(result, typeId, filter, PropertyIds.VERSION_LABEL, file.getName());
-        addPropertyId(result, typeId, filter, PropertyIds.VERSION_SERIES_ID, fileToId(file));
-        addPropertyString(result, typeId, filter, PropertyIds.CHECKIN_COMMENT, "");
-        addPropertyInteger(result, typeId, filter, PropertyIds.CONTENT_STREAM_LENGTH, file
-            .length());
-        addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_MIME_TYPE,
-            MIMETypes.getMIMEType(file));
-        addPropertyString(result, typeId, filter, PropertyIds.CONTENT_STREAM_FILE_NAME, file
-            .getName());
-
-        objectInfo.setContentType(MIMETypes.getMIMEType(file));
-        objectInfo.setFileName(file.getName());
-      }
-
-      // read custom properties
-      readCustomProperties(file, result, filter, objectInfo);
-
-      if (filter != null) {
-        if (!filter.isEmpty()) {
-          debug("Unknown filter properties: " + filter.toString(), null);
-        }
-      }
-
-      if (objectInfos != null) {
-        objectInfos.addObjectInfo(objectInfo);
-      }
-
-      return result;
-    }
-    catch (Exception e) {
-      if (e instanceof CmisBaseException) {
-        throw (CmisBaseException) e;
-      }
-      throw new CmisRuntimeException(e.getMessage());
-    }
-  }
-
-  /**
-   * Reads and adds properties.
-   */
-  @SuppressWarnings("unchecked")
-  private void readCustomProperties(File file, PropertiesImpl properties, Set<String> filter,
-      ObjectInfoImpl objectInfo) {
-    File propFile = getPropertiesFile(file);
-
-    // if it doesn't exists, ignore it
-    if (!propFile.exists()) {
-      return;
-    }
-
-    // parse it
-    JAXBElement<CmisObjectType> obj = null;
-    try {
-      Unmarshaller u = JaxBHelper.createUnmarshaller();
-      obj = (JAXBElement<CmisObjectType>) u.unmarshal(propFile);
-    }
-    catch (Exception e) {
-      warn("Unvalid CMIS properties: " + propFile.getAbsolutePath(), e);
-    }
-
-    if ((obj == null) || (obj.getValue() == null) || (obj.getValue().getProperties() == null)) {
-      return;
-    }
-
-    // add it to properties
-    for (CmisProperty cmisProp : obj.getValue().getProperties().getProperty()) {
-      PropertyData<?> prop = Converter.convert(cmisProp);
-
-      // overwrite object info
-      if (prop instanceof PropertyString) {
-        String firstValueStr = ((PropertyString) prop).getFirstValue();
-        if (PropertyIds.NAME.equals(prop.getId())) {
-          objectInfo.setName(firstValueStr);
-        }
-        else if (PropertyIds.OBJECT_TYPE_ID.equals(prop.getId())) {
-          objectInfo.setTypeId(firstValueStr);
-        }
-        else if (PropertyIds.CREATED_BY.equals(prop.getId())) {
-          objectInfo.setCreatedBy(firstValueStr);
-        }
-        else if (PropertyIds.CONTENT_STREAM_MIME_TYPE.equals(prop.getId())) {
-          objectInfo.setContentType(firstValueStr);
-        }
-        else if (PropertyIds.CONTENT_STREAM_FILE_NAME.equals(prop.getId())) {
-          objectInfo.setFileName(firstValueStr);
-        }
-      }
-
-      if (prop instanceof PropertyDateTime) {
-        GregorianCalendar firstValueCal = ((PropertyDateTime) prop).getFirstValue();
-        if (PropertyIds.CREATION_DATE.equals(prop.getId())) {
-          objectInfo.setCreationDate(firstValueCal);
-        }
-        else if (PropertyIds.LAST_MODIFICATION_DATE.equals(prop.getId())) {
-          objectInfo.setLastModificationDate(firstValueCal);
-        }
-      }
-
-      // check filter
-      if (filter != null) {
-        if (!filter.contains(prop.getId())) {
-          continue;
-        }
-        else {
-          filter.remove(prop.getId());
-        }
-      }
-
-      // don't overwrite id
-      if (PropertyIds.OBJECT_ID.equals(prop.getId())) {
-        continue;
-      }
-
-      // don't overwrite base type
-      if (PropertyIds.BASE_TYPE_ID.equals(prop.getId())) {
-        continue;
-      }
-
-      // add it
-      properties.addProperty(prop);
-    }
-  }
-
-  /**
-   * 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) {
-    PropertiesImpl result = new PropertiesImpl();
-    Set<String> addedProps = new HashSet<String>();
-
-    if ((properties == null) || (properties.getProperties() == null)) {
-      throw new CmisConstraintException("No properties!");
-    }
-
-    // get the property definitions
-    TypeDefinition type = fTypes.getType(typeId);
-    if (type == null) {
-      throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
-    }
-
-    // check if all required properties are there
-    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!");
-      }
-
-      // skip type id
-      if (propType.getId().equals(PropertyIds.OBJECT_TYPE_ID)) {
-        continue;
-      }
-
-      // can it be set?
-      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!");
-      }
-
-      // add it
-      result.addProperty(prop);
-      addedProps.add(prop.getId());
-    }
-
-    // check if required properties are missing
-    for (PropertyDefinition<?> propDef : type.getPropertyDefinitions().values()) {
-      if (!addedProps.contains(propDef.getId())
-          && (propDef.getUpdatability() != Updatability.READONLY)) {
-        if (!addPropertyDefault(result, propDef) && propDef.isRequired()) {
-          throw new CmisConstraintException("Property '" + propDef.getId() + "' is required!");
-        }
-      }
-    }
-
-    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.
-   */
-  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 = fTypes.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;
-      }
-
-      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;
-  }
-
-  private boolean isEmptyProperty(PropertyData<?> prop) {
-    if ((prop == null) || (prop.getValues() == null)) {
-      return true;
-    }
-
-    return prop.getValues().isEmpty();
-  }
-
-  private void addPropertyId(PropertiesImpl props, String typeId, Set<String> filter,
-      String id, String value) {
-    if (!checkAddProperty(props, typeId, filter, id)) {
-      return;
-    }
-
-    if (value == null) {
-      throw new IllegalArgumentException("Value must not be null!");
-    }
-
-    props.addProperty(new PropertyIdImpl(id, value));
-  }
-
-  private void addPropertyString(PropertiesImpl props, String typeId, Set<String> filter,
-      String id, String value) {
-    if (!checkAddProperty(props, typeId, filter, id)) {
-      return;
-    }
-
-    props.addProperty(new PropertyStringImpl(id, value));
-  }
-
-  private void addPropertyInteger(PropertiesImpl props, String typeId, Set<String> filter,
-      String id, long value) {
-    if (!checkAddProperty(props, typeId, filter, id)) {
-      return;
-    }
-
-    props.addProperty(new PropertyIntegerImpl(id, BigInteger.valueOf(value)));
-  }
-
-  private void addPropertyBoolean(PropertiesImpl props, String typeId, Set<String> filter,
-      String id, boolean value) {
-    if (!checkAddProperty(props, typeId, filter, id)) {
-      return;
-    }
-
-    props.addProperty(new PropertyBooleanImpl(id, value));
-  }
-
-  private void addPropertyDateTime(PropertiesImpl props, String typeId, Set<String> filter,
-      String id, GregorianCalendar value) {
-    if (!checkAddProperty(props, typeId, filter, id)) {
-      return;
-    }
-
-    props.addProperty(new PropertyDateTimeImpl(id, value));
-  }
-
-  private 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 = fTypes.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)) {
-        return false;
-      }
-      else {
-        filter.remove(queryName);
-      }
-    }
-
-    return true;
-  }
-
-  /**
-   * Adds the default value of property if defined.
-   */
-  @SuppressWarnings("unchecked")
-  private boolean addPropertyDefault(PropertiesImpl props, PropertyDefinition<?> propDef) {
-    if ((props == null) || (props.getProperties() == null)) {
-      throw new IllegalArgumentException("Props must not be null!");
-    }
-
-    if (propDef == null) {
-      return false;
-    }
-
-    List<?> defaultValue = propDef.getDefaultValue();
-    if ((defaultValue != null) && (!defaultValue.isEmpty())) {
-      switch (propDef.getPropertyType()) {
-      case BOOLEAN:
-        props
-            .addProperty(new PropertyBooleanImpl(propDef.getId(), (List<Boolean>) defaultValue));
-        break;
-      case DATETIME:
-        props.addProperty(new PropertyDateTimeImpl(propDef.getId(),
-            (List<GregorianCalendar>) defaultValue));
-        break;
-      case DECIMAL:
-        props.addProperty(new PropertyDecimalImpl(propDef.getId(),
-            (List<BigDecimal>) defaultValue));
-        break;
-      case HTML:
-        props.addProperty(new PropertyHtmlImpl(propDef.getId(), (List<String>) defaultValue));
-        break;
-      case ID:
-        props.addProperty(new PropertyIdImpl(propDef.getId(), (List<String>) defaultValue));
-        break;
-      case INTEGER:
-        props.addProperty(new PropertyIntegerImpl(propDef.getId(),
-            (List<BigInteger>) defaultValue));
-        break;
-      case STRING:
-        props.addProperty(new PropertyStringImpl(propDef.getId(), (List<String>) defaultValue));
-        break;
-      case URI:
-        props.addProperty(new PropertyUriImpl(propDef.getId(), (List<String>) defaultValue));
-        break;
-      default:
-        throw new RuntimeException("Unknown datatype! Spec change?");
-      }
-
-      return true;
-    }
-
-    return false;
-  }
-
-  /**
-   * Compiles the allowable actions for a file or folder.
-   */
-  private AllowableActions compileAllowableActions(File file, boolean userReadOnly) {
-    if (file == null) {
-      throw new IllegalArgumentException("File must not be null!");
-    }
-
-    // we can gather properties if the file or folder doesn't exist
-    if (!file.exists()) {
-      throw new CmisObjectNotFoundException("Object not found!");
-    }
-
-    boolean isReadOnly = !file.canWrite();
-    boolean isFolder = file.isDirectory();
-    boolean isRoot = fRoot.equals(file);
-
-    Set<Action> aam = new HashSet<Action>();
-
-    // TODO XXX use enum
-//    aam.put(AllowableActionsData.ACTION_CAN_GET_OBJECT_PARENTS, !isRoot);
-//    aam.put(AllowableActionsData.ACTION_CAN_CREATE_RELATIONSHIP, false);
-//    aam.put(AllowableActionsData.ACTION_CAN_GET_PROPERTIES, true);
-//    aam.put(AllowableActionsData.ACTION_CAN_GET_RENDITIONS, false);
-//    aam.put(AllowableActionsData.ACTION_CAN_UPDATE_PROPERTIES, !userReadOnly && !isReadOnly);
-//    aam.put(AllowableActionsData.ACTION_CAN_MOVE_OBJECT, !userReadOnly);
-//    aam.put(AllowableActionsData.ACTION_CAN_DELETE_OBJECT, !userReadOnly && !isReadOnly);
-//    aam.put(AllowableActionsData.ACTION_CAN_GET_OBJECT_RELATIONSHIPS, false);
-//    aam.put(AllowableActionsData.ACTION_CAN_APPLY_POLICY, false);
-//    aam.put(AllowableActionsData.ACTION_CAN_REMOVE_POLICY, false);
-//    aam.put(AllowableActionsData.ACTION_CAN_GET_ACL, true);
-//    aam.put(AllowableActionsData.ACTION_CAN_APPLY_ACL, false);
-
-    if (isFolder) {
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_DESCENDANTS, true);
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_CHILDREN, true);
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_FOLDER_PARENT, !isRoot);
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_FOLDER_TREE, true);
-//        aam.put(AllowableActionsData.ACTION_CAN_CREATE_DOCUMENT, !userReadOnly);
-//        aam.put(AllowableActionsData.ACTION_CAN_CREATE_FOLDER, !userReadOnly);
-//        aam.put(AllowableActionsData.ACTION_CAN_DELETE_TREE, !userReadOnly && !isReadOnly);
-    }
-    else {
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_CONTENT_STREAM, true);
-//        aam.put(AllowableActionsData.ACTION_CAN_SET_CONTENT_STREAM, !userReadOnly && !isReadOnly);
-//        aam.put(AllowableActionsData.ACTION_CAN_DELETE_CONTENT_STREAM, !userReadOnly && !isReadOnly);
-//        aam.put(AllowableActionsData.ACTION_CAN_ADD_OBJECT_TO_FOLDER, false);
-//        aam.put(AllowableActionsData.ACTION_CAN_REMOVE_OBJECT_FROM_FOLDER, false);
-//        aam.put(AllowableActionsData.ACTION_CAN_CHECK_OUT, false);
-//        aam.put(AllowableActionsData.ACTION_CAN_CANCEL_CHECK_OUT, false);
-//        aam.put(AllowableActionsData.ACTION_CAN_CHECK_IN, false);
-//        aam.put(AllowableActionsData.ACTION_CAN_GET_ALL_VERSIONS, true);
-    }
-
-    AllowableActionsImpl result = new AllowableActionsImpl();
-    result.setAllowableActions(aam);
-
-    return result;
-  }
-
-  /**
-   * Compiles the ACL for a file or folder.
-   */
-  private Acl compileAcl(File file) {
-    AccessControlListImpl result = new AccessControlListImpl();
-    result.setAces(new ArrayList<Ace>());
-
-    for (Map.Entry<String, Boolean> ue : fUserMap.entrySet()) {
-      // create principal
-      AccessControlPrincipalDataImpl principal = new AccessControlPrincipalDataImpl();
-      principal.setPrincipalId(ue.getKey());
-
-      // create ACE
-      AccessControlEntryImpl entry = new AccessControlEntryImpl();
-      entry.setPrincipal(principal);
-      entry.setPermissions(new ArrayList<String>());
-      entry.getPermissions().add(CMIS_READ);
-      if (!ue.getValue().booleanValue() && file.canWrite()) {
-        entry.getPermissions().add(CMIS_WRITE);
-        entry.getPermissions().add(CMIS_ALL);
-      }
-
-      entry.setDirect(true);
-
-      // add ACE
-      result.getAces().add(entry);
-    }
-
-    return result;
-  }
-
-  /**
-   * Writes the properties for a document or folder.
-   */
-  private 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
-    CmisObjectType object = new CmisObjectType();
-    object.setProperties(Converter.convert(properties));
-
-    // write it
-    try {
-      JaxBHelper.CMIS_EXTRA_OBJECT_FACTORY.createObject(object);
-      JAXBElement<CmisObjectType> objElement = JaxBHelper.CMIS_EXTRA_OBJECT_FACTORY
-          .createObject(object);
-
-      Marshaller m = JaxBHelper.createMarshaller();
-      m.setProperty("jaxb.formatted.output", true);
-      m.marshal(objElement, propFile);
-    }
-    catch (Exception e) {
-      throw new CmisStorageException("Couldn't store properties!", e);
-    }
-  }
-
-  // --- internal stuff ---
-
-  /**
-   * Converts milliseconds into a calendar object.
-   */
-  private GregorianCalendar millisToCalendar(long millis) {
-    GregorianCalendar result = new GregorianCalendar();
-    result.setTimeZone(TimeZone.getTimeZone("GMT"));
-    result.setTimeInMillis(millis);
-
-    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 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.
-   */
-  private 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!");
-    }
-
-    return typeId;
-  }
-
-  /**
-   * Returns the first value of an id property.
-   */
-  private String getIdProperty(Properties properties, String name) {
-    PropertyData<?> property = properties.getProperties().get(name);
-    if (!(property instanceof PropertyId)) {
-      return null;
-    }
-
-    return ((PropertyId) property).getFirstValue();
-  }
-
-  /**
-   * Returns the first value of an string property.
-   */
-  private String getStringProperty(Properties properties, String name) {
-    PropertyData<?> property = properties.getProperties().get(name);
-    if (!(property instanceof PropertyString)) {
-      return null;
-    }
-
-    return ((PropertyString) property).getFirstValue();
-  }
-
-  /**
-   * Returns the first value of an datetime property.
-   */
-  private GregorianCalendar getDateTimeProperty(Properties properties, String name) {
-    PropertyData<?> property = properties.getProperties().get(name);
-    if (!(property instanceof PropertyDateTime)) {
-      return null;
-    }
-
-    return ((PropertyDateTime) property).getFirstValue();
-  }
-
-  /**
-   * Checks if the user in the given context is valid for this repository and if the user has the
-   * required permissions.
-   */
-  private boolean checkUser(CallContext context, boolean writeRequired) {
-    if (context == null) {
-      throw new CmisPermissionDeniedException("No user context!");
-    }
-
-    Boolean readOnly = fUserMap.get(context.getUsername());
-    if (readOnly == null) {
-      throw new CmisPermissionDeniedException("Unknown user!");
-    }
-
-    if (readOnly.booleanValue() && writeRequired) {
-      throw new CmisPermissionDeniedException("No write permission!");
-    }
-
-    return readOnly.booleanValue();
-  }
-
-  /**
-   * Returns the properties file of the given file.
-   */
-  private File getPropertiesFile(File file) {
-    if (file.isDirectory()) {
-      return new File(file, SHADOW_FOLDER);
-    }
-
-    return new File(file.getAbsolutePath() + SHADOW_EXT);
-  }
-
-  /**
-   * Returns the File object by id or throws an appropriate exception.
-   */
-  private File getFile(String id) {
-    try {
-      return idToFile(id);
-    }
-    catch (Exception e) {
-      throw new CmisObjectNotFoundException(e.getMessage(), e);
-    }
-  }
-
-  /**
-   * Converts an id to a File object. A simple and insecure implementation, but good enough for now.
-   */
-  private File idToFile(String id) throws Exception {
-    if ((id == null) || (id.length() == 0)) {
-      throw new CmisInvalidArgumentException("Id is not valid!");
-    }
-
-    if (id.equals(ROOT_ID)) {
-      return fRoot;
-    }
-
-    return new File(fRoot, (new String(Base64.decodeBase64(id.getBytes("ISO-8859-1")), "UTF-8"))
-        .replace('/', File.separatorChar));
-  }
-
-  /**
-   * Returns the id of a File object or throws an appropriate exception.
-   */
-  private String getId(File file) {
-    try {
-      return fileToId(file);
-    }
-    catch (Exception e) {
-      throw new CmisRuntimeException(e.getMessage());
-    }
-  }
-
-  /**
-   * Creates a File object from an id. A simple and insecure implementation, but good enough for
-   * now.
-   */
-  private String fileToId(File file) throws Exception {
-    if (file == null) {
-      throw new IllegalArgumentException("File is not valid!");
-    }
-
-    if (fRoot.equals(file)) {
-      return ROOT_ID;
-    }
-
-    String path = getRepositoryPath(file);
-
-    return new String(Base64.encodeBase64(path.getBytes("UTF-8")), "ISO-8859-1");
-  }
-
-  private String getRepositoryPath(File file) {
-    return file.getAbsolutePath().substring(fRoot.getAbsolutePath().length()).replace(
-        File.separatorChar, '/');
-  }
-
-  private void warn(String msg, Throwable t) {
-    log.warn("<" + fRepositoryId + "> " + msg, t);
-  }
-
-  private void debug(String msg) {
-    debug(msg, null);
-  }
-
-  private void debug(String msg, Throwable t) {
-    log.debug("<" + fRepositoryId + "> " + msg, t);
-  }
+	private static final String ROOT_ID = "@root@";
+	private static final String SHADOW_EXT = ".cmis.xml";
+	private static final String SHADOW_FOLDER = "cmis.xml";
+
+	private static final String USER_UNKNOWN = "<unknown>";
+
+	private static final String CMIS_READ = "cmis:read";
+	private static final String CMIS_WRITE = "cmis:write";
+	private static final String CMIS_ALL = "cmis:all";
+
+	private static final int BUFFER_SIZE = 64 * 1024;
+
+	private static final Log log = LogFactory.getLog(FileShareRepository.class);
+
+	/** Repository id */
+	private String fRepositoryId;
+	/** Root directory */
+	private File fRoot;
+	/** Types */
+	private TypeManager fTypes;
+	/** User table */
+	private Map<String, Boolean> fUserMap;
+	/** Repository info */
+	private RepositoryInfoImpl fRepositoryInfo;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param repId
+	 *            CMIS repository id
+	 * @param root
+	 *            root folder
+	 * @param types
+	 *            type manager object
+	 */
+	public FileShareRepository(String repId, String root, TypeManager types) {
+		// check repository id
+		if ((repId == null) || (repId.trim().length() == 0)) {
+			throw new IllegalArgumentException("Invalid repository id!");
+		}
+
+		fRepositoryId = repId;
+
+		// check root folder
+		if ((root == null) || (root.trim().length() == 0)) {
+			throw new IllegalArgumentException("Invalid root folder!");
+		}
+
+		fRoot = new File(root);
+		if (!fRoot.isDirectory()) {
+			throw new IllegalArgumentException("Root is not a directory!");
+		}
+
+		// set types
+		fTypes = types;
+
+		// set up user table
+		fUserMap = new HashMap<String, Boolean>();
+
+		// compile repository info
+		fRepositoryInfo = new RepositoryInfoImpl();
+
+		fRepositoryInfo.setRepositoryId(fRepositoryId);
+		fRepositoryInfo.setRepositoryName(fRepositoryId);
+		fRepositoryInfo.setRepositoryDescription(fRepositoryId);
+
+		fRepositoryInfo.setCmisVersionSupported("1.0");
+
+		fRepositoryInfo.setProductName("OpenCMIS FileShare");
+		fRepositoryInfo.setProductVersion("0.1");
+		fRepositoryInfo.setVendorName("OpenCMIS");
+
+		fRepositoryInfo.setRootFolder(ROOT_ID);
+
+		fRepositoryInfo.setThinClientUri("");
+
+		RepositoryCapabilitiesImpl capabilities = new RepositoryCapabilitiesImpl();
+		capabilities.setCapabilityAcl(CapabilityAcl.DISCOVER);
+		capabilities.setAllVersionsSearchable(false);
+		capabilities.setCapabilityJoin(CapabilityJoin.NONE);
+		capabilities.setSupportsMultifiling(false);
+		capabilities.setSupportsUnfiling(false);
+		capabilities.setSupportsVersionSpecificFiling(false);
+		capabilities.setIsPwcSearchable(false);
+		capabilities.setIsPwcUpdatable(false);
+		capabilities.setCapabilityQuery(CapabilityQuery.NONE);
+		capabilities.setCapabilityChanges(CapabilityChanges.NONE);
+		capabilities.setCapabilityContentStreamUpdates(CapabilityContentStreamUpdates.ANYTIME);
+		capabilities.setSupportsGetDescendants(true);
+		capabilities.setSupportsGetFolderTree(true);
+		capabilities.setCapabilityRendition(CapabilityRenditions.NONE);
+
+		fRepositoryInfo.setRepositoryCapabilities(capabilities);
+
+		AclCapabilitiesDataImpl aclCapability = new AclCapabilitiesDataImpl();
+		aclCapability.setSupportedPermissions(SupportedPermissions.BASIC);
+		aclCapability.setAclPropagation(AclPropagation.OBJECTONLY);
+
+		// permissions
+		List<PermissionDefinition> permissions = new ArrayList<PermissionDefinition>();
+		permissions.add(createPermission(CMIS_READ, "Read"));
+		permissions.add(createPermission(CMIS_WRITE, "Write"));
+		permissions.add(createPermission(CMIS_ALL, "All"));
+		aclCapability.setPermissionDefinitionData(permissions);
+
+		// mapping
+		List<PermissionMapping> list = new ArrayList<PermissionMapping>();
+		list.add(createMapping(PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_CREATE_FOLDER_FOLDER, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT, CMIS_WRITE));
+		list.add(createMapping(PermissionMapping.CAN_DELETE_OBJECT, CMIS_ALL));
+		list.add(createMapping(PermissionMapping.CAN_DELETE_TREE_FOLDER, CMIS_ALL));
+		list.add(createMapping(PermissionMapping.CAN_GET_ACL_OBJECT, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_CHILDREN_FOLDER, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_DESCENDENTS_FOLDER, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_PARENTS_FOLDER, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_GET_PROPERTIES_OBJECT, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_MOVE_OBJECT, CMIS_WRITE));
+		list.add(createMapping(PermissionMapping.CAN_MOVE_SOURCE, CMIS_READ));
+		list.add(createMapping(PermissionMapping.CAN_MOVE_TARGET, CMIS_WRITE));
+		list.add(createMapping(PermissionMapping.CAN_SET_CONTENT_DOCUMENT, CMIS_WRITE));
+		list.add(createMapping(PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, CMIS_WRITE));
+		list.add(createMapping(PermissionMapping.CAN_VIEW_CONTENT_OBJECT, CMIS_READ));
+		Map<String, PermissionMapping> map = new LinkedHashMap<String, PermissionMapping>();
+		for (PermissionMapping pm : list) {
+			map.put(pm.getKey(), pm);
+		}
+		aclCapability.setPermissionMappingData(map);
+
+		fRepositoryInfo.setAclCapabilities(aclCapability);
+	}
+
+	private PermissionDefinition createPermission(String permission, String description) {
+		PermissionDefinitionDataImpl pd = new PermissionDefinitionDataImpl();
+		pd.setPermission(permission);
+		pd.setDescription(description);
+
+		return pd;
+	}
+
+	private PermissionMapping createMapping(String key, String permission) {
+		PermissionMappingDataImpl pm = new PermissionMappingDataImpl();
+		pm.setKey(key);
+		pm.setPermissions(Collections.singletonList(permission));
+
+		return pm;
+	}
+
+	/**
+	 * Adds a user to the repository.
+	 */
+	public void addUser(String user, boolean readOnly) {
+		if ((user == null) || (user.length() == 0)) {
+			return;
+		}
+
+		fUserMap.put(user, readOnly);
+	}
+
+	// --- the public stuff ---
+
+	/**
+	 * Returns the repository id.
+	 */
+	public String getRepositoryId() {
+		return fRepositoryId;
+	}
+
+	/**
+	 * CMIS getRepositoryInfo.
+	 */
+	public RepositoryInfo getRepositoryInfo(CallContext context) {
+		debug("getRepositoryInfo");
+		checkUser(context, false);
+
+		return fRepositoryInfo;
+	}
+
+	/**
+	 * CMIS getTypesChildren.
+	 */
+	public TypeDefinitionList getTypesChildren(CallContext context, String typeId, boolean includePropertyDefinitions,
+			BigInteger maxItems, BigInteger skipCount) {
+		debug("getTypesChildren");
+		checkUser(context, false);
+
+		return fTypes.getTypesChildren(context, typeId, includePropertyDefinitions, maxItems, skipCount);
+	}
+
+	/**
+	 * CMIS getTypeDefinition.
+	 */
+	public TypeDefinition getTypeDefinition(CallContext context, String typeId) {
+		debug("getTypeDefinition");
+		checkUser(context, false);
+
+		return fTypes.getTypeDefinition(context, typeId);
+	}
+
+	/**
+	 * CMIS getTypesDescendants.
+	 */
+	public List<TypeDefinitionContainer> getTypesDescendants(CallContext context, String typeId, BigInteger depth,
+			Boolean includePropertyDefinitions) {
+		debug("getTypesDescendants");
+		checkUser(context, false);
+
+		return fTypes.getTypesDescendants(context, typeId, depth, includePropertyDefinitions);
+	}
+
+	/**
+	 * Create* dispatch for AtomPub.
+	 */
+	public ObjectData create(CallContext context, Properties properties, String folderId, ContentStream contentStream,
+			VersioningState versioningState, ObjectInfoHolder objectInfos) {
+		debug("create");
+		boolean userReadOnly = checkUser(context, true);
+
+		String typeId = getTypeId(properties);
+		TypeDefinition type = fTypes.getType(typeId);
+		if (type == null) {
+			throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+		}
+
+		String objectId = null;
+		if (type.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) {
+			objectId = createDocument(context, properties, folderId, contentStream, versioningState);
+		} else if (type.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) {
+			objectId = createFolder(context, properties, folderId);
+		} else {
+			throw new CmisObjectNotFoundException("Cannot create object of type '" + typeId + "'!");
+		}
+
+		return compileObjectType(getFile(objectId), null, false, false, userReadOnly, objectInfos);
+	}
+
+	/**
+	 * CMIS createDocument.
+	 */
+	public String createDocument(CallContext context, Properties properties, String folderId,
+			ContentStream contentStream, VersioningState versioningState) {
+		debug("createDocument");
+		checkUser(context, true);
+
+		// check properties
+		if ((properties == null) || (properties.getProperties() == null)) {
+			throw new CmisInvalidArgumentException("Properties must be set!");
+		}
+
+		// check versioning state
+		if (VersioningState.NONE != versioningState) {
+			throw new CmisConstraintException("Versioning not supported!");
+		}
+
+		// check type
+		String typeId = getTypeId(properties);
+		TypeDefinition type = fTypes.getType(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);
+
+		// check the name
+		String name = getStringProperty(properties, PropertyIds.NAME);
+		if (!isValidName(name)) {
+			throw new CmisNameConstraintViolationException("Name is not valid!");
+		}
+
+		// get parent File
+		File parent = getFile(folderId);
+		if (!parent.isDirectory()) {
+			throw new CmisObjectNotFoundException("Parent is not a folder!");
+		}
+
+		// check the file
+		File newFile = new File(parent, name);
+		if (newFile.exists()) {
+			throw new CmisNameConstraintViolationException("Document already exists!");
+		}
+
+		// create the file
+		try {
+			newFile.createNewFile();
+		} catch (IOException e) {
+			throw new CmisStorageException("Could not create file: " + e.getMessage());
+		}
+
+		// 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);
+			}
+		}
+
+		// write properties
+		writePropertiesFile(newFile, props);
+
+		return getId(newFile);
+	}
+
+	/**
+	 * CMIS createDocumentFromSource.
+	 */
+	public String createDocumentFromSource(CallContext context, String sourceId, Properties properties,
+			String folderId, VersioningState versioningState) {
+
+		// check versioning state
+		if (VersioningState.NONE != versioningState) {
+			throw new CmisConstraintException("Versioning not supported!");
+		}
+
+		// get parent File
+		File parent = getFile(folderId);
+		if (!parent.isDirectory()) {
+			throw new CmisObjectNotFoundException("Parent is not a folder!");
+		}
+
+		// get source File
+		File source = getFile(sourceId);
+		if (!source.isFile()) {
+			throw new CmisObjectNotFoundException("Source is not a document!");
+		}
+
+		// file name
+		String name = source.getName();
+
+		// get properties
+		PropertiesImpl sourceProperties = new PropertiesImpl();
+		readCustomProperties(source, sourceProperties, null, new ObjectInfoImpl());
+
+		// get the type id
+		String typeId = getIdProperty(sourceProperties, PropertyIds.OBJECT_TYPE_ID);
+		if (typeId == null) {
+			typeId = TypeManager.DOCUMENT_TYPE_ID;
+		}
+
+		// 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))) {
+				continue;
+			}
+
+			newProperties.addProperty(prop);
+		}
+
+		// replace properties
+		if (properties != null) {
+			// find new name
+			String newName = getStringProperty(properties, PropertyIds.NAME);
+			if (newName != null) {
+				if (!isValidName(newName)) {
+					throw new CmisNameConstraintViolationException("Name is not valid!");
+				}
+				name = newName;
+			}
+
+			// get the property definitions
+			TypeDefinition type = fTypes.getType(typeId);
+			if (type == null) {
+				throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+			}
+
+			// replace with new values
+			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.READWRITE)) {
+					throw new CmisConstraintException("Property '" + prop.getId() + "' cannot be updated!");
+				}
+
+				// empty properties are invalid
+				if (isEmptyProperty(prop)) {
+					throw new CmisConstraintException("Property '" + prop.getId() + "' must not be empty!");
+				}
+
+				newProperties.addProperty(prop);
+			}
+		}
+
+		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()));
+		addPropertyString(newProperties, typeId, null, PropertyIds.LAST_MODIFIED_BY, context.getUsername());
+
+		// check the file
+		File newFile = new File(parent, name);
+		if (newFile.exists()) {
+			throw new CmisNameConstraintViolationException("Document already exists.");
+		}
+
+		// create the file
+		try {
+			newFile.createNewFile();
+		} catch (IOException e) {
+			throw new CmisStorageException("Could not create file: " + e.getMessage(), e);
+		}
+
+		// copy content
+		try {
+			OutputStream out = new BufferedOutputStream(new FileOutputStream(newFile));
+			InputStream in = new BufferedInputStream(new FileInputStream(source));
+
+			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 roead or write content: " + e.getMessage(), e);
+		}
+
+		// write properties
+		writePropertiesFile(newFile, newProperties);
+
+		return getId(newFile);
+	}
+
+	/**
+	 * CMIS createFolder.
+	 */
+	public String createFolder(CallContext context, Properties properties, String folderId) {
+		debug("createFolder");
+		checkUser(context, true);
+
+		// check properties
+		if ((properties == null) || (properties.getProperties() == null)) {
+			throw new CmisInvalidArgumentException("Properties must be set!");
+		}
+
+		// check type
+		String typeId = getTypeId(properties);
+		TypeDefinition type = fTypes.getType(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);
+
+		// check the name
+		String name = getStringProperty(properties, PropertyIds.NAME);
+		if (!isValidName(name)) {
+			throw new CmisNameConstraintViolationException("Name is not valid.");
+		}
+
+		// get parent File
+		File parent = getFile(folderId);
+		if (!parent.isDirectory()) {
+			throw new CmisObjectNotFoundException("Parent is not a folder!");
+		}
+
+		// create the folder
+		File newFolder = new File(parent, name);
+		if (!newFolder.mkdir()) {
+			throw new CmisStorageException("Could not create folder!");
+		}
+
+		// write properties
+		writePropertiesFile(newFolder, props);
+
+		return getId(newFolder);
+	}
+
+	/**
+	 * CMIS moveObject.
+	 */
+	public ObjectData moveObject(CallContext context, Holder<String> objectId, String targetFolderId,
+			ObjectInfoHolder objectInfos) {
+		debug("moveObject");
+		boolean userReadOnly = checkUser(context, true);
+
+		if (objectId == null) {
+			throw new CmisInvalidArgumentException("Id is not valid!");
+		}
+
+		// get the file and parent
+		File file = getFile(objectId.getValue());
+		File parent = getFile(targetFolderId);
+
+		// build new path
+		File newFile = new File(parent, file.getName());
+		if (newFile.exists()) {
+			throw new CmisStorageException("Object already exists!");
+		}
+
+		// move it
+		if (!file.renameTo(newFile)) {
+			throw new CmisStorageException("Move failed!");
+		} else {
+			// set new id
+			objectId.setValue(getId(newFile));
+
+			// if it is a file, move properties file too
+			if (newFile.isFile()) {
+				File propFile = getPropertiesFile(file);
+				if (propFile.exists()) {
+					File newPropFile = new File(parent, propFile.getName());
+					propFile.renameTo(newPropFile);
+				}
+			}
+		}
+
+		return compileObjectType(newFile, null, false, false, userReadOnly, objectInfos);
+	}
+
+	/**
+	 * CMIS setContentStream and deleteContentStream.
+	 */
+	public void setContentStream(CallContext context, Holder<String> objectId, Boolean overwriteFlag,
+			ContentStream contentStream) {
+		debug("setContentStream or deleteContentStream");
+		checkUser(context, true);
+
+		if (objectId == null) {
+			throw new CmisInvalidArgumentException("Id is not valid!");
+		}
+
+		// get the file
+		File file = getFile(objectId.getValue());
+		if (!file.isFile()) {
+			throw new CmisStreamNotSupportedException("Not a file!");
+		}
+
+		// check overwrite
+		boolean owf = (overwriteFlag == null ? true : overwriteFlag.booleanValue());
+		if (!owf && file.length() > 0) {
+			throw new CmisContentAlreadyExistsException("Content already exists!");
+		}
+
+		try {
+			OutputStream out = new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE);
+
+			if ((contentStream == null) || (contentStream.getStream() == null)) {
+				// delete content
+				out.write(new byte[0]);
+			} else {
+				// set content
+				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);
+				}
+
+				in.close();
+			}
+
+			out.close();
+		} catch (Exception e) {
+			throw new CmisStorageException("Could not write content: " + e.getMessage(), e);
+		}
+	}
+
+	/**
+	 * CMIS deleteObject.
+	 */
+	public void deleteObject(CallContext context, String objectId) {
+		debug("deleteObject");
+		checkUser(context, true);
+
+		// get the file or folder
+		File file = getFile(objectId);
+		if (!file.exists()) {
+			throw new CmisObjectNotFoundException("Object not found!");
+		}
+
+		// check if it is a folder and if it is empty
+		if (!isFolderEmpty(file)) {
+			throw new CmisConstraintException("Folder is not empty!");
+		}
+
+		// delete properties and actual file
+		getPropertiesFile(file).delete();
+		if (!file.delete()) {
+			throw new CmisStorageException("Deletion failed!");
+		}
+	}
+
+	/**
+	 * CMIS deleteTree.
+	 */
+	public FailedToDeleteData deleteTree(CallContext context, String folderId, Boolean continueOnFailure) {
+		debug("deleteTree");
+		checkUser(context, true);
+
+		boolean cof = (continueOnFailure == null ? false : continueOnFailure.booleanValue());
+
+		// get the file or folder
+		File file = getFile(folderId);
+
+		FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
+		result.setIds(new ArrayList<String>());
+
+		// if it is a folder, remove it recursively
+		if (file.isDirectory()) {
+			deleteFolder(file, cof, result);
+		} else {
+			getPropertiesFile(file).delete();
+			if (!file.delete()) {
+				result.getIds().add(getId(file));
+			}
+		}
+
+		return result;
+	}
+
+	/**
+	 * CMIS updateProperties.
+	 */
+	public ObjectData updateProperties(CallContext context, Holder<String> objectId, Properties properties,
+			ObjectInfoHolder objectInfos) {
+		debug("updateProperties");
+		boolean userReadOnly = checkUser(context, true);
+
+		if (objectId == null) {
+			throw new CmisInvalidArgumentException("Id is not valid!");
+		}
+
+		// get the file or folder
+		File file = getFile(objectId.getValue());
+
+		// get and check the new name
+		String newName = getStringProperty(properties, PropertyIds.NAME);
+		boolean isRename = (newName != null) && (!file.getName().equals(newName));
+		if (isRename && !isValidName(newName)) {
+			throw new CmisNameConstraintViolationException("Name is not valid!");
+		}
+
+		// get old properties
+		PropertiesImpl oldProperties = new PropertiesImpl();
+		readCustomProperties(file, oldProperties, null, new ObjectInfoImpl());
+
+		// get the type id
+		String typeId = getIdProperty(oldProperties, PropertyIds.OBJECT_TYPE_ID);
+		if (typeId == null) {
+			typeId = (file.isDirectory() ? TypeManager.FOLDER_TYPE_ID : TypeManager.DOCUMENT_TYPE_ID);
+		}
+
+		// get the creator
+		String creator = getStringProperty(oldProperties, PropertyIds.CREATED_BY);
+		if (creator == null) {
+			creator = context.getUsername();
+		}
+
+		// get creation date
+		GregorianCalendar creationDate = getDateTimeProperty(oldProperties, PropertyIds.CREATION_DATE);
+		if (creationDate == null) {
+			creationDate = millisToCalendar(file.lastModified());
+		}
+
+		// compile the properties
+		Properties props = updateProperties(typeId, creator, creationDate, context.getUsername(), oldProperties,
+				properties);
+
+		// write properties
+		writePropertiesFile(file, props);
+
+		// rename file or folder if necessary
+		File newFile = file;
+		if (isRename) {
+			File parent = file.getParentFile();
+			File propFile = getPropertiesFile(file);
+			newFile = new File(parent, newName);
+			if (!file.renameTo(newFile)) {
+				// if something went wrong, throw an exception
+				throw new CmisUpdateConflictException("Could not rename object!");
+			} else {

[... 1254 lines stripped ...]