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 2018/10/02 12:32:18 UTC

[2/6] syncope git commit: [SYNCOPE-1369] User requests forms now support dropdowns - via Flowable customization

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
new file mode 100644
index 0000000..251eb89
--- /dev/null
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.common.lib.to;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.AbstractBaseBean;
+
+@XmlRootElement(name = "userRequest")
+@XmlType
+public class UserRequest extends AbstractBaseBean {
+
+    private static final long serialVersionUID = -8430826310789942133L;
+
+    private String bpmnProcess;
+
+    private String user;
+
+    private String executionId;
+
+    private String activityId;
+
+    public String getBpmnProcess() {
+        return bpmnProcess;
+    }
+
+    public void setBpmnProcess(final String bpmnProcess) {
+        this.bpmnProcess = bpmnProcess;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(final String user) {
+        this.user = user;
+    }
+
+    public String getExecutionId() {
+        return executionId;
+    }
+
+    public void setExecutionId(final String executionId) {
+        this.executionId = executionId;
+    }
+
+    public String getActivityId() {
+        return activityId;
+    }
+
+    public void setActivityId(final String activityId) {
+        this.activityId = activityId;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
index 897cfc0..22118b5 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestForm.java
@@ -37,8 +37,12 @@ public class UserRequestForm extends AbstractBaseBean {
 
     private static final long serialVersionUID = -7044543391316529128L;
 
+    private String bpmnProcess;
+
     private String username;
 
+    private String executionId;
+
     private String taskId;
 
     private String formKey;
@@ -55,6 +59,14 @@ public class UserRequestForm extends AbstractBaseBean {
 
     private final List<UserRequestFormProperty> properties = new ArrayList<>();
 
+    public String getBpmnProcess() {
+        return bpmnProcess;
+    }
+
+    public void setBpmnProcess(final String bpmnProcess) {
+        this.bpmnProcess = bpmnProcess;
+    }
+
     public String getUsername() {
         return username;
     }
@@ -63,6 +75,14 @@ public class UserRequestForm extends AbstractBaseBean {
         this.username = username;
     }
 
+    public String getExecutionId() {
+        return executionId;
+    }
+
+    public void setExecutionId(final String executionId) {
+        this.executionId = executionId;
+    }
+
     public String getTaskId() {
         return taskId;
     }
@@ -138,9 +158,9 @@ public class UserRequestForm extends AbstractBaseBean {
         return properties.stream().filter(property -> id.equals(property.getId())).findFirst();
     }
 
-    @XmlElementWrapper(name = "workflowFormProperties")
-    @XmlElement(name = "workflowFormProperty")
-    @JsonProperty("workflowFormProperties")
+    @XmlElementWrapper(name = "properties")
+    @XmlElement(name = "property")
+    @JsonProperty("properties")
     public List<UserRequestFormProperty> getProperties() {
         return properties;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
index 0a431c6..95f2fec 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestFormProperty.java
@@ -51,6 +51,9 @@ public class UserRequestFormProperty extends AbstractBaseBean {
     @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
     private final Map<String, String> enumValues = new HashMap<>();
 
+    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
+    private final Map<String, String> dropdownValues = new HashMap<>();
+
     private String value;
 
     public String getId() {
@@ -114,6 +117,11 @@ public class UserRequestFormProperty extends AbstractBaseBean {
         return enumValues;
     }
 
+    @JsonProperty
+    public Map<String, String> getDropdownValues() {
+        return dropdownValues;
+    }
+
     public String getValue() {
         return value;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestTO.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestTO.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestTO.java
deleted file mode 100644
index d5fad6c..0000000
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/UserRequestTO.java
+++ /dev/null
@@ -1,70 +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.common.lib.to;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.AbstractBaseBean;
-
-@XmlRootElement(name = "userRequest")
-@XmlType
-public class UserRequestTO extends AbstractBaseBean {
-
-    private static final long serialVersionUID = -8430826310789942133L;
-
-    private String processInstanceId;
-
-    private String executionId;
-
-    private String bpmnProcess;
-
-    private String user;
-
-    public String getProcessInstanceId() {
-        return processInstanceId;
-    }
-
-    public void setProcessInstanceId(final String processInstanceId) {
-        this.processInstanceId = processInstanceId;
-    }
-
-    public String getExecutionId() {
-        return executionId;
-    }
-
-    public void setExecutionId(final String executionId) {
-        this.executionId = executionId;
-    }
-
-    public String getBpmnProcess() {
-        return bpmnProcess;
-    }
-
-    public void setBpmnProcess(final String bpmnProcess) {
-        this.bpmnProcess = bpmnProcess;
-    }
-
-    public String getUser() {
-        return user;
-    }
-
-    public void setUser(final String user) {
-        this.user = user;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
index 43825a4..fbf5b02 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/FlowableEntitlement.java
@@ -36,19 +36,13 @@ public final class FlowableEntitlement {
 
     public static final String WORKFLOW_TASK_LIST = "WORKFLOW_TASK_LIST";
 
-    public static final String WORKFLOW_FORM_LIST = "WORKFLOW_FORM_LIST";
+    public static final String USER_REQUEST_LIST = "USER_REQUEST_LIST";
 
-    public static final String WORKFLOW_FORM_READ = "WORKFLOW_FORM_READ";
+    public static final String USER_REQUEST_FORM_LIST = "USER_REQUEST_FORM_LIST";
 
-    public static final String WORKFLOW_FORM_CLAIM = "WORKFLOW_FORM_CLAIM";
+    public static final String USER_REQUEST_FORM_CLAIM = "USER_REQUEST_FORM_CLAIM";
 
-    public static final String WORKFLOW_FORM_SUBMIT = "WORKFLOW_FORM_SUBMIT";
-
-    public static final String USER_REQUEST_DEF_CREATE = "USER_REQUEST_DEF_CREATE";
-
-    public static final String USER_REQUEST_DEF_UPDATE = "USER_REQUEST_DEF_UPDATE";
-
-    public static final String USER_REQUEST_DEF_DELETE = "USER_REQUEST_DEF_DELETE";
+    public static final String USER_REQUEST_FORM_SUBMIT = "USER_REQUEST_FORM_SUBMIT";
 
     public static final String USER_REQUEST_START = "USER_REQUEST_START";
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
----------------------------------------------------------------------
diff --git a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
index 9565f99..227296b 100644
--- a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
+++ b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/types/UserRequestFormPropertyType.java
@@ -27,6 +27,7 @@ public enum UserRequestFormPropertyType {
     Long,
     Enum,
     Date,
-    Boolean
+    Boolean,
+    Dropdown
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/BpmnProcessManager.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/BpmnProcessManager.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/BpmnProcessManager.java
index 9247fd4..f7d1747 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/BpmnProcessManager.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/BpmnProcessManager.java
@@ -20,7 +20,7 @@ package org.apache.syncope.core.flowable.api;
 
 import java.io.OutputStream;
 import java.util.List;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.common.lib.types.BpmnProcessFormat;
 
 public interface BpmnProcessManager {
@@ -28,7 +28,7 @@ public interface BpmnProcessManager {
     /**
      * @return all available workflow processes.
      */
-    List<BpmnProcessTO> getProcesses();
+    List<BpmnProcess> getProcesses();
 
     /**
      * Export the process for the given key, in the requested format.

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/DropdownValueProvider.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/DropdownValueProvider.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/DropdownValueProvider.java
new file mode 100644
index 0000000..b6c79cf
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/DropdownValueProvider.java
@@ -0,0 +1,31 @@
+/*
+ * 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.core.flowable.api;
+
+import java.util.Map;
+
+/**
+ * Implementations of this interface are used with {@link org.apache.syncope.core.flowable.support.DropdownFormType}.
+ */
+public interface DropdownValueProvider {
+
+    String NAME = "dropdownValueProvider";
+
+    Map<String, String> getValues();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
index b11b97c..c3c6f0d 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/api/UserRequestHandler.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.flowable.api;
 import java.util.List;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserRequestForm;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -33,13 +33,25 @@ import org.springframework.transaction.event.TransactionalEventListener;
 public interface UserRequestHandler {
 
     /**
+     * Get the running user requests matching the provided parameters.
+     *
+     * @param userKey user key (optional)
+     * @param page result page
+     * @param size items per page
+     * @param orderByClauses sort conditions
+     * @return total number of user requests, list of user requests matching the provided parameters
+     */
+    Pair<Integer, List<UserRequest>> getUserRequests(
+            String userKey, int page, int size, List<OrderByClause> orderByClauses);
+
+    /**
      * Starts a new user request, for the given BPMN process and user.
      *
      * @param bpmnProcess BPMN process
      * @param user user
      * @return data about the started request service, including execution id
      */
-    UserRequestTO start(String bpmnProcess, User user);
+    UserRequest start(String bpmnProcess, User user);
 
     /**
      * Parses the given execution id to find matching user request and owner.
@@ -73,22 +85,16 @@ public interface UserRequestHandler {
     void cancelByUser(AnyDeletedEvent event);
 
     /**
-     * Get the forms for current workflow process instances matching the provided parameters.
+     * Get the forms matching the provided parameters.
      *
+     * @param userKey user key (optional)
      * @param page result page
      * @param size items per page
      * @param orderByClauses sort conditions
      * @return total number of forms, list of forms matching the provided parameters
      */
-    Pair<Integer, List<UserRequestForm>> getForms(int page, int size, List<OrderByClause> orderByClauses);
-
-    /**
-     * Get forms for given user (if present).
-     *
-     * @param userKey user key
-     * @return form (if present), otherwise null
-     */
-    List<UserRequestForm> getForms(String userKey);
+    Pair<Integer, List<UserRequestForm>> getForms(
+            String userKey, int page, int size, List<OrderByClause> orderByClauses);
 
     /**
      * Claim a form for a given object.

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManager.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManager.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManager.java
index bca8b25..50b078b 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManager.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableBpmnProcessManager.java
@@ -28,9 +28,10 @@ import java.io.OutputStream;
 import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.common.lib.types.BpmnProcessFormat;
 import org.apache.syncope.core.flowable.support.DomainProcessEngine;
+import org.apache.syncope.core.flowable.support.DropdownAwareJsonConverter;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.workflow.api.WorkflowException;
 import org.flowable.bpmn.converter.BpmnXMLConverter;
@@ -73,11 +74,11 @@ public class FlowableBpmnProcessManager implements BpmnProcessManager {
     }
 
     @Override
-    public List<BpmnProcessTO> getProcesses() {
+    public List<BpmnProcess> getProcesses() {
         try {
             return engine.getRepositoryService().createProcessDefinitionQuery().latestVersion().list().stream().
                     map(procDef -> {
-                        BpmnProcessTO defTO = new BpmnProcessTO();
+                        BpmnProcess defTO = new BpmnProcess();
                         defTO.setKey(procDef.getKey());
                         defTO.setName(procDef.getName());
 
@@ -163,7 +164,7 @@ public class FlowableBpmnProcessManager implements BpmnProcessManager {
                                 "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES);
                     }
 
-                    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode);
+                    BpmnModel bpmnModel = new DropdownAwareJsonConverter().convertToBpmnModel(definitionNode);
                     deployment = FlowableDeployUtils.deployDefinition(
                             engine,
                             resourceName,

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java
index 12d8faa..cdf035d 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableRuntimeUtils.java
@@ -22,6 +22,7 @@ import java.util.Base64;
 import java.util.List;
 import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.flowable.support.DomainProcessEngine;
@@ -33,7 +34,6 @@ import org.apache.syncope.core.workflow.api.WorkflowException;
 import org.flowable.common.engine.api.FlowableException;
 import org.flowable.engine.history.HistoricActivityInstance;
 import org.flowable.engine.impl.RuntimeServiceImpl;
-import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
 import org.flowable.engine.repository.ProcessDefinition;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.Task;
@@ -97,8 +97,17 @@ public final class FlowableRuntimeUtils {
         return procInst == null ? null : procInst.getId();
     }
 
-    public static String getProcBusinessKey(final String processDefinitionId, final String userKey) {
-        return processDefinitionId + ":" + userKey;
+    public static String getProcBusinessKey(final String procDefId, final String userKey) {
+        return procDefId + ":" + userKey;
+    }
+
+    public static Pair<String, String> splitProcBusinessKey(final String procBusinessKey) {
+        String[] split = procBusinessKey.split(":");
+        if (split == null || split.length != 2) {
+            throw new WorkflowException(new IllegalArgumentException("Unexpected business key: " + procBusinessKey));
+        }
+
+        return Pair.of(split[0], split[1]);
     }
 
     public static ProcessDefinition getLatestProcDefByKey(final DomainProcessEngine engine, final String key) {
@@ -111,17 +120,17 @@ public final class FlowableRuntimeUtils {
     }
 
     public static Set<String> getPerformedTasks(
-            final DomainProcessEngine engine, final String procInstID, final User user) {
+            final DomainProcessEngine engine, final String procInstId, final User user) {
 
         return engine.getHistoryService().createHistoricActivityInstanceQuery().
-                executionId(procInstID).
+                executionId(procInstId).
                 list().stream().
                 map(HistoricActivityInstance::getActivityId).
                 collect(Collectors.toSet());
     }
 
-    public static void updateStatus(final DomainProcessEngine engine, final String procInstID, final User user) {
-        List<Task> tasks = createTaskQuery(engine, false).processInstanceId(procInstID).list();
+    public static void updateStatus(final DomainProcessEngine engine, final String procInstId, final User user) {
+        List<Task> tasks = createTaskQuery(engine, false).processInstanceId(procInstId).list();
         if (tasks.isEmpty() || tasks.size() > 1) {
             LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
         } else {
@@ -129,13 +138,6 @@ public final class FlowableRuntimeUtils {
         }
     }
 
-    public static List<ProcessInstance> getProcessInstances(final DomainProcessEngine engine, final String userKey) {
-        return engine.getRuntimeService().createNativeProcessInstanceQuery().
-                sql("SELECT ID_,PROC_INST_ID_ FROM " + engine.getManagementService().getTableName(ExecutionEntity.class)
-                        + " WHERE BUSINESS_KEY_ LIKE '" + getProcBusinessKey("%", userKey) + "'"
-                        + " AND PARENT_ID_ IS NULL").list();
-    }
-
     public static TaskQuery createTaskQuery(final DomainProcessEngine engine, final boolean onlyFormTasks) {
         SyncopeTaskQueryImpl taskQuery = new SyncopeTaskQueryImpl(
                 ((RuntimeServiceImpl) engine.getRuntimeService()).getCommandExecutor());
@@ -145,10 +147,10 @@ public final class FlowableRuntimeUtils {
         return taskQuery;
     }
 
-    public static String getFormTask(final DomainProcessEngine engine, final String procInstID) {
+    public static String getFormTask(final DomainProcessEngine engine, final String procInstId) {
         String result = null;
 
-        List<Task> tasks = createTaskQuery(engine, true).processInstanceId(procInstID).list();
+        List<Task> tasks = createTaskQuery(engine, true).processInstanceId(procInstId).list();
         if (tasks.isEmpty() || tasks.size() > 1) {
             LOG.debug("While checking if form task: unexpected task number ({})", tasks.size());
         } else {
@@ -162,7 +164,7 @@ public final class FlowableRuntimeUtils {
      * Saves resources to be propagated and password for later - after form submission - propagation.
      *
      * @param engine Flowable engine
-     * @param procInstID process instance id
+     * @param procInstId process instance id
      * @param user user JPA entity
      * @param userTO user transfer object
      * @param password password
@@ -171,35 +173,35 @@ public final class FlowableRuntimeUtils {
      */
     public static void saveForFormSubmit(
             final DomainProcessEngine engine,
-            final String procInstID,
+            final String procInstId,
             final User user,
             final UserTO userTO,
             final String password,
             final Boolean enabled,
             final PropagationByResource propByRes) {
 
-        String formTaskId = getFormTask(engine, procInstID);
+        String formTaskId = getFormTask(engine, procInstId);
         if (formTaskId == null) {
             return;
         }
 
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.USER_TO, userTO);
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.USER_TO, userTO);
 
         if (password == null) {
             String encryptedPwd = engine.getRuntimeService().
-                    getVariable(procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
+                    getVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
             if (encryptedPwd != null) {
                 userTO.setPassword(decrypt(encryptedPwd));
             }
         } else {
             userTO.setPassword(password);
             engine.getRuntimeService().
-                    setVariable(procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD, encrypt(password));
+                    setVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD, encrypt(password));
         }
 
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.ENABLED, enabled);
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.ENABLED, enabled);
 
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.PROP_BY_RESOURCE, propByRes);
         if (propByRes != null) {
             propByRes.clear();
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
index 1c198a8..45a834b 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/impl/FlowableUserRequestHandler.java
@@ -31,13 +31,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.patch.PasswordPatch;
 import org.apache.syncope.common.lib.patch.UserPatch;
-import org.apache.syncope.common.lib.to.UserRequestTO;
+import org.apache.syncope.common.lib.to.UserRequest;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.to.UserRequestFormProperty;
 import org.apache.syncope.common.lib.to.UserRequestForm;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.UserRequestFormPropertyType;
+import org.apache.syncope.core.flowable.api.DropdownValueProvider;
 import org.apache.syncope.core.flowable.api.WorkflowTaskManager;
 import org.apache.syncope.core.flowable.support.DomainProcessEngine;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
@@ -49,16 +50,21 @@ import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.spring.BeanUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.workflow.api.WorkflowException;
 import org.flowable.common.engine.api.FlowableException;
+import org.flowable.common.engine.api.FlowableIllegalArgumentException;
 import org.flowable.engine.form.FormProperty;
 import org.flowable.engine.form.FormType;
 import org.flowable.engine.form.TaskFormData;
 import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
 import org.flowable.engine.impl.persistence.entity.HistoricFormPropertyEntity;
+import org.flowable.engine.runtime.NativeProcessInstanceQuery;
 import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.engine.runtime.ProcessInstanceQuery;
 import org.flowable.task.api.Task;
 import org.flowable.task.api.TaskQuery;
 import org.flowable.task.api.history.HistoricTaskInstance;
@@ -93,6 +99,93 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     @Autowired
     protected EntityFactory entityFactory;
 
+    protected NativeProcessInstanceQuery createProcessInstanceQuery(final String userKey) {
+        return engine.getRuntimeService().createNativeProcessInstanceQuery().
+                sql("SELECT DISTINCT ID_,BUSINESS_KEY_,ACT_ID_ FROM "
+                        + engine.getManagementService().getTableName(ExecutionEntity.class)
+                        + " WHERE BUSINESS_KEY_ LIKE '"
+                        + FlowableRuntimeUtils.getProcBusinessKey("%", userKey) + "'"
+                        + " AND BUSINESS_KEY_ NOT LIKE '"
+                        + FlowableRuntimeUtils.getProcBusinessKey(FlowableRuntimeUtils.WF_PROCESS_ID, "%") + "'"
+                        + " AND PARENT_ID_ IS NULL");
+    }
+
+    protected int countProcessInstances(final String userKey) {
+        return (int) engine.getRuntimeService().createNativeProcessInstanceQuery().
+                sql("SELECT COUNT(ID_) FROM "
+                        + engine.getManagementService().getTableName(ExecutionEntity.class)
+                        + " WHERE BUSINESS_KEY_ LIKE '"
+                        + FlowableRuntimeUtils.getProcBusinessKey("%", userKey) + "'"
+                        + " AND BUSINESS_KEY_ NOT LIKE '"
+                        + FlowableRuntimeUtils.getProcBusinessKey(FlowableRuntimeUtils.WF_PROCESS_ID, "%") + "'"
+                        + " AND PARENT_ID_ IS NULL").count();
+    }
+
+    protected UserRequest getUserRequest(final ProcessInstance procInst) {
+        Pair<String, String> split = FlowableRuntimeUtils.splitProcBusinessKey(procInst.getBusinessKey());
+
+        UserRequest userRequest = new UserRequest();
+        userRequest.setBpmnProcess(split.getLeft());
+        userRequest.setUser(split.getRight());
+        userRequest.setExecutionId(procInst.getId());
+        userRequest.setActivityId(procInst.getActivityId());
+        return userRequest;
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public Pair<Integer, List<UserRequest>> getUserRequests(
+            final String userKey,
+            final int page,
+            final int size,
+            final List<OrderByClause> orderByClauses) {
+
+        Integer count = null;
+        List<UserRequest> result = null;
+        if (userKey == null) {
+            ProcessInstanceQuery query = engine.getRuntimeService().createProcessInstanceQuery().active();
+            for (OrderByClause clause : orderByClauses) {
+                boolean sorted = true;
+                switch (clause.getField().trim()) {
+                    case "processDefinitionId":
+                        query.orderByProcessDefinitionId();
+                        break;
+
+                    case "processDefinitionKey":
+                        query.orderByProcessDefinitionKey();
+                        break;
+
+                    case "processInstanceId":
+                        query.orderByProcessInstanceId();
+                        break;
+
+                    default:
+                        LOG.warn("User request sort request by {}: unsupported, ignoring", clause.getField().trim());
+                        sorted = false;
+                }
+                if (sorted) {
+                    if (clause.getDirection() == OrderByClause.Direction.ASC) {
+                        query.asc();
+                    } else {
+                        query.desc();
+                    }
+                }
+
+                count = (int) query.count();
+                result = query.listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
+                        map(procInst -> getUserRequest(procInst)).
+                        collect(Collectors.toList());
+            }
+        } else {
+            count = countProcessInstances(userKey);
+            result = createProcessInstanceQuery(userKey).listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
+                    map(procInst -> getUserRequest(procInst)).
+                    collect(Collectors.toList());
+        }
+
+        return Pair.of(count, result);
+    }
+
     protected User lazyLoad(final User user) {
         // using BeanUtils to access all user's properties and trigger lazy loading - we are about to
         // serialize a User instance for availability within workflow tasks, and this breaks transactions
@@ -101,8 +194,8 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     }
 
     @Override
-    public UserRequestTO start(final String bpmnProcess, final User user) {
-        Map<String, Object> variables = new HashMap<>(2);
+    public UserRequest start(final String bpmnProcess, final User user) {
+        Map<String, Object> variables = new HashMap<>();
         variables.put(FlowableRuntimeUtils.WF_EXECUTOR, AuthContextUtils.getUsername());
         variables.put(FlowableRuntimeUtils.USER, lazyLoad(user));
         variables.put(FlowableRuntimeUtils.USER_TO, dataBinder.getUserTO(user, true));
@@ -118,12 +211,8 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
                 procInst.getProcessInstanceId(),
                 FlowableRuntimeUtils.getProcBusinessKey(bpmnProcess, user.getKey()));
 
-        UserRequestTO userRequestTO = new UserRequestTO();
-        userRequestTO.setProcessInstanceId(procInst.getProcessInstanceId());
-        userRequestTO.setExecutionId(procInst.getId());
-        userRequestTO.setBpmnProcess(bpmnProcess);
-        userRequestTO.setUser(user.getKey());
-        return userRequestTO;
+        return getUserRequest(engine.getRuntimeService().createProcessInstanceQuery().
+                processInstanceId(procInst.getProcessInstanceId()).singleResult());
     }
 
     @Override
@@ -132,7 +221,11 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         try {
             procInst = engine.getRuntimeService().
                     createProcessInstanceQuery().processInstanceId(executionId).singleResult();
+            if (procInst == null) {
+                throw new FlowableIllegalArgumentException("ProcessInstance with id " + executionId);
+            }
         } catch (FlowableException e) {
+            LOG.error("Could find execution ProcessInstance with id {}", executionId, e);
             throw new NotFoundException("User request execution with id " + executionId);
         }
 
@@ -163,7 +256,7 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     public void cancelByUser(final AnyDeletedEvent event) {
         if (AuthContextUtils.getDomain().equals(event.getDomain()) && event.getAnyTypeKind() == AnyTypeKind.USER) {
             String username = event.getAnyName();
-            FlowableRuntimeUtils.getProcessInstances(engine, event.getAnyKey()).
+            createProcessInstanceQuery(event.getAnyKey()).list().
                     forEach(procInst -> {
                         engine.getRuntimeService().deleteProcessInstance(
                                 procInst.getId(), "Cascade Delete user " + username);
@@ -192,6 +285,10 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
                     result = UserRequestFormPropertyType.Boolean;
                     break;
 
+                case "dropdown":
+                    result = UserRequestFormPropertyType.Dropdown;
+                    break;
+
                 case "string":
                 default:
                     break;
@@ -201,19 +298,19 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         return result;
     }
 
-    protected UserRequestForm getFormTO(final Task task) {
-        return getFormTO(task, engine.getFormService().getTaskFormData(task.getId()));
+    protected UserRequestForm getForm(final Task task) {
+        return FlowableUserRequestHandler.this.getForm(task, engine.getFormService().getTaskFormData(task.getId()));
     }
 
-    protected UserRequestForm getFormTO(final Task task, final TaskFormData fd) {
+    protected UserRequestForm getForm(final Task task, final TaskFormData fd) {
         UserRequestForm formTO =
-                getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
+                getForm(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
         BeanUtils.copyProperties(task, formTO);
 
         return formTO;
     }
 
-    protected UserRequestForm getFormTO(final HistoricTaskInstance task) {
+    protected UserRequestForm getForm(final HistoricTaskInstance task) {
         List<HistoricFormPropertyEntity> props = engine.getHistoryService().
                 createHistoricDetailQuery().taskId(task.getId()).list().stream().
                 filter(HistoricFormPropertyEntity.class::isInstance).
@@ -244,16 +341,19 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     }
 
     protected UserRequestForm getHistoricFormTO(
-            final String procInstID,
+            final String procInstId,
             final String taskId,
             final String formKey,
             final List<HistoricFormPropertyEntity> props) {
 
         UserRequestForm formTO = new UserRequestForm();
 
-        User user = userDAO.find(getUserKey(procInstID));
+        formTO.setBpmnProcess(engine.getRuntimeService().createProcessInstanceQuery().
+                processInstanceId(procInstId).singleResult().getProcessDefinitionKey());
+
+        User user = userDAO.find(getUserKey(procInstId));
         if (user == null) {
-            throw new NotFoundException("User for process instance id " + procInstID);
+            throw new NotFoundException("User for process instance id " + procInstId);
         }
         formTO.setUsername(user.getUsername());
 
@@ -261,9 +361,9 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         formTO.setFormKey(formKey);
 
         formTO.setUserTO(engine.getRuntimeService().
-                getVariable(procInstID, FlowableRuntimeUtils.USER_TO, UserTO.class));
+                getVariable(procInstId, FlowableRuntimeUtils.USER_TO, UserTO.class));
         formTO.setUserPatch(engine.getRuntimeService().
-                getVariable(procInstID, FlowableRuntimeUtils.USER_PATCH, UserPatch.class));
+                getVariable(procInstId, FlowableRuntimeUtils.USER_PATCH, UserPatch.class));
 
         formTO.getProperties().addAll(props.stream().map(prop -> {
             UserRequestFormProperty propertyTO = new UserRequestFormProperty();
@@ -277,37 +377,58 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     }
 
     @SuppressWarnings("unchecked")
-    protected UserRequestForm getFormTO(
-            final String procInstID,
+    protected UserRequestForm getForm(
+            final String procInstId,
             final String taskId,
             final String formKey,
             final List<FormProperty> props) {
 
         UserRequestForm formTO = new UserRequestForm();
 
-        User user = userDAO.find(getUserKey(procInstID));
+        formTO.setBpmnProcess(engine.getRuntimeService().createProcessInstanceQuery().
+                processInstanceId(procInstId).singleResult().getProcessDefinitionKey());
+
+        User user = userDAO.find(getUserKey(procInstId));
         if (user == null) {
-            throw new NotFoundException("User for process instance id " + procInstID);
+            throw new NotFoundException("User for process instance id " + procInstId);
         }
         formTO.setUsername(user.getUsername());
 
+        formTO.setExecutionId(procInstId);
         formTO.setTaskId(taskId);
         formTO.setFormKey(formKey);
 
         formTO.setUserTO(engine.getRuntimeService().
-                getVariable(procInstID, FlowableRuntimeUtils.USER_TO, UserTO.class));
+                getVariable(procInstId, FlowableRuntimeUtils.USER_TO, UserTO.class));
         formTO.setUserPatch(engine.getRuntimeService().
-                getVariable(procInstID, FlowableRuntimeUtils.USER_PATCH, UserPatch.class));
+                getVariable(procInstId, FlowableRuntimeUtils.USER_PATCH, UserPatch.class));
 
         formTO.getProperties().addAll(props.stream().map(fProp -> {
             UserRequestFormProperty propertyTO = new UserRequestFormProperty();
             BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS);
             propertyTO.setType(fromFlowableFormType(fProp.getType()));
-            if (propertyTO.getType() == UserRequestFormPropertyType.Date) {
-                propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
-            }
-            if (propertyTO.getType() == UserRequestFormPropertyType.Enum) {
-                propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
+            switch (propertyTO.getType()) {
+                case Date:
+                    propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
+                    break;
+
+                case Enum:
+                    propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
+                    break;
+
+                case Dropdown:
+                    String valueProviderBean = (String) fProp.getType().getInformation(DropdownValueProvider.NAME);
+                    try {
+                        DropdownValueProvider valueProvider = ApplicationContextProvider.getApplicationContext().
+                                getBean(valueProviderBean, DropdownValueProvider.class);
+                        propertyTO.getDropdownValues().putAll(valueProvider.getValues());
+                    } catch (Exception e) {
+                        LOG.error("Could not find bean {} of type {} for form property {}",
+                                valueProviderBean, DropdownValueProvider.class.getName(), propertyTO.getId(), e);
+                    }
+                    break;
+
+                default:
             }
             return propertyTO;
         }).collect(Collectors.toList()));
@@ -318,26 +439,28 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     @Transactional(readOnly = true)
     @Override
     public Pair<Integer, List<UserRequestForm>> getForms(
-            final int page, final int size, final List<OrderByClause> orderByClauses) {
+            final String userKey,
+            final int page,
+            final int size,
+            final List<OrderByClause> orderByClauses) {
 
-        Pair<Integer, List<UserRequestForm>> forms = null;
+        Pair<Integer, List<UserRequestForm>> forms;
 
-        TaskQuery formTaskQuery = FlowableRuntimeUtils.createTaskQuery(engine, true);
+        TaskQuery query = FlowableRuntimeUtils.createTaskQuery(engine, true);
+        if (userKey != null) {
+            query.processInstanceBusinessKeyLike(FlowableRuntimeUtils.getProcBusinessKey("%", userKey));
+        }
 
         String authUser = AuthContextUtils.getUsername();
         if (adminUser.equals(authUser)) {
-            forms = getForms(formTaskQuery, page, size, orderByClauses);
+            forms = getForms(query, page, size, orderByClauses);
         } else {
             User user = userDAO.findByUsername(authUser);
-            if (user == null) {
-                throw new NotFoundException("Syncope User " + authUser);
-            }
-
-            forms = getForms(formTaskQuery.taskCandidateOrAssigned(user.getUsername()), page, size, orderByClauses);
+            forms = getForms(query.taskCandidateOrAssigned(user.getUsername()), page, size, orderByClauses);
 
             List<String> candidateGroups = new ArrayList<>(userDAO.findAllGroupNames(user));
             if (!candidateGroups.isEmpty()) {
-                forms = getForms(formTaskQuery.taskCandidateGroupIn(candidateGroups), page, size, orderByClauses);
+                forms = getForms(query.taskCandidateGroupIn(candidateGroups), page, size, orderByClauses);
             }
         }
 
@@ -349,75 +472,56 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
     protected Pair<Integer, List<UserRequestForm>> getForms(
             final TaskQuery query, final int page, final int size, final List<OrderByClause> orderByClauses) {
 
-        TaskQuery sortedQuery = query;
         for (OrderByClause clause : orderByClauses) {
-            boolean ack = true;
+            boolean sorted = true;
             switch (clause.getField().trim()) {
+                case "bpmnProcess":
+                    query.orderByProcessDefinitionId();
+                    break;
+
+                case "executionId":
+                    query.orderByExecutionId();
+                    break;
+
                 case "taskId":
-                    sortedQuery = sortedQuery.orderByTaskId();
+                    query.orderByTaskId();
                     break;
 
                 case "createTime":
-                    sortedQuery = sortedQuery.orderByTaskCreateTime();
+                    query.orderByTaskCreateTime();
                     break;
 
                 case "dueDate":
-                    sortedQuery = sortedQuery.orderByTaskDueDate();
+                    query.orderByTaskDueDate();
                     break;
 
                 case "owner":
-                    sortedQuery = sortedQuery.orderByTaskOwner();
+                    query.orderByTaskOwner();
                     break;
 
                 default:
                     LOG.warn("Form sort request by {}: unsupported, ignoring", clause.getField().trim());
-                    ack = false;
+                    sorted = false;
             }
-            if (ack) {
-                sortedQuery = clause.getDirection() == OrderByClause.Direction.ASC
-                        ? sortedQuery.asc()
-                        : sortedQuery.desc();
+            if (sorted) {
+                if (clause.getDirection() == OrderByClause.Direction.ASC) {
+                    query.asc();
+                } else {
+                    query.desc();
+                }
             }
         }
 
-        List<UserRequestForm> result = sortedQuery.listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
+        List<UserRequestForm> result = query.listPage(size * (page <= 0 ? 0 : page - 1), size).stream().
                 map(task -> task instanceof HistoricTaskInstance
-                ? getFormTO((HistoricTaskInstance) task) : getFormTO(task)).
+                ? FlowableUserRequestHandler.this.getForm((HistoricTaskInstance) task)
+                : FlowableUserRequestHandler.this.getForm(task)).
                 collect(Collectors.toList());
 
         return Pair.of((int) query.count(), result);
     }
 
-    @Override
-    public List<UserRequestForm> getForms(final String userKey) {
-        List<UserRequestForm> result = new ArrayList<>();
-        FlowableRuntimeUtils.getProcessInstances(engine, userKey).forEach(procInst -> {
-            Task task;
-            try {
-                task = FlowableRuntimeUtils.createTaskQuery(engine, true).
-                        processInstanceId(procInst.getProcessInstanceId()).singleResult();
-            } catch (FlowableException e) {
-                throw new WorkflowException("While reading form for process instance "
-                        + procInst.getProcessInstanceId(), e);
-            }
-
-            if (task != null) {
-                TaskFormData formData;
-                try {
-                    formData = engine.getFormService().getTaskFormData(task.getId());
-                } catch (FlowableException e) {
-                    throw new WorkflowException("Error while getting form data for task " + task.getId(), e);
-                }
-                if (formData != null && !formData.getFormProperties().isEmpty()) {
-                    result.add(getFormTO(task, formData));
-                }
-            }
-        });
-
-        return result;
-    }
-
-    protected Pair<Task, TaskFormData> checkTask(final String taskId, final String authUser) {
+    protected Pair<Task, TaskFormData> parseTask(final String taskId) {
         Task task;
         try {
             task = FlowableRuntimeUtils.createTaskQuery(engine, true).taskId(taskId).singleResult();
@@ -435,27 +539,20 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
             throw new NotFoundException("Form for Flowable Task " + taskId, e);
         }
 
-        if (!adminUser.equals(authUser)) {
-            User user = userDAO.findByUsername(authUser);
-            if (user == null) {
-                throw new NotFoundException("Syncope User " + authUser);
-            }
-        }
-
         return Pair.of(task, formData);
     }
 
     @Override
     public UserRequestForm claimForm(final String taskId) {
-        String authUser = AuthContextUtils.getUsername();
-        Pair<Task, TaskFormData> checked = checkTask(taskId, authUser);
+        Pair<Task, TaskFormData> parsed = parseTask(taskId);
 
+        String authUser = AuthContextUtils.getUsername();
         if (!adminUser.equals(authUser)) {
             List<Task> tasksForUser = FlowableRuntimeUtils.createTaskQuery(engine, true).
-                    taskId(taskId).taskCandidateUser(authUser).list();
+                    taskId(taskId).taskCandidateOrAssigned(authUser).list();
             if (tasksForUser.isEmpty()) {
                 throw new WorkflowException(
-                        new IllegalArgumentException(authUser + " is not candidate for task " + taskId));
+                        new IllegalArgumentException(authUser + " is not candidate nor assignee of task " + taskId));
             }
         }
 
@@ -467,7 +564,7 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
             throw new WorkflowException("While reading task " + taskId, e);
         }
 
-        return getFormTO(task, checked.getRight());
+        return FlowableUserRequestHandler.this.getForm(task, parsed.getRight());
     }
 
     private Map<String, String> getPropertiesForSubmit(final UserRequestForm form) {
@@ -482,36 +579,36 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
 
     @Override
     public WorkflowResult<UserPatch> submitForm(final UserRequestForm form) {
-        String authUser = AuthContextUtils.getUsername();
-        Pair<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser);
+        Pair<Task, TaskFormData> parsed = parseTask(form.getTaskId());
 
-        if (!checked.getLeft().getOwner().equals(authUser)) {
+        String authUser = AuthContextUtils.getUsername();
+        if (!parsed.getLeft().getOwner().equals(authUser)) {
             throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
-                    + checked.getLeft().getOwner() + " but submitted by " + authUser));
+                    + parsed.getLeft().getOwner() + " but submitted by " + authUser));
         }
 
-        String procInstID = checked.getLeft().getProcessInstanceId();
+        String procInstId = parsed.getLeft().getProcessInstanceId();
 
-        User user = userDAO.find(getUserKey(procInstID));
+        User user = userDAO.find(getUserKey(procInstId));
         if (user == null) {
-            throw new NotFoundException("User with key " + getUserKey(procInstID));
+            throw new NotFoundException("User with key " + getUserKey(procInstId));
         }
 
-        Set<String> preTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID, user);
+        Set<String> preTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstId, user);
 
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.TASK, "submit");
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.FORM_SUBMITTER, authUser);
-        engine.getRuntimeService().setVariable(procInstID, FlowableRuntimeUtils.USER, lazyLoad(user));
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.TASK, "submit");
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.FORM_SUBMITTER, authUser);
+        engine.getRuntimeService().setVariable(procInstId, FlowableRuntimeUtils.USER, lazyLoad(user));
         try {
             engine.getFormService().submitTaskFormData(form.getTaskId(), getPropertiesForSubmit(form));
         } catch (FlowableException e) {
             FlowableRuntimeUtils.throwException(e, "While submitting form for task " + form.getTaskId());
         }
-        Set<String> postTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstID, user);
+        Set<String> postTasks = FlowableRuntimeUtils.getPerformedTasks(engine, procInstId, user);
         postTasks.removeAll(preTasks);
         postTasks.add(form.getTaskId());
-        if (procInstID.equals(FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey()))) {
-            FlowableRuntimeUtils.updateStatus(engine, procInstID, user);
+        if (procInstId.equals(FlowableRuntimeUtils.getWFProcInstID(engine, user.getKey()))) {
+            FlowableRuntimeUtils.updateStatus(engine, procInstId, user);
         }
 
         user = userDAO.save(user);
@@ -521,34 +618,34 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
         PropagationByResource propByRes = null;
 
         ProcessInstance afterSubmitPI = engine.getRuntimeService().
-                createProcessInstanceQuery().processInstanceId(procInstID).singleResult();
+                createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
         if (afterSubmitPI != null) {
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.TASK);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.FORM_SUBMITTER);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_TO);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.TASK);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.FORM_SUBMITTER);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.USER);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.USER_TO);
 
             // see if there is any propagation to be done
             propByRes = engine.getRuntimeService().
-                    getVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.PROP_BY_RESOURCE);
+                    getVariable(procInstId, FlowableRuntimeUtils.PROP_BY_RESOURCE, PropagationByResource.class);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.PROP_BY_RESOURCE);
 
             // fetch - if available - the encrypted password
             String encryptedPwd = engine.getRuntimeService().
-                    getVariable(procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.ENCRYPTED_PWD);
+                    getVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD, String.class);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.ENCRYPTED_PWD);
             if (StringUtils.isNotBlank(encryptedPwd)) {
                 clearPassword = FlowableRuntimeUtils.decrypt(encryptedPwd);
             }
 
             Boolean enabled = engine.getRuntimeService().
-                    getVariable(procInstID, FlowableRuntimeUtils.ENABLED, Boolean.class);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.ENABLED);
+                    getVariable(procInstId, FlowableRuntimeUtils.ENABLED, Boolean.class);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.ENABLED);
 
             // supports approval chains
             FlowableRuntimeUtils.saveForFormSubmit(
                     engine,
-                    procInstID,
+                    procInstId,
                     user,
                     dataBinder.getUserTO(user, true),
                     clearPassword,
@@ -556,8 +653,8 @@ public class FlowableUserRequestHandler implements UserRequestHandler {
                     propByRes);
 
             userPatch = engine.getRuntimeService().
-                    getVariable(procInstID, FlowableRuntimeUtils.USER_PATCH, UserPatch.class);
-            engine.getRuntimeService().removeVariable(procInstID, FlowableRuntimeUtils.USER_PATCH);
+                    getVariable(procInstId, FlowableRuntimeUtils.USER_PATCH, UserPatch.class);
+            engine.getRuntimeService().removeVariable(procInstId, FlowableRuntimeUtils.USER_PATCH);
         }
         if (userPatch == null) {
             userPatch = new UserPatch();

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java
index 162b113..de2bbfe 100644
--- a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DomainProcessEngineFactoryBean.java
@@ -18,13 +18,16 @@
  */
 package org.apache.syncope.core.flowable.support;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import javax.sql.DataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.flowable.engine.ProcessEngine;
 import org.flowable.common.engine.impl.cfg.SpringBeanFactoryProxyMap;
 import org.flowable.common.engine.impl.interceptor.EngineConfigurationConstants;
+import org.flowable.engine.form.AbstractFormType;
 import org.flowable.engine.impl.util.EngineServiceUtil;
 import org.flowable.idm.spring.SpringIdmEngineConfiguration;
 import org.flowable.spring.SpringExpressionManager;
@@ -81,6 +84,9 @@ public class DomainProcessEngineFactoryBean
                                 EngineConfigurationConstants.KEY_IDM_ENGINE_CONFIG,
                                 ctx.getBean(SpringIdmEngineConfiguration.class));
                     }
+                    List<AbstractFormType> customFormTypes = new ArrayList<>();
+                    customFormTypes.add(new DropdownFormType(null));
+                    conf.setCustomFormTypes(customFormTypes);
 
                     engines.put(domain, conf.buildProcessEngine());
                 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareJsonConverter.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareJsonConverter.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareJsonConverter.java
new file mode 100644
index 0000000..ad29e17
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareJsonConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.core.flowable.support;
+
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.editor.constants.StencilConstants;
+import org.flowable.editor.language.json.converter.BpmnJsonConverter;
+
+public class DropdownAwareJsonConverter extends BpmnJsonConverter {
+
+    public DropdownAwareJsonConverter() {
+        convertersToBpmnMap.put(StencilConstants.STENCIL_TASK_USER, DropdownAwareUserTaskJsonConverter.class);
+        convertersToJsonMap.put(UserTask.class, DropdownAwareUserTaskJsonConverter.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareUserTaskJsonConverter.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareUserTaskJsonConverter.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareUserTaskJsonConverter.java
new file mode 100644
index 0000000..838201c
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownAwareUserTaskJsonConverter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.core.flowable.support;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.flowable.bpmn.model.BaseElement;
+import org.flowable.bpmn.model.FormProperty;
+import org.flowable.bpmn.model.FormValue;
+import org.flowable.bpmn.model.StartEvent;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.editor.language.json.converter.BpmnJsonConverterUtil;
+import org.flowable.editor.language.json.converter.UserTaskJsonConverter;
+
+public class DropdownAwareUserTaskJsonConverter extends UserTaskJsonConverter {
+
+    @Override
+    protected void convertJsonToFormProperties(final JsonNode objectNode, final BaseElement element) {
+        JsonNode formPropertiesNode = getProperty(PROPERTY_FORM_PROPERTIES, objectNode);
+        if (formPropertiesNode != null) {
+            formPropertiesNode = BpmnJsonConverterUtil.validateIfNodeIsTextual(formPropertiesNode);
+            JsonNode propertiesArray = formPropertiesNode.get("formProperties");
+            if (propertiesArray != null) {
+                for (JsonNode formNode : propertiesArray) {
+                    JsonNode formIdNode = formNode.get(PROPERTY_FORM_ID);
+                    if (formIdNode != null && StringUtils.isNotEmpty(formIdNode.asText())) {
+
+                        FormProperty formProperty = new FormProperty();
+                        formProperty.setId(formIdNode.asText());
+                        formProperty.setName(getValueAsString(PROPERTY_FORM_NAME, formNode));
+                        formProperty.setType(getValueAsString(PROPERTY_FORM_TYPE, formNode));
+                        formProperty.setExpression(getValueAsString(PROPERTY_FORM_EXPRESSION, formNode));
+                        formProperty.setVariable(getValueAsString(PROPERTY_FORM_VARIABLE, formNode));
+
+                        if ("date".equalsIgnoreCase(formProperty.getType())) {
+                            formProperty.setDatePattern(getValueAsString(PROPERTY_FORM_DATE_PATTERN, formNode));
+
+                        } else if ("enum".equalsIgnoreCase(formProperty.getType())
+                                || "dropdown".equalsIgnoreCase(formProperty.getType())) {
+
+                            JsonNode enumValuesNode = formNode.get(PROPERTY_FORM_ENUM_VALUES);
+                            if (enumValuesNode != null) {
+                                List<FormValue> formValueList = new ArrayList<>();
+                                for (JsonNode enumNode : enumValuesNode) {
+                                    if (enumNode.get(PROPERTY_FORM_ENUM_VALUES_ID) != null && !enumNode.get(
+                                            PROPERTY_FORM_ENUM_VALUES_ID).isNull() && enumNode.get(
+                                                    PROPERTY_FORM_ENUM_VALUES_NAME) != null
+                                            && !enumNode.get(PROPERTY_FORM_ENUM_VALUES_NAME).isNull()) {
+
+                                        FormValue formValue = new FormValue();
+                                        formValue.setId(enumNode.get(PROPERTY_FORM_ENUM_VALUES_ID).asText());
+                                        formValue.setName(enumNode.get(PROPERTY_FORM_ENUM_VALUES_NAME).asText());
+                                        formValueList.add(formValue);
+
+                                    } else if (enumNode.get("value") != null && !enumNode.get("value").isNull()) {
+                                        FormValue formValue = new FormValue();
+                                        formValue.setId(enumNode.get("value").asText());
+                                        formValue.setName(enumNode.get("value").asText());
+                                        formValueList.add(formValue);
+                                    }
+                                }
+                                formProperty.setFormValues(formValueList);
+                            }
+                        }
+
+                        formProperty.setRequired(getValueAsBoolean(PROPERTY_FORM_REQUIRED, formNode));
+                        formProperty.setReadable(getValueAsBoolean(PROPERTY_FORM_READABLE, formNode));
+                        formProperty.setWriteable(getValueAsBoolean(PROPERTY_FORM_WRITABLE, formNode));
+
+                        if (element instanceof StartEvent) {
+                            ((StartEvent) element).getFormProperties().add(formProperty);
+                        } else if (element instanceof UserTask) {
+                            ((UserTask) element).getFormProperties().add(formProperty);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownFormType.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownFormType.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownFormType.java
new file mode 100644
index 0000000..6c612f6
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/DropdownFormType.java
@@ -0,0 +1,59 @@
+/*
+ * 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.core.flowable.support;
+
+import org.flowable.engine.form.AbstractFormType;
+
+/**
+ * Extension to predefined Flowable form types relying on the provided
+ * {@link org.apache.syncope.core.flowable.api.DropdownValueProvider} bean to populate values.
+ */
+public class DropdownFormType extends AbstractFormType {
+
+    private static final long serialVersionUID = -3549337216346168946L;
+
+    protected final String dropdownValueProvider;
+
+    public DropdownFormType(final String dropdownValueProvider) {
+        this.dropdownValueProvider = dropdownValueProvider;
+    }
+
+    @Override
+    public String getName() {
+        return "dropdown";
+    }
+
+    @Override
+    public Object getInformation(final String key) {
+        if (key.equals("dropdownValueProvider")) {
+            return dropdownValueProvider;
+        }
+        return null;
+    }
+
+    @Override
+    public Object convertFormValueToModelValue(final String propertyValue) {
+        return propertyValue;
+    }
+
+    @Override
+    public String convertModelValueToFormValue(final Object modelValue) {
+        return modelValue == null ? null : modelValue.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeFormHandlerHelper.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeFormHandlerHelper.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeFormHandlerHelper.java
new file mode 100644
index 0000000..9f6e751
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeFormHandlerHelper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.core.flowable.support;
+
+import org.flowable.bpmn.model.FlowElement;
+import org.flowable.bpmn.model.UserTask;
+import org.flowable.bpmn.model.Process;
+import org.flowable.engine.impl.form.FormHandlerHelper;
+import org.flowable.engine.impl.form.TaskFormHandler;
+import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.impl.util.ProcessDefinitionUtil;
+import org.flowable.engine.repository.ProcessDefinition;
+
+/**
+ * Used to inject {@link SyncopeTaskFormHandler} rather than
+ * {@link org.flowable.engine.impl.form.DefaultTaskFormHandler}.
+ */
+public class SyncopeFormHandlerHelper extends FormHandlerHelper {
+
+    @Override
+    public TaskFormHandler getTaskFormHandlder(final String procDefId, final String taskId) {
+        Process process = ProcessDefinitionUtil.getProcess(procDefId);
+        FlowElement flowElement = process.getFlowElement(taskId, true);
+        if (flowElement instanceof UserTask) {
+            UserTask userTask = (UserTask) flowElement;
+
+            ProcessDefinition processDefinitionEntity = ProcessDefinitionUtil.getProcessDefinition(procDefId);
+            DeploymentEntity deploymentEntity = CommandContextUtil.getProcessEngineConfiguration().
+                    getDeploymentEntityManager().findById(processDefinitionEntity.getDeploymentId());
+
+            TaskFormHandler taskFormHandler = new SyncopeTaskFormHandler();
+            taskFormHandler.parseConfiguration(
+                    userTask.getFormProperties(), userTask.getFormKey(), deploymentEntity, processDefinitionEntity);
+            return taskFormHandler;
+        }
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandler.java
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandler.java b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandler.java
new file mode 100644
index 0000000..69c5e50
--- /dev/null
+++ b/ext/flowable/flowable-bpmn/src/main/java/org/apache/syncope/core/flowable/support/SyncopeTaskFormHandler.java
@@ -0,0 +1,112 @@
+/*
+ * 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.core.flowable.support;
+
+import java.util.List;
+import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.flowable.api.DropdownValueProvider;
+import org.flowable.bpmn.model.FormProperty;
+import org.flowable.common.engine.api.delegate.Expression;
+import org.flowable.common.engine.impl.el.ExpressionManager;
+import org.flowable.engine.form.AbstractFormType;
+import org.flowable.engine.impl.form.DefaultTaskFormHandler;
+import org.flowable.engine.impl.form.FormPropertyHandler;
+import org.flowable.engine.impl.form.FormTypes;
+import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
+import org.flowable.engine.impl.util.CommandContextUtil;
+import org.flowable.engine.repository.ProcessDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends {@link DefaultTaskFormHandler} with purpose of supporting more form types than Flowable's default.
+ */
+public class SyncopeTaskFormHandler extends DefaultTaskFormHandler {
+
+    private static final long serialVersionUID = -5271243544388455797L;
+
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeTaskFormHandler.class);
+
+    protected Optional<AbstractFormType> parseFormPropertyType(
+            final FormProperty formProperty, final ExpressionManager expressionManager) {
+
+        AbstractFormType formType = null;
+
+        switch (formProperty.getType()) {
+            case "dropdown":
+                if (formProperty.getFormValues().isEmpty()
+                        || !DropdownValueProvider.NAME.equals(formProperty.getFormValues().get(0).getId())) {
+
+                    LOG.warn("A single value with id '" + DropdownValueProvider.NAME + "' was expected, ignoring");
+                } else {
+                    formType = new DropdownFormType(formProperty.getFormValues().get(0).getName());
+                }
+                break;
+
+            default:
+        }
+
+        return Optional.ofNullable(formType);
+    }
+
+    @Override
+    public void parseConfiguration(
+            final List<FormProperty> formProperties,
+            final String formKey,
+            final DeploymentEntity deployment,
+            final ProcessDefinition processDefinition) {
+
+        this.deploymentId = deployment.getId();
+
+        ExpressionManager expressionManager = CommandContextUtil.getProcessEngineConfiguration().getExpressionManager();
+
+        if (StringUtils.isNotEmpty(formKey)) {
+            this.formKey = expressionManager.createExpression(formKey);
+        }
+
+        FormTypes formTypes = CommandContextUtil.getProcessEngineConfiguration().getFormTypes();
+
+        formProperties.forEach(formProperty -> {
+            FormPropertyHandler formPropertyHandler = new FormPropertyHandler();
+            formPropertyHandler.setId(formProperty.getId());
+            formPropertyHandler.setName(formProperty.getName());
+
+            AbstractFormType type = parseFormPropertyType(formProperty, expressionManager).
+                    orElse(formTypes.parseFormPropertyType(formProperty));
+            formPropertyHandler.setType(type);
+            formPropertyHandler.setRequired(formProperty.isRequired());
+            formPropertyHandler.setReadable(formProperty.isReadable());
+            formPropertyHandler.setWritable(formProperty.isWriteable());
+            formPropertyHandler.setVariableName(formProperty.getVariable());
+
+            if (StringUtils.isNotEmpty(formProperty.getExpression())) {
+                Expression expression = expressionManager.createExpression(formProperty.getExpression());
+                formPropertyHandler.setVariableExpression(expression);
+            }
+
+            if (StringUtils.isNotEmpty(formProperty.getDefaultExpression())) {
+                Expression defaultExpression = expressionManager.createExpression(formProperty.getDefaultExpression());
+                formPropertyHandler.setDefaultExpression(defaultExpression);
+            }
+
+            formPropertyHandlers.add(formPropertyHandler);
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/flowable-bpmn/src/main/resources/workflowFlowableContext.xml
----------------------------------------------------------------------
diff --git a/ext/flowable/flowable-bpmn/src/main/resources/workflowFlowableContext.xml b/ext/flowable/flowable-bpmn/src/main/resources/workflowFlowableContext.xml
index 9e27f47..4f34237 100644
--- a/ext/flowable/flowable-bpmn/src/main/resources/workflowFlowableContext.xml
+++ b/ext/flowable/flowable-bpmn/src/main/resources/workflowFlowableContext.xml
@@ -38,6 +38,8 @@ under the License.
     <property name="idmEngineConfiguration" ref="syncopeIdmEngineConfiguration"/> 
   </bean>
 
+  <bean id="syncopeFormHandlerHelper" class="org.apache.syncope.core.flowable.support.SyncopeFormHandlerHelper"/>
+
   <bean class="org.apache.syncope.core.flowable.support.DomainProcessEngineConfiguration" scope="prototype">
     <property name="databaseSchemaUpdate" value="true"/>
 
@@ -52,6 +54,8 @@ under the License.
         <bean class="org.apache.syncope.core.flowable.support.SyncopeEntitiesVariableType"/>
       </list>
     </property>
+    
+    <property name="formHandlerHelper" ref="syncopeFormHandlerHelper"/>
   </bean>
 
   <bean id="bpmnProcessManager" class="org.apache.syncope.core.flowable.impl.FlowableBpmnProcessManager"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/6f6d9156/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/BpmnProcessLogic.java
----------------------------------------------------------------------
diff --git a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/BpmnProcessLogic.java b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/BpmnProcessLogic.java
index 8d280ac..ad76605 100644
--- a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/BpmnProcessLogic.java
+++ b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/BpmnProcessLogic.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.logic;
 import java.io.OutputStream;
 import java.lang.reflect.Method;
 import java.util.List;
-import org.apache.syncope.common.lib.to.BpmnProcessTO;
+import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.common.lib.types.FlowableEntitlement;
 import org.apache.syncope.common.lib.types.BpmnProcessFormat;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -31,14 +31,14 @@ import org.springframework.transaction.annotation.Transactional;
 import org.apache.syncope.core.flowable.api.BpmnProcessManager;
 
 @Component
-public class BpmnProcessLogic extends AbstractTransactionalLogic<BpmnProcessTO> {
+public class BpmnProcessLogic extends AbstractTransactionalLogic<BpmnProcess> {
 
     @Autowired
     private BpmnProcessManager bpmnProcessManager;
 
     @PreAuthorize("hasRole('" + FlowableEntitlement.BPMN_PROCESS_LIST + "')")
     @Transactional(readOnly = true)
-    public List<BpmnProcessTO> list() {
+    public List<BpmnProcess> list() {
         return bpmnProcessManager.getProcesses();
     }
 
@@ -65,7 +65,7 @@ public class BpmnProcessLogic extends AbstractTransactionalLogic<BpmnProcessTO>
     }
 
     @Override
-    protected BpmnProcessTO resolveReference(final Method method, final Object... args)
+    protected BpmnProcess resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {
 
         throw new UnresolvedReferenceException();