public interface ProjectStorage {

    /**
     * Return the project with given <code>projectId</code>.
     *
     * @param projectId the project id.
     * @return get the given project.
     * @throws EntityNotFoundException when user is not authorized to get project.
     */
    ProjectDto getProject(Long projectId) throws EntityNotFoundException;

    Set<String> getManager(long projectId);

    Set<String> getParticipator(long projectId);

    /**
     * Create a project with given <code>project</code> model object.
     *
     * @param project the given project.
     * @return create the given project.
     */
    ProjectDto createProject(ProjectDto project);

    /**
     * Create a sub-project with given <code>project</code> model object and parent project ID.
     *
     * @param project  the project metadata to create.
     * @param parentId parent project ID
     * @return Create a sub-project.
     * @throws EntityNotFoundException the project associated with <code>parentId</code> doesn't exist.
     */
    ProjectDto createProject(ProjectDto project, long parentId) throws EntityNotFoundException;


    ProjectDto updateProject(ProjectDto project);

    void updateProjectNoReturn(ProjectDto project);

    /**
     * Remove the project with given <code>projectId</code>,
     * and also its descendants if <code>deleteChild</code> is true.
     *
     * @param projectId   the given project id.
     * @param deleteChild delete Child.
     * @throws EntityNotFoundException when user is not authorized to remove project.
     */
    void removeProject(long projectId, boolean deleteChild) throws EntityNotFoundException;

    /**
     * Clone a project with given <code>projectId</code>. If <code>cloneTask</code> is true,
     * it will also clone all non-completed tasks from the project.
     *
     * @param projectId The id of a project which it copies from.
     * @param cloneTask If false, it will clone only project metadata.
     *                  Otherwise, it also clones all non-completed tasks from the project.
     * @return The cloned project.
     * @throws EntityNotFoundException when user is not authorized to clone project.
     */
    ProjectDto cloneProject(long projectId, boolean cloneTask) throws EntityNotFoundException;

    /**
     * Return a list of children of a parent project with given <code>parentId</code>.
     *
     * @param parentId The parent id of a project.
     * @param offset term to offset results.
     * @param limit term to limit results.
     * @return The list of children of a parent project.
     * @throws Exception when can't get sub projects.
     */
    List<ProjectDto> getSubProjects(long parentId,int offset ,int limit) throws Exception;

    List<ProjectDto> findProjects(ProjectQuery query,int offset ,int limit);

    int countProjects(ProjectQuery query);

    List<ProjectDto> findProjects(List<String> memberships, String keyword, OrderBy order,int offset ,int limit);

    List<ProjectDto> findCollaboratedProjects(String userName, String keyword,int offset ,int limit);

    List<ProjectDto> findNotEmptyProjects(List<String> memberships, String keyword,int offset ,int limit);

    int countCollaboratedProjects(String userName, String keyword);

    int countNotEmptyProjects(List<String> memberships, String keyword);

