/**
   * The startRenderTask creates a new OSD of the object type render_task in the repository in the
   * specified folder. This task object is read by the render server and updated with status updates
   * by the render process (which are written to custom metadata
   * /meta/metaset=render_output/messages/). It is up to the client to interpret the messages. The
   * overall status of the render task can be determined by looking at the procstate attribute:<br>
   * While the task is waiting for the render server to pick it up, its procstate is set to
   * "waiting". While the task is running, its procstate is set to "running". After the task is
   * finished, the task's procstate attribute is set to "finished". If the task should fail, the
   * task's procstate is set to "failed".
   *
   * <h2>Required permissions</h2>
   *
   * CREATE_OBJECT
   *
   * @param cmd a Map of HTTP request parameters containing:<br>
   *     <ul>
   *       <li>command=startrendertask
   *       <li>[name]=optional name of the task, defaults to "RenderTask"
   *       <li>ticket=session ticket
   *       <li>parentid = id of the folder where the task-object will be created
   *       <li>metadata = xml to use as the metadata metaset=render_input field. It must contain at
   *           least the element
   *           <pre>{@code <renderTaskName>}</pre>
   *           which holds the name of the render task that will be performed. It should contain the
   *           sourceId element to specify the id of the source content object to be rendered.<br>
   *           Example for metadata content:<br>
   *           <pre>{@code
   * <metaset type="render_input"><sourceId>542</sourceId><renderTaskName>foo</renderTaskName></metaset>
   *
   * }</pre>
   *     </ul>
   *
   * @return a CinnamonException if the object cannot be instantiated for any reason, or a Response
   *     object with the following XML content:
   *     <pre>{@code
   *  <startRenderTask>
   *    <taskObjectId>123</taskObjectId>
   *    <success>success.startRenderTask</success>
   * </startRenderTask>
   * }</pre>
   */
  @CinnamonMethod(checkTrigger = "true")
  public Response startRenderTask(Map<String, Object> cmd) {
    // create object and validate permission
    User user = getUser();
    ObjectSystemData osd = new ObjectSystemData(cmd, user, false);
    (new Validator(user)).validateCreate(osd.getParent());

    String renderInput;
    if (cmd.containsKey("metadata")) {
      Node meta = ParamParser.parseXml((String) cmd.get("metadata"), "error.param.metadata");
      renderInput = meta.asXML();
    } else {
      renderInput = "";
    }
    String metasetStr =
        "<meta>" + renderInput + "<metaset type=\"render_output\"></metaset></meta>";
    Node metaset = ParamParser.parseXml(metasetStr, null);
    osd.setMetadata(metaset.asXML());
    LifeCycleDAO lcDao = daoFactory.getLifeCycleDAO(em);
    LifeCycle lc = lcDao.findByName(Constants.RENDER_SERVER_LIFECYCLE);
    if (lc == null) {
      throw new CinnamonConfigurationException(
          Constants.RENDER_SERVER_LIFECYCLE + " lifecycle was not found.");
    }
    if (lc.getDefaultState() == null) {
      throw new CinnamonConfigurationException(
          Constants.RENDER_SERVER_LIFECYCLE
              + " lifecycle is not configured correctly. Needs defaultState.");
    }
    osd.setState(lc.getDefaultState());
    osd.setProcstate(Constants.RENDERSERVER_RENDER_TASK_NEW);

    if (cmd.containsKey("name")) {
      osd.setName(((String) cmd.get("name")).trim());
    } else {
      osd.setName("RenderTask");
    }

    ObjectTypeDAO otDao = daoFactory.getObjectTypeDAO(em);
    ObjectType renderTaskType = otDao.findByName(Constants.OBJECT_TYPE_RENDER_TASK);
    if (renderTaskType == null) {
      throw new CinnamonConfigurationException("Could not find required render task object type.");
    }
    osd.setType(renderTaskType);
    ObjectSystemDataDAO oDao = daoFactory.getObjectSystemDataDAO(em);
    oDao.makePersistent(osd);

    // create response
    XmlResponse resp = new XmlResponse(res);
    Element root = resp.getDoc().addElement("startRenderTask");
    root.addElement("taskObjectId").addText(String.valueOf(osd.getId()));
    root.addElement("success").addText("success.startRenderTask");
    return resp;
  }
  public RelationType(Map<String, String> cmd) {
    name = cmd.get("name");
    description = cmd.get("description");
    leftobjectprotected = cmd.get("leftobjectprotected").equals("true");
    rightobjectprotected = cmd.get("rightobjectprotected").equals("true");
    cloneOnLeftCopy = cmd.get("cloneOnLeftCopy").equals("true");
    cloneOnRightCopy = cmd.get("cloneOnRightCopy").equals("true");
    cloneOnLeftVersion = cmd.get("cloneOnLeftVersion").equals("true");
    cloneOnRightVersion = cmd.get("cloneOnRightVersion").equals("true");

    EntityManager em = HibernateSession.getLocalEntityManager();
    RelationResolverDAO rdd = daoFactory.getRelationResolverDAO(em);
    /*
     * Design note: resolve by name is intended to make it easier for testing
     * as you can create a test relation type without having to look up the id of the
     * default (or to-be-tested) relation resolver.
     */
    if (cmd.containsKey("right_resolver")) {
      rightResolver = rdd.findByName(cmd.get("right_resolver"));
    } else if (cmd.containsKey("right_resolver_id")) {
      rightResolver =
          rdd.get(
              ParamParser.parseLong(cmd.get("right_resolver_id"), "error.param.right_resolver_id"));
    } else {
      rightResolver = rdd.findByName(Constants.RELATION_RESOLVER_FIXED);
    }
    if (cmd.containsKey("left_resolver")) {
      leftResolver = rdd.findByName(cmd.get("left_resolver"));
    } else if (cmd.containsKey("left_resolver_id")) {
      rightResolver =
          rdd.get(
              ParamParser.parseLong(cmd.get("left_resolver_id"), "error.param.left_resolver_id"));
    } else {
      leftResolver = rdd.findByName(Constants.RELATION_RESOLVER_FIXED);
    }

    if (rightResolver == null) {
      throw new CinnamonException("error.param.right_resolver_id");
    }
    if (leftResolver == null) {
      throw new CinnamonException("error.param.left_resolver_id");
    }

    log.debug("leftResolver: " + leftResolver);
    log.debug("rightResolver: " + rightResolver);
  }
