[SYNCOPE-1591] Introducing support for fetch statements, used by archetype to populate Flowable tables for test users
diff --git a/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl b/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl
index 7bb0d1e..70aa21b 100644
--- a/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl
+++ b/archetype/src/main/resources/archetype-resources/core/src/test/resources/addFlowableToContent.xsl
@@ -23,28 +23,30 @@
   <xsl:template match="/dataset">
     <dataset>
       <xsl:apply-templates/>
-      
-      <ACT_RU_EXECUTION ID_="4" REV_="2" PROC_INST_ID_="4" BUSINESS_KEY_="userWorkflow:1417acbe-cbf6-4277-9372-e75e04f97000" PROC_DEF_ID_="userWorkflow:1:4" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
-      <ACT_RU_TASK ID_="5" REV_="2" EXECUTION_ID_="4" PROC_INST_ID_="4" PROC_DEF_ID_="userWorkflow:1:4" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
 
-      <ACT_RU_EXECUTION ID_="6" REV_="2" PROC_INST_ID_="6" BUSINESS_KEY_="userWorkflow:74cd8ece-715a-44a4-a736-e17b46c4e7e6" PROC_DEF_ID_="userWorkflow:1:4" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
-      <ACT_RU_TASK ID_="7" REV_="2" EXECUTION_ID_="6" PROC_INST_ID_="6" PROC_DEF_ID_="userWorkflow:1:4" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+      <fetch key="procDef" query="SELECT ID_ FROM ACT_RE_PROCDEF WHERE KEY_='userWorkflow'"/>
 
-      <ACT_RU_EXECUTION ID_="8" REV_="2" PROC_INST_ID_="8" BUSINESS_KEY_="userWorkflow:b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee" PROC_DEF_ID_="userWorkflow:1:4" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
-      <ACT_RU_TASK ID_="9" REV_="2" EXECUTION_ID_="8" PROC_INST_ID_="8" PROC_DEF_ID_="userWorkflow:1:4" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+      <ACT_RU_EXECUTION ID_="4" REV_="2" PROC_INST_ID_="4" BUSINESS_KEY_="userWorkflow:1417acbe-cbf6-4277-9372-e75e04f97000" PROC_DEF_ID_="${{procDef}}" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
+      <ACT_RU_TASK ID_="5" REV_="2" EXECUTION_ID_="4" PROC_INST_ID_="4" PROC_DEF_ID_="${{procDef}}" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
 
-      <ACT_RU_EXECUTION ID_="10" REV_="2" PROC_INST_ID_="10" BUSINESS_KEY_="userWorkflow:c9b2dec2-00a7-4855-97c0-d854842b4b24" PROC_DEF_ID_="userWorkflow:1:4" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
-      <ACT_RU_TASK ID_="11" REV_="2" EXECUTION_ID_="10" PROC_INST_ID_="10" PROC_DEF_ID_="userWorkflow:1:4" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+      <ACT_RU_EXECUTION ID_="6" REV_="2" PROC_INST_ID_="6" BUSINESS_KEY_="userWorkflow:74cd8ece-715a-44a4-a736-e17b46c4e7e6" PROC_DEF_ID_="${{procDef}}" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
+      <ACT_RU_TASK ID_="7" REV_="2" EXECUTION_ID_="6" PROC_INST_ID_="6" PROC_DEF_ID_="${{procDef}}" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
 
