@Override
  public void deleteContentStream(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      Holder<String> changeToken,
      ExtensionsData extension) {

    exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

    Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
    try {
      lock.lock();

      // //////////////////
      // Exception
      // //////////////////
      Document document = contentService.getDocument(repositoryId, objectId.getValue());
      exceptionService.objectNotFound(DomainType.OBJECT, document, document.getId());
      exceptionService.constraintContentStreamRequired(repositoryId, document);

      // //////////////////
      // Body of the method
      // //////////////////
      contentService.deleteContentStream(callContext, repositoryId, objectId);

      nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());

    } finally {
      lock.unlock();
    }
  }
  @Override
  public String createDocumentFromSource(
      CallContext callContext,
      String repositoryId,
      String sourceId,
      Properties properties,
      String folderId,
      VersioningState versioningState,
      List<String> policies,
      Acl addAces,
      Acl removeAces) {
    Document original = contentService.getDocument(repositoryId, sourceId);
    DocumentTypeDefinition td =
        (DocumentTypeDefinition)
            typeManager.getTypeDefinition(repositoryId, original.getObjectType());

    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.invalidArgumentRequired("properties", properties);
    exceptionService.invalidArgumentRequiredParentFolderId(repositoryId, folderId);
    Folder parentFolder = contentService.getFolder(repositoryId, folderId);
    exceptionService.objectNotFoundParentFolder(repositoryId, folderId, parentFolder);
    exceptionService.permissionDenied(
        callContext, repositoryId, PermissionMapping.CAN_CREATE_FOLDER_FOLDER, parentFolder);

    // //////////////////
    // Specific Exception
    // //////////////////
    exceptionService.constraintBaseTypeId(repositoryId, properties, BaseTypeId.CMIS_DOCUMENT);
    exceptionService.constraintAllowedChildObjectTypeId(parentFolder, properties);
    exceptionService.constraintPropertyValue(
        repositoryId, td, properties, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID));
    exceptionService.constraintControllableVersionable(td, versioningState, null);
    versioningState =
        (td.isVersionable() && versioningState == null) ? VersioningState.MAJOR : versioningState;
    exceptionService.constraintCotrollablePolicies(td, policies, properties);
    exceptionService.constraintCotrollableAcl(td, addAces, removeAces, properties);
    exceptionService.constraintPermissionDefined(repositoryId, addAces, null);
    exceptionService.constraintPermissionDefined(repositoryId, removeAces, null);
    exceptionService.nameConstraintViolation(properties, parentFolder);

    // //////////////////
    // Body of the method
    // //////////////////
    Document document =
        contentService.createDocumentFromSource(
            callContext,
            repositoryId,
            properties,
            parentFolder,
            original,
            versioningState,
            policies,
            addAces,
            removeAces);
    return document.getId();
  }
  @Override
  public void setContentStream(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      boolean overwriteFlag,
      ContentStream contentStream,
      Holder<String> changeToken) {

    exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

    Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
    try {
      lock.lock();
      // //////////////////
      // General Exception
      // //////////////////

      exceptionService.invalidArgumentRequired("contentStream", contentStream);
      Document doc = (Document) contentService.getContent(repositoryId, objectId.getValue());
      exceptionService.objectNotFound(DomainType.OBJECT, doc, objectId.getValue());
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
      DocumentTypeDefinition td =
          (DocumentTypeDefinition) typeManager.getTypeDefinition(repositoryId, doc.getObjectType());
      exceptionService.constraintImmutable(repositoryId, doc, td);

      // //////////////////
      // Specific Exception
      // //////////////////
      exceptionService.contentAlreadyExists(doc, overwriteFlag);
      exceptionService.streamNotSupported(td, contentStream);
      exceptionService.updateConflict(doc, changeToken);
      exceptionService.versioning(doc);
      Folder parent = contentService.getParent(repositoryId, objectId.getValue());
      exceptionService.objectNotFoundParentFolder(repositoryId, objectId.getValue(), parent);

      // //////////////////
      // Body of the method
      // //////////////////
      String oldId = objectId.getValue();

      // TODO Externalize versioningState
      if (doc.isPrivateWorkingCopy()) {
        Document result = contentService.replacePwc(callContext, repositoryId, doc, contentStream);
        objectId.setValue(result.getId());
      } else {
        Document result =
            contentService.createDocumentWithNewStream(
                callContext, repositoryId, doc, contentStream);
        objectId.setValue(result.getId());
      }

      nemakiCachePool.get(repositoryId).removeCmisCache(oldId);
    } finally {
      lock.unlock();
    }
  }
  @Override
  public ObjectData getObject(
      CallContext callContext,
      String repositoryId,
      String objectId,
      String filter,
      Boolean includeAllowableActions,
      IncludeRelationships includeRelationships,
      String renditionFilter,
      Boolean includePolicyIds,
      Boolean includeAcl,
      ExtensionsData extension) {

    exceptionService.invalidArgumentRequired("objectId", objectId);

    Lock lock = threadLockService.getReadLock(repositoryId, objectId);
    try {
      lock.lock();

      // //////////////////
      // General Exception
      // //////////////////
      Content content = contentService.getContent(repositoryId, objectId);
      // WORK AROUND: getObject(versionSeriesId) is interpreted as
      // getDocumentOflatestVersion
      if (content == null) {
        VersionSeries versionSeries = contentService.getVersionSeries(repositoryId, objectId);
        if (versionSeries != null) {
          content = contentService.getDocumentOfLatestVersion(repositoryId, objectId);
        }
      }
      exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);

      // //////////////////
      // Body of the method
      // //////////////////
      ObjectData object =
          compileService.compileObjectData(
              callContext,
              repositoryId,
              content,
              filter,
              includeAllowableActions,
              includeRelationships,
              null,
              includeAcl);

      return object;
    } finally {
      lock.unlock();
    }
  }
  @Override
  public String createRelationship(
      CallContext callContext,
      String repositoryId,
      Properties properties,
      List<String> policies,
      Acl addAces,
      Acl removeAces,
      ExtensionsData extension) {
    String objectTypeId = DataUtil.getIdProperty(properties, PropertyIds.OBJECT_TYPE_ID);
    RelationshipTypeDefinition td =
        (RelationshipTypeDefinition) typeManager.getTypeDefinition(repositoryId, objectTypeId);
    // //////////////////
    // Exception
    // //////////////////
    exceptionService.invalidArgumentRequiredCollection("properties", properties.getPropertyList());
    String sourceId = DataUtil.getIdProperty(properties, PropertyIds.SOURCE_ID);
    if (sourceId != null) {
      Content source = contentService.getContent(repositoryId, sourceId);
      if (source == null) exceptionService.constraintAllowedSourceTypes(td, source);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_CREATE_RELATIONSHIP_SOURCE, source);
    }
    String targetId = DataUtil.getIdProperty(properties, PropertyIds.TARGET_ID);
    if (targetId != null) {
      Content target = contentService.getContent(repositoryId, targetId);
      if (target == null) exceptionService.constraintAllowedTargetTypes(td, target);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_CREATE_RELATIONSHIP_TARGET, target);
    }

    exceptionService.constraintBaseTypeId(repositoryId, properties, BaseTypeId.CMIS_RELATIONSHIP);
    exceptionService.constraintPropertyValue(
        repositoryId, td, properties, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID));
    exceptionService.constraintCotrollablePolicies(td, policies, properties);
    exceptionService.constraintCotrollableAcl(td, addAces, removeAces, properties);
    exceptionService.constraintPermissionDefined(repositoryId, addAces, null);
    exceptionService.constraintPermissionDefined(repositoryId, removeAces, null);
    exceptionService.nameConstraintViolation(properties, null);

    // //////////////////
    // Body of the method
    // //////////////////
    Relationship relationship =
        contentService.createRelationship(
            callContext, repositoryId, properties, policies, addAces, removeAces, extension);
    nemakiCachePool.get(repositoryId).removeCmisCache(relationship.getSourceId());
    nemakiCachePool.get(repositoryId).removeCmisCache(relationship.getTargetId());

    return relationship.getId();
  }
  @Override
  public void updateProperties(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      Properties properties,
      Holder<String> changeToken) {

    exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

    Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
    try {
      lock.lock();

      // //////////////////
      // Exception
      // //////////////////
      Content content =
          checkExceptionBeforeUpdateProperties(
              callContext, repositoryId, objectId, properties, changeToken);

      // //////////////////
      // Body of the method
      // //////////////////
      contentService.updateProperties(callContext, repositoryId, properties, content);

      nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
    } finally {
      lock.unlock();
    }
  }
  private Content checkExceptionBeforeUpdateProperties(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      Properties properties,
      Holder<String> changeToken) {
    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.invalidArgumentRequiredCollection("properties", properties.getPropertyList());
    Content content = contentService.getContent(repositoryId, objectId.getValue());
    exceptionService.objectNotFound(DomainType.OBJECT, content, objectId.getValue());
    if (content.isDocument()) {
      Document d = (Document) content;
      exceptionService.versioning(d);
      exceptionService.constraintUpdateWhenCheckedOut(repositoryId, callContext.getUsername(), d);
      TypeDefinition typeDef = typeManager.getTypeDefinition(repositoryId, d);
      exceptionService.constraintImmutable(repositoryId, d, typeDef);
    }
    exceptionService.permissionDenied(
        callContext, repositoryId, PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT, content);
    exceptionService.updateConflict(content, changeToken);

    TypeDefinition tdf = typeManager.getTypeDefinition(repositoryId, content);
    exceptionService.constraintPropertyValue(repositoryId, tdf, properties, objectId.getValue());

    return content;
  }
  @Override
  public AllowableActions getAllowableActions(
      CallContext callContext, String repositoryId, String objectId) {

    exceptionService.invalidArgumentRequired("objectId", objectId);

    Lock lock = threadLockService.getReadLock(repositoryId, objectId);

    try {
      lock.lock();

      // //////////////////
      // General Exception
      // //////////////////
      Content content = contentService.getContent(repositoryId, objectId);
      exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
      // NOTE: The permission key doesn't exist according to CMIS
      // specification.

      // //////////////////
      // Body of the method
      // //////////////////
      return compileService.compileAllowableActions(callContext, repositoryId, content);

    } finally {
      lock.unlock();
    }
  }
  @Override
  public ContentStream getContentStream(
      CallContext callContext,
      String repositoryId,
      String objectId,
      String streamId,
      BigInteger offset,
      BigInteger length) {

    exceptionService.invalidArgumentRequired("objectId", objectId);

    Lock lock = threadLockService.getReadLock(repositoryId, objectId);
    try {
      lock.lock();

      // //////////////////
      // General Exception
      // //////////////////
      Content content = contentService.getContent(repositoryId, objectId);
      exceptionService.objectNotFound(DomainType.OBJECT, content, objectId);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);

      // //////////////////
      // Body of the method
      // //////////////////
      if (streamId == null) {
        return getContentStreamInternal(repositoryId, content, offset, length);
      } else {
        return getRenditionStream(repositoryId, content, streamId);
      }
    } finally {
      lock.unlock();
    }
  }
  @Override
  public List<RenditionData> getRenditions(
      CallContext callContext,
      String repositoryId,
      String objectId,
      String renditionFilter,
      BigInteger maxItems,
      BigInteger skipCount,
      ExtensionsData extension) {

    Lock lock = threadLockService.getReadLock(repositoryId, objectId);
    try {
      lock.lock();

      List<Rendition> renditions = contentService.getRenditions(repositoryId, objectId);

      List<RenditionData> results = new ArrayList<RenditionData>();
      for (Rendition rnd : renditions) {
        RenditionDataImpl data =
            new RenditionDataImpl(
                rnd.getId(),
                rnd.getMimetype(),
                BigInteger.valueOf(rnd.getLength()),
                rnd.getKind(),
                rnd.getTitle(),
                BigInteger.valueOf(rnd.getWidth()),
                BigInteger.valueOf(rnd.getHeight()),
                rnd.getRenditionDocumentId());
        results.add(data);
      }
      return results;
    } finally {
      lock.unlock();
    }
  }
  /** Filtering check to a list of contents based on the permission */
  @Override
  public <T> List<T> getFiltered(CallContext callContext, String repositoryId, List<T> contents) {
    List<T> result = new ArrayList<T>();

    // Validation
    // TODO refine the logic
    if (CollectionUtils.isEmpty(contents)) {
      return null;
    }

    // Filtering
    for (T _content : contents) {
      Content content = (Content) _content;
      Acl acl = contentService.calculateAcl(repositoryId, content);

      Boolean filtered =
          checkPermission(
              callContext,
              repositoryId,
              PermissionMapping.CAN_GET_PROPERTIES_OBJECT,
              acl,
              content.getType(),
              content);
      if (filtered) {
        result.add(_content);
      }
    }
    return result;
  }
  @Override
  public void appendContentStream(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      Holder<String> changeToken,
      ContentStream contentStream,
      boolean isLastChunk,
      ExtensionsData extension) {

    exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

    Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
    try {
      lock.lock();

      // //////////////////
      // General Exception
      // //////////////////

      exceptionService.invalidArgumentRequired("contentStream", contentStream);
      Document doc = (Document) contentService.getContent(repositoryId, objectId.getValue());
      exceptionService.objectNotFound(DomainType.OBJECT, doc, objectId.getValue());
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_SET_CONTENT_DOCUMENT, doc);
      DocumentTypeDefinition td =
          (DocumentTypeDefinition) typeManager.getTypeDefinition(repositoryId, doc.getObjectType());
      exceptionService.constraintImmutable(repositoryId, doc, td);

      // //////////////////
      // Specific Exception
      // //////////////////
      exceptionService.streamNotSupported(td, contentStream);
      exceptionService.updateConflict(doc, changeToken);
      exceptionService.versioning(doc);

      // //////////////////
      // Body of the method
      // //////////////////
      contentService.appendAttachment(
          callContext, repositoryId, objectId, changeToken, contentStream, isLastChunk, extension);

      nemakiCachePool.get(repositoryId).removeCmisCache(objectId.getValue());
    } finally {
      lock.unlock();
    }
  }
 @Override
 public void constraintAlreadyCheckedOut(String repositoryId, Document document) {
   VersionSeries vs = contentService.getVersionSeries(repositoryId, document);
   if (vs.isVersionSeriesCheckedOut()) {
     if (!(document.isPrivateWorkingCopy())) {
       constraint(document.getId(), "The version series is already checked out");
     }
   }
 }
 @Override
 public void invalidArgumentChangeEventNotAvailable(
     String repositoryId, Holder<String> changeLogToken) {
   if (changeLogToken != null && changeLogToken.getValue() != null) {
     Change change = contentService.getChangeEvent(repositoryId, changeLogToken.getValue());
     if (change == null)
       invalidArgument("changeLogToken:" + changeLogToken.getValue() + " does not exist");
   }
 }
  @Override
  public String createItem(
      CallContext callContext,
      String repositoryId,
      Properties properties,
      String folderId,
      List<String> policies,
      Acl addAces,
      Acl removeAces,
      ExtensionsData extension) {
    // //////////////////
    // General Exception
    // //////////////////
    TypeDefinition td =
        typeManager.getTypeDefinition(repositoryId, DataUtil.getObjectTypeId(properties));
    Folder parentFolder = contentService.getFolder(repositoryId, folderId);
    exceptionService.objectNotFoundParentFolder(repositoryId, folderId, parentFolder);
    exceptionService.invalidArgumentRequiredCollection("properties", properties.getPropertyList());

    // //////////////////
    // Specific Exception
    // //////////////////
    exceptionService.constraintBaseTypeId(repositoryId, properties, BaseTypeId.CMIS_ITEM);
    exceptionService.constraintPropertyValue(
        repositoryId, td, properties, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID));
    exceptionService.constraintCotrollablePolicies(td, policies, properties);
    exceptionService.constraintCotrollableAcl(td, addAces, removeAces, properties);

    // //////////////////
    // Body of the method
    // //////////////////
    Item item =
        contentService.createItem(
            callContext,
            repositoryId,
            properties,
            folderId,
            policies,
            addAces,
            removeAces,
            extension);
    return item.getId();
  }
  @Override
  public String createFolder(
      CallContext callContext,
      String repositoryId,
      Properties properties,
      String folderId,
      List<String> policies,
      Acl addAces,
      Acl removeAces,
      ExtensionsData extension) {
    FolderTypeDefinition td =
        (FolderTypeDefinition)
            typeManager.getTypeDefinition(repositoryId, DataUtil.getObjectTypeId(properties));
    Folder parentFolder = contentService.getFolder(repositoryId, folderId);

    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.objectNotFoundParentFolder(repositoryId, folderId, parentFolder);
    exceptionService.permissionDenied(
        callContext, repositoryId, PermissionMapping.CAN_CREATE_FOLDER_FOLDER, parentFolder);

    // //////////////////
    // Specific Exception
    // //////////////////
    exceptionService.constraintBaseTypeId(repositoryId, properties, BaseTypeId.CMIS_FOLDER);
    exceptionService.constraintAllowedChildObjectTypeId(parentFolder, properties);
    exceptionService.constraintPropertyValue(
        repositoryId, td, properties, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID));
    exceptionService.constraintCotrollablePolicies(td, policies, properties);
    exceptionService.constraintCotrollableAcl(td, addAces, removeAces, properties);
    exceptionService.constraintPermissionDefined(repositoryId, addAces, null);
    exceptionService.constraintPermissionDefined(repositoryId, removeAces, null);
    exceptionService.nameConstraintViolation(properties, parentFolder);

    // //////////////////
    // Body of the method
    // //////////////////
    Folder folder =
        contentService.createFolder(callContext, repositoryId, properties, parentFolder);
    return folder.getId();
  }
 @Override
 public void constraintObjectsStillExist(String repositoryId, String objectTypeId) {
   if (contentService.existContent(repositoryId, objectTypeId)) {
     String msg =
         "There still exists objects of the specified object type"
             + " [objectTypeId = "
             + objectTypeId
             + "]";
     throw new CmisConstraintException(msg, HTTP_STATUS_CODE_409);
   }
 }
  @Override
  public void moveObject(
      CallContext callContext,
      String repositoryId,
      Holder<String> objectId,
      String sourceFolderId,
      String targetFolderId) {

    exceptionService.invalidArgumentRequiredHolderString("objectId", objectId);

    Lock lock = threadLockService.getWriteLock(repositoryId, objectId.getValue());
    try {
      lock.lock();
      // //////////////////
      // General Exception
      // //////////////////
      exceptionService.invalidArgumentRequiredString("sourceFolderId", sourceFolderId);
      exceptionService.invalidArgumentRequiredString("targetFolderId", targetFolderId);
      Content content = contentService.getContent(repositoryId, objectId.getValue());
      exceptionService.objectNotFound(DomainType.OBJECT, content, objectId.getValue());
      Folder source = contentService.getFolder(repositoryId, sourceFolderId);
      exceptionService.objectNotFound(DomainType.OBJECT, source, sourceFolderId);
      Folder target = contentService.getFolder(repositoryId, targetFolderId);
      exceptionService.objectNotFound(DomainType.OBJECT, target, targetFolderId);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_MOVE_OBJECT, content);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_MOVE_SOURCE, source);
      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_MOVE_TARGET, target);

      // //////////////////
      // Body of the method
      // //////////////////
      contentService.move(callContext, repositoryId, content, target);

      nemakiCachePool.get(repositoryId).removeCmisCache(content.getId());
    } finally {
      lock.unlock();
    }
  }
 @Override
 public void constraintContentStreamRequired(String repositoryId, Document document) {
   String objectTypeId = document.getObjectType();
   DocumentTypeDefinition td =
       (DocumentTypeDefinition) typeManager.getTypeDefinition(repositoryId, objectTypeId);
   if (td.getContentStreamAllowed() == ContentStreamAllowed.REQUIRED) {
     if (document.getAttachmentNodeId() == null
         || contentService.getAttachment(repositoryId, document.getAttachmentNodeId()) == null) {
       constraint(document.getId(), "This document type does not allow no content stream");
     }
   }
 }
  // TODO Show also stack errors
  @Override
  public void permissionDenied(
      CallContext context, String repositoryId, String key, Content content) {
    if (content == null) {
      System.out.println();
    }

    String baseTypeId = content.getType();
    Acl acl = contentService.calculateAcl(repositoryId, content);
    permissionDeniedInternal(context, repositoryId, key, acl, baseTypeId, content);

    permissionTopLevelFolder(context, repositoryId, key, content);
  }
  @Override
  public ObjectData getObjectByPath(
      CallContext callContext,
      String repositoryId,
      String path,
      String filter,
      Boolean includeAllowableActions,
      IncludeRelationships includeRelationships,
      String renditionFilter,
      Boolean includePolicyIds,
      Boolean includeAcl,
      ExtensionsData extension) {
    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.invalidArgumentRequired("objectId", path);
    // FIXME path is not preserved in db.
    Content content = contentService.getContentByPath(repositoryId, path);

    // TODO create objectNotFoundByPath method
    exceptionService.objectNotFoundByPath(DomainType.OBJECT, content, path);

    Lock lock = threadLockService.getReadLock(repositoryId, content.getId());
    try {
      lock.lock();

      exceptionService.permissionDenied(
          callContext, repositoryId, PermissionMapping.CAN_GET_PROPERTIES_OBJECT, content);

      // //////////////////
      // Body of the method
      // //////////////////
      return compileService.compileObjectData(
          callContext,
          repositoryId,
          content,
          filter,
          includeAllowableActions,
          includeRelationships,
          renditionFilter,
          includeAcl);
    } finally {
      lock.unlock();
    }
  }
  private ContentStream getRenditionStream(String repositoryId, Content content, String streamId) {
    if (!content.isDocument() && !content.isFolder()) {
      exceptionService.constraint(
          content.getId(),
          "getRenditionStream cannnot be invoked to other than document or folder type.");
    }

    exceptionService.constraintRenditionStreamDownload(content, streamId);

    Rendition rendition = contentService.getRendition(repositoryId, streamId);

    BigInteger length = BigInteger.valueOf(rendition.getLength());
    String mimeType = rendition.getMimetype();
    InputStream is = rendition.getInputStream();
    ContentStream cs = new ContentStreamImpl("preview_" + streamId, length, mimeType, is);

    return cs;
  }
 @Override
 public void constraintUpdateWhenCheckedOut(
     String repositoryId, String currentUserId, Document document) {
   VersionSeries vs = contentService.getVersionSeries(repositoryId, document);
   if (vs.isVersionSeriesCheckedOut()) {
     if (document.isPrivateWorkingCopy()) {
       // Can update by only the use who has checked it out
       String whoCheckedOut = vs.getVersionSeriesCheckedOutBy();
       if (!currentUserId.equals(whoCheckedOut)) {
         constraint(
             document.getId(),
             "This private working copy can be modified only by the user who has checked it out. ");
       }
     } else {
       // All versions except for PWC are locked.
       constraint(document.getId(), "All versions except for PWC are locked when checked out.");
     }
   }
 }
  // TODO Implement HTTP range(offset and length of stream), though it is not
  // obligatory.
  private ContentStream getContentStreamInternal(
      String repositoryId, Content content, BigInteger rangeOffset, BigInteger rangeLength) {
    if (!content.isDocument()) {
      exceptionService.constraint(
          content.getId(), "getContentStream cannnot be invoked to other than document type.");
    }
    Document document = (Document) content;
    exceptionService.constraintContentStreamDownload(repositoryId, document);
    AttachmentNode attachment =
        contentService.getAttachment(repositoryId, document.getAttachmentNodeId());
    attachment.setRangeOffset(rangeOffset);
    attachment.setRangeLength(rangeLength);

    // Set content stream
    BigInteger length = BigInteger.valueOf(attachment.getLength());
    String name = attachment.getName();
    String mimeType = attachment.getMimeType();
    InputStream is = attachment.getInputStream();
    ContentStream cs = new ContentStreamImpl(name, length, mimeType, is);

    return cs;
  }
  @Override
  public String createPolicy(
      CallContext callContext,
      String repositoryId,
      Properties properties,
      String folderId,
      List<String> policies,
      Acl addAces,
      Acl removeAces,
      ExtensionsData extension) {
    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.invalidArgumentRequiredCollection("properties", properties.getPropertyList());
    // NOTE: folderId is ignored because policy is not filable in Nemaki
    TypeDefinition td =
        typeManager.getTypeDefinition(
            repositoryId, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_TYPE_ID));
    exceptionService.constraintPropertyValue(
        repositoryId, td, properties, DataUtil.getIdProperty(properties, PropertyIds.OBJECT_ID));

    // //////////////////
    // Specific Exception
    // //////////////////
    exceptionService.constraintBaseTypeId(repositoryId, properties, BaseTypeId.CMIS_POLICY);
    // exceptionService.constraintAllowedChildObjectTypeId(parent,
    // properties);
    exceptionService.constraintCotrollablePolicies(td, policies, properties);
    exceptionService.constraintCotrollableAcl(td, addAces, removeAces, properties);
    // exceptionService.nameConstraintViolation(properties, parent);

    // //////////////////
    // Body of the method
    // //////////////////
    Policy policy =
        contentService.createPolicy(
            callContext, repositoryId, properties, policies, addAces, removeAces, extension);
    return policy.getId();
  }
  private Boolean isAllowableBaseType(
      String key, String baseType, Content content, String repositoryId) {
    // NavigationServices
    if (PermissionMapping.CAN_GET_DESCENDENTS_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_GET_CHILDREN_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_GET_FOLDER_PARENT_OBJECT.equals(key))
      if (contentService.isRoot(repositoryId, content)) {
        return false;
      } else {
        return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
      }
    if (PermissionMapping.CAN_GET_PARENTS_FOLDER.equals(key))
      if (contentService.isRoot(repositoryId, content)) {
        return false;
      } else {
        return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
            || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
            || BaseTypeId.CMIS_POLICY.value().equals(baseType)
            || BaseTypeId.CMIS_ITEM.value().equals(baseType));
      }

    // Object Services
    if (PermissionMapping.CAN_CREATE_DOCUMENT_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_CREATE_FOLDER_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_CREATE_POLICY_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_CREATE_RELATIONSHIP_SOURCE.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.equals(baseType));
    if (PermissionMapping.CAN_CREATE_RELATIONSHIP_TARGET.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_GET_PROPERTIES_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_UPDATE_PROPERTIES_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_MOVE_OBJECT.equals(key))
      if (contentService.isRoot(repositoryId, content)) {
        return false;
      } else {
        return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
            || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
            || BaseTypeId.CMIS_POLICY.value().equals(baseType)
            || BaseTypeId.CMIS_ITEM.value().equals(baseType));
      }
    if (PermissionMapping.CAN_MOVE_TARGET.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_MOVE_SOURCE.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    if (PermissionMapping.CAN_DELETE_OBJECT.equals(key))
      if (contentService.isRoot(repositoryId, content)) {
        return false;
      } else {
        return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
            || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
            || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
            || BaseTypeId.CMIS_POLICY.value().equals(baseType)
            || BaseTypeId.CMIS_ITEM.value().equals(baseType));
      }
    if (PermissionMapping.CAN_VIEW_CONTENT_OBJECT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_SET_CONTENT_DOCUMENT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_DELETE_CONTENT_DOCUMENT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_DELETE_TREE_FOLDER.equals(key))
      return BaseTypeId.CMIS_FOLDER.value().equals(baseType);
    // Filing Services
    if (PermissionMapping.CAN_ADD_TO_FOLDER_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_ADD_TO_FOLDER_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_REMOVE_FROM_FOLDER_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_REMOVE_FROM_FOLDER_FOLDER.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType));
    // Versioning Services
    if (PermissionMapping.CAN_CHECKOUT_DOCUMENT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_CANCEL_CHECKOUT_DOCUMENT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_CHECKIN_DOCUMENT.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    if (PermissionMapping.CAN_GET_ALL_VERSIONS_VERSION_SERIES.equals(key))
      return BaseTypeId.CMIS_DOCUMENT.value().equals(baseType);
    // Relationship Services
    if (PermissionMapping.CAN_GET_OBJECT_RELATIONSHIPS_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    // Policy Services
    if (PermissionMapping.CAN_ADD_POLICY_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_ADD_POLICY_POLICY.equals(key))
      return BaseTypeId.CMIS_POLICY.value().equals(baseType);
    if (PermissionMapping.CAN_REMOVE_POLICY_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_REMOVE_POLICY_POLICY.equals(key))
      return BaseTypeId.CMIS_POLICY.value().equals(baseType);
    if (PermissionMapping.CAN_GET_APPLIED_POLICIES_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    // ACL Services
    if (PermissionMapping.CAN_GET_ACL_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));
    if (PermissionMapping.CAN_APPLY_ACL_OBJECT.equals(key))
      return (BaseTypeId.CMIS_DOCUMENT.value().equals(baseType)
          || BaseTypeId.CMIS_FOLDER.value().equals(baseType)
          || BaseTypeId.CMIS_RELATIONSHIP.value().equals(baseType)
          || BaseTypeId.CMIS_POLICY.value().equals(baseType)
          || BaseTypeId.CMIS_ITEM.value().equals(baseType));

    return false;
  }
  // TODO Merge arguments(acl, content)
  // FIXME Refactor duplicate isAllowableBaseType
  @Override
  public Boolean checkPermission(
      CallContext callContext,
      String repositoryId,
      String key,
      Acl acl,
      String baseType,
      Content content) {

    // All permission checks must go through baseType check
    if (!isAllowableBaseType(key, baseType, content, repositoryId)) return false;

    // Admin always pass a permission check
    CallContextImpl cci = (CallContextImpl) callContext;
    Boolean _isAdmin = (Boolean) cci.get(CallContextKey.IS_ADMIN);
    boolean isAdmin = (_isAdmin == null) ? false : _isAdmin;
    if (isAdmin) return true;

    // PWC doesn't accept any actions from a non-owner user
    // TODO admin can manipulate PWC even when it is checked out ?
    if (content.isDocument()) {
      Document document = (Document) content;
      if (document.isPrivateWorkingCopy()) {
        VersionSeries vs = contentService.getVersionSeries(repositoryId, document);
        if (!callContext.getUsername().equals(vs.getVersionSeriesCheckedOutBy())) {
          return false;
        }
      }
    }

    // Relation has no ACL stored in DB.
    // Though some actions are defined in the specs,
    // Some other direct actions is needed to be set here.
    if (content.isRelationship()) {
      Relationship relationship = (Relationship) content;
      return checkRelationshipPermission(callContext, repositoryId, key, relationship);
    }

    // Void Acl fails(but Admin can do an action)
    if (acl == null) return false;

    // Even if a user has multiple ACEs, the permissions is pushed into
    // Set<String> and remain unique.
    // Get ACL for the current user
    String userName = callContext.getUsername();
    List<Ace> aces = acl.getAllAces();
    Set<String> userPermissions = new HashSet<String>();
    Set<String> groups = principalService.getGroupIdsContainingUser(repositoryId, userName);
    for (Ace ace : aces) {
      // Filter ace which has not permissions
      if (ace.getPermissions() == null) continue;

      // Add user permissions
      if (ace.getPrincipalId().equals(userName)) {
        userPermissions.addAll(ace.getPermissions());
      }
      // Add inherited permissions which user inherits
      if (CollectionUtils.isNotEmpty(groups) && groups.contains(ace.getPrincipalId())) {
        userPermissions.addAll(ace.getPermissions());
      }
    }

    // Check mapping between the user and the content
    return checkCalculatedPermissions(repositoryId, key, userPermissions);
  }
 @Override
 public void invalidArgumentRootFolder(String repositoryId, Content content) {
   if (contentService.isRoot(repositoryId, content))
     invalidArgument("Cannot specify the root folder as an input parameter");
 }
  @Override
  public FailedToDeleteData deleteTree(
      CallContext callContext,
      String repositoryId,
      String folderId,
      Boolean allVersions,
      UnfileObject unfileObjects,
      Boolean continueOnFailure,
      ExtensionsData extension) {
    // //////////////////
    // Inner classes
    // //////////////////
    class DeleteTask implements Callable<Boolean> {
      private CallContext callContext;
      private String repositoryId;
      private Content content;
      private Boolean allVersions;

      public DeleteTask() {}

      public DeleteTask(
          CallContext callContext, String repositoryId, Content content, Boolean allVersions) {
        this.callContext = callContext;
        this.repositoryId = repositoryId;
        this.content = content;
        this.allVersions = allVersions;
      }

      @Override
      public Boolean call() throws Exception {
        try {
          objectServiceInternal.deleteObjectInternal(
              callContext, repositoryId, content, allVersions, true);
          return false;
        } catch (Exception e) {
          return true;
        }
      }
    }

    class WrappedExecutorService {
      private ExecutorService service;
      private Folder folder;

      private WrappedExecutorService() {};

      public WrappedExecutorService(ExecutorService service, Folder folder) {
        this.service = service;
        this.folder = folder;
      }

      public ExecutorService getService() {
        return service;
      }

      public Folder getFolder() {
        return folder;
      }
    }

    class DeleteService {
      private Map<String, Future<Boolean>> failureIds;
      private WrappedExecutorService parentService;
      private CallContext callContext;
      private String repositoryId;
      private Content content;
      private Boolean allVersions;

      public DeleteService() {}

      public DeleteService(
          Map<String, Future<Boolean>> failureIds,
          WrappedExecutorService parentService,
          CallContext callContext,
          String repositoryId,
          Content content,
          Boolean allVersions) {
        super();
        this.failureIds = failureIds;
        this.parentService = parentService;
        this.callContext = callContext;
        this.repositoryId = repositoryId;
        this.content = content;
        this.allVersions = allVersions;
      }

      public void execute() {
        if (content.isDocument()) {
          Future<Boolean> result =
              parentService
                  .getService()
                  .submit(new DeleteTask(callContext, repositoryId, content, allVersions));
          failureIds.put(content.getId(), result);
        } else if (content.isFolder()) {
          WrappedExecutorService childrenService =
              new WrappedExecutorService(Executors.newFixedThreadPool(threadMax), (Folder) content);

          List<Content> children = contentService.getChildren(repositoryId, content.getId());
          if (CollectionUtils.isNotEmpty(children)) {
            for (Content child : children) {
              DeleteService deleteService =
                  new DeleteService(
                      this.failureIds,
                      childrenService,
                      callContext,
                      repositoryId,
                      child,
                      allVersions);
              deleteService.execute();
            }
          }

          // wait til newService ends
          childrenService.getService().shutdown();
          try {
            childrenService.getService().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
          } catch (InterruptedException e) {
            log.error(e, e);
          }

          // Lastly, delete self
          Future<Boolean> result =
              parentService
                  .getService()
                  .submit(new DeleteTask(callContext, repositoryId, content, allVersions));
          failureIds.put(content.getId(), result);
        }
      }
    }

    // //////////////////
    // General Exception
    // //////////////////
    exceptionService.invalidArgumentRequiredString("objectId", folderId);
    Folder folder = contentService.getFolder(repositoryId, folderId);
    exceptionService.permissionDenied(
        callContext, repositoryId, PermissionMapping.CAN_DELETE_TREE_FOLDER, folder);
    exceptionService.constraintDeleteRootFolder(repositoryId, folderId);

    // //////////////////
    // Specific Exception
    // //////////////////
    if (folder == null)
      exceptionService.constraint(folderId, "deleteTree cannot be invoked on a non-folder object");

    // //////////////////
    // Body of the method
    // //////////////////
    // Delete descendants
    Map<String, Future<Boolean>> failureIds = new HashMap<String, Future<Boolean>>();

    DeleteService deleteService =
        new DeleteService(
            failureIds,
            new WrappedExecutorService(Executors.newFixedThreadPool(threadMax), folder),
            callContext,
            repositoryId,
            folder,
            allVersions);
    deleteService.execute();

    solrUtil.callSolrIndexing(repositoryId);

    // Check FailedToDeleteData
    // FIXME Consider orphans that was failed to be deleted
    FailedToDeleteDataImpl fdd = new FailedToDeleteDataImpl();
    List<String> ids = new ArrayList<String>();
    for (Entry<String, Future<Boolean>> entry : failureIds.entrySet()) {
      Boolean failed;
      try {
        failed = entry.getValue().get();
        if (failed) {
          ids.add(entry.getKey());
        }
      } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      } catch (ExecutionException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
    fdd.setIds(ids);
    return fdd;
  }
  @Override
  public ObjectData create(
      CallContext callContext,
      String repositoryId,
      Properties properties,
      String folderId,
      ContentStream contentStream,
      VersioningState versioningState,
      List<String> policies,
      ExtensionsData extension) {

    String typeId = DataUtil.getObjectTypeId(properties);
    TypeDefinition type = typeManager.getTypeDefinition(repositoryId, typeId);
    if (type == null) {
      throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
    }

    String objectId = null;
    // TODO ACE can be set !
    if (type.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) {
      objectId =
          createDocument(
              callContext,
              repositoryId,
              properties,
              folderId,
              contentStream,
              versioningState,
              null,
              null,
              null);
    } else if (type.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) {
      objectId =
          createFolder(
              callContext, repositoryId, properties, folderId, policies, null, null, extension);
    } else if (type.getBaseTypeId() == BaseTypeId.CMIS_RELATIONSHIP) {
      objectId =
          createRelationship(
              callContext, repositoryId, properties, policies, null, null, extension);
    } else if (type.getBaseTypeId() == BaseTypeId.CMIS_POLICY) {
      objectId =
          createPolicy(
              callContext, repositoryId, properties, folderId, policies, null, null, extension);
    } else if (type.getBaseTypeId() == BaseTypeId.CMIS_ITEM) {
      objectId =
          createItem(
              callContext, repositoryId, properties, folderId, policies, null, null, extension);
    } else {
      throw new CmisObjectNotFoundException("Cannot create object of type '" + typeId + "'!");
    }

    ObjectData object =
        compileService.compileObjectData(
            callContext,
            repositoryId,
            contentService.getContent(repositoryId, objectId),
            null,
            false,
            IncludeRelationships.NONE,
            null,
            false);

    return object;
  }