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

[11/52] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Unit tests all in

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncActions.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncActions.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncActions.java
new file mode 100644
index 0000000..d267b7d
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncActions.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.api.sync;
+
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+
+/**
+ * Interface for actions to be performed during SyncJob execution.
+ */
+public interface SyncActions extends ProvisioningActions {
+
+    /**
+     * Action to be executed before to create a synchronized user / role locally.
+     * User/role is created locally upon synchronization in case of the un-matching rule
+     * {@link org.apache.syncope.common.types.UnmatchingRule#PROVISION} (default un-matching rule) is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeProvision(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before creating (and linking to the resource) a synchronized user / role locally.
+     * User/role is created locally and linked to the synchronized resource upon synchronization in case of the
+     * un-matching rule {@link org.apache.syncope.common.types.UnmatchingRule#ASSIGN} is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeAssign(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before unlinking resource from the synchronized user / role and de-provisioning.
+     * User/role is unlinked and de-provisioned from the synchronized resource upon synchronization in case of the
+     * matching rule {@link org.apache.syncope.common.types.MatchingRule#UNASSIGN} is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeUnassign(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before de-provisioning action only.
+     * User/role is de-provisioned (without unlinking) from the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#DEPROVISION} is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeDeprovision(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before unlinking resource from the synchronized user / role.
+     * User/role is unlinked (without de-provisioning) from the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#UNLINK} is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeUnlink(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before linking resource to the synchronized user / role.
+     * User/role is linked (without updating) to the synchronized resource upon synchronization in case of
+     * the matching rule {@link org.apache.syncope.common.types.MatchingRule#LINK} is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject user / role to be created
+     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeLink(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed before to update a synchronized user / role locally.
+     * User/role is updated upon synchronization in case of the matching rule
+     * {@link org.apache.syncope.common.types.MatchingRule#UPDATE} (default matching rule) is applied.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject local user / role information
+     * @param subjectMod modification
+     * @return synchronization information used for logging and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure.
+     */
+    <T extends AbstractSubjectTO, K extends AbstractSubjectMod> SyncDelta beforeUpdate(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject,
+            final K subjectMod)
+            throws JobExecutionException;
+
+    /**
+     * Action to be executed before to delete a synchronized user / role locally.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information
+     * @param subject local user / role to be deleted
+     * @return synchronization information used for logging and to be passed to the 'after' method.
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> SyncDelta beforeDelete(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject) throws JobExecutionException;
+
+    /**
+     * Action to be executed after each local user / role synchronization.
+     *
+     * @param profile profile of the synchronization being executed.
+     * @param delta retrieved synchronization information (may be modified by
+     * 'beforeProvision/beforeUpdate/beforeDelete')
+     * @param subject synchronized local user / role
+     * @param result global synchronization results at the current synchronization step
+     * @throws JobExecutionException in case of generic failure
+     */
+    <T extends AbstractSubjectTO> void after(
+            final ProvisioningProfile<?, ?> profile,
+            final SyncDelta delta,
+            final T subject,
+            final ProvisioningResult result) throws JobExecutionException;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncCorrelationRule.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncCorrelationRule.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncCorrelationRule.java
new file mode 100644
index 0000000..4e8b22f
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncCorrelationRule.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.api.sync;
+
+import org.apache.syncope.server.persistence.api.dao.search.SearchCond;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+
+/**
+ * Interface for correlation rule to be evaluated during SyncJob execution.
+ */
+public interface SyncCorrelationRule {
+
+    /**
+     * Return a search condition.
+     *
+     * @param connObj connector object.
+     * @return search condition.
+     */
+    SearchCond getSearchCond(ConnectorObject connObj);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeResultHandler.java b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeResultHandler.java
new file mode 100644
index 0000000..6bea0f9
--- /dev/null
+++ b/syncope620/server/provisioning-api/src/main/java/org/apache/syncope/server/provisioning/api/sync/SyncopeResultHandler.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.api.sync;
+
+import org.apache.syncope.server.persistence.api.entity.task.ProvisioningTask;
+
+public interface SyncopeResultHandler<T extends ProvisioningTask, A extends ProvisioningActions> {
+
+    ProvisioningProfile<T, A> getProfile();
+
+    void setProfile(ProvisioningProfile<T, A> profile);
+
+}

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/DisabledVirAttrCache.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/DisabledVirAttrCache.java b/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/DisabledVirAttrCache.java
deleted file mode 100644
index 3491075..0000000
--- a/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/DisabledVirAttrCache.java
+++ /dev/null
@@ -1,52 +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.provisioning.common.cache;
-
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.provisioning.api.cache.VirAttrCache;
-import org.apache.syncope.provisioning.api.cache.VirAttrCacheValue;
-
-/**
- * Empty virtual attribute value cache implementation.
- */
-public class DisabledVirAttrCache implements VirAttrCache {
-
-    @Override
-    public void expire(final AttributableType type, final Long id, final String schemaName) {
-        // nothing to do
-    }
-
-    @Override
-    public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) {
-        return null;
-    }
-
-    @Override
-    public boolean isValidEntry(VirAttrCacheValue value) {
-        return false;
-    }
-
-    @Override
-    public void put(
-            final AttributableType type, final Long id, final String schemaName, final VirAttrCacheValue value) {
-
-        // nothing to do
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/MemoryVirAttrCache.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/MemoryVirAttrCache.java b/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/MemoryVirAttrCache.java
deleted file mode 100644
index 0199499..0000000
--- a/syncope620/server/provisioning-common/src/main/java/org/apache/syncope/provisioning/common/cache/MemoryVirAttrCache.java
+++ /dev/null
@@ -1,151 +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.provisioning.common.cache;
-
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.apache.syncope.common.lib.types.AttributableType;
-import org.apache.syncope.provisioning.api.cache.VirAttrCache;
-import org.apache.syncope.provisioning.api.cache.VirAttrCacheKey;
-import org.apache.syncope.provisioning.api.cache.VirAttrCacheValue;
-
-/**
- * In-memory (HashMap) virtual attribute value cache implementation.
- */
-public class MemoryVirAttrCache implements VirAttrCache {
-
-    /**
-     * Elapsed time in seconds.
-     */
-    protected int ttl;
-
-    /**
-     * Max cache size.
-     */
-    protected int maxCacheSize;
-
-    /**
-     * Cache entries.
-     */
-    protected final Map<VirAttrCacheKey, VirAttrCacheValue> cache = new HashMap<VirAttrCacheKey, VirAttrCacheValue>();
-
-    public MemoryVirAttrCache(final int ttl, final int maxCacheSize) {
-        this.ttl = ttl;
-        this.maxCacheSize = maxCacheSize;
-    }
-
-    /**
-     * Cache virtual attribute values.
-     *
-     * @param type user or role
-     * @param id user or role id
-     * @param schemaName virtual attribute name
-     * @param value virtual attribute values
-     */
-    @Override
-    public void put(
-            final AttributableType type,
-            final Long id,
-            final String schemaName,
-            final VirAttrCacheValue value) {
-
-        synchronized (cache) {
-            // this operations (retrieve cache space and put entry on) have to be thread safe.
-            if (this.cache.size() >= this.maxCacheSize) {
-                free();
-            }
-
-            cache.put(new VirAttrCacheKey(type, id, schemaName), value);
-        }
-    }
-
-    /**
-     * Retrieve cached value. Return null in case of virtual attribute not cached.
-     *
-     * @param type user or role
-     * @param id user or role id
-     * @param schemaName virtual attribute schema name.
-     * @return cached values or null if virtual attribute is not cached.
-     */
-    @Override
-    public VirAttrCacheValue get(final AttributableType type, final Long id, final String schemaName) {
-        return cache.get(new VirAttrCacheKey(type, id, schemaName));
-    }
-
-    /**
-     * Force entry expiring.
-     *
-     * @param type user or role
-     * @param id user or role id
-     * @param schemaName virtual attribute schema name
-     */
-    @Override
-    public void expire(final AttributableType type, final Long id, final String schemaName) {
-        final VirAttrCacheValue value = cache.get(new VirAttrCacheKey(type, id, schemaName));
-        if (isValidEntry(value)) {
-            synchronized (cache) {
-                value.forceExpiring();
-            }
-        }
-    }
-
-    /**
-     * Remove expired entries if exist. If required, one entry at least (the latest recently used) will be taken off.
-     * This method is not thread safe: the caller have to take care to synchronize the call.
-     */
-    private void free() {
-        final Set<VirAttrCacheKey> toBeRemoved = new HashSet<VirAttrCacheKey>();
-
-        Map.Entry<VirAttrCacheKey, VirAttrCacheValue> latest = null;
-
-        for (Map.Entry<VirAttrCacheKey, VirAttrCacheValue> entry : cache.entrySet()) {
-            if (isValidEntry(entry.getValue())) {
-                final Date date = entry.getValue().getLastAccessDate();
-                if (latest == null || latest.getValue().getLastAccessDate().after(date)) {
-                    latest = entry;
-                }
-            } else {
-                toBeRemoved.add(entry.getKey());
-            }
-        }
-
-        if (toBeRemoved.isEmpty() && latest != null) {
-            // remove the oldest entry
-            cache.remove(latest.getKey());
-        } else {
-            // remove expired entries
-            cache.keySet().removeAll(toBeRemoved);
-        }
-    }
-
-    /**
-     * Cache entry is valid if and only if value exist and it is not expired.
-     *
-     * @param value cache entry value.
-     * @return TRUE if the value is valid; FALSE otherwise.
-     */
-    @Override
-    public boolean isValidEntry(final VirAttrCacheValue value) {
-        final Date expiringDate = new Date(value == null ? 0 : value.getCreationDate().getTime() + ttl * 1000);
-        return expiringDate.after(new Date());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/pom.xml b/syncope620/server/provisioning-java/pom.xml
new file mode 100644
index 0000000..66a2a85
--- /dev/null
+++ b/syncope620/server/provisioning-java/pom.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-server</artifactId>
+    <version>2.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Server Provisioning Java</name>
+  <description>Apache Syncope Server Provisioning Java</description>
+  <groupId>org.apache.syncope.server</groupId>
+  <artifactId>syncope-provisioning-java</artifactId>
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-context-support</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.geronimo.javamail</groupId>
+      <artifactId>geronimo-javamail_1.4_mail</artifactId>
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.velocity</groupId>
+      <artifactId>velocity-tools</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-workflow-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-server-misc</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <!-- TEST -->
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-workflow-java</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.el</groupId>
+      <artifactId>javax.el-api</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.server</groupId>
+      <artifactId>syncope-persistence-jpa</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.h2database</groupId>
+      <artifactId>h2</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <inherited>true</inherited>
+        <executions>
+          <execution>
+            <id>set-bundles</id>
+            <phase>process-test-resources</phase>
+            <goals>
+              <goal>copy</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+    
+    <resources>
+      <resource>
+        <directory>${basedir}/src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+    <testResources>
+      <testResource>
+        <directory>${basedir}/src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+      <testResource>
+        <directory>${basedir}/../persistence-jpa/src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+    </testResources>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
new file mode 100644
index 0000000..a22145a
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/AsyncConnectorFacade.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Future;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.api.ConnectorFacade;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeInfo;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.Schema;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.AsyncResult;
+import org.springframework.stereotype.Component;
+
+/**
+ * Intercept calls to ConnectorFacade's methods and check if the corresponding connector instance has been configured to
+ * allow every single operation: if not, simply do nothing.
+ */
+@Component
+public class AsyncConnectorFacade {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(AsyncConnectorFacade.class);
+
+    @Async
+    public Future<Uid> authenticate(
+            final ConnectorFacade connector,
+            final String username,
+            final GuardedString password,
+            final OperationOptions options) {
+
+        return new AsyncResult<Uid>(connector.authenticate(ObjectClass.ACCOUNT, username, password, options));
+    }
+
+    @Async
+    public Future<Uid> create(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Set<Attribute> attrs,
+            final OperationOptions options) {
+
+        return new AsyncResult<Uid>(connector.create(objectClass, attrs, options));
+    }
+
+    @Async
+    public Future<Uid> update(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Uid uid,
+            final Set<Attribute> attrs,
+            final OperationOptions options) {
+
+        return new AsyncResult<Uid>(connector.update(objectClass, uid, attrs, options));
+    }
+
+    @Async
+    public Future<Uid> delete(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Uid uid,
+            final OperationOptions options) {
+
+        connector.delete(objectClass, uid, options);
+        return new AsyncResult<Uid>(uid);
+    }
+
+    @Async
+    public Future<SyncToken> getLatestSyncToken(
+            final ConnectorFacade connector, final ObjectClass objectClass) {
+
+        return new AsyncResult<SyncToken>(connector.getLatestSyncToken(objectClass));
+    }
+
+    @Async
+    public Future<ConnectorObject> getObject(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Uid uid,
+            final OperationOptions options) {
+
+        return new AsyncResult<ConnectorObject>(connector.getObject(objectClass, uid, options));
+    }
+
+    @Async
+    public Future<Attribute> getObjectAttribute(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Uid uid,
+            final OperationOptions options,
+            final String attributeName) {
+
+        Attribute attribute = null;
+
+        final ConnectorObject object = connector.getObject(objectClass, uid, options);
+        if (object == null) {
+            LOG.debug("Object for '{}' not found", uid.getUidValue());
+        } else {
+            attribute = object.getAttributeByName(attributeName);
+        }
+
+        return new AsyncResult<Attribute>(attribute);
+    }
+
+    @Async
+    public Future<Set<Attribute>> getObjectAttributes(
+            final ConnectorFacade connector,
+            final ObjectClass objectClass,
+            final Uid uid,
+            final OperationOptions options) {
+
+        final Set<Attribute> attributes = new HashSet<Attribute>();
+
+        final ConnectorObject object = connector.getObject(objectClass, uid, options);
+
+        if (object == null) {
+            LOG.debug("Object for '{}' not found", uid.getUidValue());
+        } else {
+            for (String attribute : options.getAttributesToGet()) {
+                attributes.add(object.getAttributeByName(attribute));
+            }
+        }
+
+        return new AsyncResult<Set<Attribute>>(attributes);
+    }
+
+    @Async
+    public Future<Set<String>> getSchemaNames(final ConnectorFacade connector, final boolean includeSpecial) {
+        final Set<String> schemaNames = new HashSet<String>();
+
+        try {
+            final Schema schema = connector.schema();
+            for (ObjectClassInfo info : schema.getObjectClassInfo()) {
+                for (AttributeInfo attrInfo : info.getAttributeInfo()) {
+                    if (includeSpecial || !AttributeUtil.isSpecialName(attrInfo.getName())) {
+                        schemaNames.add(attrInfo.getName());
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // catch exception in order to manage unpredictable behaviors
+            LOG.debug("While reading schema on connector {}", connector, e);
+        }
+
+        return new AsyncResult<Set<String>>(schemaNames);
+    }
+
+    @Async
+    public Future<Set<ObjectClass>> getSupportedObjectClasses(final ConnectorFacade connector) {
+        final Set<ObjectClass> objectClasses = new HashSet<ObjectClass>();
+
+        try {
+            final Schema schema = connector.schema();
+            for (ObjectClassInfo info : schema.getObjectClassInfo()) {
+                objectClasses.add(new ObjectClass(info.getType()));
+            }
+        } catch (Exception e) {
+            // catch exception in order to manage unpredictable behaviors
+            LOG.debug("While reading schema on connector {}", connector, e);
+        }
+
+        return new AsyncResult<Set<ObjectClass>>(objectClasses);
+    }
+
+    @Async
+    public Future<String> validate(final ConnectorFacade connector) {
+        connector.validate();
+        return new AsyncResult<String>("OK");
+    }
+
+    @Async
+    public Future<String> test(final ConnectorFacade connector) {
+        connector.test();
+        return new AsyncResult<String>("OK");
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnIdBundleManagerImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnIdBundleManagerImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnIdBundleManagerImpl.java
new file mode 100644
index 0000000..78591e6
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnIdBundleManagerImpl.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.server.provisioning.api.URIUtil;
+import org.identityconnectors.common.IOUtil;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.api.APIConfiguration;
+import org.identityconnectors.framework.api.ConfigurationProperties;
+import org.identityconnectors.framework.api.ConnectorInfo;
+import org.identityconnectors.framework.api.ConnectorInfoManager;
+import org.identityconnectors.framework.api.ConnectorInfoManagerFactory;
+import org.identityconnectors.framework.api.ConnectorKey;
+import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ConnIdBundleManagerImpl implements ConnIdBundleManager {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConnIdBundleManager.class);
+
+    private String stringLocations;
+
+    /**
+     * ConnId Locations.
+     */
+    private List<URI> locations;
+
+    /**
+     * ConnectorInfoManager instances.
+     */
+    private final Map<URI, ConnectorInfoManager> connInfoManagers =
+            Collections.synchronizedMap(new LinkedHashMap<URI, ConnectorInfoManager>());
+
+    @Override
+    public void setStringLocations(final String stringLocations) {
+        this.stringLocations = stringLocations;
+    }
+
+    private void init() {
+        if (locations == null) {
+            locations = new ArrayList<>();
+            for (String location : StringUtils.isBlank(stringLocations) ? new String[0] : stringLocations.split(",")) {
+                try {
+                    locations.add(URIUtil.buildForConnId(location));
+                    LOG.info("Valid ConnId location: {}", location.trim());
+                } catch (Exception e) {
+                    LOG.error("Invalid ConnId location: {}", location.trim(), e);
+                }
+            }
+            locations = Collections.unmodifiableList(locations);
+        }
+    }
+
+    private void initLocal(final URI location) {
+        // 1. Find bundles inside local directory
+        File bundleDirectory = new File(location);
+        String[] bundleFiles = bundleDirectory.list();
+        if (bundleFiles == null) {
+            throw new NotFoundException("Local bundles directory " + location);
+        }
+
+        List<URL> bundleFileURLs = new ArrayList<>();
+        for (String file : bundleFiles) {
+            try {
+                bundleFileURLs.add(IOUtil.makeURL(bundleDirectory, file));
+            } catch (IOException ignore) {
+                // ignore exception and don't add bundle
+                LOG.debug("{}/{} is not a valid connector bundle", bundleDirectory.toString(), file, ignore);
+            }
+        }
+
+        if (bundleFileURLs.isEmpty()) {
+            LOG.warn("No connector bundles found in {}", location);
+        }
+        LOG.debug("Configuring local connector server:"
+                + "\n\tFiles: {}", bundleFileURLs);
+
+        // 2. Get connector info manager
+        ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getLocalManager(
+                bundleFileURLs.toArray(new URL[bundleFileURLs.size()]));
+        if (manager == null) {
+            throw new NotFoundException("Local ConnectorInfoManager");
+        }
+
+        connInfoManagers.put(location, manager);
+    }
+
+    private void initRemote(final URI location) {
+        // 1. Extract conf params for remote connection from given URI
+        final String host = location.getHost();
+        final int port = location.getPort();
+        final GuardedString key = new GuardedString(location.getUserInfo().toCharArray());
+        final boolean useSSL = location.getScheme().equals("connids");
+
+        final List<TrustManager> trustManagers = new ArrayList<>();
+        final String[] params = StringUtils.isBlank(location.getQuery()) ? null : location.getQuery().split("&");
+        if (params != null && params.length > 0) {
+            final String[] trustAllCerts = params[0].split("=");
+            if (trustAllCerts != null && trustAllCerts.length > 1
+                    && "trustAllCerts".equalsIgnoreCase(trustAllCerts[0])
+                    && "true".equalsIgnoreCase(trustAllCerts[1])) {
+
+                trustManagers.add(new X509TrustManager() {
+
+                    @Override
+                    public void checkClientTrusted(final X509Certificate[] chain, final String authType)
+                            throws CertificateException {
+                        // no checks, trust all
+                    }
+
+                    @Override
+                    public void checkServerTrusted(final X509Certificate[] chain, final String authType)
+                            throws CertificateException {
+                        // no checks, trust all
+                    }
+
+                    @Override
+                    public X509Certificate[] getAcceptedIssuers() {
+                        return null;
+                    }
+                });
+            }
+        }
+
+        LOG.debug("Configuring remote connector server:"
+                + "\n\tHost: {}"
+                + "\n\tPort: {}"
+                + "\n\tKey: {}"
+                + "\n\tUseSSL: {}"
+                + "\n\tTrustAllCerts: {}",
+                host, port, key, useSSL, !trustManagers.isEmpty());
+
+        RemoteFrameworkConnectionInfo info =
+                new RemoteFrameworkConnectionInfo(host, port, key, useSSL, trustManagers, 60 * 1000);
+        LOG.debug("Remote connection info: {}", info);
+
+        // 2. Get connector info manager
+        ConnectorInfoManager manager = ConnectorInfoManagerFactory.getInstance().getRemoteManager(info);
+        if (manager == null) {
+            throw new NotFoundException("Remote ConnectorInfoManager");
+        }
+
+        connInfoManagers.put(location, manager);
+    }
+
+    @Override
+    public void resetConnManagers() {
+        connInfoManagers.clear();
+    }
+
+    @Override
+    public Map<URI, ConnectorInfoManager> getConnManagers() {
+        init();
+
+        if (connInfoManagers.isEmpty()) {
+            for (URI location : locations) {
+                try {
+                    if ("file".equals(location.getScheme())) {
+                        LOG.debug("Local initialization: {}", location);
+                        initLocal(location);
+                    } else if (location.getScheme().startsWith("connid")) {
+                        LOG.debug("Remote initialization: {}", location);
+                        initRemote(location);
+                    } else {
+                        LOG.warn("Unsupported scheme: {}", location);
+                    }
+                } catch (Exception e) {
+                    LOG.error("Could not process {}", location, e);
+                }
+            }
+        }
+
+        if (LOG.isDebugEnabled()) {
+            for (Map.Entry<URI, ConnectorInfoManager> entry : connInfoManagers.entrySet()) {
+                LOG.debug("Connector bundles found at {}", entry.getKey());
+                for (ConnectorInfo connInfo : entry.getValue().getConnectorInfos()) {
+                    LOG.debug("\t{}", connInfo.getConnectorDisplayName());
+                }
+            }
+        }
+
+        return connInfoManagers;
+    }
+
+    @Override
+    public ConnectorInfo getConnectorInfo(
+            final String location, final String bundleName, final String bundleVersion, final String connectorName) {
+
+        // check ConnIdLocation
+        URI uriLocation = null;
+        try {
+            uriLocation = URIUtil.buildForConnId(location);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid ConnId location " + location, e);
+        }
+
+        // create key for search all properties
+        final ConnectorKey key = new ConnectorKey(bundleName, bundleVersion, connectorName);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("\nBundle name: " + key.getBundleName()
+                    + "\nBundle version: " + key.getBundleVersion()
+                    + "\nBundle class: " + key.getConnectorName());
+        }
+
+        // get the specified connector
+        ConnectorInfo info = null;
+        if (getConnManagers().containsKey(uriLocation)) {
+            info = getConnManagers().get(uriLocation).findConnectorInfo(key);
+        }
+        if (info == null) {
+            throw new NotFoundException("Connector Info for location " + location + " and key " + key);
+        }
+
+        return info;
+    }
+
+    @Override
+    public Map<String, List<ConnectorInfo>> getConnectorInfos() {
+        final Map<String, List<ConnectorInfo>> infos = new LinkedHashMap<>();
+        for (Map.Entry<URI, ConnectorInfoManager> entry : connInfoManagers.entrySet()) {
+            infos.put(entry.getKey().toString(), entry.getValue().getConnectorInfos());
+        }
+        return infos;
+    }
+
+    @Override
+    public ConfigurationProperties getConfigurationProperties(final ConnectorInfo info) {
+        if (info == null) {
+            throw new NotFoundException("Invalid: connector info is null");
+        }
+
+        // create default configuration
+        final APIConfiguration apiConfig = info.createDefaultAPIConfiguration();
+        if (apiConfig == null) {
+            throw new NotFoundException("Default API configuration");
+        }
+
+        // retrieve the ConfigurationProperties.
+        final ConfigurationProperties properties = apiConfig.getConfigurationProperties();
+        if (properties == null) {
+            throw new NotFoundException("Configuration properties");
+        }
+
+        if (LOG.isDebugEnabled()) {
+            for (String propName : properties.getPropertyNames()) {
+                LOG.debug("Property Name: {}"
+                        + "\nProperty Type: {}",
+                        properties.getProperty(propName).getName(),
+                        properties.getProperty(propName).getType());
+            }
+        }
+
+        return properties;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
new file mode 100644
index 0000000..3e5f03a
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorFacadeProxy.java
@@ -0,0 +1,600 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.NotFoundException;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.ConnectorCapability;
+import org.apache.syncope.common.lib.types.PropagationMode;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.entity.ConnInstance;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.server.provisioning.api.ConnPoolConfUtil;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.provisioning.api.TimeoutException;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.identityconnectors.common.security.GuardedByteArray;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.api.APIConfiguration;
+import org.identityconnectors.framework.api.ConfigurationProperties;
+import org.identityconnectors.framework.api.ConnectorFacade;
+import org.identityconnectors.framework.api.ConnectorFacadeFactory;
+import org.identityconnectors.framework.api.ConnectorInfo;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.ResultsHandler;
+import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.identityconnectors.framework.common.objects.SyncResultsHandler;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.ClassUtils;
+
+public class ConnectorFacadeProxy implements Connector {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(ConnectorFacadeProxy.class);
+
+    /**
+     * Connector facade wrapped instance.
+     */
+    private final ConnectorFacade connector;
+
+    /**
+     * Active connector instance.
+     */
+    private final ConnInstance activeConnInstance;
+
+    @Autowired
+    private AsyncConnectorFacade asyncFacade;
+
+    /**
+     * Use the passed connector instance to build a ConnectorFacade that will be used to make all wrapped calls.
+     *
+     * @param connInstance the connector instance configuration
+     * @see ConnectorInfo
+     * @see APIConfiguration
+     * @see ConfigurationProperties
+     * @see ConnectorFacade
+     */
+    public ConnectorFacadeProxy(final ConnInstance connInstance) {
+        this.activeConnInstance = connInstance;
+
+        ConnIdBundleManager connIdBundleManager =
+                ApplicationContextProvider.getApplicationContext().getBean(ConnIdBundleManager.class);
+        ConnectorInfo info = connIdBundleManager.getConnectorInfo(connInstance.getLocation(),
+                connInstance.getBundleName(), connInstance.getVersion(), connInstance.getConnectorName());
+
+        // create default configuration
+        APIConfiguration apiConfig = info.createDefaultAPIConfiguration();
+
+        // set connector configuration according to conninstance's
+        ConfigurationProperties properties = apiConfig.getConfigurationProperties();
+        for (ConnConfProperty property : connInstance.getConfiguration()) {
+            if (property.getValues() != null && !property.getValues().isEmpty()) {
+                properties.setPropertyValue(property.getSchema().getName(),
+                        getPropertyValue(property.getSchema().getType(), property.getValues()));
+            }
+        }
+
+        // set pooling configuration (if supported) according to conninstance's
+        if (connInstance.getPoolConf() != null) {
+            if (apiConfig.isConnectorPoolingSupported()) {
+                ConnPoolConfUtil.updateObjectPoolConfiguration(
+                        apiConfig.getConnectorPoolConfiguration(), connInstance.getPoolConf());
+            } else {
+                LOG.warn("Connector pooling not supported for {}", info);
+            }
+        }
+
+        // gets new connector, with the given configuration
+        connector = ConnectorFacadeFactory.getInstance().newInstance(apiConfig);
+        if (connector == null) {
+            throw new NotFoundException("Connector");
+        }
+
+        // make sure we have set up the Configuration properly
+        connector.validate();
+    }
+
+    @Override
+    public Uid authenticate(final String username, final String password, final OperationOptions options) {
+        Uid result = null;
+
+        if (activeConnInstance.getCapabilities().contains(ConnectorCapability.AUTHENTICATE)) {
+            final Future<Uid> future = asyncFacade.authenticate(
+                    connector, username, new GuardedString(password.toCharArray()), options);
+            try {
+                result = future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+            } catch (java.util.concurrent.TimeoutException e) {
+                future.cancel(true);
+                throw new TimeoutException("Request timeout");
+            } catch (Exception e) {
+                LOG.error("Connector request execution failure", e);
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new IllegalArgumentException(e.getCause());
+                }
+            }
+        } else {
+            LOG.info("Authenticate was attempted, although the connector only has these capabilities: {}. No action.",
+                    activeConnInstance.getCapabilities());
+        }
+
+        return result;
+    }
+
+    @Override
+    public Uid create(final PropagationMode propagationMode, final ObjectClass objectClass, final Set<Attribute> attrs,
+            final OperationOptions options, final Set<String> propagationAttempted) {
+
+        Uid result = null;
+
+        if (propagationMode == PropagationMode.ONE_PHASE
+                ? activeConnInstance.getCapabilities().contains(ConnectorCapability.ONE_PHASE_CREATE)
+                : activeConnInstance.getCapabilities().contains(ConnectorCapability.TWO_PHASES_CREATE)) {
+
+            propagationAttempted.add("create");
+
+            final Future<Uid> future = asyncFacade.create(connector, objectClass, attrs, options);
+            try {
+                result = future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+            } catch (java.util.concurrent.TimeoutException e) {
+                future.cancel(true);
+                throw new TimeoutException("Request timeout");
+            } catch (Exception e) {
+                LOG.error("Connector request execution failure", e);
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new IllegalArgumentException(e.getCause());
+                }
+            }
+        } else {
+            LOG.info("Create was attempted, although the connector only has these capabilities: {}. No action.",
+                    activeConnInstance.getCapabilities());
+        }
+
+        return result;
+    }
+
+    @Override
+    public Uid update(final PropagationMode propagationMode, final ObjectClass objectClass, final Uid uid,
+            final Set<Attribute> attrs, final OperationOptions options, final Set<String> propagationAttempted) {
+
+        Uid result = null;
+
+        if (propagationMode == PropagationMode.ONE_PHASE
+                ? activeConnInstance.getCapabilities().contains(ConnectorCapability.ONE_PHASE_UPDATE)
+                : activeConnInstance.getCapabilities().contains(ConnectorCapability.TWO_PHASES_UPDATE)) {
+
+            propagationAttempted.add("update");
+
+            final Future<Uid> future = asyncFacade.update(connector, objectClass, uid, attrs, options);
+
+            try {
+                result = future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+            } catch (java.util.concurrent.TimeoutException e) {
+                future.cancel(true);
+                throw new TimeoutException("Request timeout");
+            } catch (Exception e) {
+                LOG.error("Connector request execution failure", e);
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new IllegalArgumentException(e.getCause());
+                }
+            }
+        } else {
+            LOG.info("Update for {} was attempted, although the "
+                    + "connector only has these capabilities: {}. No action.", uid.getUidValue(), activeConnInstance.
+                    getCapabilities());
+        }
+
+        return result;
+    }
+
+    @Override
+    public void delete(final PropagationMode propagationMode, final ObjectClass objectClass, final Uid uid,
+            final OperationOptions options, final Set<String> propagationAttempted) {
+
+        if (propagationMode == PropagationMode.ONE_PHASE
+                ? activeConnInstance.getCapabilities().contains(ConnectorCapability.ONE_PHASE_DELETE)
+                : activeConnInstance.getCapabilities().contains(ConnectorCapability.TWO_PHASES_DELETE)) {
+
+            propagationAttempted.add("delete");
+
+            final Future<Uid> future = asyncFacade.delete(connector, objectClass, uid, options);
+
+            try {
+                future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+            } catch (java.util.concurrent.TimeoutException e) {
+                future.cancel(true);
+                throw new TimeoutException("Request timeout");
+            } catch (Exception e) {
+                LOG.error("Connector request execution failure", e);
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new IllegalArgumentException(e.getCause());
+                }
+            }
+        } else {
+            LOG.info("Delete for {} was attempted, although the connector only has these capabilities: {}. No action.",
+                    uid.getUidValue(), activeConnInstance.getCapabilities());
+        }
+    }
+
+    @Override
+    public void sync(final ObjectClass objectClass, final SyncToken token, final SyncResultsHandler handler,
+            final OperationOptions options) {
+
+        if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SYNC)) {
+            connector.sync(objectClass, token, handler, options);
+        } else {
+            LOG.info("Sync was attempted, although the connector only has these capabilities: {}. No action.",
+                    activeConnInstance.getCapabilities());
+        }
+    }
+
+    @Override
+    public SyncToken getLatestSyncToken(final ObjectClass objectClass) {
+        SyncToken result = null;
+
+        if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SYNC)) {
+            final Future<SyncToken> future = asyncFacade.getLatestSyncToken(connector, objectClass);
+
+            try {
+                result = future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+            } catch (java.util.concurrent.TimeoutException e) {
+                future.cancel(true);
+                throw new TimeoutException("Request timeout");
+            } catch (Exception e) {
+                LOG.error("Connector request execution failure", e);
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new IllegalArgumentException(e.getCause());
+                }
+            }
+        } else {
+            LOG.info("getLatestSyncToken was attempted, although the "
+                    + "connector only has these capabilities: {}. No action.", activeConnInstance.getCapabilities());
+        }
+
+        return result;
+    }
+
+    @Override
+    public ConnectorObject getObject(final ObjectClass objectClass, final Uid uid, final OperationOptions options) {
+        return getObject(null, null, objectClass, uid, options);
+    }
+
+    @Override
+    public ConnectorObject getObject(final PropagationMode propagationMode, final ResourceOperation operationType,
+            final ObjectClass objectClass, final Uid uid, final OperationOptions options) {
+
+        Future<ConnectorObject> future = null;
+
+        if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
+            if (operationType == null) {
+                future = asyncFacade.getObject(connector, objectClass, uid, options);
+            } else {
+                switch (operationType) {
+                    case CREATE:
+                        if (propagationMode == null || (propagationMode == PropagationMode.ONE_PHASE
+                                ? activeConnInstance.getCapabilities().
+                                contains(ConnectorCapability.ONE_PHASE_CREATE)
+                                : activeConnInstance.getCapabilities().
+                                contains(ConnectorCapability.TWO_PHASES_CREATE))) {
+
+                            future = asyncFacade.getObject(connector, objectClass, uid, options);
+                        }
+                        break;
+                    case UPDATE:
+                        if (propagationMode == null || (propagationMode == PropagationMode.ONE_PHASE
+                                ? activeConnInstance.getCapabilities().
+                                contains(ConnectorCapability.ONE_PHASE_UPDATE)
+                                : activeConnInstance.getCapabilities().
+                                contains(ConnectorCapability.TWO_PHASES_UPDATE))) {
+
+                            future = asyncFacade.getObject(connector, objectClass, uid, options);
+                        }
+                        break;
+                    default:
+                        future = asyncFacade.getObject(connector, objectClass, uid, options);
+                }
+            }
+        } else {
+            LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
+                    activeConnInstance.getCapabilities());
+        }
+
+        try {
+            return future == null ? null : future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public List<ConnectorObject> search(final ObjectClass objectClass, final Filter filter,
+            final OperationOptions options) {
+
+        final List<ConnectorObject> result = new ArrayList<>();
+
+        search(objectClass, filter, new ResultsHandler() {
+
+            @Override
+            public boolean handle(final ConnectorObject obj) {
+                return result.add(obj);
+            }
+        }, options);
+
+        return result;
+    }
+
+    @Override
+    public void getAllObjects(
+            final ObjectClass objectClass, final SyncResultsHandler handler, final OperationOptions options) {
+
+        search(objectClass, null, new ResultsHandler() {
+
+            @Override
+            public boolean handle(final ConnectorObject obj) {
+                final SyncDeltaBuilder bld = new SyncDeltaBuilder();
+                bld.setObject(obj);
+                bld.setUid(obj.getUid());
+                bld.setDeltaType(SyncDeltaType.CREATE_OR_UPDATE);
+                bld.setToken(new SyncToken(""));
+
+                return handler.handle(bld.build());
+            }
+        }, options);
+    }
+
+    @Override
+    public Attribute getObjectAttribute(final ObjectClass objectClass, final Uid uid, final OperationOptions options,
+            final String attributeName) {
+
+        final Future<Attribute> future = asyncFacade.getObjectAttribute(connector, objectClass, uid, options,
+                attributeName);
+        try {
+            return future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public Set<Attribute> getObjectAttributes(final ObjectClass objectClass, final Uid uid,
+            final OperationOptions options) {
+
+        final Future<Set<Attribute>> future = asyncFacade.getObjectAttributes(connector, objectClass, uid, options);
+        try {
+            return future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public Set<String> getSchemaNames(final boolean includeSpecial) {
+        final Future<Set<String>> future = asyncFacade.getSchemaNames(connector, includeSpecial);
+        try {
+            return future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public Set<ObjectClass> getSupportedObjectClasses() {
+        final Future<Set<ObjectClass>> future = asyncFacade.getSupportedObjectClasses(connector);
+        try {
+            return future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public void validate() {
+        final Future<String> future = asyncFacade.test(connector);
+        try {
+            future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    @Override
+    public void test() {
+        final Future<String> future = asyncFacade.test(connector);
+        try {
+            future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+        } catch (java.util.concurrent.TimeoutException e) {
+            future.cancel(true);
+            throw new TimeoutException("Request timeout");
+        } catch (Exception e) {
+            LOG.error("Connector request execution failure", e);
+            if (e.getCause() instanceof RuntimeException) {
+                throw (RuntimeException) e.getCause();
+            } else {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        }
+    }
+
+    private void search(
+            final ObjectClass objectClass,
+            final Filter filter,
+            final ResultsHandler handler,
+            final OperationOptions options) {
+
+        if (activeConnInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
+            connector.search(objectClass, filter, handler, options);
+        } else {
+            LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
+                    activeConnInstance.getCapabilities());
+        }
+    }
+
+    @Override
+    public ConnInstance getActiveConnInstance() {
+        return activeConnInstance;
+    }
+
+    @Override
+    public OperationOptions getOperationOptions(final Collection<? extends MappingItem> mapItems) {
+        // -------------------------------------
+        // Ask just for mapped attributes
+        // -------------------------------------
+        final OperationOptionsBuilder oob = new OperationOptionsBuilder();
+
+        final Set<String> attrsToGet = new HashSet<String>();
+        attrsToGet.add(Name.NAME);
+        attrsToGet.add(Uid.NAME);
+        attrsToGet.add(OperationalAttributes.ENABLE_NAME);
+
+        for (MappingItem item : mapItems) {
+            attrsToGet.add(item.getExtAttrName());
+        }
+
+        oob.setAttributesToGet(attrsToGet);
+        // -------------------------------------
+
+        return oob.build();
+    }
+
+    private Object getPropertyValue(final String propType, final List<?> values) {
+        Object value = null;
+
+        try {
+            final Class<?> propertySchemaClass = ClassUtils.forName(propType, ClassUtils.getDefaultClassLoader());
+
+            if (GuardedString.class.equals(propertySchemaClass)) {
+                value = new GuardedString(values.get(0).toString().toCharArray());
+            } else if (GuardedByteArray.class.equals(propertySchemaClass)) {
+                value = new GuardedByteArray((byte[]) values.get(0));
+            } else if (Character.class.equals(propertySchemaClass) || Character.TYPE.equals(propertySchemaClass)) {
+                value = values.get(0) == null || values.get(0).toString().isEmpty()
+                        ? null : values.get(0).toString().charAt(0);
+            } else if (Integer.class.equals(propertySchemaClass) || Integer.TYPE.equals(propertySchemaClass)) {
+                value = Integer.parseInt(values.get(0).toString());
+            } else if (Long.class.equals(propertySchemaClass) || Long.TYPE.equals(propertySchemaClass)) {
+                value = Long.parseLong(values.get(0).toString());
+            } else if (Float.class.equals(propertySchemaClass) || Float.TYPE.equals(propertySchemaClass)) {
+                value = Float.parseFloat(values.get(0).toString());
+            } else if (Double.class.equals(propertySchemaClass) || Double.TYPE.equals(propertySchemaClass)) {
+                value = Double.parseDouble(values.get(0).toString());
+            } else if (Boolean.class.equals(propertySchemaClass) || Boolean.TYPE.equals(propertySchemaClass)) {
+                value = Boolean.parseBoolean(values.get(0).toString());
+            } else if (URI.class.equals(propertySchemaClass)) {
+                value = URI.create(values.get(0).toString());
+            } else if (File.class.equals(propertySchemaClass)) {
+                value = new File(values.get(0).toString());
+            } else if (String[].class.equals(propertySchemaClass)) {
+                value = values.toArray(new String[] {});
+            } else {
+                value = values.get(0) == null ? null : values.get(0).toString();
+            }
+        } catch (Exception e) {
+            LOG.error("Invalid ConnConfProperty specified: {} {}", propType, values, e);
+        }
+
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        return "ConnectorFacadeProxy{"
+                + "connector=" + connector + "\n" + "capabitilies=" + activeConnInstance.getCapabilities() + '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorManager.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorManager.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorManager.java
new file mode 100644
index 0000000..5269a8b
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/ConnectorManager.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.server.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.server.persistence.api.entity.ConnInstance;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.provisioning.api.ConnectorFactory;
+import org.apache.syncope.server.provisioning.api.ConnectorRegistry;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.identityconnectors.common.l10n.CurrentLocale;
+import org.identityconnectors.framework.api.ConnectorFacadeFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Load ConnId connector instances.
+ */
+@Component
+public class ConnectorManager implements ConnectorRegistry, ConnectorFactory {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ConnectorManager.class);
+
+    @Autowired
+    private ConnIdBundleManager connIdBundleManager;
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    private String getBeanName(final ExternalResource resource) {
+        return String.format("connInstance-%d-%s", resource.getConnector().getKey(), resource.getKey());
+    }
+
+    @Override
+    public Connector getConnector(final ExternalResource resource) {
+        // Try to re-create connector bean from underlying resource (useful for managing failover scenarios)
+        if (!ApplicationContextProvider.getBeanFactory().containsBean(getBeanName(resource))) {
+            registerConnector(resource);
+        }
+
+        return (Connector) ApplicationContextProvider.getBeanFactory().getBean(getBeanName(resource));
+    }
+
+    @Override
+    public Connector createConnector(final ConnInstance connInstance, final Set<ConnConfProperty> configuration) {
+        final ConnInstance connInstanceClone = SerializationUtils.clone(connInstance);
+
+        connInstanceClone.setConfiguration(configuration);
+
+        Connector connector = new ConnectorFacadeProxy(connInstanceClone);
+        ApplicationContextProvider.getBeanFactory().autowireBean(connector);
+
+        return connector;
+    }
+
+    @Override
+    public ConnInstance getOverriddenConnInstance(final ConnInstance connInstance,
+            final Set<ConnConfProperty> overridden) {
+        final Set<ConnConfProperty> configuration = new HashSet<>();
+        final Map<String, ConnConfProperty> overridable = new HashMap<>();
+
+        // add not overridable properties
+        for (ConnConfProperty prop : connInstance.getConfiguration()) {
+            if (prop.isOverridable()) {
+                overridable.put(prop.getSchema().getName(), prop);
+            } else {
+                configuration.add(prop);
+            }
+        }
+
+        // add overridden properties
+        for (ConnConfProperty prop : overridden) {
+            if (overridable.containsKey(prop.getSchema().getName()) && !prop.getValues().isEmpty()) {
+                configuration.add(prop);
+                overridable.remove(prop.getSchema().getName());
+            }
+        }
+
+        // add overridable properties not overridden
+        configuration.addAll(overridable.values());
+
+        connInstance.setConfiguration(configuration);
+
+        return connInstance;
+    }
+
+    @Override
+    public void registerConnector(final ExternalResource resource) {
+        final ConnInstance connInstance = getOverriddenConnInstance(
+                SerializationUtils.clone(resource.getConnector()), resource.getConnInstanceConfiguration());
+        final Connector connector = createConnector(resource.getConnector(), connInstance.getConfiguration());
+        LOG.debug("Connector to be registered: {}", connector);
+
+        final String beanName = getBeanName(resource);
+
+        if (ApplicationContextProvider.getBeanFactory().containsSingleton(beanName)) {
+            unregisterConnector(beanName);
+        }
+
+        ApplicationContextProvider.getBeanFactory().registerSingleton(beanName, connector);
+        LOG.debug("Successfully registered bean {}", beanName);
+    }
+
+    @Override
+    public void unregisterConnector(final String id) {
+        ApplicationContextProvider.getBeanFactory().destroySingleton(id);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public void load() {
+        // This is needed in order to avoid encoding problems when sending error messages via REST
+        CurrentLocale.set(Locale.ENGLISH);
+
+        // Load all connector bundles
+        connIdBundleManager.getConnManagers();
+
+        // Load all resource-specific connectors
+        int connectors = 0;
+        for (ExternalResource resource : resourceDAO.findAll()) {
+            LOG.info("Registering resource-connector pair {}-{}", resource, resource.getConnector());
+            try {
+                registerConnector(resource);
+                connectors++;
+            } catch (Exception e) {
+                LOG.error("While registering resource-connector pair {}-{}", resource, resource.getConnector(), e);
+            }
+        }
+
+        LOG.info("Done loading {} connectors", connectors);
+    }
+
+    @Transactional(readOnly = true)
+    @Override
+    public void unload() {
+        int connectors = 0;
+        for (ExternalResource resource : resourceDAO.findAll()) {
+            final String beanName = getBeanName(resource);
+            if (ApplicationContextProvider.getBeanFactory().containsSingleton(beanName)) {
+                LOG.info("Unegistering resource-connector pair {}-{}", resource, resource.getConnector());
+                unregisterConnector(beanName);
+                connectors++;
+            }
+        }
+
+        LOG.info("Done unloading {} connectors", connectors);
+
+        ConnectorFacadeFactory.getInstance().dispose();
+        connIdBundleManager.resetConnManagers();
+        LOG.info("All connector resources disposed");
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultAttributableTransformer.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultAttributableTransformer.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultAttributableTransformer.java
new file mode 100644
index 0000000..b8133ca
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/DefaultAttributableTransformer.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.server.provisioning.java;
+
+import org.apache.syncope.common.lib.mod.AbstractAttributableMod;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.server.provisioning.api.AttributableTransformer;
+
+/**
+ * Default empty implementation returning received input as result.
+ */
+public class DefaultAttributableTransformer implements AttributableTransformer {
+
+    @Override
+    public <T extends AbstractAttributableTO> T transform(final T input) {
+        return input;
+    }
+
+    @Override
+    public <T extends AbstractAttributableMod> T transform(final T input) {
+        return input;
+    }
+}