public class RenderServerConnector extends BaseExtension {

  static {
    /*
     * Required so BaseExtension can set the API-Class.
     */
    setExtensionClass(RenderServerConnector.class);
  }

  private Logger log = LoggerFactory.getLogger(this.getClass());

  public CommandRegistry registerApi(CommandRegistry cmdReg) {
    return findAndRegisterMethods(cmdReg, this);
  }

  static DAOFactory daoFactory = DAOFactory.instance(DAOFactory.HIBERNATE);

  /**
   * The startRenderTask creates a new OSD of the object type render_task in the repository in the
   * specified folder. This task object is read by the render server and updated with status updates
   * by the render process (which are written to custom metadata
   * /meta/metaset=render_output/messages/). It is up to the client to interpret the messages. The
   * overall status of the render task can be determined by looking at the procstate attribute:<br>
   * While the task is waiting for the render server to pick it up, its procstate is set to
   * "waiting". While the task is running, its procstate is set to "running". After the task is
   * finished, the task's procstate attribute is set to "finished". If the task should fail, the
   * task's procstate is set to "failed".
   *
   * <h2>Required permissions</h2>
   *
   * CREATE_OBJECT
   *
   * @param cmd a Map of HTTP request parameters containing:<br>
   *     <ul>
   *       <li>command=startrendertask
   *       <li>[name]=optional name of the task, defaults to "RenderTask"
   *       <li>ticket=session ticket
   *       <li>parentid = id of the folder where the task-object will be created
   *       <li>metadata = xml to use as the metadata metaset=render_input field. It must contain at
   *           least the element
   *           <pre>{@code <renderTaskName>}</pre>
   *           which holds the name of the render task that will be performed. It should contain the
   *           sourceId element to specify the id of the source content object to be rendered.<br>
   *           Example for metadata content:<br>
   *           <pre>{@code
   * <metaset type="render_input"><sourceId>542</sourceId><renderTaskName>foo</renderTaskName></metaset>
   *
   * }</pre>
   *     </ul>
   *
   * @return a CinnamonException if the object cannot be instantiated for any reason, or a Response
   *     object with the following XML content:
   *     <pre>{@code
   *  <startRenderTask>
   *    <taskObjectId>123</taskObjectId>
   *    <success>success.startRenderTask</success>
   * </startRenderTask>
   * }</pre>
   */
  @CinnamonMethod(checkTrigger = "true")
  public Response startRenderTask(Map<String, Object> cmd) {
    // create object and validate permission
    User user = getUser();
    ObjectSystemData osd = new ObjectSystemData(cmd, user, false);
    (new Validator(user)).validateCreate(osd.getParent());

    String renderInput;
    if (cmd.containsKey("metadata")) {
      Node meta = ParamParser.parseXml((String) cmd.get("metadata"), "error.param.metadata");
      renderInput = meta.asXML();
    } else {
      renderInput = "";
    }
    String metasetStr =
        "<meta>" + renderInput + "<metaset type=\"render_output\"></metaset></meta>";
    Node metaset = ParamParser.parseXml(metasetStr, null);
    osd.setMetadata(metaset.asXML());
    LifeCycleDAO lcDao = daoFactory.getLifeCycleDAO(em);
    LifeCycle lc = lcDao.findByName(Constants.RENDER_SERVER_LIFECYCLE);
    if (lc == null) {
      throw new CinnamonConfigurationException(
          Constants.RENDER_SERVER_LIFECYCLE + " lifecycle was not found.");
    }
    if (lc.getDefaultState() == null) {
      throw new CinnamonConfigurationException(
          Constants.RENDER_SERVER_LIFECYCLE
              + " lifecycle is not configured correctly. Needs defaultState.");
    }
    osd.setState(lc.getDefaultState());
    osd.setProcstate(Constants.RENDERSERVER_RENDER_TASK_NEW);

    if (cmd.containsKey("name")) {
      osd.setName(((String) cmd.get("name")).trim());
    } else {
      osd.setName("RenderTask");
    }

    ObjectTypeDAO otDao = daoFactory.getObjectTypeDAO(em);
    ObjectType renderTaskType = otDao.findByName(Constants.OBJECT_TYPE_RENDER_TASK);
    if (renderTaskType == null) {
      throw new CinnamonConfigurationException("Could not find required render task object type.");
    }
    osd.setType(renderTaskType);
    ObjectSystemDataDAO oDao = daoFactory.getObjectSystemDataDAO(em);
    oDao.makePersistent(osd);

    // create response
    XmlResponse resp = new XmlResponse(res);
    Element root = resp.getDoc().addElement("startRenderTask");
    root.addElement("taskObjectId").addText(String.valueOf(osd.getId()));
    root.addElement("success").addText("success.startRenderTask");
    return resp;
  }
}
@Entity
@Table(
    name = "relationtypes",
    uniqueConstraints = {@UniqueConstraint(columnNames = {"name"})})
