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;
+ }
+}