You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/01/24 15:16:20 UTC

[3/3] syncope git commit: [SYNCOPE-620] workflow-activiti in, IT needs to be adapted

[SYNCOPE-620] workflow-activiti in, IT needs to be adapted


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/702810d8
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/702810d8
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/702810d8

Branch: refs/heads/2_0_X
Commit: 702810d8b0f28e57896686670096cb1977106537
Parents: 0f9194e
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Sat Jan 24 15:15:58 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Sat Jan 24 15:15:58 2015 +0100

----------------------------------------------------------------------
 syncope620/pom.xml                              |  45 +
 .../server/logic/ConfigurationLogic.java        |  24 +-
 .../server/logic/LogicInvocationHandler.java    |   2 +-
 .../apache/syncope/server/logic/RoleLogic.java  |   2 +-
 .../logic/init/WorkflowAdapterLoader.java       |  22 +-
 .../logic/notification/NotificationJob.java     |   2 +-
 .../syncope/server/logic/NotificationTest.java  |   2 +-
 .../persistence/api/entity/user/User.java       |   2 +-
 .../jpa/content/XMLContentExporter.java         |   3 +-
 .../server/persistence/jpa/dao/JPARoleDAO.java  |   8 +-
 .../server/persistence/jpa/dao/JPAUserDAO.java  |  10 +-
 .../persistence/jpa/entity/user/JPAUser.java    |   2 +-
 syncope620/server/pom.xml                       |   1 +
 .../api/notification/NotificationManager.java   |  65 ++
 .../provisioning/java/job/AbstractTaskJob.java  |   2 +-
 .../java/notification/NotificationManager.java  | 437 ---------
 .../notification/NotificationManagerImpl.java   | 416 +++++++++
 .../AbstractPropagationTaskExecutor.java        |   2 +-
 .../java/sync/AbstractSyncopeResultHandler.java |   2 +-
 .../java/sync/LDAPMembershipSyncActions.java    |   2 +-
 syncope620/server/workflow-activiti/pom.xml     |  83 ++
 .../activiti/ActivitiDefinitionLoader.java      | 104 +++
 .../workflow/activiti/ActivitiImportUtils.java  |  92 ++
 .../activiti/ActivitiUserWorkflowAdapter.java   | 893 +++++++++++++++++++
 .../activiti/SyncopeEntitiesVariableType.java   |  35 +
 .../workflow/activiti/SyncopeGroupManager.java  | 122 +++
 .../activiti/SyncopeGroupQueryImpl.java         | 157 ++++
 .../workflow/activiti/SyncopeSession.java       |  26 +
 .../activiti/SyncopeSessionFactory.java         |  45 +
 .../workflow/activiti/SyncopeUserManager.java   | 170 ++++
 .../workflow/activiti/SyncopeUserQueryImpl.java | 218 +++++
 .../task/AbstractActivitiServiceTask.java       |  48 +
 .../workflow/activiti/task/AutoActivate.java    |  31 +
 .../syncope/workflow/activiti/task/Create.java  |  51 ++
 .../syncope/workflow/activiti/task/Delete.java  |  40 +
 .../workflow/activiti/task/GenerateToken.java   |  43 +
 .../syncope/workflow/activiti/task/Notify.java  |  62 ++
 .../workflow/activiti/task/PasswordReset.java   |  44 +
 .../workflow/activiti/task/Reactivate.java      |  29 +
 .../syncope/workflow/activiti/task/Suspend.java |  29 +
 .../syncope/workflow/activiti/task/Update.java  |  54 ++
 .../src/main/resources/userWorkflow.bpmn20.xml  | 232 +++++
 .../main/resources/workflowActivitiContext.xml  |  79 ++
 .../workflow/api/UserWorkflowAdapter.java       |   4 +-
 .../server/workflow/api/WorkflowAdapter.java    |   2 +-
 .../workflow/api/WorkflowDefinitionLoader.java  |  29 +
 .../workflow/api/WorkflowInstanceLoader.java    |  28 -
 .../java/AbstractRoleWorkflowAdapter.java       |   4 +-
 .../java/AbstractUserWorkflowAdapter.java       |   4 +-
 49 files changed, 3293 insertions(+), 516 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/pom.xml b/syncope620/pom.xml
index b784741..e5f86d3 100644
--- a/syncope620/pom.xml
+++ b/syncope620/pom.xml
@@ -339,6 +339,8 @@ under the License.
 
     <groovy.version>2.3.9</groovy.version>
 
+    <activiti.version>5.16.4</activiti.version>
+
     <slf4j.version>1.7.10</slf4j.version>
     <log4j.version>2.1</log4j.version>
     <disruptor.version>3.3.0</disruptor.version>
@@ -750,6 +752,49 @@ under the License.
       </dependency>
       
       <dependency>
+        <groupId>org.activiti</groupId>
+        <artifactId>activiti-engine</artifactId>
+        <version>${activiti.version}</version>
+        <!-- Using geronimo-* for this -->
+        <exclusions>
+          <exclusion>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>javax.mail</groupId>
+            <artifactId>mail</artifactId>
+          </exclusion>
+          <exclusion>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-beans</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
+        <groupId>org.activiti</groupId>
+        <artifactId>activiti-spring</artifactId>
+        <version>${activiti.version}</version>
+        <exclusions>
+          <exclusion>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+          </exclusion>
+        </exclusions>
+      </dependency>
+      <dependency>
+        <groupId>org.activiti</groupId>
+        <artifactId>activiti-json-converter</artifactId>
+        <version>${activiti.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.activiti</groupId>
+        <artifactId>activiti-webapp-explorer2</artifactId>
+        <version>${activiti.version}</version>
+        <type>war</type>
+      </dependency>
+      
+      <dependency>
         <groupId>org.slf4j</groupId>
         <artifactId>slf4j-api</artifactId>
         <version>${slf4j.version}</version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ConfigurationLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ConfigurationLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ConfigurationLogic.java
index 06bb568..7c6e43f 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ConfigurationLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/ConfigurationLogic.java
@@ -35,7 +35,7 @@ import org.apache.syncope.server.persistence.api.entity.conf.CPlainSchema;
 import org.apache.syncope.server.provisioning.api.data.ConfigurationDataBinder;
 import org.apache.syncope.server.logic.init.ImplementationClassNamesLoader;
 import org.apache.syncope.server.logic.init.WorkflowAdapterLoader;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
+import org.apache.syncope.server.provisioning.java.notification.NotificationManagerImpl;
 import org.apache.syncope.server.misc.spring.ResourceWithFallbackLoader;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.Resource;
@@ -109,20 +109,18 @@ public class ConfigurationLogic extends AbstractTransactionalLogic<ConfTO> {
 
     @PreAuthorize("hasRole('CONFIGURATION_LIST')")
     public Set<String> getMailTemplates() {
-        Set<String> htmlTemplates = new HashSet<String>();
-        Set<String> textTemplates = new HashSet<String>();
+        Set<String> htmlTemplates = new HashSet<>();
+        Set<String> textTemplates = new HashSet<>();
 
         try {
-            for (Resource resource : resourceLoader.getResources(NotificationManager.MAIL_TEMPLATES + "*.vm")) {
+            for (Resource resource : resourceLoader.getResources(NotificationManagerImpl.MAIL_TEMPLATES + "*.vm")) {
                 String template = resource.getURL().toExternalForm();
-                if (template.endsWith(NotificationManager.MAIL_TEMPLATE_HTML_SUFFIX)) {
-                    htmlTemplates.add(
-                            template.substring(template.indexOf(NotificationManager.MAIL_TEMPLATES) + 14,
-                                    template.indexOf(NotificationManager.MAIL_TEMPLATE_HTML_SUFFIX)));
-                } else if (template.endsWith(NotificationManager.MAIL_TEMPLATE_TEXT_SUFFIX)) {
-                    textTemplates.add(
-                            template.substring(template.indexOf(NotificationManager.MAIL_TEMPLATES) + 14,
-                                    template.indexOf(NotificationManager.MAIL_TEMPLATE_TEXT_SUFFIX)));
+                if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)) {
+                    htmlTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
+                                    template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)));
+                } else if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)) {
+                    textTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
+                                    template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)));
                 } else {
                     LOG.warn("Unexpected template found: {}, ignoring...", template);
                 }