public class RelationType implements Serializable, IXmlDumper {

  /** */
  private static final long serialVersionUID = 1L;

  private transient Logger log = LoggerFactory.getLogger(getClass());

  static DAOFactory daoFactory = DAOFactory.instance(DAOFactory.HIBERNATE);

  @Id
  @GeneratedValue
  @Column(name = "id")
  private long id;

  @Column(name = "name", length = Constants.NAME_LENGTH, nullable = false)
  private String name;

  @Column(name = "description", length = Constants.DESCRIPTION_SIZE, nullable = false)
  @Deprecated
  private String description = "";

  /**
   * If leftobjectprotected is true: the left object of a relation of this type may not be deleted
   * as long as the relation exists.
   */
  @Column(name = "leftobjectprotected", nullable = false)
  private Boolean leftobjectprotected;

  /**
   * If rightobjectprotect is true: the right object of a relation of this type may not be deleted
   * as long as the relation exists.
   */
  @Column(name = "rightobjectprotected", nullable = false)
  private Boolean rightobjectprotected;

  /**
   * If the right object of a relation of this type is copied, the relation will also be copied if
   * this field is true.
   */
  @Column(name = "clone_on_right_copy", nullable = false)
  private Boolean cloneOnRightCopy = false;

  /**
   * If the right object of a relation of this type is versioned, the relation will also be copied
   * for the new version if this field is true.
   */
  @Column(name = "clone_on_right_version", nullable = false)
  private Boolean cloneOnRightVersion = false;

  /**
   * If the left object of a relation of this type is copied, the relation will also be copied if
   * this field is true.
   */
  @Column(name = "clone_on_left_copy", nullable = false)
  private Boolean cloneOnLeftCopy = false;

  /**
   * If the left object of a relation of this type is versioned the relation will also be copied for
   * the new version if this field is true.
   */
  @Column(name = "clone_on_left_version", nullable = false)
  private Boolean cloneOnLeftVersion = false;

  @ManyToOne(cascade = {})
  @JoinColumn(name = "left_resolver_id", nullable = false)
  private RelationResolver leftResolver;