-      <ACT_RU_EXECUTION ID_="12" REV_="2" PROC_INST_ID_="12" BUSINESS_KEY_="userWorkflow:823074dc-d280-436d-a7dd-07399fae48ec" PROC_DEF_ID_="userWorkflow:1:4" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
-      <ACT_RU_TASK ID_="13" REV_="2" EXECUTION_ID_="12" PROC_INST_ID_="12" PROC_DEF_ID_="userWorkflow:1:4" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+      <ACT_RU_EXECUTION ID_="8" REV_="2" PROC_INST_ID_="8" BUSINESS_KEY_="userWorkflow:b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee" PROC_DEF_ID_="${{procDef}}" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
+      <ACT_RU_TASK ID_="9" REV_="2" EXECUTION_ID_="8" PROC_INST_ID_="8" PROC_DEF_ID_="${{procDef}}" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+
+      <ACT_RU_EXECUTION ID_="10" REV_="2" PROC_INST_ID_="10" BUSINESS_KEY_="userWorkflow:c9b2dec2-00a7-4855-97c0-d854842b4b24" PROC_DEF_ID_="${{procDef}}" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
+      <ACT_RU_TASK ID_="11" REV_="2" EXECUTION_ID_="10" PROC_INST_ID_="10" PROC_DEF_ID_="${{procDef}}" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
+
+      <ACT_RU_EXECUTION ID_="12" REV_="2" PROC_INST_ID_="12" BUSINESS_KEY_="userWorkflow:823074dc-d280-436d-a7dd-07399fae48ec" PROC_DEF_ID_="${{procDef}}" ACT_ID_="active" IS_ACTIVE_="1" IS_CONCURRENT_="0" IS_SCOPE_="1" IS_EVENT_SCOPE_="0" SUSPENSION_STATE_="1"/>
+      <ACT_RU_TASK ID_="13" REV_="2" EXECUTION_ID_="12" PROC_INST_ID_="12" PROC_DEF_ID_="${{procDef}}" NAME_="Active" TASK_DEF_KEY_="active" PRIORITY_="50" CREATE_TIME_="2013-02-25T17:19:03+0100"/>
     </dataset>
   </xsl:template>
-  
+
   <xsl:template match="node()|@*|comment()">
     <xsl:copy>
       <xsl:apply-templates select="@*|node()"/>
     </xsl:copy>
   </xsl:template>
 
-</xsl:stylesheet>
\ No newline at end of file
+</xsl:stylesheet>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
index 15f9e04..1673e9b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/content/ContentLoaderHandler.java
@@ -51,7 +51,9 @@
 
     private final boolean continueOnError;
 