@@ -141,7 +139,7 @@ public class ConfigurationLogic extends AbstractTransactionalLogic<ConfTO> {
     @Transactional(readOnly = true)
     public void export(final OutputStream os) {
         try {
-            exporter.export(os, wfAdapterLoader.getTablePrefix());
+            exporter.export(os, wfAdapterLoader.getPrefix());
             LOG.debug("Database content successfully exported");
         } catch (Exception e) {
             LOG.error("While exporting database content", e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LogicInvocationHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LogicInvocationHandler.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LogicInvocationHandler.java
index 0baad34..0e5ca8e 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LogicInvocationHandler.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/LogicInvocationHandler.java
@@ -22,7 +22,7 @@ import java.lang.reflect.Method;
 import java.util.Arrays;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/RoleLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/RoleLogic.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/RoleLogic.java
index c1a5582..838827c 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/RoleLogic.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/RoleLogic.java
@@ -125,7 +125,7 @@ public class RoleLogic extends AbstractSubjectLogic<RoleTO, RoleMod> {
         if (authUser == null) {
             ownedRoleIds = Collections.<Long>emptySet();
         } else {
-            ownedRoleIds = authUser.getRoleIds();
+            ownedRoleIds = authUser.getRoleKeys();
         }
 
         Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/WorkflowAdapterLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/WorkflowAdapterLoader.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/WorkflowAdapterLoader.java
index b89e165..fb9cbe7 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/WorkflowAdapterLoader.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/init/WorkflowAdapterLoader.java
@@ -21,7 +21,7 @@ package org.apache.syncope.server.logic.init;
 import org.apache.syncope.server.persistence.api.SyncopeLoader;
 import org.apache.syncope.server.workflow.api.RoleWorkflowAdapter;
 import org.apache.syncope.server.workflow.api.UserWorkflowAdapter;
-import org.apache.syncope.server.workflow.api.WorkflowInstanceLoader;
+import org.apache.syncope.server.workflow.api.WorkflowDefinitionLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeansException;
@@ -45,7 +45,7 @@ public class WorkflowAdapterLoader implements BeanFactoryAware, SyncopeLoader {
 
     private DefaultListableBeanFactory beanFactory;
 
-    private WorkflowInstanceLoader wfLoader;
+    private WorkflowDefinitionLoader wfLoader;
 
     @Override
     public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
@@ -54,20 +54,20 @@ public class WorkflowAdapterLoader implements BeanFactoryAware, SyncopeLoader {
 
     private void lazyInit() {
         if (wfLoader == null) {
-            if (uwfAdapter.getLoaderClass() != null) {
-                wfLoader = (WorkflowInstanceLoader) beanFactory.createBean(
-                        uwfAdapter.getLoaderClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+            if (uwfAdapter.getDefinitionLoaderClass() != null) {
+                wfLoader = (WorkflowDefinitionLoader) beanFactory.createBean(
+                        uwfAdapter.getDefinitionLoaderClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
             }
-            if (rwfAdapter.getLoaderClass() != null) {
-                wfLoader = (WorkflowInstanceLoader) beanFactory.createBean(
-                        rwfAdapter.getLoaderClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+            if (rwfAdapter.getDefinitionLoaderClass() != null) {
+                wfLoader = (WorkflowDefinitionLoader) beanFactory.createBean(
+                        rwfAdapter.getDefinitionLoaderClass(), AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
             }
         }
     }
 
-    public String getTablePrefix() {
+    public String getPrefix() {
         lazyInit();
-        return wfLoader == null ? null : wfLoader.getTablePrefix();
+        return wfLoader == null ? null : wfLoader.getPrefix();
     }
 
     public void init() {
@@ -86,7 +86,7 @@ public class WorkflowAdapterLoader implements BeanFactoryAware, SyncopeLoader {
     public void load() {
         lazyInit();
         if (wfLoader == null) {
-            LOG.debug("Configured workflow adapter does not need loading");
+            LOG.debug("The configured workflow adapter does not need loading");
         } else {
             LOG.debug("Loading workflow adapter by {}", wfLoader.getClass().getName());
             wfLoader.load();

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
index c91b827..de1c7d7 100644
--- a/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
+++ b/syncope620/server/logic/src/main/java/org/apache/syncope/server/logic/notification/NotificationJob.java
@@ -31,8 +31,8 @@ import org.apache.syncope.server.persistence.api.entity.EntityFactory;
 import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
 import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
 import org.apache.syncope.server.misc.ExceptionUtil;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.quartz.DisallowConcurrentExecution;
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
index 83a238f..3d25bc1 100644
--- a/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
+++ b/syncope620/server/logic/src/test/java/org/apache/syncope/server/logic/NotificationTest.java
@@ -62,8 +62,8 @@ import org.apache.syncope.server.persistence.api.entity.Notification;
 import org.apache.syncope.server.persistence.api.entity.conf.CPlainAttr;
 import org.apache.syncope.server.persistence.api.entity.conf.CPlainSchema;
 import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
 import org.apache.syncope.server.logic.notification.NotificationJob;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/persistence-api/src/main/java/org/apache/syncope/server/persistence/api/entity/user/User.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/server/persistence/api/entity/user/User.java b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/server/persistence/api/entity/user/User.java
index 918937c..d6b30d0 100644
--- a/syncope620/server/persistence-api/src/main/java/org/apache/syncope/server/persistence/api/entity/user/User.java
+++ b/syncope620/server/persistence-api/src/main/java/org/apache/syncope/server/persistence/api/entity/user/User.java
@@ -57,7 +57,7 @@ public interface User extends Subject<UPlainAttr, UDerAttr, UVirAttr> {
 
     List<String> getPasswordHistory();
 
-    Set<Long> getRoleIds();
+    Set<Long> getRoleKeys();
 
     List<Role> getRoles();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
index 3456431..8a60151 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/content/XMLContentExporter.java
@@ -319,6 +319,7 @@ public class XMLContentExporter extends AbstractContentDealer implements Content
         }
     }
 
+    @Override
     public void export(final OutputStream os, final String wfTablePrefix)
             throws SAXException, TransformerConfigurationException {
 
@@ -345,7 +346,7 @@ public class XMLContentExporter extends AbstractContentDealer implements Content
 
             rs = meta.getTables(null, StringUtils.isBlank(dbSchema) ? null : dbSchema, null, new String[] { "TABLE" });
 
-            final Set<String> tableNames = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+            final Set<String> tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
 
             while (rs.next()) {
                 String tableName = rs.getString("TABLE_NAME");

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPARoleDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPARoleDAO.java
index 1bb81b4..b7877d5 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPARoleDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPARoleDAO.java
@@ -169,8 +169,8 @@ public class JPARoleDAO extends AbstractSubjectDAO<RPlainAttr, RDerAttr, RVirAtt
 
         StringBuilder queryString = new StringBuilder("SELECT e FROM ").append(JPARole.class.getSimpleName()).
                 append(" e WHERE e.userOwner=:owner ");
-        for (Long roleId : owner.getRoleIds()) {
-            queryString.append("OR e.roleOwner.id=").append(roleId).append(' ');
+        for (Long roleKey : owner.getRoleKeys()) {
+            queryString.append("OR e.roleOwner.id=").append(roleKey).append(' ');
         }
 
         TypedQuery<Role> query = entityManager.createQuery(queryString.toString(), Role.class);
@@ -550,8 +550,8 @@ public class JPARoleDAO extends AbstractSubjectDAO<RPlainAttr, RDerAttr, RVirAtt
             throw new NotFoundException("Role " + key);
         }
 
-        Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
-        if (!allowedRoleIds.contains(role.getKey())) {
+        Set<Long> allowedRoleKeys = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+        if (!allowedRoleKeys.contains(role.getKey())) {
             throw new UnauthorizedRoleException(role.getKey());
         }
         return role;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
index 2a45ad3..b4a6171 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/dao/JPAUserDAO.java
@@ -241,11 +241,11 @@ public class JPAUserDAO extends AbstractSubjectDAO<UPlainAttr, UDerAttr, UVirAtt
         if (!AuthContextUtil.getAuthenticatedUsername().equals(anonymousUser)
                 && !AuthContextUtil.getAuthenticatedUsername().equals(user.getUsername())) {
 
-            Set<Long> roleIds = user.getRoleIds();
-            Set<Long> adminRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
-            roleIds.removeAll(adminRoleIds);
-            if (!roleIds.isEmpty()) {
-                throw new UnauthorizedRoleException(roleIds);
+            Set<Long> roleKeys = user.getRoleKeys();
+            Set<Long> adminRoleKeys = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+            roleKeys.removeAll(adminRoleKeys);
+            if (!roleKeys.isEmpty()) {
+                throw new UnauthorizedRoleException(roleKeys);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/user/JPAUser.java
----------------------------------------------------------------------
diff --git a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/user/JPAUser.java b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/user/JPAUser.java
index f7b3372..276cf63 100644
--- a/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/user/JPAUser.java
+++ b/syncope620/server/persistence-jpa/src/main/java/org/apache/syncope/server/persistence/jpa/entity/user/JPAUser.java
@@ -242,7 +242,7 @@ public class JPAUser extends AbstractSubject<UPlainAttr, UDerAttr, UVirAttr> imp
     }
 
     @Override
-    public Set<Long> getRoleIds() {
+    public Set<Long> getRoleKeys() {
         List<Role> roles = getRoles();
 
         Set<Long> result = new HashSet<>(roles.size());

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/pom.xml b/syncope620/server/pom.xml
index 240c8c3..112fabc 100644
--- a/syncope620/server/pom.xml
+++ b/syncope620/server/pom.xml
@@ -41,6 +41,7 @@ under the License.
     <module>provisioning-java</module>
     <module>workflow-api</module>
     <module>workflow-java</module>
+    <module>workflow-activiti</module>
     <module>logic</module>
     <module>rest-cxf</module>
   </modules>

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/notification/NotificationManager.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/notification/NotificationManager.java
new file mode 100644
index 0000000..f0ef4c8
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/notification/NotificationManager.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.api.notification;
+
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
+
+/**
+ * Create notification tasks that will be executed by NotificationJob.
+ *
+ * @see org.apache.syncope.server.persistence.api.entity.task.NotificationTask
+ */
+public interface NotificationManager {
+
+    /**
+     * Count the number of task executions of a given task with a given status.
+     *
+     * @param taskId task id
+     * @param status status
+     * @return number of task executions
+     */
+    long countExecutionsWithStatus(final Long taskId, final String status);
+
+    /**
+     * Create notification tasks for each notification matching the given user id and (some of) tasks performed.
+     */
+    void createTasks(final AuditElements.EventCategoryType type, final String category, final String subcategory,
+            final String event, final AuditElements.Result condition, final Object before, final Object output,
+            final Object... input);
+
+    long getMaxRetries();
+
+    /**
+     * Set execution state of NotificationTask with provided id.
+     *
+     * @param taskId task to be updated
+     * @param executed execution state
+     */
+    void setTaskExecuted(final Long taskId, final boolean executed);
+
+    /**
+     * Store execution of a NotificationTask.
+     *
+     * @param execution task execution.
+     * @return merged task execution.
+     */
+    TaskExec storeExec(final TaskExec execution);
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/AbstractTaskJob.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/AbstractTaskJob.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/AbstractTaskJob.java
index 22ce042..cf328cc 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/AbstractTaskJob.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/job/AbstractTaskJob.java
@@ -28,8 +28,8 @@ import org.apache.syncope.server.persistence.api.entity.task.Task;
 import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.server.provisioning.api.job.TaskJob;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
 import org.apache.syncope.server.misc.ExceptionUtil;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.quartz.DisallowConcurrentExecution;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
deleted file mode 100644
index 436f32c..0000000
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManager.java
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.server.provisioning.java.notification;
-
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.RoleTO;
-import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.common.lib.types.AuditElements;
-import org.apache.syncope.common.lib.types.AuditElements.Result;
-import org.apache.syncope.common.lib.types.AuditLoggerName;
-import org.apache.syncope.common.lib.types.IntMappingType;
-import org.apache.syncope.common.lib.types.SubjectType;
-import org.apache.syncope.server.persistence.api.RoleEntitlementUtil;
-import org.apache.syncope.server.persistence.api.dao.ConfDAO;
-import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
-import org.apache.syncope.server.persistence.api.dao.NotificationDAO;
-import org.apache.syncope.server.persistence.api.dao.RoleDAO;
-import org.apache.syncope.server.persistence.api.dao.SubjectSearchDAO;
-import org.apache.syncope.server.persistence.api.dao.TaskDAO;
-import org.apache.syncope.server.persistence.api.dao.UserDAO;
-import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
-import org.apache.syncope.server.persistence.api.entity.Attributable;
-import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
-import org.apache.syncope.server.persistence.api.entity.EntityFactory;
-import org.apache.syncope.server.persistence.api.entity.Notification;
-import org.apache.syncope.server.persistence.api.entity.PlainAttr;
-import org.apache.syncope.server.persistence.api.entity.Subject;
-import org.apache.syncope.server.persistence.api.entity.role.Role;
-import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
-import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.server.persistence.api.entity.user.UDerAttr;
-import org.apache.syncope.server.persistence.api.entity.user.UPlainAttr;
-import org.apache.syncope.server.persistence.api.entity.user.UVirAttr;
-import org.apache.syncope.server.persistence.api.entity.user.User;
-import org.apache.syncope.server.provisioning.api.data.RoleDataBinder;
-import org.apache.syncope.server.provisioning.api.data.UserDataBinder;
-import org.apache.syncope.server.misc.ConnObjectUtil;
-import org.apache.syncope.server.misc.search.SearchCondConverter;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.context.Context;
-import org.apache.velocity.exception.VelocityException;
-import org.apache.velocity.tools.ToolManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-/**
- * Create notification tasks that will be executed by NotificationJob.
- *
- * @see NotificationTask
- */
-@Component
-@Transactional(rollbackFor = { Throwable.class })
-public class NotificationManager {
-
-    /**
-     * Logger.
-     */
-    private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
-
-    public static final String MAIL_TEMPLATES = "mailTemplates/";
-
-    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
-
-    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
-
-    /**
-     * Notification DAO.
-     */
-    @Autowired
-    private NotificationDAO notificationDAO;
-
-    /**
-     * Configuration DAO.
-     */
-    @Autowired
-    private ConfDAO confDAO;
-
-    /**
-     * User DAO.
-     */
-    @Autowired
-    private UserDAO userDAO;
-
-    /**
-     * Role DAO.
-     */
-    @Autowired
-    private RoleDAO roleDAO;
-
-    /**
-     * User Search DAO.
-     */
-    @Autowired
-    private SubjectSearchDAO searchDAO;
-
-    /**
-     * Task DAO.
-     */
-    @Autowired
-    private TaskDAO taskDAO;
-
-    /**
-     * Velocity template engine.
-     */
-    @Autowired
-    private VelocityEngine velocityEngine;
-
-    /**
-     * Velocity tool manager.
-     */
-    @Autowired
-    private ToolManager velocityToolManager;
-
-    @Autowired
-    private EntitlementDAO entitlementDAO;
-
-    @Autowired
-    private ConnObjectUtil connObjectUtil;
-
-    @Autowired
-    private UserDataBinder userDataBinder;
-
-    @Autowired
-    private RoleDataBinder roleDataBinder;
-
-    @Autowired
-    private EntityFactory entityFactory;
-
-    @Autowired
-    private AttributableUtilFactory attrUtilFactory;
-
-    @Transactional(readOnly = true)
-    public long getMaxRetries() {
-        return confDAO.find("notification.maxRetries", "0").getValues().get(0).getLongValue();
-    }
-
-    /**
-     * Create a notification task.
-     *
-     * @param notification notification to take as model
-     * @param attributable the user this task is about
-     * @param model Velocity model
-     * @return notification task, fully populated
-     */
-    private NotificationTask getNotificationTask(
-            final Notification notification,
-            final Attributable<?, ?, ?> attributable,
-            final Map<String, Object> model) {
-
-        if (attributable != null) {
-            connObjectUtil.retrieveVirAttrValues(attributable,
-                    attrUtilFactory.getInstance(
-                            attributable instanceof User ? AttributableType.USER : AttributableType.ROLE));
-        }
-
-        final List<User> recipients = new ArrayList<>();
-
-        if (notification.getRecipients() != null) {
-            recipients.addAll(searchDAO.<User>search(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
-                    SearchCondConverter.convert(notification.getRecipients()),
-                    Collections.<OrderByClause>emptyList(), SubjectType.USER));
-        }
-
-        if (notification.isSelfAsRecipient() && attributable instanceof User) {
-            recipients.add((User) attributable);
-        }
-
-        final Set<String> recipientEmails = new HashSet<>();
-        final List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
-        for (User recipient : recipients) {
-            connObjectUtil.retrieveVirAttrValues(recipient, attrUtilFactory.getInstance(AttributableType.USER));
-
-            String email = getRecipientEmail(notification.getRecipientAttrType(),
-                    notification.getRecipientAttrName(), recipient);
-            if (email == null) {
-                LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
-            } else {
-                recipientEmails.add(email);
-                recipientTOs.add(userDataBinder.getUserTO(recipient));
-            }
-        }
-
-        if (notification.getStaticRecipients() != null) {
-            recipientEmails.addAll(notification.getStaticRecipients());
-        }
-
-        model.put("recipients", recipientTOs);
-        model.put("syncopeConf", this.findAllSyncopeConfs());
-        model.put("events", notification.getEvents());
-
-        NotificationTask task = entityFactory.newEntity(NotificationTask.class);
-        task.setTraceLevel(notification.getTraceLevel());
-        task.getRecipients().addAll(recipientEmails);
-        task.setSender(notification.getSender());
-        task.setSubject(notification.getSubject());
-
-        String htmlBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, model);
-        String textBody = mergeTemplateIntoString(
-                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, model);
-
-        task.setHtmlBody(htmlBody);
-        task.setTextBody(textBody);
-
-        return task;
-    }
-
-    private String mergeTemplateIntoString(final String templateLocation, final Map<String, Object> model) {
-        StringWriter result = new StringWriter();
-        try {
-            Context velocityContext = createVelocityContext(model);
-            velocityEngine.mergeTemplate(templateLocation, SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
-        } catch (VelocityException e) {
-            LOG.error("Could not get mail body", e);
-        } catch (RuntimeException e) {
-            // ensure same behaviour as by using Spring VelocityEngineUtils.mergeTemplateIntoString()
-            throw e;
-        } catch (Exception e) {
-            LOG.error("Could not get mail body", e);
-        }
-
-        return result.toString();
-    }
-
-    /**
-     * Create a Velocity Context for the given model, to be passed to the template for merging.
-     *
-     * @param model Velocity model
-     * @return Velocity context
-     */
-    protected Context createVelocityContext(Map<String, Object> model) {
-        Context toolContext = velocityToolManager.createContext();
-        return new VelocityContext(model, toolContext);
-    }
-
-    /**
-     * Create notification tasks for each notification matching the given user id and (some of) tasks performed.
-     */
-    public void createTasks(
-            final AuditElements.EventCategoryType type,
-            final String category,
-            final String subcategory,
-            final String event,
-            final Result condition,
-            final Object before,
-            final Object output,
-            final Object... input) {
-
-        SubjectType subjectType = null;
-        Subject<?, ?, ?> subject = null;
-
-        if (before instanceof UserTO) {
-            subjectType = SubjectType.USER;
-            subject = userDAO.find(((UserTO) before).getKey());
-        } else if (output instanceof UserTO) {
-            subjectType = SubjectType.USER;
-            subject = userDAO.find(((UserTO) output).getKey());
-        } else if (before instanceof RoleTO) {
-            subjectType = SubjectType.ROLE;
-            subject = roleDAO.find(((RoleTO) before).getKey());
-        } else if (output instanceof RoleTO) {
-            subjectType = SubjectType.ROLE;
-            subject = roleDAO.find(((RoleTO) output).getKey());
-        }
-
-        LOG.debug("Search notification for [{}]{}", subjectType, subject);
-
-        for (Notification notification : notificationDAO.findAll()) {
-            LOG.debug("Notification available user about {}", notification.getUserAbout());
-            LOG.debug("Notification available role about {}", notification.getRoleAbout());
-            if (notification.isActive()) {
-
-                final Set<String> events = new HashSet<>(notification.getEvents());
-                events.retainAll(Collections.<String>singleton(AuditLoggerName.buildEvent(
-                        type, category, subcategory, event, condition)));
-
-                if (events.isEmpty()) {
-                    LOG.debug("No events found about {}", subject);
-                } else if (subjectType == null || subject == null
-                        || notification.getUserAbout() == null || notification.getRoleAbout() == null
-                        || searchDAO.matches(subject,
-                                SearchCondConverter.convert(notification.getUserAbout()), subjectType)
-                        || searchDAO.matches(subject,
-                                SearchCondConverter.convert(notification.getRoleAbout()), subjectType)) {
-
-                    LOG.debug("Creating notification task for events {} about {}", events, subject);
-
-                    final Map<String, Object> model = new HashMap<>();
-                    model.put("type", type);
-                    model.put("category", category);
-                    model.put("subcategory", subcategory);
-                    model.put("event", event);
-                    model.put("condition", condition);
-                    model.put("before", before);
-                    model.put("output", output);
-                    model.put("input", input);
-
-                    if (subject instanceof User) {
-                        model.put("user", userDataBinder.getUserTO((User) subject));
-                    } else if (subject instanceof Role) {
-                        model.put("role", roleDataBinder.getRoleTO((Role) subject));
-                    }
-
-                    taskDAO.save(getNotificationTask(notification, subject, model));
-                }
-            } else {
-                LOG.debug("Notification {}, userAbout {}, roleAbout {} is deactivated, "
-                        + "notification task will not be created", notification.getKey(),
-                        notification.getUserAbout(), notification.getRoleAbout());
-            }
-        }
-    }
-
-    private String getRecipientEmail(
-            final IntMappingType recipientAttrType, final String recipientAttrName, final User user) {
-
-        String email = null;
-
-        switch (recipientAttrType) {
-            case Username:
-                email = user.getUsername();
-                break;
-
-            case UserPlainSchema:
-                UPlainAttr attr = user.getPlainAttr(recipientAttrName);
-                if (attr != null && !attr.getValuesAsStrings().isEmpty()) {
-                    email = attr.getValuesAsStrings().get(0);
-                }
-                break;
-
-            case UserVirtualSchema:
-                UVirAttr virAttr = user.getVirAttr(recipientAttrName);
-                if (virAttr != null && !virAttr.getValues().isEmpty()) {
-                    email = virAttr.getValues().get(0);
-                }
-                break;
-
-            case UserDerivedSchema:
-                UDerAttr derAttr = user.getDerAttr(recipientAttrName);
-                if (derAttr != null) {
-                    email = derAttr.getValue(user.getPlainAttrs());
-                }
-                break;
-
-            default:
-        }
-
-        return email;
-    }
-
-    /**
-     * Store execution of a NotificationTask.
-     *
-     * @param execution task execution.
-     * @return merged task execution.
-     */
-    public TaskExec storeExec(final TaskExec execution) {
-        NotificationTask task = taskDAO.find(execution.getTask().getKey());
-        task.addExec(execution);
-        task.setExecuted(true);
-        taskDAO.save(task);
-        // this flush call is needed to generate a value for the execution id
-        taskDAO.flush();
-        return execution;
-    }
-
-    /**
-     * Set execution state of NotificationTask with provided id.
-     *
-     * @param taskId task to be updated
-     * @param executed execution state
-     */
-    public void setTaskExecuted(final Long taskId, final boolean executed) {
-        NotificationTask task = taskDAO.find(taskId);
-        task.setExecuted(executed);
-        taskDAO.save(task);
-    }
-
-    /**
-     * Count the number of task executions of a given task with a given status.
-     *
-     * @param taskId task id
-     * @param status status
-     * @return number of task executions
-     */
-    public long countExecutionsWithStatus(final Long taskId, final String status) {
-        NotificationTask task = taskDAO.find(taskId);
-        long count = 0;
-        for (TaskExec taskExec : task.getExecs()) {
-            if (status == null) {
-                if (taskExec.getStatus() == null) {
-                    count++;
-                }
-            } else if (status.equals(taskExec.getStatus())) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    protected Map<String, String> findAllSyncopeConfs() {
-        Map<String, String> syncopeConfMap = new HashMap<>();
-        for (PlainAttr attr : confDAO.get().getPlainAttrs()) {
-            syncopeConfMap.put(attr.getSchema().getKey(), attr.getValuesAsStrings().get(0));
-        }
-        return syncopeConfMap;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManagerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManagerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManagerImpl.java
new file mode 100644
index 0000000..dd323bc
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/notification/NotificationManagerImpl.java
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java.notification;
+
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.server.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.server.persistence.api.dao.ConfDAO;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.server.persistence.api.dao.RoleDAO;
+import org.apache.syncope.server.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.server.persistence.api.dao.TaskDAO;
+import org.apache.syncope.server.persistence.api.dao.UserDAO;
+import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.server.persistence.api.entity.Attributable;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.server.persistence.api.entity.EntityFactory;
+import org.apache.syncope.server.persistence.api.entity.Notification;
+import org.apache.syncope.server.persistence.api.entity.PlainAttr;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.role.Role;
+import org.apache.syncope.server.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.server.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.server.persistence.api.entity.user.UDerAttr;
+import org.apache.syncope.server.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.server.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.data.RoleDataBinder;
+import org.apache.syncope.server.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.server.misc.ConnObjectUtil;
+import org.apache.syncope.server.misc.search.SearchCondConverter;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.tools.ToolManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+@Transactional(rollbackFor = { Throwable.class })
+public class NotificationManagerImpl implements NotificationManager {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(NotificationManager.class);
+
+    public static final String MAIL_TEMPLATES = "mailTemplates/";
+
+    public static final String MAIL_TEMPLATE_HTML_SUFFIX = ".html.vm";
+
+    public static final String MAIL_TEMPLATE_TEXT_SUFFIX = ".txt.vm";
+
+    /**
+     * Notification DAO.
+     */
+    @Autowired
+    private NotificationDAO notificationDAO;
+
+    /**
+     * Configuration DAO.
+     */
+    @Autowired
+    private ConfDAO confDAO;
+
+    /**
+     * User DAO.
+     */
+    @Autowired
+    private UserDAO userDAO;
+
+    /**
+     * Role DAO.
+     */
+    @Autowired
+    private RoleDAO roleDAO;
+
+    /**
+     * User Search DAO.
+     */
+    @Autowired
+    private SubjectSearchDAO searchDAO;
+
+    /**
+     * Task DAO.
+     */
+    @Autowired
+    private TaskDAO taskDAO;
+
+    /**
+     * Velocity template engine.
+     */
+    @Autowired
+    private VelocityEngine velocityEngine;
+
+    /**
+     * Velocity tool manager.
+     */
+    @Autowired
+    private ToolManager velocityToolManager;
+
+    @Autowired
+    private EntitlementDAO entitlementDAO;
+
+    @Autowired
+    private ConnObjectUtil connObjectUtil;
+
+    @Autowired
+    private UserDataBinder userDataBinder;
+
+    @Autowired
+    private RoleDataBinder roleDataBinder;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private AttributableUtilFactory attrUtilFactory;
+
+    @Transactional(readOnly = true)
+    @Override
+    public long getMaxRetries() {
+        return confDAO.find("notification.maxRetries", "0").getValues().get(0).getLongValue();
+    }
+
+    /**
+     * Create a notification task.
+     *
+     * @param notification notification to take as model
+     * @param attributable the user this task is about
+     * @param model Velocity model
+     * @return notification task, fully populated
+     */
+    private NotificationTask getNotificationTask(
+            final Notification notification,
+            final Attributable<?, ?, ?> attributable,
+            final Map<String, Object> model) {
+
+        if (attributable != null) {
+            connObjectUtil.retrieveVirAttrValues(attributable,
+                    attrUtilFactory.getInstance(
+                            attributable instanceof User ? AttributableType.USER : AttributableType.ROLE));
+        }
+
+        final List<User> recipients = new ArrayList<>();
+
+        if (notification.getRecipients() != null) {
+            recipients.addAll(searchDAO.<User>search(RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
+                    SearchCondConverter.convert(notification.getRecipients()),
+                    Collections.<OrderByClause>emptyList(), SubjectType.USER));
+        }
+
+        if (notification.isSelfAsRecipient() && attributable instanceof User) {
+            recipients.add((User) attributable);
+        }
+
+        final Set<String> recipientEmails = new HashSet<>();
+        final List<UserTO> recipientTOs = new ArrayList<>(recipients.size());
+        for (User recipient : recipients) {
+            connObjectUtil.retrieveVirAttrValues(recipient, attrUtilFactory.getInstance(AttributableType.USER));
+
+            String email = getRecipientEmail(notification.getRecipientAttrType(),
+                    notification.getRecipientAttrName(), recipient);
+            if (email == null) {
+                LOG.warn("{} cannot be notified: {} not found", recipient, notification.getRecipientAttrName());
+            } else {
+                recipientEmails.add(email);
+                recipientTOs.add(userDataBinder.getUserTO(recipient));
+            }
+        }
+
+        if (notification.getStaticRecipients() != null) {
+            recipientEmails.addAll(notification.getStaticRecipients());
+        }
+
+        model.put("recipients", recipientTOs);
+        model.put("syncopeConf", this.findAllSyncopeConfs());
+        model.put("events", notification.getEvents());
+
+        NotificationTask task = entityFactory.newEntity(NotificationTask.class);
+        task.setTraceLevel(notification.getTraceLevel());
+        task.getRecipients().addAll(recipientEmails);
+        task.setSender(notification.getSender());
+        task.setSubject(notification.getSubject());
+
+        String htmlBody = mergeTemplateIntoString(
+                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_HTML_SUFFIX, model);
+        String textBody = mergeTemplateIntoString(
+                MAIL_TEMPLATES + notification.getTemplate() + MAIL_TEMPLATE_TEXT_SUFFIX, model);
+
+        task.setHtmlBody(htmlBody);
+        task.setTextBody(textBody);
+
+        return task;
+    }
+
+    private String mergeTemplateIntoString(final String templateLocation, final Map<String, Object> model) {
+        StringWriter result = new StringWriter();
+        try {
+            Context velocityContext = createVelocityContext(model);
+            velocityEngine.mergeTemplate(templateLocation, SyncopeConstants.DEFAULT_ENCODING, velocityContext, result);
+        } catch (VelocityException e) {
+            LOG.error("Could not get mail body", e);
+        } catch (RuntimeException e) {
+            // ensure same behaviour as by using Spring VelocityEngineUtils.mergeTemplateIntoString()
+            throw e;
+        } catch (Exception e) {
+            LOG.error("Could not get mail body", e);
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * Create a Velocity Context for the given model, to be passed to the template for merging.
+     *
+     * @param model Velocity model
+     * @return Velocity context
+     */
+    protected Context createVelocityContext(Map<String, Object> model) {
+        Context toolContext = velocityToolManager.createContext();
+        return new VelocityContext(model, toolContext);
+    }
+
+    @Override
+    public void createTasks(
+            final AuditElements.EventCategoryType type,
+            final String category,
+            final String subcategory,
+            final String event,
+            final Result condition,
+            final Object before,
+            final Object output,
+            final Object... input) {
+
+        SubjectType subjectType = null;
+        Subject<?, ?, ?> subject = null;
+
+        if (before instanceof UserTO) {
+            subjectType = SubjectType.USER;
+            subject = userDAO.find(((UserTO) before).getKey());
+        } else if (output instanceof UserTO) {
+            subjectType = SubjectType.USER;
+            subject = userDAO.find(((UserTO) output).getKey());
+        } else if (before instanceof RoleTO) {
+            subjectType = SubjectType.ROLE;
+            subject = roleDAO.find(((RoleTO) before).getKey());
+        } else if (output instanceof RoleTO) {
+            subjectType = SubjectType.ROLE;
+            subject = roleDAO.find(((RoleTO) output).getKey());
+        }
+
+        LOG.debug("Search notification for [{}]{}", subjectType, subject);
+
+        for (Notification notification : notificationDAO.findAll()) {
+            LOG.debug("Notification available user about {}", notification.getUserAbout());
+            LOG.debug("Notification available role about {}", notification.getRoleAbout());
+            if (notification.isActive()) {
+
+                final Set<String> events = new HashSet<>(notification.getEvents());
+                events.retainAll(Collections.<String>singleton(AuditLoggerName.buildEvent(
+                        type, category, subcategory, event, condition)));
+
+                if (events.isEmpty()) {
+                    LOG.debug("No events found about {}", subject);
+                } else if (subjectType == null || subject == null
+                        || notification.getUserAbout() == null || notification.getRoleAbout() == null
+                        || searchDAO.matches(subject,
+                                SearchCondConverter.convert(notification.getUserAbout()), subjectType)
+                        || searchDAO.matches(subject,
+                                SearchCondConverter.convert(notification.getRoleAbout()), subjectType)) {
+
+                    LOG.debug("Creating notification task for events {} about {}", events, subject);
+
+                    final Map<String, Object> model = new HashMap<>();
+                    model.put("type", type);
+                    model.put("category", category);
+                    model.put("subcategory", subcategory);
+                    model.put("event", event);
+                    model.put("condition", condition);
+                    model.put("before", before);
+                    model.put("output", output);
+                    model.put("input", input);
+
+                    if (subject instanceof User) {
+                        model.put("user", userDataBinder.getUserTO((User) subject));
+                    } else if (subject instanceof Role) {
+                        model.put("role", roleDataBinder.getRoleTO((Role) subject));
+                    }
+
+                    taskDAO.save(getNotificationTask(notification, subject, model));
+                }
+            } else {
+                LOG.debug("Notification {}, userAbout {}, roleAbout {} is deactivated, "
+                        + "notification task will not be created", notification.getKey(),
+                        notification.getUserAbout(), notification.getRoleAbout());
+            }
+        }
+    }
+
+    private String getRecipientEmail(
+            final IntMappingType recipientAttrType, final String recipientAttrName, final User user) {
+
+        String email = null;
+
+        switch (recipientAttrType) {
+            case Username:
+                email = user.getUsername();
+                break;
+
+            case UserPlainSchema:
+                UPlainAttr attr = user.getPlainAttr(recipientAttrName);
+                if (attr != null && !attr.getValuesAsStrings().isEmpty()) {
+                    email = attr.getValuesAsStrings().get(0);
+                }
+                break;
+
+            case UserVirtualSchema:
+                UVirAttr virAttr = user.getVirAttr(recipientAttrName);
+                if (virAttr != null && !virAttr.getValues().isEmpty()) {
+                    email = virAttr.getValues().get(0);
+                }
+                break;
+
+            case UserDerivedSchema:
+                UDerAttr derAttr = user.getDerAttr(recipientAttrName);
+                if (derAttr != null) {
+                    email = derAttr.getValue(user.getPlainAttrs());
+                }
+                break;
+
+            default:
+        }
+
+        return email;
+    }
+
+    @Override
+    public TaskExec storeExec(final TaskExec execution) {
+        NotificationTask task = taskDAO.find(execution.getTask().getKey());
+        task.addExec(execution);
+        task.setExecuted(true);
+        taskDAO.save(task);
+        // this flush call is needed to generate a value for the execution id
+        taskDAO.flush();
+        return execution;
+    }
+
+    @Override
+    public void setTaskExecuted(final Long taskId, final boolean executed) {
+        NotificationTask task = taskDAO.find(taskId);
+        task.setExecuted(executed);
+        taskDAO.save(task);
+    }
+
+    @Override
+    public long countExecutionsWithStatus(final Long taskId, final String status) {
+        NotificationTask task = taskDAO.find(taskId);
+        long count = 0;
+        for (TaskExec taskExec : task.getExecs()) {
+            if (status == null) {
+                if (taskExec.getStatus() == null) {
+                    count++;
+                }
+            } else if (status.equals(taskExec.getStatus())) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    protected Map<String, String> findAllSyncopeConfs() {
+        Map<String, String> syncopeConfMap = new HashMap<>();
+        for (PlainAttr attr : confDAO.get().getPlainAttrs()) {
+            syncopeConfMap.put(attr.getSchema().getKey(), attr.getValuesAsStrings().get(0));
+        }
+        return syncopeConfMap;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 86df210..725a561 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -47,10 +47,10 @@ import org.apache.syncope.server.provisioning.api.propagation.PropagationActions
 import org.apache.syncope.server.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.server.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
 import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.server.misc.ConnObjectUtil;
 import org.apache.syncope.server.misc.ExceptionUtil;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.identityconnectors.framework.common.exceptions.ConnectorException;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeUtil;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncopeResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncopeResultHandler.java
index fbd5b7e..c991685 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncopeResultHandler.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/AbstractSyncopeResultHandler.java
@@ -32,8 +32,8 @@ import org.apache.syncope.server.provisioning.api.sync.ProvisioningActions;
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
 import org.apache.syncope.server.provisioning.api.sync.SyncopeResultHandler;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
 import org.apache.syncope.server.misc.ConnObjectUtil;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.server.workflow.api.RoleWorkflowAdapter;
 import org.apache.syncope.server.workflow.api.UserWorkflowAdapter;
 import org.slf4j.Logger;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
index 145cd4d..5d2d6ad 100644
--- a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/LDAPMembershipSyncActions.java
@@ -47,7 +47,7 @@ import org.apache.syncope.server.provisioning.api.propagation.PropagationTaskExe
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
 import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
 import org.apache.syncope.server.misc.AuditManager;
-import org.apache.syncope.server.provisioning.java.notification.NotificationManager;
+import org.apache.syncope.server.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.server.workflow.api.UserWorkflowAdapter;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.ObjectClass;

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/workflow-activiti/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/workflow-activiti/pom.xml b/syncope620/server/workflow-activiti/pom.xml
new file mode 100644
index 0000000..3d6db04
--- /dev/null
+++ b/syncope620/server/workflow-activiti/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-server</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Server Workflow Activiti</name>
+  <description>Apache Syncope Server Workflow Activiti</description>
+  <groupId>org.apache.syncope.server</groupId>
+  <artifactId>syncope-server-workflow-activiti</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+      
+    <dependency>
+      <groupId>org.activiti</groupId>
+      <artifactId>activiti-engine</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.activiti</groupId>
+      <artifactId>activiti-spring</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.activiti</groupId>
+      <artifactId>activiti-json-converter</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-server-workflow-java</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-server-misc</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiDefinitionLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiDefinitionLoader.java b/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiDefinitionLoader.java
new file mode 100644
index 0000000..05bcc94
--- /dev/null
+++ b/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiDefinitionLoader.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.workflow.activiti;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import javax.annotation.Resource;
+import org.activiti.editor.constants.ModelDataJsonConstants;
+import org.activiti.engine.RepositoryService;
+import org.activiti.engine.repository.Model;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.activiti.spring.SpringProcessEngineConfiguration;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.server.misc.spring.ResourceWithFallbackLoader;
+import org.apache.syncope.server.workflow.api.WorkflowDefinitionLoader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class ActivitiDefinitionLoader implements WorkflowDefinitionLoader {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ActivitiDefinitionLoader.class);
+
+    @Resource(name = "userWorkflowDef")
+    private ResourceWithFallbackLoader userWorkflowDef;
+
+    @Autowired
+    private RepositoryService repositoryService;
+
+    @Autowired
+    private SpringProcessEngineConfiguration conf;
+
+    @Autowired
+    private ActivitiImportUtils importUtils;
+
+    @Override
+    public String getPrefix() {
+        return "ACT_";
+    }
+
+    @Override
+    public void init() {
+        // jump to the next ID block
+        for (int i = 0; i < conf.getIdBlockSize(); i++) {
+            conf.getIdGenerator().getNextId();
+        }
+    }
+
+    @Override
+    public void load() {
+        List<ProcessDefinition> processes = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
+                ActivitiUserWorkflowAdapter.WF_PROCESS_ID).list();
+        LOG.debug(ActivitiUserWorkflowAdapter.WF_PROCESS_ID + " Activiti processes in repository: {}", processes);
+
+        // Only loads process definition from file if not found in repository
+        if (processes.isEmpty()) {
+            InputStream wfIn = null;
+            try {
+                wfIn = userWorkflowDef.getResource().getInputStream();
+                repositoryService.createDeployment().addInputStream(ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE,
+                        new ByteArrayInputStream(IOUtils.toByteArray(wfIn))).deploy();
+
+                ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
+                        ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult();
+
+                Model model = repositoryService.newModel();
+                ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
+                modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, procDef.getName());
+                modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
+                modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, procDef.getDescription());
+                model.setMetaInfo(modelObjectNode.toString());
+                model.setName(procDef.getName());
+                model.setDeploymentId(procDef.getDeploymentId());
+                importUtils.fromJSON(procDef, model);
+
+                LOG.debug("Activiti Workflow definition loaded");
+            } catch (IOException e) {
+                LOG.error("While loading " + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
+            } finally {
+                IOUtils.closeQuietly(wfIn);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/702810d8/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiImportUtils.java
----------------------------------------------------------------------
diff --git a/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiImportUtils.java b/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiImportUtils.java
new file mode 100644
index 0000000..388f7e9
--- /dev/null
+++ b/syncope620/server/workflow-activiti/src/main/java/org/apache/syncope/workflow/activiti/ActivitiImportUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.workflow.activiti;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import org.activiti.bpmn.converter.BpmnXMLConverter;
+import org.activiti.bpmn.model.BpmnModel;
+import org.activiti.editor.language.json.converter.BpmnJsonConverter;
+import org.activiti.engine.ActivitiException;
+import org.activiti.engine.RepositoryService;
+import org.activiti.engine.repository.Model;
+import org.activiti.engine.repository.ProcessDefinition;
+import org.apache.commons.io.IOUtils;
+import org.apache.syncope.server.workflow.api.WorkflowException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ActivitiImportUtils {
+
+    @Autowired
+    private RepositoryService repositoryService;
+
+    public void fromXML(final byte[] definition) {
+        try {
+            repositoryService.createDeployment().addInputStream(ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE,
+                    new ByteArrayInputStream(definition)).deploy();
+        } catch (ActivitiException e) {
+            throw new WorkflowException("While updating process " + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
+        }
+    }
+
+    public void fromJSON(final byte[] definition, final ProcessDefinition procDef, final Model model) {
+        try {
+            model.setVersion(procDef.getVersion());
+            model.setDeploymentId(procDef.getDeploymentId());
+            repositoryService.saveModel(model);
+
+            repositoryService.addModelEditorSource(model.getId(), definition);
+        } catch (Exception e) {
+            throw new WorkflowException("While updating process " + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
+        }
+    }
+
+    public void fromJSON(final ProcessDefinition procDef, final Model model) {
+        InputStream bpmnStream = null;
+        InputStreamReader isr = null;
+        XMLStreamReader xtr = null;
+        try {
+            bpmnStream = repositoryService.getResourceAsStream(
+                    procDef.getDeploymentId(), procDef.getResourceName());
+            isr = new InputStreamReader(bpmnStream);
+            xtr = XMLInputFactory.newInstance().createXMLStreamReader(isr);
+            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
+
+            fromJSON(new BpmnJsonConverter().convertToJson(bpmnModel).toString().getBytes(), procDef, model);
+        } catch (Exception e) {
+            throw new WorkflowException("While updating process " + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
+        } finally {
+            if (xtr != null) {
+                try {
+                    xtr.close();
+                } catch (XMLStreamException e) {
+                    // ignore
+                }
+            }
+            IOUtils.closeQuietly(isr);
+            IOUtils.closeQuietly(bpmnStream);
+        }
+    }
+}