    int countProjects(List<String> memberships, String keyword);

} public interface StatusStorage {

  /**
   * Return the <code>Status</code> with given <code>statusId</code>.
   *
   * @param statusId the given status id.
   * @return the status of the given statusId.
   */
  StatusDto getStatus(long statusId);

  /**
   * Return the default status of the project which is ideally the first step in
   * the project workflow.
   *
   * @param projectId the given project id.
   * @return the default status of the given project.
   */
  StatusDto getDefaultStatus(long projectId);

  /**
   * Return the list of statuses from a project with given <code>projectId</code>.
   *
   * @param projectId the given project id.
   * @return the status of the given project.
   */
  List<StatusDto> getStatuses(long projectId);

  StatusDto createStatus(ProjectDto project, String status);

  StatusDto createStatus(ProjectDto project, String status, int rank) throws NotAllowedOperationOnEntityException;

  void removeStatus(long statusId) throws Exception;

  StatusDto updateStatus(long statusId, String statusName) throws EntityNotFoundException, NotAllowedOperationOnEntityException;

  StatusDto updateStatus(StatusDto statusDto) throws EntityNotFoundException,
                                                                  NotAllowedOperationOnEntityException;


} public class ProjectStorageImpl implements ProjectStorage {

    private static final Log LOG = ExoLogger.getExoLogger(ProjectStorageImpl.class);

    private static final Pattern pattern = Pattern.compile("@([^\\s]+)|@([^\\s]+)$");


    @Inject
    private final DAOHandler daoHandler;



    public ProjectStorageImpl(DAOHandler daoHandler) {
        this.daoHandler = daoHandler;
    }

    @Override
    public ProjectDto getProject(Long projectId) throws EntityNotFoundException {
        return StorageUtil.projectToDto(daoHandler.getProjectHandler().find(projectId),this);
    }

    @Override
    public Set<String> getManager(long projectId) {
        ProjectQuery query = new ProjectQuery();
        query.setId(projectId);
        List<String> manager = daoHandler.getProjectHandler().selectProjectField(query, "manager");
        return new HashSet<String>(manager);
    }

    @Override
    public Set<String> getParticipator(long projectId) {
        ProjectQuery query = new ProjectQuery();
        query.setId(projectId);
        List<String> manager = daoHandler.getProjectHandler().selectProjectField(query, "participator");
        return new HashSet<String>(manager);
    }

    @Override
    public ProjectDto createProject(ProjectDto project) {
        return StorageUtil.projectToDto(daoHandler.getProjectHandler().create(StorageUtil.projectToEntity(project)),this);
    }

    @Override
    public ProjectDto createProject(ProjectDto project, long parentId) throws EntityNotFoundException {
        return null;
    }

    @Override
    public ProjectDto updateProject(ProjectDto project) {
        return StorageUtil.projectToDto(daoHandler.getProjectHandler().update(StorageUtil.projectToEntity(project)),this);
    }

    @Override
    public void updateProjectNoReturn(ProjectDto project) {
        daoHandler.getProjectHandler().update(StorageUtil.projectToEntity(project));
    }

    @Override
    public void removeProject(long projectId, boolean deleteChild) throws EntityNotFoundException {
        ProjectDto project = getProject(projectId);
        if (project == null) throw new EntityNotFoundException(projectId, Project.class);
        daoHandler.getProjectHandler().removeProject(projectId, deleteChild);
    }

    @Override
    public ProjectDto cloneProject(long projectId, boolean cloneTask) throws EntityNotFoundException {
        return null;
    }

    @Override
    public List<ProjectDto> getSubProjects(long parentId, int offset, int limit) throws Exception {
        ProjectDto parent = getProject(parentId);
        return Arrays.asList(daoHandler.getProjectHandler().findSubProjects(StorageUtil.projectToEntity(parent)).load(offset, limit)).stream().map((Project project) -> StorageUtil.projectToDto(project,this)).collect(Collectors.toList());

    }

    @Override
    public List<ProjectDto> findProjects(ProjectQuery query, int offset, int limit) {
        try {
            return Arrays.asList(daoHandler.getProjectHandler().findProjects(query).load(offset, limit)).stream().map((Project project) -> StorageUtil.projectToDto(project,this)).collect(Collectors.toList());
        } catch (Exception e) {
            return new ArrayList<ProjectDto>();
        }

    }

    @Override
    public int countProjects(ProjectQuery query) {
        try {
            return daoHandler.getProjectHandler().findProjects(query).getSize();
        } catch (Exception e) {
            return 0;
        }

    }

    @Override
    public List<ProjectDto> findProjects(List<String> memberships, String keyword, OrderBy order, int offset, int limit) {
        try {
            return Arrays.asList(daoHandler.getProjectHandler().findAllByMembershipsAndKeyword(memberships, keyword, order).load(offset, limit)).stream().map((Project project) -> StorageUtil.projectToDto(project,this)).collect(Collectors.toList());
        } catch (Exception e) {
            return new ArrayList<ProjectDto>();
        }
    }

    @Override
    public List<ProjectDto> findCollaboratedProjects(String userName, String keyword,int offset ,int limit){
        try {
            return daoHandler.getProjectHandler().findCollaboratedProjects(userName,keyword,offset, limit).stream().map((Project project) -> StorageUtil.projectToDto(project,this)).collect(Collectors.toList());
        } catch (Exception e) {
            return new ArrayList<ProjectDto>();
        }
    }

    @Override
    public List<ProjectDto> findNotEmptyProjects(List<String> memberships, String keyword,int offset ,int limit) {
        try {
            return daoHandler.getProjectHandler().findNotEmptyProjects(memberships,keyword,offset, limit).stream().map((Project project) -> StorageUtil.projectToDto(project,this)).collect(Collectors.toList());
        } catch (Exception e) {
            return new ArrayList<ProjectDto>();
        }
    }

    @Override
    public int countCollaboratedProjects(String userName, String keyword){
        try {
            return daoHandler.getProjectHandler().countCollaboratedProjects(userName,keyword);
        } catch (Exception e) {
            return 0;
        }
    }

    @Override
    public int countNotEmptyProjects(List<String> memberships, String keyword) {
        try {
            return daoHandler.getProjectHandler().countNotEmptyProjects(memberships,keyword);
        } catch (Exception e) {
            return 0;
        }
    }

    @Override
    public int countProjects(List<String> memberships, String keyword) {
        try {
            return daoHandler.getProjectHandler().findAllByMembershipsAndKeyword(memberships, keyword,null).getSize();
        } catch (Exception e) {
            return 0;
        }

    }

} public class TestDtoUtils {

  private static Connection conn;

  private static Liquibase  liquibase;

  public static long        EXISTING_TASK_ID      = 1;

  public static long        UNEXISTING_TASK_ID    = 2;

  public static long        EXISTING_PROJECT_ID   = 1;

  public static long        UNEXISTING_PROJECT_ID = 2;

  public static long        EXISTING_STATUS_ID    = 1;

  public static long        UNEXISTING_STATUS_ID  = 2;

  public static long        EXISTING_COMMENT_ID   = 1;

  public static long        UNEXISTING_COMMENT_ID = 2;

  public static void initH2DB() throws SQLException, ClassNotFoundException, LiquibaseException {

    Class.forName("org.h2.Driver");
    conn = DriverManager.getConnection("jdbc:h2:target/h2-db", "sa", "");

    initDB();
  }

  public static void initHSQLDB() throws LiquibaseException, SQLException, ClassNotFoundException {

    Class.forName("org.hsqldb.jdbcDriver");
    conn = DriverManager.getConnection("jdbc:hsqldb:file:target/hsql-db", "sa", "");

    initDB();
  }

  private static void initDB() throws LiquibaseException {
    Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(conn));

    liquibase = new Liquibase("db/changelog/task.db.changelog-1.0.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-1.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-1.3.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-2.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.0.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.2.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String) null);
  }

  public static void closeDB() throws LiquibaseException, SQLException {
    liquibase.rollback(1000, null);
    conn.close();
  }

  public static TaskDto getDefaultTask() {
    return getDefaultTaskWithId(EXISTING_TASK_ID);
  }

  public static TaskDto getDefaultTaskWithId(long id) {
    TaskDto task = new TaskDto();
    task.setId(id);
    task.setTitle("Default task");
    task.setAssignee("root");
    task.setCreatedBy("root");
    task.setPriority(Priority.NORMAL);
    task.setCreatedTime(new Date());
    Set<String> coworker = new HashSet<String>();
    task.setCoworker(coworker);
    return task;
  }

  public static CommentDto getDefaultComment() {
    CommentDto comment = new CommentDto();
    comment.setId(EXISTING_COMMENT_ID);
    comment.setComment("Bla bla");
    comment.setAuthor("Tib");
    comment.setCreatedTime(new Date());
    return comment;
  }

  public static StatusDto getDefaultStatus() {
    StatusDto status = new StatusDto();
    status.setId(EXISTING_STATUS_ID);
    status.setName("TODO");
    status.setRank(1);
    return status;
  }

  public static ProjectDto getDefaultProject() {
    ProjectDto project = new ProjectDto();
    project.setId(EXISTING_PROJECT_ID);
    project.setName("Default project");
    project.setDescription("The default project");
    project.setDueDate(new Date());
    project.setLastModifiedDate(System.currentTimeMillis());
    Set<String> participator = new HashSet<String>();
    participator.add("Tib");
    project.setParticipator(participator);
    Set<String> managers = new HashSet<String>();
    managers.add("Tib");
    project.setManager(managers);
    return project;
  }

  public static ProjectDto getParentProject() {
    ProjectDto project = new ProjectDto();
    project.setId(EXISTING_PROJECT_ID);
    project.setName("Default project");
    project.setDescription("The default project");
    project.setDueDate(new Date());
    Set<String> participator = new HashSet<String>();
    participator.add("Tib");
    project.setParticipator(participator);
    Set<String> managers = new HashSet<String>();
    managers.add("Tib");
    project.setManager(managers);
    return project;
  }

} public class TestUtils {

  private static Connection conn;
  private static Liquibase liquibase;

  public static long EXISTING_TASK_ID = 1;
  public static long UNEXISTING_TASK_ID = 2;

  public static long EXISTING_PROJECT_ID = 1;
  public static long UNEXISTING_PROJECT_ID = 2;

  public static long EXISTING_STATUS_ID = 1;
  public static long UNEXISTING_STATUS_ID = 2;

  public static long EXISTING_COMMENT_ID = 1;
  public static long UNEXISTING_COMMENT_ID = 2;

  public static long EXISTING_LABEL_ID = 1;
  public static long UNEXISTING_LABEL_ID = 2;

  public static void initH2DB() throws SQLException,
      ClassNotFoundException, LiquibaseException {

    Class.forName("org.h2.Driver");
    conn = DriverManager.getConnection("jdbc:h2:target/h2-db", "sa", "");

    initDB();
  }

  public static void initHSQLDB() throws LiquibaseException, SQLException,
      ClassNotFoundException {

    Class.forName("org.hsqldb.jdbcDriver");
    conn = DriverManager.getConnection("jdbc:hsqldb:file:target/hsql-db", "sa", "");

    initDB();
  }

  private static void initDB() throws LiquibaseException {
    Database database = DatabaseFactory.getInstance()
        .findCorrectDatabaseImplementation(new JdbcConnection(conn));

    liquibase = new Liquibase("db/changelog/task.db.changelog-1.0.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-1.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-1.3.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-2.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.0.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.1.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
    liquibase = new Liquibase("db/changelog/task.db.changelog-3.2.0.xml", new ClassLoaderResourceAccessor(), database);
    liquibase.update((String)null);
  }

  public static void closeDB() throws LiquibaseException, SQLException {
    liquibase.rollback(1000, null);
    conn.close();
  }

  public static Task getDefaultTask() {
    return getDefaultTaskWithId(EXISTING_TASK_ID);
  }

  public static TaskDto getDefaultTaskDto() {
    return getDefaultTaskDtoWithId(EXISTING_TASK_ID);
  }

  public static Task getDefaultTaskWithId(long id) {
    Task task = new Task();
    task.setId(id);
    task.setTitle("Default task");
    task.setAssignee("root");
    task.setCreatedBy("root");
    task.setCreatedTime(new Date());
    return task;
  }
  public static TaskDto getDefaultTaskDtoWithId(long id) {
    TaskDto task = new TaskDto();
    task.setId(id);
    task.setTitle("Default task");
    task.setAssignee("root");
    task.setCreatedBy("root");
    task.setCreatedTime(new Date());
    return task;
  }

  public static Comment getDefaultComment() {
    Comment comment = new Comment();
    comment.setId(EXISTING_COMMENT_ID);
    comment.setComment("Bla bla");
    comment.setAuthor("Tib");
    comment.setCreatedTime(new Date());
    comment.setTask(getDefaultTask());
    return comment;
  }

  public static Comment getDefaultCommentWithMention() {
    Comment comment = new Comment();
    comment.setId(EXISTING_COMMENT_ID);
    comment.setComment("Bla bla @testa");
    comment.setAuthor("Tib");
    comment.setCreatedTime(new Date());
    comment.setTask(getDefaultTask());
    return comment;
  }

  public static CommentDto getDefaultCommentDto() {
    CommentDto comment = new CommentDto();
    comment.setId(EXISTING_COMMENT_ID);
    comment.setComment("Bla bla");
    comment.setAuthor("Tib");
    comment.setCreatedTime(new Date());
    comment.setTask(getDefaultTaskDto());
    return comment;
  }

  public static Status getDefaultStatus() {
    Status status = new Status();
    status.setId(EXISTING_STATUS_ID);
    status.setName("TODO");
    status.setRank(1);
    return status;
  }
  public static StatusDto getDefaultStatusDto() {
    StatusDto status = new StatusDto();
    status.setId(EXISTING_STATUS_ID);
    status.setName("TODO");
    status.setRank(1);
    return status;
  }

  public static UserSettingDto getDefaultUserSettingDto(){
    UserSettingDto userSettingDto = new UserSettingDto();
    userSettingDto.setUsername("user");
    userSettingDto.setShowHiddenProject(true);
    userSettingDto.setShowHiddenLabel(true);
    return userSettingDto;
  }

  public static LabelDto getDefaultLabel() {
    LabelDto labelDto = new LabelDto();
    labelDto.setId(EXISTING_LABEL_ID);
    labelDto.setName("TODO");
    labelDto.setUsername("label");
    return labelDto;
  }

  public static Project getDefaultProject() {
    Project project = new Project();
    project.setId(EXISTING_PROJECT_ID);
    project.setName("Default project");
    project.setDescription("The default project");
    project.setDueDate(new Date());
    Set<String> managers = new HashSet<String>();
    managers.add("Tib");
    project.setManager(managers);
    return project;
  }
  public static ProjectDto getDefaultProjectDto() {
    ProjectDto project = new ProjectDto();
    project.setId(EXISTING_PROJECT_ID);
    project.setName("Default project");
    project.setDescription("The default project");
    project.setDueDate(new Date());
    Set<String> managers = new HashSet<String>();
    managers.add("Tib");
    project.setManager(managers);
    return project;
  }

  public static User getUser() {
    User user = new User();
    user.setUsername("root");
    user.setDisplayName("root");
    user.setFirstName("root");
    user.setLastName("root");
    user.setEmail("root@gmail.com");

    return user;
  }

  public static User getUserA() {
    User user = new User();
    user.setUsername("userA");
    user.setDisplayName("userA");
    user.setFirstName("userA");
    user.setLastName("userA");
    user.setEmail("userA@gmail.com");

    return user;
  }

  public static org.exoplatform.social.core.identity.model.Identity getUserAIdentity() {
    org.exoplatform.social.core.identity.model.Identity userIdentity = new org.exoplatform.social.core.identity.model.Identity(OrganizationIdentityProvider.NAME, "userA");

    userIdentity.setEnable(true);
    userIdentity.setDeleted(false);
    userIdentity.setRemoteId("userA");

    Profile userProfile = new Profile(userIdentity);
    userProfile.setProperty(Profile.FULL_NAME, "userA");
    userProfile.setProperty(Profile.AVATAR, "/userA.png");
    userIdentity.setProfile(userProfile);

    return userIdentity;
  }

} public interface DAOHandler {
  public ProjectHandler getProjectHandler();

  public TaskHandler getTaskHandler();
  
  public LabelHandler getLabelHandler();
  
  public LabelTaskMappingHandler getLabelTaskMappingHandler();

  public CommentHandler getCommentHandler();

  public TaskLogHandler getTaskLogHandler();

  public StatusHandler getStatusHandler();

  public UserSettingHandler getUserSettingHandler();
} public interface TaskHandler extends GenericDAO<Task, Long> {

  ListAccess<Task> findTasksByLabel(long labelId, List<Long> projectIds, String username, OrderBy orderBy);
 
  List<Task> findByUser(String user);

  ListAccess<Task> findTasks(TaskQuery query);

  public List<Task> findAllByMembership(String user, List<String> memberships);

  public <T> List<T> selectTaskField(TaskQuery query, String fieldName);

  Task findTaskByActivityId(String activityId);

  void updateStatus(Status stOld, Status stNew);

  void updateTaskOrder(long currentTaskId, Status newStatus, long[] orders);

  Set<String> getCoworker(long taskid);

  Task getTaskWithCoworkers(long id);

  List<Task> getUncompletedTasks(String user, int limit);

  List<Task> getWatchedTasks(String user, int limit);

  Long countWatchedTasks(String user);

  List<Task> getCollaboratedTasks(String user, int limit);

  Long countCollaboratedTasks(String user);

    List<Task> getAssignedTasks(String user, int limit);

  Long countAssignedTasks(String user);

  List<Task> getByStatus(long statusid);

  Long countUncompletedTasks(String user);

  ListAccess<Task> getIncomingTasks(String user);

  List<Task> getOverdueTasks(String user, int limit);

  Long countOverdueTasks(String user);

  void addWatcherToTask(String username, Task task) throws Exception;

  void deleteWatcherOfTask(String username,Task task) throws Exception;

  Set<String> getWatchersOfTask(Task task);

  /**
   * Find tasks assigned to a user using a term to find in title or description
   * of the task
   * 
   * @param user username
   * @param memberships memberships
   * @param query term to search in title or description
   * @param limit term to limit results.
   * @return {@link List} of {@link Task}
   */
  List<Task> findTasks(String user, List<String> memberships, String query, int limit);

  /**
   * Count tasks assigned to a user using a search term to find in title or
   * description of the task
   * 
   * @param user username
   * @param query term to search in title or description
   * @return tasks count
   */
  long countTasks(String user, String query);

    List<Object[]> countTaskStatusByProject(long projectId);
} @Entity(name = "TaskTask")
@ExoEntity
@Table(name = "TASK_TASKS")
@NamedQueries({
    @NamedQuery(name = "Task.findByMemberships",
        query = "SELECT ta FROM TaskTask ta LEFT JOIN ta.coworker coworkers " +
            "WHERE ta.assignee = :userName " +
            "OR ta.createdBy = :userName " +
            "OR coworkers = :userName " +
            "OR ta.status IN (SELECT st.id FROM TaskStatus st " +
            "WHERE project IN " +
            "(SELECT pr1.id FROM TaskProject pr1 LEFT JOIN pr1.manager managers WHERE managers IN :memberships) " +
            "OR project IN " +
            "(SELECT pr2.id FROM TaskProject pr2 LEFT JOIN pr2.participator participators " +
            "WHERE participators IN :memberships) " +
            ") "),
    @NamedQuery(name = "Task.findTaskByProject",
        query = "SELECT t FROM TaskTask t WHERE t.status.project.id = :projectId"),
    @NamedQuery(name = "Task.findTaskByActivityId",
        query = "SELECT t FROM TaskTask t WHERE t.activityId = :activityId"),
    @NamedQuery(name = "Task.getCoworker",
        query = "SELECT c FROM TaskTask t inner join t.coworker c WHERE t.id = :taskid"),
    @NamedQuery(name = "Task.getWatcher",
        query = "SELECT w FROM TaskTask t inner join t.watcher w WHERE t.id = :taskid"),
    @NamedQuery(name = "Task.updateStatus",
        query = "UPDATE TaskTask t SET t.status = :status_new WHERE t.status = :status_old"),
    @NamedQuery(name = "Task.getTaskWithCoworkers",
        query = "SELECT t FROM TaskTask t LEFT JOIN FETCH t.coworker c WHERE t.id = :taskid"),
    @NamedQuery(name = "Task.getUncompletedTasks",
        query = "SELECT ta FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND (ta.assignee = :userName " +
                "OR :userName in (select co FROM ta.coworker co) " +
                ")"),
    @NamedQuery(name = "Task.countUncompletedTasks",
        query = "SELECT COUNT(ta) FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND (ta.assignee = :userName " +
                "OR :userName in (select co FROM ta.coworker co) " +
                ")"),
    @NamedQuery(name = "Task.getCollaboratedTasks",
        query = "SELECT ta FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND :userName in (select co FROM ta.coworker co) "),
    @NamedQuery(name = "Task.countCollaboratedTasks",
        query = "SELECT COUNT(ta) FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND :userName in (select co FROM ta.coworker co) "),

   @NamedQuery(name = "Task.getAssignedTasks",
        query = "SELECT ta FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND ta.assignee = :userName "),

   @NamedQuery(name = "Task.countAssignedTasks",
        query = "SELECT COUNT(ta)  FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND ta.assignee = :userName "),

   @NamedQuery(name = "Task.getWatchedTasks",
        query = "SELECT ta FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND :userName in (select wa FROM ta.watcher wa) "),

   @NamedQuery(name = "Task.countWatchedTasks",
        query = "SELECT COUNT(ta)  FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND :userName in (select wa FROM ta.watcher wa) "),

    @NamedQuery(name = "Task.getOverdueTasks",
        query = "SELECT ta FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND ta.dueDate < CURDATE() " +
                "AND (ta.assignee = :userName " +
                "OR :userName in (select co FROM ta.coworker co) " +
                ")"),
    @NamedQuery(name = "Task.countOverdueTasks",
        query = "SELECT COUNT(ta) FROM TaskTask ta " +
                "WHERE ta.completed = false " +
                "AND ta.dueDate < CURDATE() " +
                "AND (ta.assignee = :userName " +
                "OR :userName in (select co FROM ta.coworker co) " +
                ")"),
    @NamedQuery(
        name = "Task.findTasks",
        query = "SELECT ta FROM TaskTask ta " +
            "WHERE (ta.assignee = :userName OR ta.createdBy = :userName OR :userName in (select co FROM ta.coworker co) OR (SELECT participator FROM TaskProject p LEFT JOIN p.participator participator where p.id = ta.status.project.id) IN (:memberships) ) " +
            "AND (lower(ta.title) LIKE lower(:term)  OR lower(ta.description) LIKE :term) " +
            "ORDER BY ta.createdTime DESC"
    ),
    @NamedQuery(
        name = "Task.countTasks",
        query = "SELECT COUNT(ta) FROM TaskTask ta " +
            "WHERE (ta.assignee = :userName OR ta.createdBy = :userName OR :userName in (select co FROM ta.coworker co)) " +
            "AND (lower(ta.title) LIKE lower(:term)  OR lower(ta.description) LIKE :term) "
    ),
        @NamedQuery(name = "Task.countTaskStatusByProject",
                query = "SELECT m.status.name AS name, COUNT(m) AS total FROM TaskTask AS m where m.status.project.id = :projectId GROUP BY m.status.name ORDER BY m.status.name ASC"),

        @NamedQuery(name = "Task.getByStatus",
                query = "SELECT t FROM TaskTask t  WHERE t.status.id = :statusid")
})
public class Task {

  public static final String PREFIX_CLONE = "Copy of ";

  @Id
  @SequenceGenerator(name="SEQ_TASK_TASKS_TASK_ID", sequenceName="SEQ_TASK_TASKS_TASK_ID")
  @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_TASK_TASKS_TASK_ID")
  @Column(name = "TASK_ID")
  private long        id;

  private String      title;

  private String      description;

  @Enumerated(EnumType.ORDINAL)
  private Priority    priority;

  private String      context;

  private String      assignee;

  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name = "STATUS_ID")
  private Status      status;

  @Column(name = "TASK_RANK")
  private int         rank;

  private boolean completed = false;

  @ElementCollection(fetch = FetchType.LAZY)
  @CollectionTable(name = "TASK_TASK_COWORKERS",
      joinColumns = @JoinColumn(name = "TASK_ID"))
  private Set<String> coworker = new HashSet<String>();

  @ElementCollection(fetch = FetchType.LAZY)
  @CollectionTable(name = "TASK_TASK_WATCHERS",
      joinColumns = @JoinColumn(name = "TASK_ID"))
  private Set<String> watcher =  new HashSet<String>();

  @Column(name = "CREATED_BY")
  private String      createdBy;

  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "CREATED_TIME")
  private Date        createdTime;

  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "START_DATE")
  private Date        startDate;
  
  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "END_DATE")
  private Date        endDate;

  @Temporal(TemporalType.TIMESTAMP)
  @Column(name = "DUE_DATE")
  private Date        dueDate;

  //This field is only used for remove cascade
  @OneToMany(mappedBy = "task", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
  private List<Comment> comments = new ArrayList<Comment>();

  //This field is only used for remove cascade
  @OneToMany(mappedBy = "task", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
  private List<ChangeLog> logs = new ArrayList<ChangeLog>();

  @OneToMany(mappedBy = "task", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE, orphanRemoval = true)
  private Set<LabelTaskMapping> lblMapping = new HashSet<LabelTaskMapping>();

  @Column(name = "ACTIVITY_ID")
  private String activityId;

  public Task() {
    this.priority = Priority.NORMAL;
  }



  public Task(String title, String description, Priority priority, String context, String assignee, Set<String> coworker, Set<String> watcher, Status status, String createdBy , Date endDate, Date startDate, Date dueDate) {
    this.title = title;
    this.assignee = assignee;
    this.coworker = coworker;
    this.watcher = watcher;
    this.context = context;
    this.createdBy = createdBy;
    this.description = description;
    this.priority = priority;
    this.startDate = startDate;
    this.endDate = endDate;
    this.dueDate = dueDate;
    this.status = status;
  }

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public String getDescription() {
    return description;
  }

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

  public Priority getPriority() {
    return priority;
  }

  public void setPriority(Priority priority) {
    this.priority = priority;
  }

  public String getContext() {
    return context;
  }

  public void setContext(String context) {
    this.context = context;
  }

  public String getAssignee() {
    return assignee;
  }

  public void setAssignee(String assignee) {
    this.assignee = assignee;
  }

  public Status getStatus() {
    return status;
  }

  public void setStatus(Status status) {
    this.status = status;
  }

  public int getRank() {
    return rank;
  }

  public void setRank(int rank) {
    this.rank = rank;
  }

  public boolean isCompleted() {
    return completed;
  }

  public void setCompleted(boolean completed) {
    this.completed = completed;
  }

  public String getCreatedBy() {
    return createdBy;
  }

  public void setCreatedBy(String createdBy) {
    this.createdBy = createdBy;
  }

  public Date getCreatedTime() {
    return createdTime;
  }

  public void setCreatedTime(Date createdTime) {
    this.createdTime = createdTime;
  }

  public Date getEndDate() {
    return endDate;
  }

  public void setEndDate(Date endDate) {
    this.endDate = endDate;
  }

  public Date getStartDate() {
    return startDate;
  }

  public void setStartDate(Date startDate) {
    this.startDate = startDate;
  }

  public Date getDueDate() {
    return dueDate;
  }

  public void setDueDate(Date dueDate) {
    this.dueDate = dueDate;
  }

  public Set<String> getCoworker() {
    return coworker;
  }

  public void setCoworker(Set<String> coworker) {
    this.coworker = coworker;
  }

  public Set<String> getWatcher() {
    return watcher;
  }

  public void setWatcher(Set<String> watcher) {
    this.watcher = watcher;
  }

  public String getActivityId() {
    return activityId;
  }

  public void setActivityId(String activityId) {
    this.activityId = activityId;
  }

  public Task clone() {
    Set<String> coworkerClone = new HashSet<String>();
    Set<String> watcherClone = new HashSet<String>();
    if (getCoworker() != null) {
      coworkerClone = new HashSet<String>(getCoworker());
    }
    if (getWatcher() != null) {
      watcherClone = new HashSet<String>(getWatcher());
    }
    Task newTask = new Task(this.getTitle(), this.getDescription(), this.getPriority(), this.getContext(), this.getAssignee(), coworkerClone, watcherClone, this.getStatus() != null ? this.getStatus().clone() : null, this.getCreatedBy() , this.getEndDate(), this.getStartDate(), this.getDueDate());

    newTask.setCreatedTime(getCreatedTime());
    newTask.setActivityId(getActivityId());
    newTask.setCompleted(isCompleted());
    newTask.setRank(getRank());
    newTask.setId(getId());

    return newTask;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Task task = (Task) o;

    if (completed != task.completed) return false;
    if (id != task.id) return false;
    if (assignee != null ? !assignee.equals(task.assignee) : task.assignee != null) return false;
    if (context != null ? !context.equals(task.context) : task.context != null) return false;
    if (coworker != null ? !coworker.equals(task.coworker) : task.coworker != null) return false;
    if (watcher != null ? !watcher.equals(task.watcher) : task.watcher != null) return false;
    if (createdBy != null ? !createdBy.equals(task.createdBy) : task.createdBy != null) return false;
    if (createdTime != null ? !createdTime.equals(task.createdTime) : task.createdTime != null) return false;
    if (description != null ? !description.equals(task.description) : task.description != null) return false;
    if (dueDate != null ? !dueDate.equals(task.dueDate) : task.dueDate != null) return false;
    if (priority != task.priority) return false;
    if (startDate != null ? !startDate.equals(task.startDate) : task.startDate != null) return false;
    if (endDate != null ? !endDate.equals(task.endDate) : task.endDate != null) return false;
    if (status != null ? !status.equals(task.status) : task.status != null) return false;
    if (title != null ? !title.equals(task.title) : task.title != null) return false;

    return true;
  }

  @Override
  public int hashCode() {
    return Objects.hash(id, title, description, priority, context, assignee, status, completed, coworker,watcher,
            createdBy, createdTime, startDate, endDate, dueDate);
  }
} @Data
public class TaskDto  implements Serializable {
    private long        id;
    private String      title;
    private String      description;
    private Priority priority;
    private String      context;
    private String      assignee;
    private StatusDto status;
    private int         rank;
    private boolean completed;
    private Set<String> coworker;
    private Set<String> watcher;
    private String      createdBy;
    private Date createdTime;
    private Date        startDate;
    private Date        endDate;
    private Date        dueDate;
    private String activityId;

    public TaskDto clone() {
        Set<String> coworkerClone = new HashSet<String>();
        Set<String> watcherClone = new HashSet<String>();
        if (getCoworker() != null) {
            coworkerClone = new HashSet<String>(getCoworker());
        }
        if (getWatcher() != null) {
            watcherClone = new HashSet<String>(getWatcher());
        }
        TaskDto newTask = new TaskDto();
        newTask.setTitle(this.getTitle());
        newTask.setDescription(this.getDescription());
        newTask.setPriority(this.getPriority());
        newTask.setContext(this.getContext());
        newTask.setAssignee(this.getAssignee());
        newTask.setCoworker(coworkerClone);
        newTask.setWatcher(watcherClone);
        newTask.setStatus(this.getStatus() != null ? this.getStatus().clone() : null);
        newTask.setRank(this.getRank());
        newTask.setActivityId(this.getActivityId());
        newTask.setCompleted(this.isCompleted());
        newTask.setCreatedBy(this.getCreatedBy());
        newTask.setCreatedTime(this.getCreatedTime());
        newTask.setEndDate(this.getEndDate());
        newTask.setStartDate(this.getStartDate());
        newTask.setDueDate(this.getDueDate());
        newTask.setCreatedTime(getCreatedTime());
        newTask.setActivityId(getActivityId());
        newTask.setCompleted(isCompleted());
        newTask.setRank(getRank());
        newTask.setId(getId());

        return newTask;
    }

} @Singleton
public class TaskServiceImpl implements TaskService {

    private static final Log LOG = ExoLogger.getExoLogger(TaskServiceImpl.class);


    @Inject
    private TaskStorage taskStorage;

    @Inject
    private DAOHandler daoHandler;

    private ListenerService listenerService;

    private ProjectService projectService;

    public TaskServiceImpl(TaskStorage taskStorage, DAOHandler daoHandler, ListenerService listenerService) {
        this.taskStorage = taskStorage;
        this.daoHandler = daoHandler;
        this.listenerService = listenerService;
        this.projectService = projectService;
    }

    @Override
    @ExoTransactional
    public TaskDto createTask(TaskDto task) {
        TaskDto result = taskStorage.createTask(task);
        //
        TaskPayload event = new TaskPayload(null, result);
        try {
            listenerService.broadcast(TASK_CREATION, null, event);
            if(result.getStatus()!=null){
                listenerService.broadcast("exo.project.projectModified", null,result.getStatus().getProject() );
            }
        } catch (Exception e) {
            LOG.error("Error while broadcasting task creation event", e);
        }

        return result;
    }

    @Override
    @ExoTransactional
    public TaskDto updateTask(TaskDto task) {
        if (task == null) {
            throw new IllegalArgumentException("TaskDto must not be NULL");
        }

        TaskDto oldTaskEntity = taskStorage.getTaskWithCoworkers(task.getId());
        TaskDto newTaskEntity = taskStorage.update(task);
        TaskPayload event = new TaskPayload(oldTaskEntity, newTaskEntity);
        try {
            listenerService.broadcast(TASK_UPDATE, null, event);
            if(task.getStatus()!=null){
                listenerService.broadcast("exo.project.projectModified", null, task.getStatus().getProject() );
            }
        } catch (Exception e) {
            LOG.error("Error while broadcasting task creation event", e);
        }

        return newTaskEntity;
    }

    @Override
    @ExoTransactional
    public void updateTaskOrder(long currentTaskId, Status newStatus, long[] orders) {
        taskStorage.updateTaskOrder(currentTaskId, newStatus, orders);
    }

    @Override
    @ExoTransactional
    public void removeTask(long id) throws EntityNotFoundException {
        TaskDto task = getTask(id);// Can throw TaskNotFoundException
        taskStorage.delete(task);
        LOG.info("Task {} removed", id);
    }

    @Override
    @ExoTransactional
    public TaskDto cloneTask(long id) throws EntityNotFoundException {
        TaskDto task = getTask(id);// Can throw TaskNotFoundException
        TaskDto newTask = task.clone();
        newTask.setId(0L);
        newTask.setCoworker(getCoworker(id));
        newTask.setTitle(Task.PREFIX_CLONE + newTask.getTitle());
        return createTask(newTask);
    }

    @Override
    public TaskDto getTask(long id) throws EntityNotFoundException {
        TaskDto task = taskStorage.getTaskById(id);
        if (task == null) {
            LOG.info("Can not find task with ID: " + id);
            throw new org.exoplatform.task.exception.EntityNotFoundException(id, Task.class);
        }
        return task;
    }

    @Override
    public Set<String> getMentionedUsers(long taskId) {
        return daoHandler.getCommentHandler().findMentionedUsersOfTask(taskId);
    }


    @Override
    @ExoTransactional
    public ChangeLogEntry addTaskLog(long id, String username, String actionName, String target) throws EntityNotFoundException {
        ChangeLogEntry log = new ChangeLogEntry();
        log.setTask(StorageUtil.taskToEntity(getTask(id)));
        log.setAuthor(username);
        log.setActionName(actionName);
        log.setTarget(target);
        return taskStorage.addTaskLog(log);
    }

    @Override
    public List<ChangeLogEntry> getTaskLogs(long taskId, int offset, int limit) throws Exception {
        return taskStorage.getTaskLogs(taskId,  offset,  limit);
    }


    @Override
    public List<TaskDto> findTasks(TaskQuery query, int offset, int limit) throws Exception {
        return taskStorage.findTasks(query, offset, limit);
    }

    @Override
    public int countTasks(TaskQuery query) throws Exception {
        return taskStorage.countTasks(query);
    }

    @Override
    public <T> List<T> selectTaskField(TaskQuery query, String fieldName) {
        return taskStorage.selectTaskField(query, fieldName);
    }

    @Override
    public List<TaskDto> findTasksByLabel(LabelDto label, List<Long> projectIds, String username, OrderBy orderBy, int offset, int limit) throws Exception {

        return taskStorage.findTasksByLabel(label, projectIds, username, orderBy, offset, limit);
    }

    @Override
    public int countTasksByLabel(LabelDto label, List<Long> projectIds, String username, OrderBy orderBy) throws Exception {
        return taskStorage.countTasksByLabel(label, projectIds, username, orderBy);
    }

    public TaskDto findTaskByActivityId(String id) {
        return taskStorage.findTaskByActivityId(id);
    }

    @Override
    public Set<String> getCoworker(long taskId) {
        return taskStorage.getCoworker(taskId);
    }

    @Override
    public List<TaskDto> getUncompletedTasks(String user, int limit) {
        return taskStorage.getUncompletedTasks(user, limit);
    }

    @Override
    public Long countUncompletedTasks(String user) {
        return taskStorage.countUncompletedTasks(user);
    }


    @Override
    public List<TaskDto> getWatchedTasks(String user, int limit) {
        return taskStorage.getWatchedTasks(user, limit);
    }


    @Override
    public Long countWatchedTasks(String user) {
        return taskStorage.countWatchedTasks(user);
    }



    @Override
    public List<TaskDto> getAssignedTasks(String user, int limit) {
        return taskStorage.getAssignedTasks(user, limit);
    }


    @Override
    public Long countAssignedTasks(String user) {
        return taskStorage.countWatchedTasks(user);
    }


    @Override
    public List<TaskDto> getCollaboratedTasks(String user, int limit) {
        return taskStorage.getCollaboratedTasks(user, limit);
    }


    @Override
    public Long countCollaboratedTasks(String user) {
        return taskStorage.countCollaboratedTasks(user);
    }



    @Override
    public List<TaskDto> findTasksByMemberShips(String user, List<String> memberships, String query, int limit) {
        if (StringUtils.isBlank(user)) {
            throw new IllegalArgumentException("user parameter is mandatory");
        }
        if (StringUtils.isBlank(query)) {
            throw new IllegalArgumentException("query parameter is mandatory");
        }
        return taskStorage.findTasks(user, memberships, query, limit);
    }

    @Override
    public List<TaskDto> findTasks(String user, String query, int limit) {
        return findTasksByMemberShips(user,new ArrayList<>(), query, limit);
    }

    @Override
    public long countTasks(String user, String query) {
        if (StringUtils.isBlank(user)) {
            throw new IllegalArgumentException("user parameter is mandatory");
        }
        if (StringUtils.isBlank(query)) {
            throw new IllegalArgumentException("query parameter is mandatory");
        }
        return taskStorage.countTasks(user, query);
    }

    @Override
    public List<TaskDto> getIncomingTasks(String user, int offset, int limit) throws Exception {
        return taskStorage.getIncomingTasks(user, offset, limit);
    }

    @Override
    public int countIncomingTasks(String user) throws Exception {
        return taskStorage.countIncomingTasks(user);
    }

    @Override
    public List<TaskDto> getOverdueTasks(String user, int limit) {
        return taskStorage.getOverdueTasks(user, limit);
    }

    @Override
    public Long countOverdueTasks(String user) {
        return taskStorage.countOverdueTasks(user);
    }

    @Override
    public void addWatcherToTask(String user, TaskDto task) throws Exception {
        taskStorage.addWatcherToTask(user, task);
    }

    @Override
    public void deleteWatcherOfTask(String user, TaskDto task) throws Exception {
        taskStorage.deleteWatcherOfTask(user, task);
    }

    public Set<String> getWatchersOfTask(TaskDto task) {
        return taskStorage.getWatchersOfTask(task);
    }


    @Override
    public List<Object[]> countTaskStatusByProject(long projectId) {
        return taskStorage.countTaskStatusByProject(projectId);
    }

    @Override
    public TasksList filterTasks(String query, long projectId, String keyword, List<Long> labels, TaskUtil.DUE dueDate, Priority priority, List<String> assignees, List<String> coworker, List<String> watchers, Long labelId, Long statusId, Identity currIdentity, String dueCategory, String space_group_id , TimeZone userTimezone, boolean isShowCompleted, boolean advanceSearch, boolean noProjPermission, boolean noLblPermission, String orderBy, String groupBy, int offset, int limit) throws Exception {

        List<String> defOrders;
        List<String> defGroupBys;

        List<Long> spaceProjectIds = null;
        if (space_group_id != null) {
            spaceProjectIds = new LinkedList<Long>();
            List<ProjectDto> projects = ProjectUtil.flattenTree(ProjectUtil.getProjectTree(space_group_id, projectService,offset,limit), projectService,offset,limit);
            for (ProjectDto p : projects) {
                if (p.canView(currIdentity)) {
                    spaceProjectIds.add(p.getId());
                }
            }
        }
        List<Long> allProjectIds = null;
        if (projectId == 0) {
            allProjectIds = new LinkedList<Long>();
            List<ProjectDto> projects = ProjectUtil.flattenTree(ProjectUtil.getProjectTree(null, projectService,offset,limit), projectService,offset,limit);
            for (ProjectDto p : projects) {
                if (p.canView(currIdentity)) {
                    allProjectIds.add(p.getId());
                }
            }
        }

        OrderBy order = null;
        if(orderBy != null && !orderBy.trim().isEmpty()) {
            order = TaskUtil.TITLE.equals(orderBy) || TaskUtil.DUEDATE.equals(orderBy) ? new OrderBy.ASC(orderBy) : new OrderBy.DESC(orderBy);
        }

        TaskQuery taskQuery = new TaskQuery();
        if (advanceSearch) {
            Status status = statusId != null ? daoHandler.getStatusHandler().find(statusId) : null;
            TaskUtil.buildTaskQuery(taskQuery, keyword, labels, status, dueDate, priority, assignees, coworker, watchers, isShowCompleted, userTimezone);
        } else {
            taskQuery.setKeyword(query);
            taskQuery.setCompleted(false);
        }

        //Get Tasks in good order
        if(projectId == ProjectUtil.INCOMING_PROJECT_ID) {
            //. Default order by CreatedDate
            if (orderBy == null || orderBy.isEmpty()) {
                orderBy = TaskUtil.CREATED_TIME;
            }
            order = new OrderBy.DESC(orderBy);

            taskQuery.setIsIncomingOf(currIdentity.getUserId());
            taskQuery.setOrderBy(Arrays.asList(order));
        } else if (projectId == ProjectUtil.TODO_PROJECT_ID) {
            defGroupBys =  Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL, TaskUtil.DUEDATE,TaskUtil.ASSIGNEE);
            defOrders =  Arrays.asList(TaskUtil.TITLE, TaskUtil.STATUS, TaskUtil.DUEDATE, TaskUtil.PRIORITY, TaskUtil.RANK);
            if (dueDate != null || !dueCategory.equals("")){
                taskQuery.setAssigneeIsTodoOf(currIdentity.getUserId());
            } else {
                taskQuery.setIsTodoOf(currIdentity.getUserId());
            }

            //TODO: process fiter here
            if ("overDue".equalsIgnoreCase(dueCategory)) {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL);
                defOrders = Arrays.asList(TaskUtil.TITLE, TaskUtil.PRIORITY, TaskUtil.DUEDATE);
                groupBy = groupBy == null || !defGroupBys.contains(groupBy) ? TaskUtil.PROJECT : groupBy;
            } else if ("today".equalsIgnoreCase(dueCategory)) {
                defGroupBys =  Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL);
                defOrders =  Arrays.asList(TaskUtil.TITLE, TaskUtil.PRIORITY, TaskUtil.RANK);
                if (orderBy == null) {
                    order = new OrderBy.DESC(TaskUtil.PRIORITY);
                    orderBy = TaskUtil.PRIORITY;
                }
                groupBy = groupBy == null || !defGroupBys.contains(groupBy) ? TaskUtil.NONE : groupBy;
            } else if ("tomorrow".equalsIgnoreCase(dueCategory)) {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL);
                defOrders = Arrays.asList(TaskUtil.TITLE, TaskUtil.PRIORITY, TaskUtil.RANK);
                if (orderBy == null || !defOrders.contains(orderBy)) {
                    order = new OrderBy.DESC(TaskUtil.PRIORITY);
                    orderBy = TaskUtil.PRIORITY;
                }
                groupBy = groupBy == null || !defGroupBys.contains(groupBy) ? TaskUtil.NONE : groupBy;
            } else if ("upcoming".equalsIgnoreCase(dueCategory)) {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL);
                defOrders =  Arrays.asList(TaskUtil.TITLE, TaskUtil.PRIORITY, TaskUtil.DUEDATE, TaskUtil.RANK);
                groupBy = groupBy == null || !defGroupBys.contains(groupBy) ? TaskUtil.NONE : groupBy;
            }

            if (orderBy == null || !defOrders.contains(orderBy)) {
                orderBy = TaskUtil.DUEDATE;
                order = new OrderBy.ASC(TaskUtil.DUEDATE);
            }
            if (groupBy == null || !defGroupBys.contains(groupBy)) {
                groupBy = TaskUtil.DUEDATE;
            }

            Date[] filterDate = TaskUtil.convertDueDate(dueCategory, userTimezone);
            taskQuery.setDueDateFrom(filterDate[0]);
            taskQuery.setDueDateTo(filterDate[1]);
            taskQuery.setOrderBy(Arrays.asList(order));

        } else if (projectId >= 0) {
            if (projectId == 0) {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.ASSIGNEE, TaskUtil.PROJECT, TaskUtil.LABEL, TaskUtil.DUEDATE, TaskUtil.STATUS);

                if (orderBy == null || orderBy.isEmpty()) {
                    orderBy = TaskUtil.DUEDATE;
                    order = new OrderBy.ASC(orderBy);
                }

                if (spaceProjectIds != null) {
                    taskQuery.setProjectIds(spaceProjectIds);
                } else {
                    taskQuery.setProjectIds(allProjectIds);
                }
            } else {
                //. Default order by CreatedDate
                if (orderBy == null || orderBy.isEmpty()) {
                    orderBy = TaskUtil.DUEDATE;
                    order = new OrderBy.ASC(orderBy);
                }
                if (!noProjPermission) {
                    taskQuery.setProjectIds(Arrays.asList(projectId));
                }
            }
            taskQuery.setOrderBy(Arrays.asList(order));

        } else if (labelId != null && labelId >= 0) {
            defOrders =  Arrays.asList(TaskUtil.TITLE, TaskUtil.CREATED_TIME, TaskUtil.DUEDATE, TaskUtil.PRIORITY);
            if (orderBy == null || orderBy.isEmpty() || !defOrders.contains(orderBy)) {
                orderBy = TaskUtil.DUEDATE;
                order = new OrderBy.ASC(TaskUtil.DUEDATE);
            }

            if (labelId > 0) {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.DUEDATE, TaskUtil.STATUS);
                if (groupBy == null || groupBy.isEmpty() || !defGroupBys.contains(groupBy)) {
                    groupBy = TaskUtil.NONE;
                }
                if (!noLblPermission) {
                    taskQuery.setLabelIds(Arrays.asList(labelId));
                }
            } else {
                defGroupBys = Arrays.asList(TaskUtil.NONE, TaskUtil.PROJECT, TaskUtil.LABEL, TaskUtil.DUEDATE, TaskUtil.STATUS);
                if (groupBy == null || groupBy.isEmpty() || !defGroupBys.contains(groupBy)) {
                    groupBy = TaskUtil.LABEL;
                }
                taskQuery.setIsLabelOf(currIdentity.getUserId());
            }

            if (spaceProjectIds != null) {
                taskQuery.setProjectIds(spaceProjectIds);
            }
            taskQuery.setOrderBy(Arrays.asList(order));
        }



        List<TaskDto> listTasks = null;
        long tasksSize = 0;
        if ((spaceProjectIds != null  && spaceProjectIds.isEmpty()) || ( projectId == 0 && allProjectIds.isEmpty()) ||
                (noLblPermission && labelId != null && labelId > 0) || (noProjPermission &&  projectId > 0)) {
            listTasks = new ArrayList<>();
        } else {
            listTasks = findTasks(taskQuery,offset,limit);
            tasksSize = countTasks(taskQuery);
        }
       return new TasksList(listTasks,tasksSize);
    }

    @Override
    public boolean isExternal(String userId) {
        IdentityManager identityManager = CommonsUtils.getService(IdentityManager.class);
        org.exoplatform.social.core.identity.model.Identity userIdentity =  identityManager.getOrCreateIdentity(
            OrganizationIdentityProvider.NAME, userId);
        return userIdentity.getProfile() != null && userIdentity.getProfile().getProperty(Profile.EXTERNAL) != null && userIdentity.getProfile().getProperty(Profile.EXTERNAL).equals("true");
    }

} public interface TaskStorage {

    /**
     * Find task by its id
     *
     * @param id the given id.
     * @return {@link TaskDto}
     */
    TaskDto getTaskById(long id);

    /**
     * Create a new task.
     *
     * @param task the given task
     * @return {@link TaskDto}
     */
    TaskDto createTask(TaskDto task);

    /**
     * Update the provided task.
     *
     * @param task the given task.
     * @return {@link TaskDto}
     */
    TaskDto update(TaskDto task);

    /**
     * Delete the provided task.
     *
     * @param task the provided task.
     */
    void delete(TaskDto task);

    List<TaskDto> findTasksByLabel(LabelDto label,
                                   List<Long> projectIds,
                                   String username,
                                   OrderBy orderBy,
                                   int offset,
                                   int limit) throws Exception;

    int countTasksByLabel(LabelDto label, List<Long> projectIds, String username, OrderBy orderBy) throws Exception;

    List<TaskDto> findByUser(String user);

    List<TaskDto> findTasks(TaskQuery query, int offset, int limit) throws Exception;

    int countTasks(TaskQuery query) throws Exception;

    <T> List<T> selectTaskField(TaskQuery query, String fieldName);

    TaskDto findTaskByActivityId(String activityId);

    void updateStatus(Status stOld, Status stNew);

    void updateTaskOrder(long currentTaskId, Status newStatus, long[] orders);

    Set<String> getCoworker(long taskid);

    TaskDto getTaskWithCoworkers(long id);

    List<TaskDto> getUncompletedTasks(String user, int limit);

    List<TaskDto> getAssignedTasks(String user, int limit);

    Long countAssignedTasks(String user);

    List<TaskDto> getWatchedTasks(String user, int limit);

    Long countUncompletedTasks(String user);

    Long countWatchedTasks(String user);

    List<TaskDto> getCollaboratedTasks(String user, int limit);

    Long countCollaboratedTasks(String user);

    List<TaskDto> getIncomingTasks(String user, int offset, int limit) throws Exception;

    int countIncomingTasks(String user) throws Exception;

    List<TaskDto> getOverdueTasks(String user, int limit);

    Long countOverdueTasks(String user);

    void addWatcherToTask(String username, TaskDto task) throws Exception;

    void deleteWatcherOfTask(String username, TaskDto task) throws Exception;

    Set<String> getWatchersOfTask(TaskDto task);

    /**
     * Find tasks assigned to a user using a term to find in title or description of
     * the task
     *
     * @param user  username
     * @param memberships memberships
     * @param query term to search in title or description
     * @param limit term to limit results.
     * @return {@link List} of {@link TaskDto}
     */
    List<TaskDto> findTasks(String user, List<String> memberships, String query, int limit);

    /**
     * Count tasks assigned to a user using a search term to find in title or
     * description of the task
     *
     * @param user  username
     * @param query term to search in title or description
     * @return tasks count
     */
    long countTasks(String user, String query);

    /**
     * Create a log associated with a task with given <code>changeLogEntry</code>.
     *
     * @param changeLogEntry changeLogEntry
     * @return Create a log associated
     * @throws EntityNotFoundException when user is not authorized to add taskLog.
     */
    ChangeLogEntry addTaskLog(ChangeLogEntry changeLogEntry) throws EntityNotFoundException;

    List<ChangeLogEntry> getTaskLogs(long taskId, int offset, int limit) throws Exception;

    List<Object[]> countTaskStatusByProject(long projectId);

} public class TaskStorageImpl implements TaskStorage {

    private static final Log LOG = ExoLogger.getExoLogger(TaskStorageImpl.class);

    private static final Pattern pattern = Pattern.compile("@([^\\s]+)|@([^\\s]+)$");


    @Inject
    private final DAOHandler daoHandler;

    @Inject
    private final UserService userService;

    @Inject
    private final ProjectStorage projectStorage;


    public TaskStorageImpl(DAOHandler daoHandler, UserService userService, ProjectStorage projectStorage) {
        this.daoHandler = daoHandler;
        this.userService = userService;
        this.projectStorage = projectStorage;
    }

    @Override
    public TaskDto getTaskById(long id) {
        return StorageUtil.taskToDto(daoHandler.getTaskHandler().find(id),projectStorage);
    }

    @Override
    public TaskDto createTask(TaskDto task) {
        return StorageUtil.taskToDto(daoHandler.getTaskHandler().create(StorageUtil.taskToEntity(task)),projectStorage);
    }

    @Override
    public TaskDto update(TaskDto task) {
        return StorageUtil.taskToDto(daoHandler.getTaskHandler().update(StorageUtil.taskToEntity(task)),projectStorage);
    }

    @Override
    public void delete(TaskDto task) {
        daoHandler.getTaskHandler().delete(StorageUtil.taskToEntity(task));
    }

    @Override
    public List<TaskDto> findTasksByLabel(LabelDto label, List<Long> projectIds, String username, OrderBy orderBy, int offset, int limit) throws Exception {
        List<Task> taskEntities = Arrays.asList(daoHandler.getTaskHandler().findTasksByLabel(label.getId(), projectIds, username, orderBy).load(offset, limit));
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public int countTasksByLabel(LabelDto label, List<Long> projectIds, String username, OrderBy orderBy) throws Exception {
        return daoHandler.getTaskHandler().findTasksByLabel(label.getId(), projectIds, username, orderBy).getSize();
    }

    @Override
    public List<TaskDto> findByUser(String user) {
        List<String> memberships = new ArrayList<String>();
        memberships.add(user);
        List<Task> taskEntities = daoHandler.getTaskHandler().findAllByMembership(user, memberships);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public List<TaskDto> findTasks(TaskQuery query, int offset, int limit) throws Exception {
        List<Task> taskEntities = Arrays.asList(daoHandler.getTaskHandler().findTasks(query).load(offset, limit));
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    public int countTasks(TaskQuery query) throws Exception {
        return daoHandler.getTaskHandler().findTasks(query).getSize();
    }

    @Override
    public <T> List<T> selectTaskField(TaskQuery query, String fieldName) {
        return daoHandler.getTaskHandler().selectTaskField(query, fieldName);
    }

    @Override
    public TaskDto findTaskByActivityId(String activityId) {
        return StorageUtil.taskToDto(daoHandler.getTaskHandler().findTaskByActivityId(activityId),projectStorage);
    }

    @Override
    public void updateStatus(Status stOld, Status stNew) {
        daoHandler.getTaskHandler().updateStatus(stOld, stNew);
    }

    @Override
    public void updateTaskOrder(long currentTaskId, Status newStatus, long[] orders) {
        daoHandler.getTaskHandler().updateTaskOrder(currentTaskId, newStatus, orders);
    }

    @Override
    public Set<String> getCoworker(long taskid) {
        return daoHandler.getTaskHandler().getCoworker(taskid);
    }

    @Override
    public TaskDto getTaskWithCoworkers(long id) {
        return StorageUtil.taskToDto(daoHandler.getTaskHandler().getTaskWithCoworkers(id),projectStorage);
    }

    @Override
    public List<TaskDto> getUncompletedTasks(String user, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().getUncompletedTasks(user, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public Long countUncompletedTasks(String user) {
        return daoHandler.getTaskHandler().countUncompletedTasks(user);
    }

    @Override
    public List<TaskDto> getAssignedTasks(String user, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().getAssignedTasks(user, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public Long countAssignedTasks(String user) {
        return daoHandler.getTaskHandler().countAssignedTasks(user);
    }


    @Override
    public List<TaskDto> getWatchedTasks(String user, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().getWatchedTasks(user, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public Long countWatchedTasks(String user) {
        return daoHandler.getTaskHandler().countWatchedTasks(user);
    }


    @Override
    public List<TaskDto> getCollaboratedTasks(String user, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().getCollaboratedTasks(user, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }


    @Override
    public Long countCollaboratedTasks(String user) {
        return daoHandler.getTaskHandler().countCollaboratedTasks(user);
    }


    @Override
    public List<TaskDto> getIncomingTasks(String user, int offset, int limit) throws Exception {
        List<Task> taskEntities = Arrays.asList(daoHandler.getTaskHandler().getIncomingTasks(user).load(offset, limit));
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public int countIncomingTasks(String user) throws Exception {
        return daoHandler.getTaskHandler().getIncomingTasks(user).getSize();
    }

    @Override
    public List<TaskDto> getOverdueTasks(String user, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().getOverdueTasks(user, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    @Override
    public Long countOverdueTasks(String user) {
        return daoHandler.getTaskHandler().countOverdueTasks(user);
    }

    @Override
    public void addWatcherToTask(String username, TaskDto task) {
        Set<String> watchers = getWatchersOfTask(task);
        if (watchers != null && !watchers.contains(username)) {
            watchers.add(username);
            task.setWatcher(watchers);
            daoHandler.getTaskHandler().update(StorageUtil.taskToEntity(task));
        }
    }

    @Override
    public void deleteWatcherOfTask(String username, TaskDto task) throws Exception {
        Set<String> watchers = getWatchersOfTask(task);
        if (watchers != null && watchers.contains(username)) {
            watchers.remove(username);
            task.setWatcher(watchers);
            daoHandler.getTaskHandler().update(StorageUtil.taskToEntity(task));
        } else {
            throw new Exception("Cannot remove watcher " + username + "of task because watcher does not exist.");
        }
    }

    @Override
    public Set<String> getWatchersOfTask(TaskDto task) {
        return daoHandler.getTaskHandler().getWatchersOfTask(StorageUtil.taskToEntity(task));
    }

    /**
     * Find tasks assigned to a user using a term to find in title or description
     * of the task
     *
     * @param user  username
     * @param query term to search in title or description
     * @param limit term to limit results.
     * @return {@link List} of {@link Task}
     */
    @Override
    public List<TaskDto> findTasks(String user, List<String> memberships, String query, int limit) {
        List<Task> taskEntities = daoHandler.getTaskHandler().findTasks(user, memberships, query, limit);
        return taskEntities.stream().map((Task taskEntity) -> StorageUtil.taskToDto(taskEntity,projectStorage)).collect(Collectors.toList());
    }

    /**
     * Count tasks assigned to a user using a search term to find in title or
     * description of the task
     *
     * @param user  username
     * @param query term to search in title or description
     * @return tasks count
     */
    @Override
    public long countTasks(String user, String query) {
        return daoHandler.getTaskHandler().countTasks(user, query);
    }

    @Override
    public ChangeLogEntry addTaskLog(ChangeLogEntry changeLogEntry) throws EntityNotFoundException {
        return StorageUtil.changeLogToDto(daoHandler.getTaskLogHandler().create(StorageUtil.changeLogToEntity(changeLogEntry,userService)),userService);
    }

    @Override
    public List<ChangeLogEntry> getTaskLogs(long taskId, int offset, int limit) throws Exception {
        return Arrays.asList(daoHandler.getTaskLogHandler().findTaskLogs(taskId).load(offset, limit))
                .stream()
                .map((ChangeLog changeLog) -> StorageUtil.changeLogToDto(changeLog,userService))
                .collect(Collectors.toList());

    }

    @Override
    public List<Object[]> countTaskStatusByProject(long projectId) {
        return daoHandler.getTaskHandler().countTaskStatusByProject(projectId);
    }

}
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import org.exoplatform.task.storage.ProjectStorage;
import org.exoplatform.task.storage.StatusStorage;
import org.exoplatform.task.storage.impl.ProjectStorageImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.container.PortalContainer;
import org.exoplatform.services.listener.ListenerService;
import org.exoplatform.services.security.ConversationState;
import org.exoplatform.services.security.Identity;
import org.exoplatform.task.TestDtoUtils;
import org.exoplatform.task.TestUtils;
import org.exoplatform.task.dao.CommentHandler;
import org.exoplatform.task.dao.DAOHandler;
import org.exoplatform.task.dao.TaskHandler;
import org.exoplatform.task.domain.Task;
import org.exoplatform.task.dto.TaskDto;
import org.exoplatform.task.exception.EntityNotFoundException;
import org.exoplatform.task.exception.ParameterEntityException;
import org.exoplatform.task.service.impl.TaskServiceImpl;
import org.exoplatform.task.storage.TaskStorage;
import org.exoplatform.task.storage.impl.TaskStorageImpl;
    newCoworkers.add("PhuongTuyen");
    newCoworkers.add("Tuyen");

    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setCoworker(newCoworkers);
    taskService.updateTask(task);

    verify(taskHandler, times(1)).update(taskCaptor.capture());

    Set<String> coworker = new HashSet<String>();
    for (String v : newCoworkers) {
      coworker.add(v);
    }
    assertEquals(coworker, taskCaptor.getValue().getCoworker());

  }

  @Test
  public void testUpdateTaskWatcher() throws EntityNotFoundException {

    Set<String> newWatchers = new HashSet<String>();
    newWatchers.add("Tib");
    newWatchers.add("Trong");
    newWatchers.add("Phuong");
    newWatchers.add("Tuyen");

    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setWatcher(newWatchers);
    when(daoHandler.getTaskHandler().getWatchersOfTask(any())).thenReturn(newWatchers);
    taskService.getWatchersOfTask(task);

    verify(taskHandler, times(1)).getWatchersOfTask(taskCaptor.capture());

    Set<String> newWatcher = new HashSet<String>();
    for (String v : newWatchers) {
      newWatcher.add(v);
    }
    assertEquals(newWatcher, taskCaptor.getValue().getWatcher());

  }

  @Test
  public void testWatcherToTask() throws Exception {

    Set<String> newWatchers = new HashSet<String>();
    newWatchers.add("Tib");
    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setWatcher(newWatchers);
    taskService.addWatcherToTask("Tib",task);
    verify(taskHandler, times(1)).getWatchersOfTask(taskCaptor.capture());
    Set<String> newWatcher = new HashSet<String>();
    for (String v : newWatchers) {
      newWatcher.add(v);
    }
    assertEquals(newWatcher, taskCaptor.getValue().getWatcher());

  }


  @Test
  public void testUpdateTaskStatus() throws ParameterEntityException, EntityNotFoundException {

    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setStatus(statusService.getStatus(TestUtils.EXISTING_STATUS_ID));
    taskService.updateTask(task);
    verify(taskHandler, times(1)).update(taskCaptor.capture());
    //assertEquals(TestUtils.getDefaultStatus(), taskCaptor.getValue().getStatus());

  }

  @Test
  public void testUpdateTaskDueDate() throws ParameterEntityException, EntityNotFoundException, ParseException {

    String dueDate = "1989-01-19";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    Date date = sdf.parse(dueDate);

    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setDueDate(date);
    taskService.updateTask(task);

    verify(taskHandler, times(1)).update(taskCaptor.capture());

    assertEquals(date, taskCaptor.getValue().getDueDate());

  }

  @Test
  public void testFindTasks() throws  EntityNotFoundException {

    String newAssignee = "Tib";

    TaskDto task = taskService.getTask(TestUtils.EXISTING_TASK_ID);
    task.setAssignee(newAssignee);
    taskService.updateTask(task);

    verify(taskHandler, times(1)).update(taskCaptor.capture());

    assertEquals(newAssignee, taskCaptor.getValue().getAssignee());

    taskService.findTasks(newAssignee, newAssignee, 1);
    verify(taskHandler, times(1)).findTasks(newAssignee,new ArrayList<>(),newAssignee,1);

    assertEquals(task.getId(), taskCaptor.getValue().getId());

  }



  @Test
  public void testDeleteTaskById() throws EntityNotFoundException {
    taskService.removeTask(TestUtils.EXISTING_TASK_ID);
    verify(taskHandler, times(1)).delete(taskCaptor.capture());

    assertEquals(TestUtils.EXISTING_TASK_ID, taskCaptor.getValue().getId());
  }

  @Test
  public void testCloneTaskById() throws EntityNotFoundException {

    TaskDto defaultTask = TestDtoUtils.getDefaultTask();