  @ManyToOne(cascade = {})
  @JoinColumn(name = "right_resolver_id", nullable = false)
  private RelationResolver rightResolver;

  @Version
  @Column(name = "obj_version")
  @SuppressWarnings("unused")
  private Long obj_version = 0L;

  public RelationType() {}

  public RelationType(
      String name,
      String description,
      Boolean leftobjectprotected,
      Boolean rightobjectprotected,
      RelationResolver leftResolver,
      RelationResolver rightResolver) {
    this.name = name;
    this.description = description;
    this.leftobjectprotected = leftobjectprotected;
    this.rightobjectprotected = rightobjectprotected;
    this.leftResolver = leftResolver;
    this.rightResolver = rightResolver;
  }

  public RelationType(
      String name,
      String description,
      Boolean leftobjectprotected,
      Boolean rightobjectprotected,
      RelationResolver leftResolver,
      RelationResolver rightResolver,
      Boolean cloneOnRightCopy,
      Boolean cloneOnLeftCopy) {
    this.name = name;
    this.description = description;
    this.leftobjectprotected = leftobjectprotected;
    this.rightobjectprotected = rightobjectprotected;
    this.leftResolver = leftResolver;
    this.rightResolver = rightResolver;
    this.cloneOnLeftCopy = cloneOnLeftCopy;
    this.cloneOnRightCopy = cloneOnRightCopy;
  }

  public RelationType(
      String name,
      String description,
      Boolean leftobjectprotected,
      Boolean rightobjectprotected,
      RelationResolver leftResolver,
      RelationResolver rightResolver,
      Boolean cloneOnRightCopy,
      Boolean cloneOnLeftCopy,
      Boolean cloneOnRightVersion,
      Boolean cloneOnLeftVersion) {
    this.name = name;
    this.description = description;
    this.leftobjectprotected = leftobjectprotected;
    this.rightobjectprotected = rightobjectprotected;
    this.leftResolver = leftResolver;
    this.rightResolver = rightResolver;
    this.cloneOnLeftCopy = cloneOnLeftCopy;
    this.cloneOnRightCopy = cloneOnRightCopy;
    this.cloneOnLeftVersion = cloneOnLeftVersion;
    this.cloneOnRightVersion = cloneOnRightVersion;
  }