-    private final StringSubstitutor envParamSubstitutor;
+    private final Map<String, String> fetches = new HashMap<>();
+
+    private final StringSubstitutor paramSubstitutor;
 
     public ContentLoaderHandler(
             final DataSource dataSource,
@@ -62,8 +64,8 @@
         this.jdbcTemplate = new JdbcTemplate(dataSource);
         this.rootElement = rootElement;
         this.continueOnError = continueOnError;
-        this.envParamSubstitutor = new StringSubstitutor(key -> {
-            String value = env.getProperty(key);
+        this.paramSubstitutor = new StringSubstitutor(key -> {
+            String value = env.getProperty(key, fetches.get(key));
             return StringUtils.isBlank(value) ? null : value;
         });
     }
@@ -86,7 +88,7 @@
                 colType = Types.VARCHAR;
             }
 
-            String value = envParamSubstitutor.replace(attrs.getValue(i));
+            String value = paramSubstitutor.replace(attrs.getValue(i));
             if (value == null) {
                 LOG.warn("Variable ${} could not be resolved", attrs.getValue(i));
                 value = attrs.getValue(i);
@@ -97,53 +99,53 @@
                 case Types.TINYINT:
                 case Types.SMALLINT:
                     try {
-                        parameters[i] = Integer.valueOf(value);
-                    } catch (NumberFormatException e) {
-                        LOG.error("Unparsable Integer '{}'", value);
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = Integer.valueOf(value);
+                } catch (NumberFormatException e) {
+                    LOG.error("Unparsable Integer '{}'", value);
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.NUMERIC:
                 case Types.DECIMAL:
                 case Types.BIGINT:
                     try {
-                        parameters[i] = Long.valueOf(value);
-                    } catch (NumberFormatException e) {
-                        LOG.error("Unparsable Long '{}'", value);
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = Long.valueOf(value);
+                } catch (NumberFormatException e) {
+                    LOG.error("Unparsable Long '{}'", value);
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.DOUBLE:
                     try {
-                        parameters[i] = Double.valueOf(value);
-                    } catch (NumberFormatException e) {
-                        LOG.error("Unparsable Double '{}'", value);
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = Double.valueOf(value);
+                } catch (NumberFormatException e) {
+                    LOG.error("Unparsable Double '{}'", value);
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.REAL:
                 case Types.FLOAT:
                     try {
-                        parameters[i] = Float.valueOf(value);
-                    } catch (NumberFormatException e) {
-                        LOG.error("Unparsable Float '{}'", value);
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = Float.valueOf(value);
+                } catch (NumberFormatException e) {
+                    LOG.error("Unparsable Float '{}'", value);
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.DATE:
                 case Types.TIME:
                 case Types.TIMESTAMP:
                     try {
-                        parameters[i] = FormatUtils.parseDate(value);
-                    } catch (ParseException e) {
-                        LOG.error("Unparsable Date '{}'", value);
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = FormatUtils.parseDate(value);
+                } catch (ParseException e) {
+                    LOG.error("Unparsable Date '{}'", value);
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.BIT:
                 case Types.BOOLEAN:
@@ -154,22 +156,22 @@
                 case Types.VARBINARY:
                 case Types.LONGVARBINARY:
                     try {
-                        parameters[i] = DatatypeConverter.parseHexBinary(value);
-                    } catch (IllegalArgumentException e) {
-                        parameters[i] = value;
-                    }
-                    break;
+                    parameters[i] = DatatypeConverter.parseHexBinary(value);
+                } catch (IllegalArgumentException e) {
+                    parameters[i] = value;
+                }
+                break;
 
                 case Types.BLOB:
                     try {
-                        parameters[i] = DatatypeConverter.parseHexBinary(value);
-                    } catch (IllegalArgumentException e) {
-                        LOG.warn("Error decoding hex string to specify a blob parameter", e);
-                        parameters[i] = value;
-                    } catch (Exception e) {
-                        LOG.warn("Error creating a new blob parameter", e);
-                    }
-                    break;
+                    parameters[i] = DatatypeConverter.parseHexBinary(value);
+                } catch (IllegalArgumentException e) {
+                    LOG.warn("Error decoding hex string to specify a blob parameter", e);
+                    parameters[i] = value;
+                } catch (Exception e) {
+                    LOG.warn("Error creating a new blob parameter", e);
+                }
+                break;
 
                 default:
                     parameters[i] = value;
@@ -187,27 +189,32 @@
         if (rootElement.equals(qName)) {
             return;
         }
+        if ("fetch".equalsIgnoreCase(qName)) {
+            String value = jdbcTemplate.queryForObject(atts.getValue("query"), String.class);
+            String key = atts.getValue("key");
+            fetches.put(key, value);
+        } else {
+            StringBuilder query = new StringBuilder("INSERT INTO ").append(qName).append('(');
 
-        StringBuilder query = new StringBuilder("INSERT INTO ").append(qName).append('(');
+            StringBuilder values = new StringBuilder();
 
-        StringBuilder values = new StringBuilder();
-
-        for (int i = 0; i < atts.getLength(); i++) {
-            query.append(atts.getQName(i));
-            values.append('?');
-            if (i < atts.getLength() - 1) {
-                query.append(',');
-                values.append(',');
+            for (int i = 0; i < atts.getLength(); i++) {
+                query.append(atts.getQName(i));
+                values.append('?');
+                if (i < atts.getLength() - 1) {
+                    query.append(',');
+                    values.append(',');
+                }
             }
-        }
-        query.append(") VALUES (").append(values).append(')');
+            query.append(") VALUES (").append(values).append(')');
 
-        try {
-            jdbcTemplate.update(query.toString(), getParameters(qName, atts));
-        } catch (DataAccessException e) {
-            LOG.error("While trying to perform {} with params {}", query, getParameters(qName, atts), e);
-            if (!continueOnError) {
-                throw e;
+            try {
+                jdbcTemplate.update(query.toString(), getParameters(qName, atts));
+            } catch (DataAccessException e) {
+                LOG.error("While trying to perform {} with params {}", query, getParameters(qName, atts), e);
+                if (!continueOnError) {
+                    throw e;
+                }
             }
         }
     }