/**
   * Unlock a Content Relation for other user access.
   *
   * @param id Objid of Content Relation
   * @param param XML TaskParam
   * @return Result XML data structure
   * @throws ContentRelationNotFoundException e
   * @throws LockingException e
   * @throws InvalidContentException e
   * @throws MissingMethodParameterException e
   * @throws SystemException e
   * @throws OptimisticLockingException e
   * @throws InvalidStatusException Thrown if resource is not locked.
   */
  @Override
  public String unlock(final String id, final String param)
      throws ContentRelationNotFoundException, LockingException, MissingMethodParameterException,
          SystemException, OptimisticLockingException, InvalidContentException,
          InvalidStatusException, XmlCorruptedException {

    final ContentRelationCreate cr = setContentRelation(id);
    final TaskParamHandler taskParameter = XmlUtility.parseTaskParam(param);

    Utility.checkOptimisticLockingCriteria(
        cr.getProperties().getLastModificationDate(),
        taskParameter.getLastModificationDate(),
        "Content relation " + id);
    if (cr.getProperties().isLocked()) {
      lockHandler.unlock(cr.getObjid());

      cr.getProperties().setLockStatus(LockStatus.UNLOCKED);
      cr.getProperties().setLockOwnerId(null);
      cr.getProperties().setLockOwnerId(null);
      cr.getProperties().setLockDate((DateTime) null);
    }

    fireContentRelationModified(cr, this.contentRelationXmlProvider.getContentRelationXml(cr));

    return Utility.prepareReturnXml(cr.getProperties().getLastModificationDate(), null);
  }
  /**
   * Submit the Content Relation.
   *
   * @param id objid of ContentRelation
   * @param param Task parameter
   * @return XML result
   * @throws ContentRelationNotFoundException e
   * @throws LockingException e
   * @throws InvalidStatusException e
   * @throws MissingMethodParameterException e
   * @throws SystemException e
   * @throws OptimisticLockingException e
   * @throws InvalidContentException e
   */
  @Override
  public String submit(final String id, final String param)
      throws ContentRelationNotFoundException, LockingException, InvalidStatusException,
          MissingMethodParameterException, SystemException, OptimisticLockingException,
          InvalidContentException, XmlCorruptedException {

    final ContentRelationCreate cr = setContentRelation(id);
    final TaskParamHandler taskParameter = XmlUtility.parseTaskParam(param);
    checkLocked(cr);

    validateToSubmitStatus(cr);

    // check optimistic locking criteria
    Utility.checkOptimisticLockingCriteria(
        cr.getProperties().getLastModificationDate(),
        taskParameter.getLastModificationDate(),
        "Content relation " + id);

    cr.getProperties().setStatus(StatusType.SUBMITTED);
    // set status comment
    if (taskParameter.getComment() != null) {
      cr.getProperties().setStatusComment(taskParameter.getComment());
    } else {
      cr.getProperties().setStatusComment("Status changed to 'submitted'");
    }

    cr.persistProperties(true);

    // load metadata content to resource
    enrichWithMetadataContent(cr);
    fireContentRelationModified(cr, this.contentRelationXmlProvider.getContentRelationXml(cr));

    return Utility.prepareReturnXml(cr.getProperties().getLastModificationDate(), null);
  }
  /**
   * Assign persistent identifier to a Content-relation object.
   *
   * @param id The Id of the Content-relation witch is to assign with an ObjectPid.
   * @param taskParam XML snippet with parameter for the persistent identifier system.
   * @return The assigned persistent identifier for the Content-relation.
   * @throws ContentRelationNotFoundException Thrown if the object with id is does not exist or is
   *     no Item.
   * @throws LockingException Thrown if the Item is locked
   * @throws MissingMethodParameterException Thrown if a parameter is missing within {@code
   *     taskParam}.
   * @throws OptimisticLockingException Thrown if Item was altered in the mean time.
   * @throws PidAlreadyAssignedException Thrown if a Content-relation is already assigned a PID.
   * @throws SystemException Thrown in case of internal error.
   */
  @Override
  public String assignObjectPid(final String id, final String taskParam)
      throws ContentRelationNotFoundException, LockingException, MissingMethodParameterException,
          OptimisticLockingException, SystemException, PidAlreadyAssignedException,
          XmlCorruptedException {

    final ContentRelationCreate cr = setContentRelation(id);
    if (cr.getProperties().getPid() != null) {
      throw new PidAlreadyAssignedException(
          "A content relation with id " + id + " is already assigned a PID");
    }
    final TaskParamHandler taskParameter = XmlUtility.parseTaskParam(taskParam);
    checkLocked(cr);
    Utility.checkOptimisticLockingCriteria(
        cr.getProperties().getLastModificationDate(),
        taskParameter.getLastModificationDate(),
        "Content-relation " + cr.getObjid());

    String pid = taskParameter.getPid();
    if (pid == null) {
      // get PID from external PID System
      pid = getPid(id, taskParam);
    }
    cr.getProperties().setPid(pid);
    cr.persist();
    return prepareResponse(cr, pid);
  }
  /**
   * See Interface for functional description.
   *
   * @param xmlData aggregationDefinition as xml in aggregationDefinition schema.
   * @return Returns the XML representation of the resource.
   * @throws MissingMethodParameterException ex
   * @throws ScopeNotFoundException ex
   * @throws SystemException ex
   * @see de.escidoc.core.sm.business.interfaces
   *     .AggregationDefinitionHandlerInterface#create(java.lang.String)
   */
  @Override
  @Transactional(rollbackFor = {SystemException.class, RuntimeException.class})
  public String create(final String xmlData)
      throws MissingMethodParameterException, ScopeNotFoundException, SystemException {
    if (xmlData == null || xmlData.length() == 0) {
      throw new MissingMethodParameterException("xml may not be null");
    }

    // parse
    final StaxParser sp = new StaxParser();
    final AggregationDefinitionStaxHandler handler = new AggregationDefinitionStaxHandler(sp);
    sp.addHandler(handler);
    try {
      sp.parse(xmlData);
    } catch (final Exception e) {
      throw new SystemException(e);
    }

    final String scopeId = handler.getAggregationDefinition().getScope().getId();
    final Scope scope = scopesDao.retrieve(scopeId);

    // get AggregationDefinitionObject to insert aggregation-definition
    // into database
    final AggregationDefinition aggregationDefinition = handler.getAggregationDefinition();
    aggregationDefinition.setCreatorId(utility.getCurrentUserId());
    aggregationDefinition.setCreationDate(new Timestamp(System.currentTimeMillis()));
    aggregationDefinition.setScope(scope);

    dao.save(aggregationDefinition);
    handler.setAggregationDefinition(aggregationDefinition);

    // AggregationStatisticDataSelectors
    for (final AggregationStatisticDataSelector selector :
        handler.getAggregationStatisticDataSelectors()) {
      dao.save(selector);
    }
    aggregationDefinition.setAggregationStatisticDataSelectors(
        handler.getAggregationStatisticDataSelectors());

    // AggregationTables
    for (final AggregationTable aggregationTable : handler.getAggregationTables()) {
      dao.save(aggregationTable);
    }
    aggregationDefinition.setAggregationTables(handler.getAggregationTables());

    // Get databaseTableVos for all Aggregation-Tables
    // defined in Aggregation Definition
    final Collection<DatabaseTableVo> databaseTableVos =
        generateAggregationDatabaseTableVos(aggregationDefinition);
    if (databaseTableVos != null) {
      for (final DatabaseTableVo databaseTableVo : databaseTableVos) {
        // create aggregation table in Database
        dbAccessor.createTable(databaseTableVo);
      }
    }

    return renderer.render(aggregationDefinition);
  }
  /**
   * @param id objid of ContentRelation
   * @param param Task parameter
   * @return XML result
   * @throws ContentRelationNotFoundException e
   * @throws LockingException e
   * @throws InvalidStatusException e
   * @throws MissingMethodParameterException e
   * @throws SystemException e
   * @throws OptimisticLockingException e
   * @throws InvalidContentException e
   */
  @Override
  public String release(final String id, final String param)
      throws ContentRelationNotFoundException, LockingException, InvalidStatusException,
          MissingMethodParameterException, SystemException, OptimisticLockingException,
          InvalidContentException, XmlCorruptedException {

    final ContentRelationCreate cr = setContentRelation(id);
    final TaskParamHandler taskParameter = XmlUtility.parseTaskParam(param);
    checkLocked(cr);
    checkReleased(cr);
    if (cr.getProperties().getStatus() != StatusType.SUBMITTED) {
      throw new InvalidStatusException(
          "The object is not in state '"
              + Constants.STATUS_SUBMITTED
              + "' and can not be "
              + Constants.STATUS_RELEASED
              + '.');
    }
    Utility.checkOptimisticLockingCriteria(
        cr.getProperties().getLastModificationDate(),
        taskParameter.getLastModificationDate(),
        "Content relation " + id);

    cr.getProperties().setStatus(StatusType.RELEASED);

    // set status comment
    if (taskParameter.getComment() != null) {
      cr.getProperties().setStatusComment(taskParameter.getComment());
    } else {
      cr.getProperties().setStatusComment("Status changed to 'released'");
    }

    cr.persistProperties(true);

    // load metadata content to resource
    enrichWithMetadataContent(cr);
    fireContentRelationModified(cr, this.contentRelationXmlProvider.getContentRelationXml(cr));

    return Utility.prepareReturnXml(cr.getProperties().getLastModificationDate(), null);
  }
  @Override
  public StartElement startElement(final StartElement element) throws XMLStreamException {
    final String curPath = parser.getCurPath();
    if (this.inside) {
      this.deepLevel++;
      writeElement(element);

      final int attCount = element.getAttributeCount();
      for (int i = 0; i < attCount; i++) {
        final Attribute curAtt = element.getAttribute(i);
        writeAttribute(
            curAtt.getNamespace(),
            element.getLocalName(),
            curAtt.getLocalName(),
            curAtt.getValue(),
            curAtt.getPrefix());
      }
    } else {
      if (curPath.endsWith("components/component")) {
        final int indexObjid = element.indexOfAttribute(null, "objid");
        final int indexHref = element.indexOfAttribute(Constants.XLINK_NS_URI, "href");
        if (!(indexObjid > -1 && element.getAttribute(indexObjid).getValue().length() > 0
            || indexHref > -1
                && Utility.getId(element.getAttribute(indexHref).getValue()).length() > 0)) {

          // start new component if there is no ID
          final ByteArrayOutputStream out = new ByteArrayOutputStream();

          this.writer = XmlUtility.createXmlStreamWriter(out);
          outputStreams.add(out);

          this.inside = true;
          this.deepLevel++;
          writeElement(element);

          final int attCount = element.getAttributeCount();
          for (int i = 0; i < attCount; i++) {
            final Attribute curAtt = element.getAttribute(i);
            writeAttribute(
                curAtt.getNamespace(),
                element.getLocalName(),
                curAtt.getLocalName(),
                curAtt.getValue(),
                curAtt.getPrefix());
          }
        }
      }
    }
    return element;
  }
  @Override
  public StartElement startElement(final StartElement element)
      throws TripleStoreSystemException, WebserverSystemException, InvalidContentException {
    final String curPath = parser.getCurPath();
    if (curPath.startsWith(this.componentPath) && curPath.equals(this.componentPath)) {
      // do my job
      // save componentId
      final int indexObjid = element.indexOfAttribute(null, "objid");
      final int indexHref = element.indexOfAttribute(Constants.XLINK_NS_URI, "href");
      if (indexObjid >= 0 || indexHref >= 0) {
        final String componentId =
            indexObjid >= 0
                ? element.getAttribute(indexObjid).getValue()
                : Utility.getId(element.getAttribute(indexHref).getValue());

        if (componentId.length() > 0) {
          // check if component exists
          boolean componentExists = false;
          final List<String> existingComponents =
              TripleStoreUtility.getInstance().getComponents(this.itemId);
          for (final String existingComponent : existingComponents) {
            if (existingComponent.equals(componentId)) {
              componentExists = true;
              break;
            }
          }
          if (!componentExists) {
            throw new InvalidContentException(
                "Component with id "
                    + componentId
                    + " does not exist in item "
                    + this.itemId
                    + '.');
          }
        }
      }
    }
    return element;
  }
  /**
   * Update Content Relation.
   *
   * @param id objid of Content Relation
   * @param xmlData XML representation of Content Relation
   * @return XML representation of updated Content Relation
   * @throws ContentRelationNotFoundException Thrown if no Content Relation could be found under
   *     provided objid
   * @throws OptimisticLockingException Thrown if resource is updated in the meantime and last
   *     modification date differs
   * @throws InvalidStatusException Thrown if resource has invalid status to update
   * @throws MissingAttributeValueException Thrown if attribute value is missing
   * @throws LockingException Thrown if resource is locked through other user
   * @throws MissingMethodParameterException Thrown if method parameter is missing
   * @throws ReferencedResourceNotFoundException Thrown if referenced resource does not exist.
   * @throws RelationPredicateNotFoundException Thrown if the predicate is not registered.
   * @throws InvalidContentException Thrown if content is invalid
   * @throws SystemException Thrown if internal error occur
   */
  @Override
  public String update(final String id, final String xmlData)
      throws ContentRelationNotFoundException, OptimisticLockingException, InvalidContentException,
          InvalidStatusException, LockingException, MissingAttributeValueException,
          MissingMethodParameterException, SystemException, ReferencedResourceNotFoundException,
          RelationPredicateNotFoundException, XmlCorruptedException {

    // instance of stored Content Relation
    final ContentRelationCreate cr = setContentRelation(id);
    // instance of the update representation
    final ContentRelationCreate updatedCR = parseContentRelation(xmlData);

    Utility.checkOptimisticLockingCriteria(
        cr.getProperties().getLastModificationDate(),
        updatedCR.getLastModificationDate(),
        "Content Relation " + cr.getObjid());

    enrichWithMetadataContent(cr);

    checkLocked(cr);
    checkReleased(cr);

    validate(updatedCR);

    // now compare this.contentRelation with updatedCR and transfer data
    boolean resourceChanged = false;
    if (cr.merge(updatedCR) > 0) {
      cr.persist();
      resourceChanged = true;
    }
    final String result = this.contentRelationXmlProvider.getContentRelationXml(cr);

    if (resourceChanged) {
      fireContentRelationModified(cr, result);
    }

    return result;
  }
 /**
  * Prepare the assignment response message.
  *
  * <p>Preconditions: The TripleStore must be in sync with the repository.
  *
  * @param cr content relation
  * @param pid The new assigned PID.
  * @return response message
  * @throws WebserverSystemException Thrown in case of internal error.
  * @throws TripleStoreSystemException Thrown in case of TripleStore error.
  */
 private String prepareResponse(final ContentRelationCreate cr, final String pid) {
   return Utility.prepareReturnXml(
       cr.getProperties().getLastModificationDate(), "<pid>" + pid + "</pid>\n");
 }