  public RelationType(Map<String, String> cmd) {
    name = cmd.get("name");
    description = cmd.get("description");
    leftobjectprotected = cmd.get("leftobjectprotected").equals("true");
    rightobjectprotected = cmd.get("rightobjectprotected").equals("true");
    cloneOnLeftCopy = cmd.get("cloneOnLeftCopy").equals("true");
    cloneOnRightCopy = cmd.get("cloneOnRightCopy").equals("true");
    cloneOnLeftVersion = cmd.get("cloneOnLeftVersion").equals("true");
    cloneOnRightVersion = cmd.get("cloneOnRightVersion").equals("true");

    EntityManager em = HibernateSession.getLocalEntityManager();
    RelationResolverDAO rdd = daoFactory.getRelationResolverDAO(em);
    /*
     * Design note: resolve by name is intended to make it easier for testing
     * as you can create a test relation type without having to look up the id of the
     * default (or to-be-tested) relation resolver.
     */
    if (cmd.containsKey("right_resolver")) {
      rightResolver = rdd.findByName(cmd.get("right_resolver"));
    } else if (cmd.containsKey("right_resolver_id")) {
      rightResolver =
          rdd.get(
              ParamParser.parseLong(cmd.get("right_resolver_id"), "error.param.right_resolver_id"));
    } else {
      rightResolver = rdd.findByName(Constants.RELATION_RESOLVER_FIXED);
    }
    if (cmd.containsKey("left_resolver")) {
      leftResolver = rdd.findByName(cmd.get("left_resolver"));
    } else if (cmd.containsKey("left_resolver_id")) {
      rightResolver =
          rdd.get(
              ParamParser.parseLong(cmd.get("left_resolver_id"), "error.param.left_resolver_id"));
    } else {
      leftResolver = rdd.findByName(Constants.RELATION_RESOLVER_FIXED);
    }

    if (rightResolver == null) {
      throw new CinnamonException("error.param.right_resolver_id");
    }
    if (leftResolver == null) {
      throw new CinnamonException("error.param.left_resolver_id");
    }

    log.debug("leftResolver: " + leftResolver);
    log.debug("rightResolver: " + rightResolver);
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public long getId() {
    return id;
  }

  @SuppressWarnings("unused")
  private void setId(long id) {
    this.id = id;
  }

  public Boolean isLeftobjectprotected() {
    return leftobjectprotected;
  }

  public void setLeftobjectprotected(Boolean leftobjectprotected) {
    this.leftobjectprotected = leftobjectprotected;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Boolean isRightobjectprotected() {
    return rightobjectprotected;
  }

  public void setRightobjectprotected(Boolean rightobjectprotected) {
    this.rightobjectprotected = rightobjectprotected;
  }

  public RelationResolver getLeftResolver() {
    return leftResolver;
  }

  public void setLeftResolver(RelationResolver leftResolver) {
    this.leftResolver = leftResolver;
  }

  public RelationResolver getRightResolver() {
    return rightResolver;
  }

  public void setRightResolver(RelationResolver rightResolver) {
    this.rightResolver = rightResolver;
  }

  public Boolean getCloneOnLeftVersion() {
    return cloneOnLeftVersion;
  }

  public void setCloneOnLeftVersion(Boolean cloneOnLeftVersion) {
    this.cloneOnLeftVersion = cloneOnLeftVersion;
  }

  public Boolean getCloneOnRightVersion() {
    return cloneOnRightVersion;
  }

  public void setCloneOnRightVersion(Boolean cloneOnRightVersion) {
    this.cloneOnRightVersion = cloneOnRightVersion;
  }

  public void toXmlElement(Element root) {
    Element rt = root.addElement("relationType");
    rt.addElement("id").addText(String.valueOf(getId()));
    rt.addElement("name").addText(LocalMessage.loc(getName()));
    rt.addElement("sysName").addText(getName());
    rt.addElement("description").addText(LocalMessage.loc(getDescription()));
    rt.addElement("rightobjectprotected").addText(rightobjectprotected.toString());
    rt.addElement("leftobjectprotected").addText(leftobjectprotected.toString());
    rt.addElement("cloneOnLeftCopy").addText(cloneOnLeftCopy.toString());
    rt.addElement("cloneOnLeftVersion").addText(cloneOnLeftVersion.toString());
    rt.addElement("cloneOnRightCopy").addText(cloneOnRightCopy.toString());
    rt.addElement("cloneOnRightVersion").addText(cloneOnRightVersion.toString());
    rt.addElement("leftResolver").addText(leftResolver.getName());
    rt.addElement("rightResolver").addText(rightResolver.getName());
  }

  public String toString() {
    return "RelationResolver::name:" + getName();
  }

  public Boolean getCloneOnRightCopy() {
    return cloneOnRightCopy;
  }

  public void setCloneOnRightCopy(Boolean cloneOnRightCopy) {
    this.cloneOnRightCopy = cloneOnRightCopy;
  }

  public Boolean getCloneOnLeftCopy() {
    return cloneOnLeftCopy;
  }

  public void setCloneOnLeftCopy(Boolean cloneOnLeftCopy) {
    this.cloneOnLeftCopy = cloneOnLeftCopy;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof RelationType)) return false;

    RelationType that = (RelationType) o;

    if (!cloneOnLeftCopy.equals(that.cloneOnLeftCopy)) return false;
    if (!cloneOnLeftVersion.equals(that.cloneOnLeftVersion)) return false;
    if (!cloneOnRightCopy.equals(that.cloneOnRightCopy)) return false;
    if (!cloneOnRightVersion.equals(that.cloneOnRightVersion)) return false;
    if (!description.equals(that.description)) return false;
    if (!leftResolver.equals(that.leftResolver)) return false;
    if (!leftobjectprotected.equals(that.leftobjectprotected)) return false;
    if (!name.equals(that.name)) return false;
    if (!rightResolver.equals(that.rightResolver)) return false;
    if (!rightobjectprotected.equals(that.rightobjectprotected)) return false;

    return true;
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }

  /**
   * Find the correct OSD version for a given relation side.
   *
   * @param relation The relation for which the correct osd version is needed.
   * @param osd the OSD whose version needs to be determined.
   * @param relationSide the side of the relation for which you need the OSD version to be resolved.
   * @return the version as found by the Resolver class.
   */
  ObjectSystemData findOsdVersion(
      Relation relation, ObjectSystemData osd, RelationSide relationSide) {
    switch (relationSide) {
      case LEFT:
        return leftResolver.resolveOsdVersion(relation, osd, relationSide);
      case RIGHT:
        return rightResolver.resolveOsdVersion(relation, osd, relationSide);
    }
    return null; // is never reached unless RelationSide is null. And then you are up a certain
                 // creek without a paddle anyway.
  }
}