@Override
  @Transactional
  public Long createReply(
      UserProfile user, Long parentId, String title, String body, @Nullable String ciStatement) {
    if (parentId == null) {
      throw new IllegalArgumentException("Attempting to create reply with null parent id");
    }
    log.debug("Creating reply to {}; title: {}; body: {}", new Object[] {parentId, title, body});
    Long articleID;
    try {
      articleID =
          (Long)
              hibernateTemplate
                  .findByCriteria(
                      DetachedCriteria.forClass(Annotation.class)
                          .add(Restrictions.eq("ID", parentId))
                          .setProjection(Projections.property("articleID")),
                      0,
                      1)
                  .get(0);
    } catch (IndexOutOfBoundsException e) {
      throw new IllegalArgumentException("Invalid annotation id: " + parentId);
    }

    Annotation reply = new Annotation(user, AnnotationType.REPLY, articleID);
    reply.setParentID(parentId);
    reply.setTitle(title);
    reply.setBody(body);
    reply.setCompetingInterestBody(ciStatement);
    reply.setAnnotationUri(URIGenerator.generate(reply));
    return (Long) hibernateTemplate.save(reply);
  }
  @Override
  @Transactional
  public Long createComment(
      UserProfile user, String articleDoi, String title, String body, String ciStatement) {
    if (articleDoi == null) {
      throw new IllegalArgumentException("Attempted to create comment with null article id");
    } else if (user == null || user.getID() == null) {
      throw new IllegalArgumentException("Attempted to create comment without a creator");
    } else if (body == null || body.isEmpty()) {
      throw new IllegalArgumentException("Attempted to create comment with no body");
    }

    log.debug(
        "Creating comment on article: {}; title: {}; body: {}",
        new Object[] {articleDoi, title, body});
    Long articleID;
    try {
      articleID =
          (Long)
              hibernateTemplate
                  .findByCriteria(
                      DetachedCriteria.forClass(Article.class)
                          .add(Restrictions.eq("doi", articleDoi))
                          .setProjection(Projections.id()))
                  .get(0);
    } catch (IndexOutOfBoundsException e) {
      throw new IllegalArgumentException("Invalid doi: " + articleDoi);
    }

    // generate an annotation uri
    Annotation comment = new Annotation(user, AnnotationType.COMMENT, articleID);
    comment.setAnnotationUri(URIGenerator.generate(comment));
    comment.setTitle(title);
    comment.setBody(body);
    comment.setCompetingInterestBody(ciStatement);

    Long id = (Long) hibernateTemplate.save(comment);

    return id;
  }