@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 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 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 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;
  }