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/11/10 19:04:00 UTC
[1/4] syncope git commit: Upgrading Wicket
Repository: syncope
Updated Branches:
refs/heads/1_2_X 573a4e303 -> c6e008125
refs/heads/master e8eceae0f -> 9da95172a
Upgrading Wicket
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/573a4e30
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/573a4e30
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/573a4e30
Branch: refs/heads/master
Commit: 573a4e3035f444487b67b6cc1bed94807336a05a
Parents: 820e536
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 9 15:07:23 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 9 15:07:23 2015 +0100
----------------------------------------------------------------------
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/573a4e30/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 4376a31..de5586a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -358,7 +358,7 @@ under the License.
<cocoon.version>3.0.0-alpha-3</cocoon.version>
- <wicket.version>6.20.0</wicket.version>
+ <wicket.version>6.21.0</wicket.version>
<groovy.version>2.3.10</groovy.version>
[3/4] syncope git commit: [SYNCOPE-729] Not considering CREATE or
UPDATE but only SEARCH during getObject
Posted by il...@apache.org.
[SYNCOPE-729] Not considering CREATE or UPDATE but only SEARCH during getObject
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/c6e00812
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/c6e00812
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/c6e00812
Branch: refs/heads/1_2_X
Commit: c6e008125fb08e9b939acb73e233742ad0c3824f
Parents: 573a4e3
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Nov 10 18:54:51 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Nov 10 18:54:51 2015 +0100
----------------------------------------------------------------------
.../syncope/core/propagation/Connector.java | 15 -------
.../impl/AbstractPropagationTaskExecutor.java | 3 +-
.../propagation/impl/ConnectorFacadeProxy.java | 41 ++------------------
3 files changed, 5 insertions(+), 54 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/Connector.java b/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
index c182fe3..869d7d6 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
@@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.syncope.common.types.PropagationMode;
-import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
import org.apache.syncope.core.persistence.beans.ConnInstance;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -119,20 +118,6 @@ public interface Connector {
ConnectorObject getObject(ObjectClass objectClass, Uid uid, OperationOptions options);
/**
- * Get remote object used by the propagation manager in order to choose for a create (object doesn't exist) or an
- * update (object exists).
- *
- * @param propagationMode propagation mode
- * @param operationType resource operation type
- * @param objectClass ConnId's object class
- * @param uid ConnId's Uid
- * @param options ConnId's OperationOptions
- * @return ConnId's connector object for given uid
- */
- ConnectorObject getObject(PropagationMode propagationMode, ResourceOperation operationType, ObjectClass objectClass,
- Uid uid, OperationOptions options);
-
- /**
* Search for remote objects.
*
* @param objectClass ConnId's object class
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java b/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
index ac31ec4..54b7084 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
@@ -491,8 +491,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
ConnectorObject obj = null;
try {
- obj = connector.getObject(task.getPropagationMode(),
- task.getPropagationOperation(),
+ obj = connector.getObject(
new ObjectClass(task.getObjectClassName()),
new Uid(accountId),
connector.getOperationOptions(AttributableUtil.getInstance(task.getSubjectType()).
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java b/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
index 980a3df..fb3c872 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
@@ -30,7 +30,6 @@ import java.util.concurrent.TimeUnit;
import org.apache.syncope.common.types.ConnConfProperty;
import org.apache.syncope.common.types.ConnectorCapability;
import org.apache.syncope.common.types.PropagationMode;
-import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.connid.ConnPoolConfUtil;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
import org.apache.syncope.core.persistence.beans.ConnInstance;
@@ -305,44 +304,10 @@ public class ConnectorFacadeProxy implements Connector {
@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);
- }
- }
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
} else {
LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
activeConnInstance.getCapabilities());
@@ -351,7 +316,9 @@ public class ConnectorFacadeProxy implements Connector {
try {
return future == null ? null : future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
} catch (java.util.concurrent.TimeoutException e) {
- future.cancel(true);
+ if (future != null) {
+ future.cancel(true);
+ }
throw new TimeoutException("Request timeout");
} catch (Exception e) {
LOG.error("Connector request execution failure", e);
[2/4] syncope git commit: [SYNCOPE-729] Not considering CREATE or
UPDATE but only SEARCH during getObject
Posted by il...@apache.org.
[SYNCOPE-729] Not considering CREATE or UPDATE but only SEARCH during getObject
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/c6e00812
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/c6e00812
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/c6e00812
Branch: refs/heads/master
Commit: c6e008125fb08e9b939acb73e233742ad0c3824f
Parents: 573a4e3
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Nov 10 18:54:51 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Nov 10 18:54:51 2015 +0100
----------------------------------------------------------------------
.../syncope/core/propagation/Connector.java | 15 -------
.../impl/AbstractPropagationTaskExecutor.java | 3 +-
.../propagation/impl/ConnectorFacadeProxy.java | 41 ++------------------
3 files changed, 5 insertions(+), 54 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/Connector.java b/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
index c182fe3..869d7d6 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/Connector.java
@@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.syncope.common.types.PropagationMode;
-import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
import org.apache.syncope.core.persistence.beans.ConnInstance;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -119,20 +118,6 @@ public interface Connector {
ConnectorObject getObject(ObjectClass objectClass, Uid uid, OperationOptions options);
/**
- * Get remote object used by the propagation manager in order to choose for a create (object doesn't exist) or an
- * update (object exists).
- *
- * @param propagationMode propagation mode
- * @param operationType resource operation type
- * @param objectClass ConnId's object class
- * @param uid ConnId's Uid
- * @param options ConnId's OperationOptions
- * @return ConnId's connector object for given uid
- */
- ConnectorObject getObject(PropagationMode propagationMode, ResourceOperation operationType, ObjectClass objectClass,
- Uid uid, OperationOptions options);
-
- /**
* Search for remote objects.
*
* @param objectClass ConnId's object class
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java b/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
index ac31ec4..54b7084 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/impl/AbstractPropagationTaskExecutor.java
@@ -491,8 +491,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
ConnectorObject obj = null;
try {
- obj = connector.getObject(task.getPropagationMode(),
- task.getPropagationOperation(),
+ obj = connector.getObject(
new ObjectClass(task.getObjectClassName()),
new Uid(accountId),
connector.getOperationOptions(AttributableUtil.getInstance(task.getSubjectType()).
http://git-wip-us.apache.org/repos/asf/syncope/blob/c6e00812/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java b/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
index 980a3df..fb3c872 100644
--- a/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
+++ b/core/src/main/java/org/apache/syncope/core/propagation/impl/ConnectorFacadeProxy.java
@@ -30,7 +30,6 @@ import java.util.concurrent.TimeUnit;
import org.apache.syncope.common.types.ConnConfProperty;
import org.apache.syncope.common.types.ConnectorCapability;
import org.apache.syncope.common.types.PropagationMode;
-import org.apache.syncope.common.types.ResourceOperation;
import org.apache.syncope.core.connid.ConnPoolConfUtil;
import org.apache.syncope.core.persistence.beans.AbstractMappingItem;
import org.apache.syncope.core.persistence.beans.ConnInstance;
@@ -305,44 +304,10 @@ public class ConnectorFacadeProxy implements Connector {
@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);
- }
- }
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
} else {
LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
activeConnInstance.getCapabilities());
@@ -351,7 +316,9 @@ public class ConnectorFacadeProxy implements Connector {
try {
return future == null ? null : future.get(activeConnInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
} catch (java.util.concurrent.TimeoutException e) {
- future.cancel(true);
+ if (future != null) {
+ future.cancel(true);
+ }
throw new TimeoutException("Request timeout");
} catch (Exception e) {
LOG.error("Connector request execution failure", e);
[4/4] syncope git commit: [SYNCOPE-729] Merge from 1_2_X
Posted by il...@apache.org.
[SYNCOPE-729] Merge from 1_2_X
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/9da95172
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/9da95172
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/9da95172
Branch: refs/heads/master
Commit: 9da95172a5eadfe9ffcd021ccc4ba8b00a0a9ace
Parents: e8eceae c6e0081
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Nov 10 19:03:48 2015 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Nov 10 19:03:48 2015 +0100
----------------------------------------------------------------------
.../core/provisioning/api/Connector.java | 16 ----------
.../provisioning/java/ConnectorFacadeProxy.java | 33 +-------------------
.../AbstractPropagationTaskExecutor.java | 5 ++-
3 files changed, 3 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/9da95172/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
----------------------------------------------------------------------
diff --cc core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
index 2a3a0a1,0000000..c93c2c4
mode 100644,000000..100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
@@@ -1,211 -1,0 +1,195 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.api;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
- import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.identityconnectors.framework.common.objects.Attribute;
+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.ResultsHandler;
+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;
+
+/**
+ * Entry point for making requests on underlying connector bundles.
+ */
+public interface Connector {
+
+ /**
+ * Authenticate user on a connector instance.
+ *
+ * @param username the name based credential for authentication
+ * @param password the password based credential for authentication
+ * @param options ConnId's OperationOptions
+ * @return Uid of the account that was used to authenticate
+ */
+ Uid authenticate(String username, String password, OperationOptions options);
+
+ /**
+ * Create user / group on a connector instance.
+ *
+ * @param objectClass ConnId's object class
+ * @param attrs attributes for creation
+ * @param options ConnId's OperationOptions
+ * @param propagationAttempted if creation is actually performed (based on connector instance's capabilities)
+ * @return Uid for created object
+ */
+ Uid create(
+ ObjectClass objectClass,
+ Set<Attribute> attrs,
+ OperationOptions options,
+ Boolean[] propagationAttempted);
+
+ /**
+ * Update user / group on a connector instance.
+ *
+ * @param objectClass ConnId's object class
+ * @param uid user to be updated
+ * @param attrs attributes for update
+ * @param options ConnId's OperationOptions
+ * @param propagationAttempted if creation is actually performed (based on connector instance's capabilities)
+ * @return Uid for updated object
+ */
+ Uid update(
+ ObjectClass objectClass,
+ Uid uid,
+ Set<Attribute> attrs,
+ OperationOptions options,
+ Boolean[] propagationAttempted);
+
+ /**
+ * Delete user / group on a connector instance.
+ *
+ * @param objectClass ConnId's object class
+ * @param uid user to be deleted
+ * @param options ConnId's OperationOptions
+ * @param propagationAttempted if deletion is actually performed (based on connector instance's capabilities)
+ */
+ void delete(
+ ObjectClass objectClass,
+ Uid uid,
+ OperationOptions options,
+ Boolean[] propagationAttempted);
+
+ /**
+ * Fetches all remote objects (for use during full reconciliation).
+ *
+ * @param objectClass ConnId's object class.
+ * @param handler to be used to handle deltas.
+ * @param options ConnId's OperationOptions.
+ */
+ void getAllObjects(ObjectClass objectClass, SyncResultsHandler handler, OperationOptions options);
+
+ /**
+ * Sync remote objects from a connector instance.
+ *
+ * @param objectClass ConnId's object class
+ * @param token to be passed to the underlying connector
+ * @param handler to be used to handle deltas
+ * @param options ConnId's OperationOptions
+ */
+ void sync(ObjectClass objectClass, SyncToken token, SyncResultsHandler handler, OperationOptions options);
+
+ /**
+ * Read latest sync token from a connector instance.
+ *
+ * @param objectClass ConnId's object class.
+ * @return latest sync token
+ */
+ SyncToken getLatestSyncToken(ObjectClass objectClass);
+
+ /**
+ * Get remote object.
+ *
+ * @param objectClass ConnId's object class
+ * @param uid ConnId's Uid
+ * @param options ConnId's OperationOptions
+ * @return ConnId's connector object for given uid
+ */
+ ConnectorObject getObject(ObjectClass objectClass, Uid uid, OperationOptions options);
+
+ /**
- * Get remote object with check for intended operation to perform on external resource.
- *
- * @param operationType resource operation type
- * @param objectClass ConnId's object class
- * @param uid ConnId's Uid
- * @param options ConnId's OperationOptions
- * @return ConnId's connector object for given uid
- */
- ConnectorObject getObject(
- ResourceOperation operationType,
- ObjectClass objectClass,
- Uid uid,
- OperationOptions options);
-
- /**
+ * Search for remote objects.
+ *
+ * @param objectClass ConnId's object class
+ * @param filter search filter
+ * @param handler class responsible for working with the objects returned from the search; may be null.
+ * @param options ConnId's OperationOptions
+ */
+ void search(
+ ObjectClass objectClass,
+ Filter filter,
+ ResultsHandler handler,
+ OperationOptions options);
+
+ /**
+ * Search for remote objects.
+ *
+ * @param objectClass ConnId's object class
+ * @param filter search filter
+ * @param handler class responsible for working with the objects returned from the search; may be null.
+ * @param pageSize requested page results page size
+ * @param pagedResultsCookie an opaque cookie which is used by the connector to track its position in the set of
+ * query results
+ * @param orderBy the sort keys which should be used for ordering the {@link ConnectorObject} returned by
+ * search request
+ * @param mapItems mapping items
+ */
+ void search(
+ ObjectClass objectClass,
+ Filter filter,
+ ResultsHandler handler,
+ int pageSize,
+ String pagedResultsCookie,
+ List<OrderByClause> orderBy,
+ Iterator<? extends MappingItem> mapItems);
+
+ /**
+ * Builds metadata description of ConnId {@link ObjectClass}.
+ *
+ * @return metadata description of ConnId ObjectClass
+ */
+ Set<ObjectClassInfo> getObjectClassInfo();
+
+ /**
+ * Validate a connector instance.
+ */
+ void validate();
+
+ /**
+ * Check connection to resource.
+ */
+ void test();
+
+ /**
+ * Getter for active connector instance.
+ *
+ * @return active connector instance.
+ */
+ ConnInstance getConnInstance();
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/9da95172/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index d39c9e9,0000000..86add58
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@@ -1,556 -1,0 +1,525 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java;
+
+import java.io.File;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.types.ConnConfProperty;
+import org.apache.syncope.common.lib.types.ConnectorCapability;
- import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.misc.utils.MappingUtils;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.core.provisioning.api.ConnPoolConfUtils;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+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.ObjectClass;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
+import org.identityconnectors.framework.common.objects.ResultsHandler;
+import org.identityconnectors.framework.common.objects.SearchResult;
+import org.identityconnectors.framework.common.objects.SortKey;
+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.identityconnectors.framework.spi.SearchResultsHandler;
+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 {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ConnectorFacadeProxy.class);
+
+ private static final Integer DEFAULT_PAGE_SIZE = 100;
+
+ /**
+ * Connector facade wrapped instance.
+ */
+ private final ConnectorFacade connector;
+
+ /**
+ * Active connector instance.
+ */
+ private final ConnInstance connInstance;
+
+ @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
+ * @see ConnectorInfo
+ * @see APIConfiguration
+ * @see ConfigurationProperties
+ * @see ConnectorFacade
+ */
+ public ConnectorFacadeProxy(final ConnInstance connInstance) {
+ this.connInstance = connInstance;
+
+ ConnIdBundleManager connIdBundleManager = ApplicationContextProvider.getBeanFactory().getBean(
+ ConnIdBundleManager.class);
+ ConnectorInfo info = connIdBundleManager.getConnectorInfo(connInstance);
+
+ // create default configuration
+ APIConfiguration apiConfig = info.createDefaultAPIConfiguration();
+ // enable filtered results handler in validation mode
+ apiConfig.getResultsHandlerConfiguration().setFilteredResultsHandlerInValidationMode(true);
+
+ // set connector configuration according to conninstance's
+ ConfigurationProperties properties = apiConfig.getConfigurationProperties();
+ for (ConnConfProperty property : connInstance.getConf()) {
+ 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()) {
+ ConnPoolConfUtils.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);
+
+ // 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 (connInstance.getCapabilities().contains(ConnectorCapability.AUTHENTICATE)) {
+ Future<Uid> future = asyncFacade.authenticate(
+ connector, username, new GuardedString(password.toCharArray()), options);
+ try {
+ result = future.get(connInstance.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.",
+ connInstance.getCapabilities());
+ }
+
+ return result;
+ }
+
+ @Override
+ public Uid create(
+ final ObjectClass objectClass,
+ final Set<Attribute> attrs,
+ final OperationOptions options,
+ final Boolean[] propagationAttempted) {
+
+ Uid result = null;
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.CREATE)) {
+ propagationAttempted[0] = true;
+
+ Future<Uid> future = asyncFacade.create(connector, objectClass, attrs, options);
+ try {
+ result = future.get(connInstance.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.",
+ connInstance.getCapabilities());
+ }
+
+ return result;
+ }
+
+ @Override
+ public Uid update(
+ final ObjectClass objectClass,
+ final Uid uid,
+ final Set<Attribute> attrs,
+ final OperationOptions options,
+ final Boolean[] propagationAttempted) {
+
+ Uid result = null;
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.UPDATE)) {
+ propagationAttempted[0] = true;
+
+ Future<Uid> future = asyncFacade.update(connector, objectClass, uid, attrs, options);
+
+ try {
+ result = future.get(connInstance.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(), connInstance.getCapabilities());
+ }
+
+ return result;
+ }
+
+ @Override
+ public void delete(
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options,
+ final Boolean[] propagationAttempted) {
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.DELETE)) {
+ propagationAttempted[0] = true;
+
+ Future<Uid> future = asyncFacade.delete(connector, objectClass, uid, options);
+
+ try {
+ future.get(connInstance.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(), connInstance.getCapabilities());
+ }
+ }
+
+ @Override
+ public void sync(final ObjectClass objectClass, final SyncToken token, final SyncResultsHandler handler,
+ final OperationOptions options) {
+
+ if (connInstance.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.",
+ connInstance.getCapabilities());
+ }
+ }
+
+ @Override
+ public SyncToken getLatestSyncToken(final ObjectClass objectClass) {
+ SyncToken result = null;
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.SYNC)) {
+ Future<SyncToken> future = asyncFacade.getLatestSyncToken(connector, objectClass);
+
+ try {
+ result = future.get(connInstance.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.", connInstance.getCapabilities());
+ }
+
+ return result;
+ }
+
+ @Override
+ public ConnectorObject getObject(final ObjectClass objectClass, final Uid uid, final OperationOptions options) {
- return getObject(null, objectClass, uid, options);
- }
-
- @Override
- public ConnectorObject getObject(
- final ResourceOperation operationType,
- final ObjectClass objectClass,
- final Uid uid,
- final OperationOptions options) {
-
- boolean hasCapablities = false;
++ Future<ConnectorObject> future = null;
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
- if (operationType == null) {
- hasCapablities = true;
- } else {
- switch (operationType) {
- case CREATE:
- hasCapablities = connInstance.getCapabilities().contains(ConnectorCapability.CREATE);
- break;
-
- case UPDATE:
- hasCapablities = connInstance.getCapabilities().contains(ConnectorCapability.UPDATE);
- break;
-
- default:
- hasCapablities = true;
- }
- }
- }
-
- Future<ConnectorObject> future = null;
- if (hasCapablities) {
+ future = asyncFacade.getObject(connector, objectClass, uid, options);
+ } else {
+ LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
+ connInstance.getCapabilities());
+ }
+
+ try {
+ return future == null ? null : future.get(connInstance.getConnRequestTimeout(), TimeUnit.SECONDS);
+ } catch (java.util.concurrent.TimeoutException e) {
+ if (future != null) {
+ 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 getAllObjects(
+ final ObjectClass objectClass, final SyncResultsHandler handler, final OperationOptions options) {
+
+ search(objectClass, null, new ResultsHandler() {
+
+ @Override
+ public boolean handle(final ConnectorObject obj) {
+ return handler.handle(new SyncDeltaBuilder().
+ setObject(obj).
+ setUid(obj.getUid()).
+ setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
+ setToken(new SyncToken("")).
+ build());
+ }
+ }, options);
+ }
+
+ @Override
+ public Set<ObjectClassInfo> getObjectClassInfo() {
+ Future<Set<ObjectClassInfo>> future = asyncFacade.getObjectClassInfo(connector);
+ try {
+ return future.get(connInstance.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() {
+ Future<String> future = asyncFacade.test(connector);
+ try {
+ future.get(connInstance.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() {
+ Future<String> future = asyncFacade.test(connector);
+ try {
+ future.get(connInstance.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 search(
+ final ObjectClass objectClass,
+ final Filter filter,
+ final ResultsHandler handler,
+ final OperationOptions options) {
+
+ if (connInstance.getCapabilities().contains(ConnectorCapability.SEARCH)) {
+ if (options.getPageSize() == null && options.getPagedResultsCookie() == null) {
+ OperationOptionsBuilder builder = new OperationOptionsBuilder(options);
+ builder.setPageSize(DEFAULT_PAGE_SIZE);
+
+ final String[] cookies = new String[] { null };
+ do {
+ if (cookies[0] != null) {
+ builder.setPagedResultsCookie(cookies[0]);
+ }
+
+ connector.search(objectClass, filter, new SearchResultsHandler() {
+
+ @Override
+ public void handleResult(final SearchResult result) {
+ if (handler instanceof SearchResultsHandler) {
+ SearchResultsHandler.class.cast(handler).handleResult(result);
+ }
+ cookies[0] = result.getPagedResultsCookie();
+ }
+
+ @Override
+ public boolean handle(final ConnectorObject connectorObject) {
+ return handler.handle(connectorObject);
+ }
+ }, builder.build());
+ } while (cookies[0] != null);
+ } else {
+ connector.search(objectClass, filter, handler, options);
+ }
+ } else {
+ LOG.info("Search was attempted, although the connector only has these capabilities: {}. No action.",
+ connInstance.getCapabilities());
+ }
+ }
+
+ @Override
+ public void search(
+ final ObjectClass objectClass,
+ final Filter filter,
+ final ResultsHandler handler,
+ final int pageSize,
+ final String pagedResultsCookie,
+ final List<OrderByClause> orderBy,
+ final Iterator<? extends MappingItem> mapItems) {
+
+ OperationOptionsBuilder builder = new OperationOptionsBuilder().setPageSize(pageSize);
+ if (pagedResultsCookie != null) {
+ builder.setPagedResultsCookie(pagedResultsCookie);
+ }
+ builder.setSortKeys(CollectionUtils.collect(orderBy, new Transformer<OrderByClause, SortKey>() {
+
+ @Override
+ public SortKey transform(final OrderByClause clause) {
+ return new SortKey(clause.getField(), clause.getDirection() == OrderByClause.Direction.ASC);
+ }
+ }, new ArrayList<SortKey>(orderBy.size())));
+
+ builder.setAttributesToGet(MappingUtils.buildOperationOptions(mapItems).getAttributesToGet());
+
+ search(objectClass, filter, handler, builder.build());
+ }
+
+ @Override
+ public ConnInstance getConnInstance() {
+ return connInstance;
+ }
+
+ private Object getPropertyValue(final String propType, final List<?> values) {
+ Object value = null;
+
+ try {
+ 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=" + connInstance.getCapabilities() + '}';
+ }
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/9da95172/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 37dc851,0000000..9367edc
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@@ -1,604 -1,0 +1,603 @@@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.provisioning.java.propagation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.collections4.IteratorUtils;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
+import org.apache.syncope.common.lib.types.TraceLevel;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.TaskDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
+import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.misc.AuditManager;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.misc.utils.ConnObjectUtils;
+import org.apache.syncope.core.misc.utils.ExceptionUtils2;
+import org.apache.syncope.core.misc.utils.MappingUtils;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
+import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
+import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
+import org.identityconnectors.framework.common.exceptions.ConnectorException;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+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.Uid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional(rollbackFor = { Throwable.class })
+public abstract class AbstractPropagationTaskExecutor implements PropagationTaskExecutor {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(PropagationTaskExecutor.class);
+
+ /**
+ * Connector factory.
+ */
+ @Autowired
+ protected ConnectorFactory connFactory;
+
+ /**
+ * ConnObjectUtils.
+ */
+ @Autowired
+ protected ConnObjectUtils connObjectUtils;
+
+ /**
+ * Any object DAO.
+ */
+ @Autowired
+ protected AnyObjectDAO anyObjectDAO;
+
+ /**
+ * User DAO.
+ */
+ @Autowired
+ protected UserDAO userDAO;
+
+ /**
+ * User DAO.
+ */
+ @Autowired
+ protected GroupDAO groupDAO;
+
+ /**
+ * Task DAO.
+ */
+ @Autowired
+ protected TaskDAO taskDAO;
+
+ @Autowired
+ protected VirSchemaDAO virSchemaDAO;
+
+ /**
+ * Notification Manager.
+ */
+ @Autowired
+ protected NotificationManager notificationManager;
+
+ /**
+ * Audit Manager.
+ */
+ @Autowired
+ protected AuditManager auditManager;
+
+ @Autowired
+ protected EntityFactory entityFactory;
+
+ @Autowired
+ protected VirAttrCache virAttrCache;
+
+ @Override
+ public TaskExec execute(final PropagationTask task) {
+ return execute(task, null);
+ }
+
+ protected List<PropagationActions> getPropagationActions(final ExternalResource resource) {
+ List<PropagationActions> result = new ArrayList<>();
+
+ if (!resource.getPropagationActionsClassNames().isEmpty()) {
+ for (String className : resource.getPropagationActionsClassNames()) {
+ try {
+ Class<?> actionsClass = Class.forName(className);
+ result.add((PropagationActions) ApplicationContextProvider.getBeanFactory().
+ createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true));
+ } catch (ClassNotFoundException e) {
+ LOG.error("Invalid PropagationAction class name '{}' for resource {}", resource, className, e);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Transform a {@link Collection} of {@link Attribute} instances into a {@link Map}.
+ * The key to each element in the map is the {@code name} of an {@link Attribute}.
+ * The value of each element in the map is the {@link Attribute} instance with that name.
+ * <br/>
+ * Different from the original because:
+ * <ul>
+ * <li>map keys are transformed toUpperCase()</li>
+ * <li>returned map is mutable</li>
+ * </ul>
+ *
+ * @param attributes set of attribute to transform to a map.
+ * @return a map of string and attribute.
+ *
+ * @see org.identityconnectors.framework.common.objects.AttributeUtil#toMap(java.util.Collection)
+ */
+ private Map<String, Attribute> toMap(final Collection<? extends Attribute> attributes) {
+ Map<String, Attribute> map = new HashMap<>();
+ for (Attribute attr : attributes) {
+ map.put(attr.getName().toUpperCase(), attr);
+ }
+ return map;
+ }
+
+ protected void createOrUpdate(
+ final PropagationTask task,
+ final ConnectorObject beforeObj,
+ final Connector connector,
+ final Boolean[] propagationAttempted) {
+
+ // set of attributes to be propagated
+ Set<Attribute> attributes = new HashSet<>(task.getAttributes());
+
+ // check if there is any missing or null / empty mandatory attribute
+ Set<Object> mandatoryAttrNames = new HashSet<>();
+ Attribute mandatoryMissing = AttributeUtil.find(MANDATORY_MISSING_ATTR_NAME, task.getAttributes());
+ if (mandatoryMissing != null) {
+ attributes.remove(mandatoryMissing);
+
+ if (beforeObj == null) {
+ mandatoryAttrNames.addAll(mandatoryMissing.getValue());
+ }
+ }
+ Attribute mandatoryNullOrEmpty = AttributeUtil.find(MANDATORY_NULL_OR_EMPTY_ATTR_NAME, task.getAttributes());
+ if (mandatoryNullOrEmpty != null) {
+ attributes.remove(mandatoryNullOrEmpty);
+
+ mandatoryAttrNames.addAll(mandatoryNullOrEmpty.getValue());
+ }
+ if (!mandatoryAttrNames.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Not attempted because there are mandatory attributes without value(s): " + mandatoryAttrNames);
+ }
+
+ if (beforeObj == null) {
+ LOG.debug("Create {} on {}", attributes, task.getResource().getKey());
+ connector.create(new ObjectClass(task.getObjectClassName()), attributes, null, propagationAttempted);
+ } else {
+ // 1. check if rename is really required
+ Name newName = (Name) AttributeUtil.find(Name.NAME, attributes);
+
+ LOG.debug("Rename required with value {}", newName);
+
+ if (newName != null && newName.equals(beforeObj.getName())
+ && !newName.getNameValue().equals(beforeObj.getUid().getUidValue())) {
+
+ LOG.debug("Remote object name unchanged");
+ attributes.remove(newName);
+ }
+
+ // 2. check wether anything is actually needing to be propagated, i.e. if there is attribute
+ // difference between beforeObj - just read above from the connector - and the values to be propagated
+ Map<String, Attribute> originalAttrMap = toMap(beforeObj.getAttributes());
+ Map<String, Attribute> updateAttrMap = toMap(attributes);
+
+ // Only compare attribute from beforeObj that are also being updated
+ Set<String> skipAttrNames = originalAttrMap.keySet();
+ skipAttrNames.removeAll(updateAttrMap.keySet());
+ for (String attrName : new HashSet<>(skipAttrNames)) {
+ originalAttrMap.remove(attrName);
+ }
+
+ Set<Attribute> originalAttrs = new HashSet<>(originalAttrMap.values());
+
+ if (originalAttrs.equals(attributes)) {
+ LOG.debug("Don't need to propagate anything: {} is equal to {}", originalAttrs, attributes);
+ } else {
+ LOG.debug("Attributes that would be updated {}", attributes);
+
+ Set<Attribute> strictlyModified = new HashSet<>();
+ for (Attribute attr : attributes) {
+ if (!originalAttrs.contains(attr)) {
+ strictlyModified.add(attr);
+ }
+ }
+
+ // 3. provision entry
+ LOG.debug("Update {} on {}", strictlyModified, task.getResource().getKey());
+
+ connector.update(
+ beforeObj.getObjectClass(), beforeObj.getUid(), strictlyModified, null, propagationAttempted);
+ }
+ }
+ }
+
+ protected Any<?> getAny(final PropagationTask task) {
+ Any<?> any = null;
+
+ if (task.getAnyKey() != null) {
+ switch (task.getAnyTypeKind()) {
+ case USER:
+ try {
+ any = userDAO.authFind(task.getAnyKey());
+ } catch (Exception e) {
+ LOG.error("Could not read user {}", task.getAnyKey(), e);
+ }
+ break;
+
+ case GROUP:
+ try {
+ any = groupDAO.authFind(task.getAnyKey());
+ } catch (Exception e) {
+ LOG.error("Could not read group {}", task.getAnyKey(), e);
+ }
+ break;
+
+ case ANY_OBJECT:
+ default:
+ try {
+ any = anyObjectDAO.authFind(task.getAnyKey());
+ } catch (Exception e) {
+ LOG.error("Could not read any object {}", task.getAnyKey(), e);
+ }
+ break;
+ }
+ }
+
+ return any;
+ }
+
+ protected void delete(
+ final PropagationTask task,
+ final ConnectorObject beforeObj,
+ final Connector connector,
+ final Boolean[] propagationAttempted) {
+
+ if (beforeObj == null) {
+ LOG.debug("{} not found on external resource: ignoring delete", task.getConnObjectKey());
+ } else {
+ /*
+ * We must choose here whether to
+ * a. actually delete the provided any object from the external resource
+ * b. just update the provided any object data onto the external resource
+ *
+ * (a) happens when either there is no any object associated with the PropagationTask (this takes place
+ * when the task is generated via UserLogic.delete() / GroupLogic.delete()) or the provided updated
+ * any object hasn't the current resource assigned (when the task is generated via
+ * UserController.update() / GroupLogic.update()).
+ *
+ * (b) happens when the provided updated any object does have the current resource assigned (when the task
+ * is generated via UserLogic.update() / GroupLogic.updae()): this basically means that before such
+ * update, this any object used to have the current resource assigned by more than one mean (for example,
+ * two different memberships with the same resource).
+ */
+ Any<?> any = getAny(task);
+ Collection<String> resources = any instanceof User
+ ? userDAO.findAllResourceNames((User) any)
+ : any instanceof AnyObject
+ ? anyObjectDAO.findAllResourceNames((AnyObject) any)
+ : any instanceof Group
+ ? ((Group) any).getResourceNames()
+ : Collections.<String>emptySet();
+ if (!resources.contains(task.getResource().getKey())) {
+ LOG.debug("Delete {} on {}", beforeObj.getUid(), task.getResource().getKey());
+
+ connector.delete(beforeObj.getObjectClass(), beforeObj.getUid(), null, propagationAttempted);
+ } else {
+ createOrUpdate(task, beforeObj, connector, propagationAttempted);
+ }
+ }
+ }
+
+ @Override
+ public TaskExec execute(final PropagationTask task, final PropagationReporter reporter) {
+ List<PropagationActions> actions = getPropagationActions(task.getResource());
+
+ Date startDate = new Date();
+
+ TaskExec execution = entityFactory.newEntity(TaskExec.class);
+ execution.setStatus(PropagationTaskExecStatus.CREATED.name());
+
+ String taskExecutionMessage = null;
+ String failureReason = null;
+
+ // Flag to state whether any propagation has been attempted
+ Boolean[] propagationAttempted = new Boolean[] { false };
+
+ ConnectorObject beforeObj = null;
+ ConnectorObject afterObj = null;
+
+ Provision provision = null;
+ Connector connector = null;
+ Result result;
+ try {
+ provision = task.getResource().getProvision(new ObjectClass(task.getObjectClassName()));
+ connector = connFactory.getConnector(task.getResource());
+
+ // Try to read remote object BEFORE any actual operation
+ beforeObj = getRemoteObject(task, connector, provision, false);
+
+ for (PropagationActions action : actions) {
+ action.before(task, beforeObj);
+ }
+
+ switch (task.getOperation()) {
+ case CREATE:
+ case UPDATE:
+ createOrUpdate(task, beforeObj, connector, propagationAttempted);
+ break;
+
+ case DELETE:
+ delete(task, beforeObj, connector, propagationAttempted);
+ break;
+
+ default:
+ }
+
+ execution.setStatus(propagationAttempted[0]
+ ? PropagationTaskExecStatus.SUCCESS.name()
+ : PropagationTaskExecStatus.NOT_ATTEMPTED.name());
+
+ for (PropagationActions action : actions) {
+ action.after(task, execution, afterObj);
+ }
+
+ LOG.debug("Successfully propagated to {}", task.getResource());
+ result = Result.SUCCESS;
+ } catch (Exception e) {
+ result = Result.FAILURE;
+ LOG.error("Exception during provision on resource " + task.getResource().getKey(), e);
+
+ if (e instanceof ConnectorException && e.getCause() != null) {
+ taskExecutionMessage = e.getCause().getMessage();
+ if (e.getCause().getMessage() == null) {
+ failureReason = e.getMessage();
+ } else {
+ failureReason = e.getMessage() + "\n\n Cause: " + e.getCause().getMessage().split("\n")[0];
+ }
+ } else {
+ taskExecutionMessage = ExceptionUtils2.getFullStackTrace(e);
+ if (e.getCause() == null) {
+ failureReason = e.getMessage();
+ } else {
+ failureReason = e.getMessage() + "\n\n Cause: " + e.getCause().getMessage().split("\n")[0];
+ }
+ }
+
+ try {
+ execution.setStatus(PropagationTaskExecStatus.FAILURE.name());
+ } catch (Exception wft) {
+ LOG.error("While executing KO action on {}", execution, wft);
+ }
+
+ propagationAttempted[0] = true;
+
+ for (PropagationActions action : actions) {
+ action.onError(task, execution, e);
+ }
+ } finally {
+ // Try to read remote object AFTER any actual operation
+ if (connector != null) {
+ try {
+ afterObj = getRemoteObject(task, connector, provision, true);
+ } catch (Exception ignore) {
+ // ignore exception
+ LOG.error("Error retrieving after object", ignore);
+ }
+ }
+
+ execution.setStartDate(startDate);
+ execution.setMessage(taskExecutionMessage);
+ execution.setEndDate(new Date());
+
+ LOG.debug("Execution finished: {}", execution);
+
+ if (hasToBeregistered(task, execution)) {
+ LOG.debug("Execution to be stored: {}", execution);
+
+ execution.setTask(task);
+ task.addExec(execution);
+
+ taskDAO.save(task);
+ // needed to generate a value for the execution key
+ taskDAO.flush();
+ }
+
+ if (reporter != null) {
+ reporter.onSuccessOrNonPriorityResourceFailures(
+ task,
+ PropagationTaskExecStatus.valueOf(execution.getStatus()),
+ failureReason,
+ beforeObj,
+ afterObj);
+ }
+ }
+
+ notificationManager.createTasks(
+ AuditElements.EventCategoryType.PROPAGATION,
+ task.getAnyTypeKind().name().toLowerCase(),
+ task.getResource().getKey(),
+ task.getOperation().name().toLowerCase(),
+ result,
+ beforeObj, // searching for before object is too much expensive ...
+ new Object[] { execution, afterObj },
+ task);
+
+ auditManager.audit(
+ AuditElements.EventCategoryType.PROPAGATION,
+ task.getAnyTypeKind().name().toLowerCase(),
+ task.getResource().getKey(),
+ task.getOperation().name().toLowerCase(),
+ result,
+ beforeObj, // searching for before object is too much expensive ...
+ new Object[] { execution, afterObj },
+ task);
+
+ return execution;
+ }
+
+ @Override
+ public void execute(final Collection<PropagationTask> tasks) {
+ execute(tasks, null, false);
+ }
+
+ protected abstract void doExecute(
+ Collection<PropagationTask> tasks, PropagationReporter reporter, boolean nullPriorityAsync);
+
+ @Override
+ public void execute(
+ final Collection<PropagationTask> tasks,
+ final PropagationReporter reporter,
+ final boolean nullPriorityAsync) {
+
+ try {
+ doExecute(tasks, reporter, nullPriorityAsync);
+ } catch (PropagationException e) {
+ LOG.error("Error propagation priority resource", e);
+ reporter.onPriorityResourceFailure(e.getResourceName(), tasks);
+ }
+ }
+
+ /**
+ * Check whether an execution has to be stored, for a given task.
+ *
+ * @param task propagation task
+ * @param execution to be decide whether to store or not
+ * @return true if execution has to be store, false otherwise
+ */
+ protected boolean hasToBeregistered(final PropagationTask task, final TaskExec execution) {
+ boolean result;
+
+ boolean failed = PropagationTaskExecStatus.valueOf(execution.getStatus()) != PropagationTaskExecStatus.SUCCESS;
+
+ switch (task.getOperation()) {
+
+ case CREATE:
+ result = (failed && task.getResource().getCreateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || task.getResource().getCreateTraceLevel() == TraceLevel.ALL;
+ break;
+
+ case UPDATE:
+ result = (failed && task.getResource().getUpdateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || task.getResource().getUpdateTraceLevel() == TraceLevel.ALL;
+ break;
+
+ case DELETE:
+ result = (failed && task.getResource().getDeleteTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal())
+ || task.getResource().getDeleteTraceLevel() == TraceLevel.ALL;
+ break;
+
+ default:
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Get remote object for given task.
+ *
+ * @param connector connector facade proxy.
+ * @param task current propagation task.
+ * @param provision provision
+ * @param latest 'FALSE' to retrieve object using old connObjectKey if not null.
+ * @return remote connector object.
+ */
+ protected ConnectorObject getRemoteObject(
+ final PropagationTask task,
+ final Connector connector,
+ final Provision provision,
+ final boolean latest) {
+
+ String connObjectKey = latest || task.getOldConnObjectKey() == null
+ ? task.getConnObjectKey()
+ : task.getOldConnObjectKey();
+
+ List<MappingItem> linkingMappingItems = new ArrayList<>();
+ for (VirSchema schema : virSchemaDAO.findByProvision(provision)) {
+ linkingMappingItems.add(schema.asLinkingMappingItem());
+ }
+
+ ConnectorObject obj = null;
+ try {
+ obj = connector.getObject(
- task.getOperation(),
+ new ObjectClass(task.getObjectClassName()),
+ new Uid(connObjectKey),
+ MappingUtils.buildOperationOptions(IteratorUtils.chainedIterator(
- MappingUtils.getPropagationMappingItems(provision).iterator(),
- linkingMappingItems.iterator())));
++ MappingUtils.getPropagationMappingItems(provision).iterator(),
++ linkingMappingItems.iterator())));
+
+ for (MappingItem item : linkingMappingItems) {
+ Attribute attr = obj.getAttributeByName(item.getExtAttrName());
+ if (attr == null) {
+ virAttrCache.expire(task.getAnyType(), task.getAnyKey(), item.getIntAttrName());
+ } else {
+ VirAttrCacheValue cacheValue = new VirAttrCacheValue();
+ cacheValue.setValues(attr.getValue());
+ virAttrCache.put(task.getAnyType(), task.getAnyKey(), item.getIntAttrName(), cacheValue);
+ }
+ }
+ } catch (TimeoutException toe) {
+ LOG.debug("Request timeout", toe);
+ throw toe;
+ } catch (RuntimeException ignore) {
+ LOG.debug("While resolving {}", connObjectKey, ignore);
+ }
+
+ return obj;
+ }
+}