diff --git a/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java index 3260dffac..8f5e13021 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java @@ -35,28 +35,48 @@ import javax.persistence.JoinColumn; import javax.persistence.Lob; import javax.persistence.OneToOne; import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import org.libreccm.core.Identifiable; /** + * A comment for a task. Comments are intended for other users, for example to + * inform them about problems etc. with the object. * * @author Jens Pelzetter */ @Entity @Table(name = "WORKFLOW_TASK_COMMENTS", schema = CoreConstants.DB_SCHEMA) -public class TaskComment implements Serializable { +public class TaskComment implements Identifiable, Serializable { private static final long serialVersionUID = 3842991529698351698L; + /** + * Database ID of the comment. + */ @Id @Column(name = "COMMENT_ID") @GeneratedValue(strategy = GenerationType.AUTO) private long commentId; + /** + * The UUID of the comment. + */ + @Column(name = "uuid", unique = true, nullable = false) + @NotNull + private String uuid; + + /** + * The comment. + */ @Column(name = "COMMENT") @Basic @Lob @Type(type = "org.hibernate.type.TextType") private String comment; + /** + * The author of the comment. + */ @OneToOne @JoinColumn(name = "AUTHOR_ID") private User author; @@ -69,6 +89,15 @@ public class TaskComment implements Serializable { this.commentId = commentId; } + @Override + public String getUuid() { + return uuid; + } + + protected void setUuid(final String uuid) { + this.uuid = uuid; + } + public String getComment() { return comment; } @@ -89,6 +118,7 @@ public class TaskComment implements Serializable { public int hashCode() { int hash = 3; hash = 67 * hash + (int) (commentId ^ (commentId >>> 32)); + hash = 67 * hash + Objects.hashCode(uuid); hash = 67 * hash + Objects.hashCode(comment); hash = 67 * hash + Objects.hashCode(author); return hash; @@ -113,6 +143,9 @@ public class TaskComment implements Serializable { if (commentId != other.getCommentId()) { return false; } + if (!Objects.equals(uuid, other.getUuid())) { + return false; + } if (!Objects.equals(comment, other.getComment())) { return false; } @@ -130,12 +163,14 @@ public class TaskComment implements Serializable { public String toString(final String data) { return String.format("%s{ " - + "commentId = %d, " - + "comment = \"%s\", " - + "author = %s%s" - + " }", + + "commentId = %d, " + + "uuid = \"%s\"" + + "comment = \"%s\", " + + "author = %s%s" + + " }", super.toString(), commentId, + uuid, comment, Objects.toString(author), data); diff --git a/ccm-core/src/main/java/org/libreccm/workflow/TaskManager.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskManager.java index 463ea6a3a..b1002d799 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/TaskManager.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskManager.java @@ -23,22 +23,20 @@ import org.apache.logging.log4j.Logger; import org.libreccm.core.CoreConstants; import org.libreccm.security.AuthorizationRequired; import org.libreccm.security.RequiresPrivilege; -import org.libreccm.security.Role; -import org.libreccm.security.RoleRepository; import org.libreccm.security.Shiro; import org.libreccm.security.User; -import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; +import java.util.UUID; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; import javax.transaction.Transactional; /** + * Manager for {@link Task}s. The logic of some of this methods has been taken + * from the old implementation without changes. * * @author Jens Pelzetter */ @@ -46,7 +44,7 @@ import javax.transaction.Transactional; public class TaskManager { private static final Logger LOGGER = LogManager.getLogger(TaskManager.class); - + @Inject private EntityManager entityManager; @@ -56,12 +54,15 @@ public class TaskManager { @Inject private TaskRepository taskRepo; - @Inject - private RoleRepository roleRepo; - @Inject private Shiro shiro; + /** + * Adds a {@link Task} to a {@link Workflow}. + * + * @param workflow The workflow to which the task is added. + * @param task The task to add. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -73,6 +74,12 @@ public class TaskManager { taskRepo.save(task); } + /** + * Removes a {@link Task} from a {@link Workflow}. + * + * @param workflow The workflow from which the task is removed. + * @param task The task to remove. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -84,11 +91,19 @@ public class TaskManager { taskRepo.save(task); } + /** + * Adds a dependent {@link Task} to another {@code Task}. + * + * @param parent The task to which the dependent task is added. + * @param task The dependent task. + * @throws CircularTaskDependencyException If a circular dependency is + * detected. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public void addDependentTask(final Task parent, final Task task) - throws CircularTaskDependencyException { + throws CircularTaskDependencyException { checkForCircularDependencies(parent, task); @@ -99,6 +114,12 @@ public class TaskManager { taskRepo.save(parent); } + /** + * Removes a dependent task. + * + * @param parent The task from which the dependent task is removed. + * @param task The dependent task to remove. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -110,9 +131,16 @@ public class TaskManager { taskRepo.save(parent); } + /** + * Helper method for checking for circular dependencies. + * + * @param task1 + * @param task2 + * @throws CircularTaskDependencyException + */ private void checkForCircularDependencies(final Task task1, final Task task2) - throws CircularTaskDependencyException { + throws CircularTaskDependencyException { if (dependsOn(task1, task2)) { throw new CircularTaskDependencyException(); @@ -126,7 +154,7 @@ public class TaskManager { } if (current.getDependsOn() != null - && !current.getDependsOn().isEmpty()) { + && !current.getDependsOn().isEmpty()) { return dependsOn(current, dependsOn); } } @@ -134,30 +162,57 @@ public class TaskManager { return false; } + /** + * Adds a new {@link TaskComment} containing the provided comment to a + * {@link Task}. The author of the comment is the current user. + * + * @param task The task to which the comment is added. + * @param comment The comment to add. + */ public void addComment(final Task task, final String comment) { addComment(task, shiro.getUser(), comment); } + /** + * Adds a new {@link TaskComment} containing the provided comment to a + * {@link Task}. + * + * @param task The task to which the comment is added. + * @param author the author of the comment. + * @param comment The comment to add. + */ public void addComment(final Task task, final User author, final String comment) { final TaskComment taskComment = new TaskComment(); + taskComment.setUuid(UUID.randomUUID().toString()); taskComment.setAuthor(author); taskComment.setComment(comment); - + task.addComment(taskComment); - + entityManager.persist(taskComment); taskRepo.save(task); } + /** + * Removes a comment from a task. + * + * @param task The task from which the comment is removed. + * @param comment The comment to remove. + */ public void removeComment(final Task task, final TaskComment comment) { task.removeComment(comment); taskRepo.save(task); } + /** + * Enables a {@link Task}. + * + * @param task The task to enable. + */ public void enable(final Task task) { - switch(task.getTaskState()) { + switch (task.getTaskState()) { case DISABLED: task.setTaskState(TaskState.ENABLED); taskRepo.save(task); @@ -174,53 +229,70 @@ public class TaskManager { } } + /** + * Disables a {@link Task}. + * + * @param task The task to disable. + */ public void disable(final Task task) { task.setTaskState(TaskState.DISABLED); taskRepo.save(task); } - + + /** + * Finishes a {@link Task}. + * + * @param task The task to finish. + */ public void finish(final Task task) { if (task == null) { throw new IllegalArgumentException("Can't finished null..."); } - + if (task.getTaskState() != TaskState.ENABLED) { throw new IllegalArgumentException(String.format( - "Task %s is not enabled.", - Objects.toString(task))); + "Task %s is not enabled.", + Objects.toString(task))); } - + task.setTaskState(TaskState.FINISHED); taskRepo.save(task); - + task.getDependentTasks().forEach(dependent -> updateState(dependent)); } + /** + * Helper method for updating the state of {@link Task}. Called by + * {@link #finish(org.libreccm.workflow.Task)} to update the state of all + * dependent tasks. + * + * @param task + */ protected void updateState(final Task task) { LOGGER.debug("Updating state for task {}...", Objects.toString(task)); - + boolean dependenciesSatisfied = true; - + if (task.getTaskState() == TaskState.DELETED || !task.isActive()) { return; } - - for(final Task dependsOnTask : task.getDependsOn()) { + + for (final Task dependsOnTask : task.getDependsOn()) { LOGGER.debug("Checking dependency {}...", Objects.toString(dependsOnTask)); if (dependsOnTask.getTaskState() != TaskState.FINISHED - && dependsOnTask.isActive()) { - + && dependsOnTask.isActive()) { + LOGGER.debug("Dependency is not yet satisfied."); - + dependenciesSatisfied = false; break; } } - + LOGGER.debug("Dependencies state is {}", dependenciesSatisfied); - + // Rollback case. Previously finished task, but parent tasks // are re-enabled. if (task.getTaskState() == TaskState.FINISHED) { @@ -232,19 +304,19 @@ public class TaskManager { return; } } - + if (task.getTaskState() == TaskState.ENABLED) { if (!dependenciesSatisfied) { disable(task); return; } } - + if (task.getTaskState() == TaskState.DISABLED) { if (dependenciesSatisfied) { enable(task); } } - } + } } diff --git a/ccm-core/src/main/java/org/libreccm/workflow/TaskRepository.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskRepository.java index 696984497..68e13b423 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/TaskRepository.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskRepository.java @@ -23,7 +23,8 @@ import org.libreccm.core.AbstractEntityRepository; import javax.enterprise.context.RequestScoped; /** - * + * Repository for {@link Task}s. + * * @author Jens Pelzetter */ @RequestScoped diff --git a/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java index 8951cee64..f6e553494 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java @@ -19,7 +19,8 @@ package org.libreccm.workflow; /** - * + * The possible states of a {@link Task}. + * * @author Jens Pelzetter */ public enum TaskState { diff --git a/ccm-core/src/main/java/org/libreccm/workflow/Workflow.java b/ccm-core/src/main/java/org/libreccm/workflow/Workflow.java index a73fde010..9b1ffeda8 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/Workflow.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/Workflow.java @@ -48,8 +48,12 @@ import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import org.libreccm.core.Identifiable; /** + * A workflow is a collection of tasks which are performed on an object. Tasks + * can depend on each other. * * @author Jens Pelzetter */ @@ -58,57 +62,93 @@ import javax.persistence.Table; @Inheritance(strategy = InheritanceType.JOINED) @NamedQueries({ @NamedQuery( - name = "Workflow.findForObject", - query = "SELECT w FROM Workflow w " - + "WHERE W.object = :object") + name = "Workflow.findForObject", + query = "SELECT w FROM Workflow w " + + "WHERE W.object = :object") }) -public class Workflow implements Serializable { +public class Workflow implements Identifiable, Serializable { private static final long serialVersionUID = 4322500264543325829L; + /** + * Database id of the workflow. + */ @Id @Column(name = "WORKFLOW_ID") @GeneratedValue(strategy = GenerationType.AUTO) private long workflowId; + /** + * UUID of the workflow. + */ + @Column(name = "uuid", unique = true, nullable = false) + @NotNull + private String uuid; + + /** + * The template which was used the generate the workflow. + */ @ManyToOne @JoinColumn(name = "TEMPLATE_ID") private WorkflowTemplate template; + /** + * Human readable name of the workflow. + */ @Embedded @AssociationOverride( - name = "values", - joinTable = @JoinTable(name = "WORKFLOW_NAMES", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "WORKFLOW_ID")})) + name = "values", + joinTable = @JoinTable(name = "WORKFLOW_NAMES", + schema = DB_SCHEMA, + joinColumns = { + @JoinColumn(name = "WORKFLOW_ID")})) private LocalizedString name; + /** + * Description of the workflow. + */ @Embedded @AssociationOverride( - name = "values", - joinTable = @JoinTable(name = "WORKFLOW_DESCRIPTIONS", - schema = DB_SCHEMA, - joinColumns = { - @JoinColumn(name = "WORKFLOW_ID") - })) + name = "values", + joinTable = @JoinTable(name = "WORKFLOW_DESCRIPTIONS", + schema = DB_SCHEMA, + joinColumns = { + @JoinColumn(name = "WORKFLOW_ID") + })) private LocalizedString description; + /** + * The current state of the workflow. + */ @Column(name = "WORKFLOW_STATE") @Enumerated(EnumType.STRING) private WorkflowState state; + /** + * Is the workflow active? + */ @Column(name = "ACTIVE") private boolean active; + /** + * The task state of the workflow. This field is a leftover from the old + * implementation of workflow were workflow extended {@link Task}. Because + * we wanted to keep the basic logic this field is here. + */ @Column(name = "TASKS_STATE") @Enumerated(EnumType.STRING) private TaskState tasksState; - + + /** + * The object for which this workflow was generated. + */ @OneToOne @JoinColumn(name = "OBJECT_ID") private CcmObject object; + /** + * The tasks belonging to this workflow. + */ @OneToMany(mappedBy = "workflow") private List tasks; @@ -124,10 +164,19 @@ public class Workflow implements Serializable { return workflowId; } - public void setWorkflowId(final long workflowId) { + protected void setWorkflowId(final long workflowId) { this.workflowId = workflowId; } + @Override + public String getUuid() { + return uuid; + } + + protected void setUuid(final String uuid) { + this.uuid = uuid; + } + public WorkflowTemplate getTemplate() { return template; } @@ -171,11 +220,11 @@ public class Workflow implements Serializable { public TaskState getTasksState() { return tasksState; } - + protected void setTasksState(final TaskState tasksState) { this.tasksState = tasksState; } - + public CcmObject getObject() { return object; } @@ -207,8 +256,9 @@ public class Workflow implements Serializable { @Override public int hashCode() { int hash = 5; - hash = 79 * hash + (int) (this.workflowId ^ (this.workflowId >>> 32)); - hash = 79 * hash + Objects.hashCode(this.name); + hash = 79 * hash + (int) (workflowId ^ (workflowId >>> 32)); + hash = 79 * hash + Objects.hashCode(uuid); + hash = 79 * hash + Objects.hashCode(name); hash = 79 * hash + Objects.hashCode(description); hash = 79 * hash + Objects.hashCode(state); hash = 79 * hash + (active ? 1 : 0); @@ -234,6 +284,10 @@ public class Workflow implements Serializable { return false; } + if (!Objects.equals(uuid, other.getUuid())) { + return false; + } + if (!Objects.equals(name, other.getName())) { return false; } @@ -249,7 +303,7 @@ public class Workflow implements Serializable { if (active != other.isActive()) { return false; } - + if (!Objects.equals(tasksState, other.getTasksState())) { return false; } @@ -269,15 +323,17 @@ public class Workflow implements Serializable { public String toString(final String data) { return String.format("%s{ " - + "workflowId = %d, " - + "name = \"%s\", " - + "description = \"%s\", " - + "state = \"%s\", " - + "active = %b" - + "object = \"%s\"%s" - + " }", + + "workflowId = %d, " + + "uuid = \"%s\", " + + "name = \"%s\", " + + "description = \"%s\", " + + "state = \"%s\", " + + "active = %b" + + "object = \"%s\"%s" + + " }", super.toString(), workflowId, + uuid, Objects.toString(name), Objects.toString(description), Objects.toString(state), diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowConstants.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowConstants.java deleted file mode 100644 index c6c3aa45c..000000000 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowConstants.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2016 LibreCCM Foundation. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ -package org.libreccm.workflow; - -/** - * - * @author Jens Pelzetter - */ -public final class WorkflowConstants { - - private WorkflowConstants() { - //Nothing - } - - public final static int NONE = -1; - public final static int STARTED = 0; - public final static int STOPPED = 1; - public final static int DELETED = 2; - public final static int INIT = 3; - -} diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowManager.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowManager.java index 642d7c486..14f1603be 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowManager.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowManager.java @@ -49,8 +49,18 @@ import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.transaction.Transactional; +import org.apache.shiro.subject.Subject; /** + * Manager for {@link Workflow}s. The logic of some of these classes has been + * ported from the workflow implementation. The methods have only been edited to + * fit into the new architecture. + * + * Most of the methods of this manager require the {@code ADMIN} privilege. To + * use this methods with other users the caller has the check the permissions + * first and than wrap the call to the method of this class into a new context + * with the system user by using + * {@link Subject#execute(java.util.concurrent.Callable)}. * * @author Jens Pelzetter */ @@ -58,7 +68,7 @@ import javax.transaction.Transactional; public class WorkflowManager { private final static Logger LOGGER = LogManager.getLogger( - WorkflowManager.class); + WorkflowManager.class); @Inject private EntityManager entityManager; @@ -80,28 +90,49 @@ public class WorkflowManager { private Locale defaultLocale; + /** + * Populates the {@link #defaultLocale} field. + */ @PostConstruct private void init() { final KernelConfig kernelConfig = confManager.findConfiguration( - KernelConfig.class); + KernelConfig.class); defaultLocale = kernelConfig.getDefaultLocale(); } + /** + * Creates an {@link Workflow} for the provided {@link CcmObject} using the + * provided {@link WorkflowTemplate}. + * + * @param template The template which is used to create the new workflow. + * @param object The object for which th workflow is generated. + * @return The new workflow. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public Workflow createWorkflow(final WorkflowTemplate template, final CcmObject object) { + if (template == null) { + throw new IllegalArgumentException( + "Can't create a workflow without a template."); + } + + if (object == null) { + throw new IllegalArgumentException( + "Can't create a workflow without an object."); + } + final Workflow workflow = new Workflow(); final LocalizedString name = new LocalizedString(); template.getName().getValues().forEach( - (locale, str) -> name.addValue(locale, str)); + (locale, str) -> name.addValue(locale, str)); workflow.setName(name); final LocalizedString description = new LocalizedString(); template.getDescription().getValues().forEach( - (locale, str) -> description.addValue(locale, str)); + (locale, str) -> description.addValue(locale, str)); workflow.setDescription(description); final Map tasks = new HashMap<>(); @@ -109,7 +140,7 @@ public class WorkflowManager { template.getTasks().forEach(taskTemplate -> createTask(taskTemplate, tasks)); template.getTasks().forEach(taskTemplate -> fixTaskDependencies( - taskTemplate, tasks.get(taskTemplate.getTaskId()), tasks)); + taskTemplate, tasks.get(taskTemplate.getTaskId()), tasks)); workflow.setObject(object); workflow.setState(WorkflowState.INIT); @@ -120,6 +151,15 @@ public class WorkflowManager { return workflow; } + /** + * Helper method for + * {@link #createWorkflow(org.libreccm.workflow.WorkflowTemplate, org.libreccm.core.CcmObject)} + * for creating the tasks of the new workflow from the tasks of the workflow + * template. + * + * @param template The template for the task from the workflow template. + * @param tasks A map for storing the new tasks. + */ private void createTask(final Task template, final Map tasks) { final Class templateClass = template.getClass(); final Task task; @@ -137,14 +177,14 @@ public class WorkflowManager { } for (PropertyDescriptor propertyDesc : templateBeanInfo - .getPropertyDescriptors()) { + .getPropertyDescriptors()) { try { if ("taskId".equals(propertyDesc.getName()) - || "workflow".equals(propertyDesc.getName()) - || "dependentTasks".equals(propertyDesc.getName()) - || "dependsOn".equals(propertyDesc.getName()) - || "assignments".equals(propertyDesc.getName()) - || "class".equals(propertyDesc.getName())) { + || "workflow".equals(propertyDesc.getName()) + || "dependentTasks".equals(propertyDesc.getName()) + || "dependsOn".equals(propertyDesc.getName()) + || "assignments".equals(propertyDesc.getName()) + || "class".equals(propertyDesc.getName())) { continue; } @@ -161,15 +201,15 @@ public class WorkflowManager { final LocalizedString copy = new LocalizedString(); localized.getValues().forEach( - (locale, str) -> copy.addValue(locale, str)); + (locale, str) -> copy.addValue(locale, str)); writeMethod.invoke(task, copy); } else { writeMethod.invoke(task, value); } } catch (IllegalAccessException - | IllegalArgumentException - | InvocationTargetException ex) { + | IllegalArgumentException + | InvocationTargetException ex) { throw new RuntimeException(); } @@ -177,64 +217,100 @@ public class WorkflowManager { } } + /** + * Helper method for + * {@link #createWorkflow(org.libreccm.workflow.WorkflowTemplate, org.libreccm.core.CcmObject)} + * and {@link #createTask(org.libreccm.workflow.Task, java.util.Map)} for + * creating the task dependencies. + * + * @param template + * @param task + * @param tasks + */ private void fixTaskDependencies(final Task template, final Task task, final Map tasks) { if (template.getDependentTasks() != null - && !template.getDependentTasks().isEmpty()) { + && !template.getDependentTasks().isEmpty()) { template.getDependentTasks().forEach(dependent - -> task.addDependentTask(tasks.get(dependent.getTaskId()))); + -> task.addDependentTask(tasks.get(dependent.getTaskId()))); } if (template.getDependsOn() != null - && !template.getDependsOn().isEmpty()) { + && !template.getDependsOn().isEmpty()) { template.getDependsOn().forEach(dependsOn - -> task.addDependsOn(tasks.get(dependsOn.getTaskId()))); + -> task.addDependsOn(tasks.get(dependsOn.getTaskId()))); } } + /** + * Finds the enabled {@link Task}s of a {@link Workflow}. + * + * @param workflow The workflow. + * @return A unmodifiable list of the enabled tasks of the provided + * {@code workflow}. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public List findEnabledTasks(final Workflow workflow) { if (workflow.getState() == WorkflowState.DELETED - || workflow.getState() == WorkflowState.STOPPED) { + || workflow.getState() == WorkflowState.STOPPED) { LOGGER.debug(String.format("Workflow state is \"%s\". Workflow " - + "has no enabled tasks.", + + "has no enabled tasks.", workflow.getState().toString())); return Collections.emptyList(); } final TypedQuery query = entityManager.createNamedQuery( - "Task.findEnabledTasks", Task.class); + "Task.findEnabledTasks", Task.class); query.setParameter("workflow", workflow); return Collections.unmodifiableList(query.getResultList()); } + /** + * Finds the finished {@link Task}s of a workflow. + * + * @param workflow The workflow. + * @return An unmodifiable list of the finished tasks of the provided + * {@code Workflow}. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public List findFinishedTasks(final Workflow workflow) { final TypedQuery query = entityManager.createNamedQuery( - "Task.findFinishedTasks", Task.class); + "Task.findFinishedTasks", Task.class); query.setParameter("workflow", workflow); return Collections.unmodifiableList(query.getResultList()); } + /** + * Finds the {@link Task}s of a {@link Workflow} which are overdue. + * + * @param workflow The workflow. + * @return A unmodifiable list of the overdue tasks of the provided + * {@code workflow}. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public List findOverdueTasks(final Workflow workflow) { final TypedQuery query = entityManager.createNamedQuery( - "AssignableTask.findOverdueTasks", AssignableTask.class); + "AssignableTask.findOverdueTasks", AssignableTask.class); query.setParameter("workflow", workflow); query.setParameter("now", new Date()); return Collections.unmodifiableList(query.getResultList()); } + /** + * Starts a {@link Workflow}. + * + * @param workflow The workflow to start. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -255,13 +331,18 @@ public class WorkflowManager { workflowRepo.save(workflow); } + /** + * Helper method for updating the state of a {@link Workflow}. + * + * @param workflow The workflow to update. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) private void updateState(final Workflow workflow) { if (workflow.getTasksState() == TaskState.ENABLED) { final TypedQuery query = entityManager.createNamedQuery( - "Task.countUnfinishedAndActiveTasksForWorkflow", Long.class); + "Task.countUnfinishedAndActiveTasksForWorkflow", Long.class); query.setParameter("workflow", workflow); final Long result = query.getSingleResult(); @@ -275,7 +356,7 @@ public class WorkflowManager { if (workflow.getTasksState() == TaskState.FINISHED) { final TypedQuery query = entityManager.createNamedQuery( - "Task.countUnfinishedTasksForWorkflow", Long.class); + "Task.countUnfinishedTasksForWorkflow", Long.class); query.setParameter("workflow", workflow); final Long result = query.getSingleResult(); @@ -286,6 +367,11 @@ public class WorkflowManager { } } + /** + * Stops a workflow. + * + * @param workflow The workflow to stop. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -294,14 +380,19 @@ public class WorkflowManager { workflowRepo.save(workflow); } + /** + * Finished a {@link Workflow}. + * + * @param workflow The workflow to finish. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) public void finish(final Workflow workflow) { if (workflow.getTasksState() != TaskState.ENABLED) { throw new IllegalArgumentException(String.format( - "Workflow \"%s\" is not enabled.", - workflow.getName().getValue(defaultLocale))); + "Workflow \"%s\" is not enabled.", + workflow.getName().getValue(defaultLocale))); } workflow.setTasksState(TaskState.FINISHED); @@ -309,6 +400,11 @@ public class WorkflowManager { } + /** + * Enables a {@link Workflow}. + * + * @param workflow The workflow to enable. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) @@ -331,13 +427,18 @@ public class WorkflowManager { break; default: LOGGER.debug("Workflow \"{}\" has tasksState \"{}\", " - + "#enable(Workflow) does nothing.", + + "#enable(Workflow) does nothing.", workflow.getName().getValue(defaultLocale), workflow.getTasksState()); break; } } + /** + * Disables a {@link Workflow}. + * + * @param workflow The workflow to disable. + */ @AuthorizationRequired @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN) @Transactional(Transactional.TxType.REQUIRED) diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowRepository.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowRepository.java index a09f2918c..198172e16 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowRepository.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowRepository.java @@ -22,12 +22,14 @@ import org.libreccm.core.AbstractEntityRepository; import org.libreccm.core.CcmObject; import java.util.Optional; +import java.util.UUID; import javax.enterprise.context.RequestScoped; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; /** + * Repository for {@link Workflow}s. * * @author Jens Pelzetter */ @@ -43,22 +45,35 @@ public class WorkflowRepository extends AbstractEntityRepository public boolean isNew(final Workflow workflow) { return workflow.getWorkflowId() == 0; } - + + @Override + public void initNewEntity(final Workflow workflow) { + workflow.setUuid(UUID.randomUUID().toString()); + } + + /** + * Finds the workflow for an given object if the object has workflow. + * + * @param object The object + * @return An {@link Optional} containing the workflow assigned to the + * {@code object} if the object has a workflow. Otherwise an empty + * {@link Optional} is returned. + */ public Optional findWorkflowForObject(final CcmObject object) { if (object == null) { throw new IllegalArgumentException( - "Can't find a workflow for object null."); + "Can't find a workflow for object null."); } - + final TypedQuery query = getEntityManager().createNamedQuery( - "Workflow.findForObject", Workflow.class); + "Workflow.findForObject", Workflow.class); query.setParameter("object", object); - + try { return Optional.of(query.getSingleResult()); - } catch(NoResultException ex) { + } catch (NoResultException ex) { return Optional.empty(); } } - + } diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java index 9cb8541d8..c4872a172 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java @@ -19,7 +19,8 @@ package org.libreccm.workflow; /** - * + * The possible states of a workflow. + * * @author Jens Pelzetter */ public enum WorkflowState { @@ -28,6 +29,6 @@ public enum WorkflowState { STOPPED, DELETED, INIT, - NONE + NONE; } diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplate.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplate.java index 1bbd3f39e..9a3477915 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplate.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplate.java @@ -28,7 +28,9 @@ import javax.persistence.Entity; import javax.persistence.Table; /** - * + * Objects of these class are used as templates for new workflows. The tasks + * in the template are copied when a new workflow is generated. + * * @author Jens Pelzetter */ @Entity @@ -37,6 +39,13 @@ public class WorkflowTemplate extends Workflow implements Serializable { private static final long serialVersionUID = 5770519379144947171L; + /** + * A workflow template has no object. Therefore the {@code setObject(CcmObject) + * method has been overwritten the throw an {@link UnsupportedOperationException} + * when called on the workflow template. + * + * @param object + */ @Override protected void setObject(final CcmObject object) { throw new UnsupportedOperationException( diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplateRepository.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplateRepository.java index 0ebb9c7e6..fed04a3f6 100644 --- a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplateRepository.java +++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowTemplateRepository.java @@ -23,7 +23,8 @@ import org.libreccm.core.AbstractEntityRepository; import javax.enterprise.context.RequestScoped; /** - * + * A repository for {@link WorkflowTemplate}s. + * * @author Jens Pelzetter */ @RequestScoped