diff --git a/ccm-cms/src/main/java/org/librecms/workflow/CmsTaskManager.java b/ccm-cms/src/main/java/org/librecms/workflow/CmsTaskManager.java
new file mode 100644
index 000000000..61d144ade
--- /dev/null
+++ b/ccm-cms/src/main/java/org/librecms/workflow/CmsTaskManager.java
@@ -0,0 +1,50 @@
+/*
+ * 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.librecms.workflow;
+
+import com.arsdigita.cms.workflow.TaskURLGenerator;
+import com.arsdigita.util.UncheckedWrapperException;
+
+import org.libreccm.workflow.AssignableTaskManager;
+import org.librecms.contentsection.ContentItem;
+
+import javax.enterprise.context.RequestScoped;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class CmsTaskManager extends AssignableTaskManager {
+
+ public String getFinishUrl(final ContentItem item, final CmsTask task) {
+ final Class extends TaskURLGenerator> urlGeneratorClass = task
+ .getTaskType().getUrlGenerator();
+ final TaskURLGenerator urlGenerator;
+ try {
+ urlGenerator = urlGeneratorClass.newInstance();
+ } catch (IllegalAccessException
+ | InstantiationException ex) {
+ throw new UncheckedWrapperException(ex);
+ }
+
+ return urlGenerator.generateURL(item.getObjectId(), task.getTaskId());
+ }
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskManager.java b/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskManager.java
new file mode 100644
index 000000000..eb43887f5
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/AssignableTaskManager.java
@@ -0,0 +1,152 @@
+/*
+ * 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;
+
+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 javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class AssignableTaskManager extends TaskManager {
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Inject
+ private WorkflowRepository workflowRepo;
+
+ @Inject
+ private TaskRepository taskRepo;
+
+ @Inject
+ private RoleRepository roleRepo;
+
+ @Inject
+ private Shiro shiro;
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void assignTask(final AssignableTask task, final Role role) {
+ final TaskAssignment assignment = new TaskAssignment();
+ assignment.setTask(task);
+ assignment.setRole(role);
+
+ task.addAssignment(assignment);
+ role.addAssignedTask(assignment);
+
+ entityManager.persist(assignment);
+ taskRepo.save(task);
+ roleRepo.save(role);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void retractTask(final AssignableTask task, final Role role) {
+ final List result = task.getAssignments().stream()
+ .filter(assigned -> role.equals(assigned.getRole()))
+ .collect(Collectors.toList());
+
+ if (!result.isEmpty()) {
+ final TaskAssignment assignment = result.get(0);
+ task.removeAssignment(assignment);
+ role.removeAssignedTask(assignment);
+ entityManager.remove(assignment);
+ }
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void lockTask(final AssignableTask task) {
+ task.setLocked(true);
+ task.setLockingUser(shiro.getUser());
+
+ taskRepo.save(task);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void unlockTask(final AssignableTask task) {
+ task.setLocked(false);
+ task.setLockingUser(null);
+
+ taskRepo.save(task);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public List lockedBy(final User user) {
+ final TypedQuery query = entityManager.createNamedQuery(
+ "UserTask.findLockedBy", AssignableTask.class);
+ query.setParameter("user", user);
+
+ return query.getResultList();
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void finish(final AssignableTask task) {
+ final User currentUser = shiro.getUser();
+
+ if (!currentUser.equals(task.getLockingUser())) {
+ throw new IllegalArgumentException(String.format(
+ "Current user %s is not locking user for task %s. Task is"
+ + "locaked by user %s.",
+ Objects.toString(currentUser),
+ Objects.toString(task),
+ Objects.toString(task.getLockingUser())));
+ }
+
+ super.finish(task);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void finish(final AssignableTask task,
+ final String comment) {
+ addComment(task, comment);
+ finish(task);
+ }
+
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/workflow/CircularTaskDependencyException.java b/ccm-core/src/main/java/org/libreccm/workflow/CircularTaskDependencyException.java
new file mode 100644
index 000000000..e9a79a03a
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/CircularTaskDependencyException.java
@@ -0,0 +1,67 @@
+/*
+ * 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 class CircularTaskDependencyException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of CircularTaskDependencyException without detail message.
+ */
+ public CircularTaskDependencyException() {
+ super();
+ }
+
+
+ /**
+ * Constructs an instance of CircularTaskDependencyException with the specified detail message.
+ *
+ * @param msg The detail message.
+ */
+ public CircularTaskDependencyException(final String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance of CircularTaskDependencyException which wraps the
+ * specified exception.
+ *
+ * @param exception The exception to wrap.
+ */
+ public CircularTaskDependencyException(final Exception exception) {
+ super(exception);
+ }
+
+ /**
+ * Constructs an instance of CircularTaskDependencyException with the specified message which also wraps the
+ * specified exception.
+ *
+ * @param msg The detail message.
+ * @param exception The exception to wrap.
+ */
+ public CircularTaskDependencyException(final String msg, final Exception exception) {
+ super(msg, exception);
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java
new file mode 100644
index 000000000..3260dffac
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskComment.java
@@ -0,0 +1,144 @@
+/*
+ * 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;
+
+import org.hibernate.annotations.Type;
+import org.libreccm.core.CoreConstants;
+import org.libreccm.security.User;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+import javax.persistence.Basic;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@Entity
+@Table(name = "WORKFLOW_TASK_COMMENTS", schema = CoreConstants.DB_SCHEMA)
+public class TaskComment implements Serializable {
+
+ private static final long serialVersionUID = 3842991529698351698L;
+
+ @Id
+ @Column(name = "COMMENT_ID")
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private long commentId;
+
+ @Column(name = "COMMENT")
+ @Basic
+ @Lob
+ @Type(type = "org.hibernate.type.TextType")
+ private String comment;
+
+ @OneToOne
+ @JoinColumn(name = "AUTHOR_ID")
+ private User author;
+
+ public long getCommentId() {
+ return commentId;
+ }
+
+ protected void setCommentId(final long commentId) {
+ this.commentId = commentId;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ protected void setComment(final String comment) {
+ this.comment = comment;
+ }
+
+ public User getAuthor() {
+ return author;
+ }
+
+ protected void setAuthor(final User author) {
+ this.author = author;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 3;
+ hash = 67 * hash + (int) (commentId ^ (commentId >>> 32));
+ hash = 67 * hash + Objects.hashCode(comment);
+ hash = 67 * hash + Objects.hashCode(author);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof TaskComment)) {
+ return false;
+ }
+ final TaskComment other = (TaskComment) obj;
+ if (!other.canEqual(this)) {
+ return false;
+ }
+
+ if (commentId != other.getCommentId()) {
+ return false;
+ }
+ if (!Objects.equals(comment, other.getComment())) {
+ return false;
+ }
+ return Objects.equals(author, other.getAuthor());
+ }
+
+ public boolean canEqual(final Object obj) {
+ return obj instanceof TaskComment;
+ }
+
+ @Override
+ public final String toString() {
+ return toString("");
+ }
+
+ public String toString(final String data) {
+ return String.format("%s{ "
+ + "commentId = %d, "
+ + "comment = \"%s\", "
+ + "author = %s%s"
+ + " }",
+ super.toString(),
+ commentId,
+ 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
new file mode 100644
index 000000000..463ea6a3a
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskManager.java
@@ -0,0 +1,250 @@
+/*
+ * 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;
+
+import org.apache.logging.log4j.LogManager;
+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 javax.enterprise.context.RequestScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.TypedQuery;
+import javax.transaction.Transactional;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+@RequestScoped
+public class TaskManager {
+
+ private static final Logger LOGGER = LogManager.getLogger(TaskManager.class);
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Inject
+ private WorkflowRepository workflowRepo;
+
+ @Inject
+ private TaskRepository taskRepo;
+
+ @Inject
+ private RoleRepository roleRepo;
+
+ @Inject
+ private Shiro shiro;
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void addTask(final Workflow workflow, final Task task) {
+ workflow.addTask(task);
+ task.setWorkflow(workflow);
+
+ workflowRepo.save(workflow);
+ taskRepo.save(task);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void removeTask(final Workflow workflow, final Task task) {
+ workflow.removeTask(task);
+ task.setWorkflow(null);
+
+ workflowRepo.save(workflow);
+ taskRepo.save(task);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void addDependentTask(final Task parent, final Task task)
+ throws CircularTaskDependencyException {
+
+ checkForCircularDependencies(parent, task);
+
+ parent.addDependentTask(task);
+ task.addDependsOn(parent);
+
+ taskRepo.save(task);
+ taskRepo.save(parent);
+ }
+
+ @AuthorizationRequired
+ @RequiresPrivilege(CoreConstants.PRIVILEGE_ADMIN)
+ @Transactional(Transactional.TxType.REQUIRED)
+ public void removeDependentTask(final Task parent, final Task task) {
+ parent.removeDependentTask(task);
+ task.removeDependsOn(parent);
+
+ taskRepo.save(task);
+ taskRepo.save(parent);
+ }
+
+ private void checkForCircularDependencies(final Task task1,
+ final Task task2)
+ throws CircularTaskDependencyException {
+
+ if (dependsOn(task1, task2)) {
+ throw new CircularTaskDependencyException();
+ }
+ }
+
+ private boolean dependsOn(final Task task, final Task dependsOn) {
+ for (final Task current : task.getDependsOn()) {
+ if (current.equals(dependsOn)) {
+ return true;
+ }
+
+ if (current.getDependsOn() != null
+ && !current.getDependsOn().isEmpty()) {
+ return dependsOn(current, dependsOn);
+ }
+ }
+
+ return false;
+ }
+
+ public void addComment(final Task task, final String comment) {
+ addComment(task, shiro.getUser(), comment);
+ }
+
+ public void addComment(final Task task,
+ final User author,
+ final String comment) {
+ final TaskComment taskComment = new TaskComment();
+ taskComment.setAuthor(author);
+ taskComment.setComment(comment);
+
+ task.addComment(taskComment);
+
+ entityManager.persist(taskComment);
+ taskRepo.save(task);
+ }
+
+ public void removeComment(final Task task, final TaskComment comment) {
+ task.removeComment(comment);
+ taskRepo.save(task);
+ }
+
+ public void enable(final Task task) {
+ switch(task.getTaskState()) {
+ case DISABLED:
+ task.setTaskState(TaskState.ENABLED);
+ taskRepo.save(task);
+ break;
+ case FINISHED:
+ task.setTaskState(TaskState.ENABLED);
+ taskRepo.save(task);
+ break;
+ default:
+ LOGGER.debug("Task {} is in state \"{}\"; doing nothing.",
+ Objects.toString(task),
+ Objects.toString(task.getTaskState()));
+ break;
+ }
+ }
+
+ public void disable(final Task task) {
+ task.setTaskState(TaskState.DISABLED);
+ taskRepo.save(task);
+ }
+
+ 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.setTaskState(TaskState.FINISHED);
+ taskRepo.save(task);
+
+ task.getDependentTasks().forEach(dependent -> updateState(dependent));
+ }
+
+ 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()) {
+ LOGGER.debug("Checking dependency {}...",
+ Objects.toString(dependsOnTask));
+ if (dependsOnTask.getTaskState() != TaskState.FINISHED
+ && 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) {
+ if (dependenciesSatisfied) {
+ enable(task);
+ return;
+ } else {
+ disable(task);
+ 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/TaskState.java b/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java
new file mode 100644
index 000000000..8951cee64
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/TaskState.java
@@ -0,0 +1,32 @@
+/*
+ * 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 enum TaskState {
+
+ ENABLED,
+ DISABLED,
+ FINISHED,
+ DELETED
+
+}
diff --git a/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java
new file mode 100644
index 000000000..9cb8541d8
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/workflow/WorkflowState.java
@@ -0,0 +1,33 @@
+/*
+ * 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 enum WorkflowState {
+
+ STARTED,
+ STOPPED,
+ DELETED,
+ INIT,
+ NONE
+
+}