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;
  }
  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;
  }
      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);
        }
      }
  // 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;
  }
  // 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);
  }