This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit d3e17cf2586839bb5f69b3fce0bcc274314594d6
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Mon Nov 2 10:33:43 2026 +0100

    Improving concurrent Push handling
---
 .../syncope/core/logic/ReconciliationLogic.java    | 44 +++++++++++++----
 .../apache/syncope/core/logic/ResourceLogic.java   | 55 ++++++++++++++++------
 .../api/pushpull/ProvisioningProfile.java          |  8 ++++
 .../java/pushpull/AbstractPushResultHandler.java   |  9 ++--
 .../pushpull/DefaultUserPushResultHandler.java     | 21 ++++-----
 .../java/pushpull/OutboundMatcher.java             | 45 ++++--------------
 .../java/pushpull/PushJobDelegate.java             | 37 +++++++++++++--
 7 files changed, 143 insertions(+), 76 deletions(-)

diff --git 
a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
 
b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index 9c37e4df0f..e248beae34 100644
--- 
a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ 
b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -18,13 +18,17 @@
  */
 package org.apache.syncope.core.logic;
 
+import static org.apache.syncope.core.logic.AbstractLogic.LOG;
+
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -70,6 +74,7 @@ import 
org.apache.syncope.core.persistence.api.utils.RealmUtils;
 import org.apache.syncope.core.provisioning.api.ConnectorManager;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import 
org.apache.syncope.core.provisioning.api.pushpull.ConstantReconFilterBuilder;
 import 
org.apache.syncope.core.provisioning.api.pushpull.KeyValueReconFilterBuilder;
 import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
@@ -86,6 +91,7 @@ import 
org.apache.syncope.core.provisioning.java.pushpull.stream.StreamPullJobDe
 import 
org.apache.syncope.core.provisioning.java.pushpull.stream.StreamPushJobDelegate;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.implementation.ImplementationManager;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import 
org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.identityconnectors.framework.common.objects.Attribute;
@@ -133,6 +139,8 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
 
     protected final ConfigurableApplicationContext ctx;
 
+    protected final Map<String, PropagationActions> 
perContextPropagationActions = new ConcurrentHashMap<>();
+
     public ReconciliationLogic(
             final AnyUtilsFactory anyUtilsFactory,
             final AnyTypeDAO anyTypeDAO,
@@ -235,7 +243,7 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
                 orElseThrow(() -> new NotFoundException("Resource '" + 
resourceKey + '\''));
         Provision provision = resource.getProvisionByAnyType(anyType.getKey()).
                 orElseThrow(() -> new NotFoundException(
-                "Provision for " + anyType + " on Resource '" + resourceKey + 
"'"));
+                        "Provision for " + anyType + " on Resource '" + 
resourceKey + "'"));
         if (provision.getMapping() == null) {
             throw new NotFoundException("Mapping for " + anyType + " on 
Resource '" + resourceKey + "'");
         }
@@ -243,6 +251,24 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
         return new ProvisioningInfo(anyType, resource, provision);
     }
 
+    protected List<PropagationActions> getPropagationActions(final 
ExternalResource resource) {
+        List<PropagationActions> result = new ArrayList<>();
+
+        resource.getPropagationActions().forEach(impl -> {
+            try {
+                result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
+                        impl,
+                        () -> perContextPropagationActions.get(impl.getKey()),
+                        instance -> 
perContextPropagationActions.put(impl.getKey(), instance)));
+            } catch (Exception e) {
+                LOG.error("While building {}", impl, e);
+            }
+        });
+
+        return result;
+    }
+
     @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
     @Transactional(readOnly = true)
     public ReconStatus status(
@@ -255,8 +281,8 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
 
         Item connObjectKeyItem = 
MappingUtils.getConnObjectKeyItem(info.provision()).
                 orElseThrow(() -> new NotFoundException(
-                "ConnObjectKey for " + info.anyType().getKey()
-                + " on resource '" + info.resource().getKey() + "'"));
+                        "ConnObjectKey for " + info.anyType().getKey()
+                        + " on resource '" + info.resource().getKey() + "'"));
 
         Any any = getAny(info.provision(), info.anyType().getKind(), anyKey);
 
@@ -267,12 +293,14 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
         status.setRealm(any.getRealm().getFullPath());
         status.setOnSyncope(getOnSyncope(any, connObjectKeyItem, 
info.resource(), info.provision()));
 
+        getPropagationActions(info.resource()).
+                forEach(action -> 
moreAttrsToGet.addAll(action.moreAttrsToGet(Optional.empty(), 
info.provision())));
         List<ConnectorObject> connObjs = outboundMatcher.match(
                 connectorManager.getConnector(info.resource()),
                 any,
                 info.resource(),
                 info.provision(),
-                Optional.of(moreAttrsToGet.toArray(String[]::new)));
+                moreAttrsToGet.toArray(String[]::new));
         if (!connObjs.isEmpty()) {
             status.setOnResource(ConnObjectUtils.getConnObjectTO(
                     outboundMatcher.getFIQL(connObjs.getFirst(), 
info.resource(), info.provision()),
@@ -335,8 +363,8 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
         if (syncDeltaBuilder.getObject() != null) {
             Item connObjectKeyItem = 
MappingUtils.getConnObjectKeyItem(info.provision()).
                     orElseThrow(() -> new NotFoundException(
-                    "ConnObjectKey for " + info.anyType().getKey()
-                    + " on resource '" + info.resource().getKey() + "'"));
+                            "ConnObjectKey for " + info.anyType().getKey()
+                            + " on resource '" + info.resource().getKey() + 
"'"));
 
             inboundMatcher.match(
                     syncDeltaBuilder.build(), info.resource(), 
info.provision(), info.anyType().getKind()).
@@ -547,8 +575,8 @@ public class ReconciliationLogic extends 
AbstractLogic<EntityTO> {
 
         String connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, 
info.resource(), info.provision()).
                 orElseThrow(() -> new NotFoundException(
-                "ConnObjectKey for " + info.anyType().getKey()
-                + " on resource '" + info.resource().getKey() + "'"));
+                        "ConnObjectKey for " + info.anyType().getKey()
+                        + " on resource '" + info.resource().getKey() + "'"));
 
         return pull(
                 info.resource(),
diff --git 
a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java 
b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index 4678327172..f02a14208f 100644
--- 
a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ 
b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -20,9 +20,12 @@ package org.apache.syncope.core.logic;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -50,9 +53,11 @@ import org.apache.syncope.core.provisioning.api.Connector;
 import org.apache.syncope.core.provisioning.api.ConnectorManager;
 import org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.implementation.ImplementationManager;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import 
org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
@@ -83,6 +88,8 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
 
     protected final AnyUtilsFactory anyUtilsFactory;
 
+    protected final Map<String, PropagationActions> 
perContextPropagationActions = new ConcurrentHashMap<>();
+
     public ResourceLogic(
             final ExternalResourceDAO resourceDAO,
             final AnyTypeDAO anyTypeDAO,
@@ -179,7 +186,7 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
                     orElseThrow(() -> new NotFoundException("AnyType " + 
anyTypeKey));
             Provision provision = 
resource.getProvisionByAnyType(anyType.getKey()).
                     orElseThrow(() -> new NotFoundException(
-                    "Provision for AnyType '" + anyTypeKey + "' in Resource '" 
+ key + '\''));
+                            "Provision for AnyType '" + anyTypeKey + "' in 
Resource '" + key + '\''));
 
             provision.setSyncToken(ConnObjectUtils.toString(
                     connector.getLatestSyncToken(new 
ObjectClass(provision.getObjectClass()))));
@@ -208,7 +215,7 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
                     orElseThrow(() -> new NotFoundException("AnyType " + 
anyTypeKey));
             Provision provision = 
resource.getProvisionByAnyType(anyType.getKey()).
                     orElseThrow(() -> new NotFoundException(
-                    "Provision for AnyType '" + anyTypeKey + "' in Resource '" 
+ key + '\''));
+                            "Provision for AnyType '" + anyTypeKey + "' in 
Resource '" + key + '\''));
 
             provision.setSyncToken(null);
         }
@@ -261,7 +268,7 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
                 orElseThrow(() -> new NotFoundException("Resource '" + 
resourceKey + '\''));
         Provision provision = resource.getProvisionByAnyType(anyType.getKey()).
                 orElseThrow(() -> new NotFoundException(
-                "Provision for " + anyType + " on Resource '" + resourceKey + 
"'"));
+                        "Provision for " + anyType + " on Resource '" + 
resourceKey + "'"));
         if (provision.getMapping() == null) {
             throw new NotFoundException("Mapping for " + anyType + " on 
Resource '" + resourceKey + "'");
         }
@@ -285,8 +292,26 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
 
         // 2.get ConnObjectKey value
         return mappingManager.getConnObjectKeyValue(any, info.resource(), 
info.provision()).
-                orElseThrow(() -> new NotFoundException(
-                "No ConnObjectKey value found for " + anyTypeKey + " " + 
anyKey + " on resource '" + key + "'"));
+                orElseThrow(() -> new NotFoundException("No ConnObjectKey 
value found for "
+                        + anyTypeKey + " " + anyKey + " on resource '" + key + 
"'"));
+    }
+
+    protected List<PropagationActions> getPropagationActions(final 
ExternalResource resource) {
+        List<PropagationActions> result = new ArrayList<>();
+
+        resource.getPropagationActions().forEach(impl -> {
+            try {
+                result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
+                        impl,
+                        () -> perContextPropagationActions.get(impl.getKey()),
+                        instance -> 
perContextPropagationActions.put(impl.getKey(), instance)));
+            } catch (Exception e) {
+                LOG.error("While building {}", impl, e);
+            }
+        });
+
+        return result;
     }
 
     @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
@@ -304,12 +329,15 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
                 orElseThrow(() -> new NotFoundException(info.anyType() + " " + 
anyKey));
 
         // 2. find on resource
+        Set<String> moreAttrsToGet = new HashSet<>();
+        getPropagationActions(info.resource()).
+                forEach(action -> 
moreAttrsToGet.addAll(action.moreAttrsToGet(Optional.empty(), 
info.provision())));
         List<ConnectorObject> connObjs = outboundMatcher.match(
                 connectorManager.getConnector(info.resource()),
                 any,
                 info.resource(),
                 info.provision(),
-                Optional.empty());
+                moreAttrsToGet.toArray(String[]::new));
         if (connObjs.isEmpty()) {
             throw new NotFoundException(
                     "Object " + any + " with class " + 
info.provision().getObjectClass()
@@ -336,21 +364,20 @@ public class ResourceLogic extends 
AbstractTransactionalLogic<ResourceTO> {
 
         Item connObjectKeyItem = 
MappingUtils.getConnObjectKeyItem(info.provision()).
                 orElseThrow(() -> new NotFoundException(
-                "ConnObjectKey mapping for " + info.anyType().getKey()
-                + " on resource '" + info.resource().getKey() + "'"));
+                        "ConnObjectKey mapping for " + info.anyType().getKey()
+                        + " on resource '" + info.resource().getKey() + "'"));
 
         return outboundMatcher.matchByConnObjectKeyValue(
                 connectorManager.getConnector(info.resource()),
                 connObjectKeyItem,
                 connObjectKeyValue,
-                info.provision(),
-                Optional.empty()).
+                info.provision()).
                 map(connectorObject -> ConnObjectUtils.getConnObjectTO(
-                outboundMatcher.getFIQL(connectorObject, info.resource(), 
info.provision()),
-                connectorObject.getAttributes())).
+                        outboundMatcher.getFIQL(connectorObject, 
info.resource(), info.provision()),
+                        connectorObject.getAttributes())).
                 orElseThrow(() -> new NotFoundException(
-                "Object " + connObjectKeyValue + " with class " + 
info.provision().getObjectClass()
-                + " not found on resource " + info.resource().getKey()));
+                        "Object " + connObjectKeyValue + " with class " + 
info.provision().getObjectClass()
+                        + " not found on resource " + 
info.resource().getKey()));
     }
 
     @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_LIST_CONNOBJECT + "')")
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
index 3cb8e6034a..cc90962e68 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
+++ 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.api.pushpull;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import org.apache.syncope.common.lib.to.ProvisioningReport;
@@ -25,6 +26,7 @@ import 
org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 
 public class ProvisioningProfile<T extends ProvisioningTask<?>, A extends 
ProvisioningActions> {
 
@@ -42,6 +44,8 @@ public class ProvisioningProfile<T extends 
ProvisioningTask<?>, A extends Provis
 
     private final List<A> actions;
 
+    private final List<PropagationActions> propagationActions = new 
ArrayList<>();
+
     private final List<ProvisioningReport> results = new 
CopyOnWriteArrayList<>();
 
     public ProvisioningProfile(
@@ -86,6 +90,10 @@ public class ProvisioningProfile<T extends 
ProvisioningTask<?>, A extends Provis
         return actions;
     }
 
+    public List<PropagationActions> getPropagationActions() {
+        return propagationActions;
+    }
+
     public List<ProvisioningReport> getResults() {
         return results;
     }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index 418c70bd2a..06db727110 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -300,13 +300,16 @@ public abstract class AbstractPushResultHandler extends 
AbstractSyncopeResultHan
 
         // Try to read remote object BEFORE any actual operation
         Set<String> moreAttrsToGet = new HashSet<>();
-        profile.getActions().forEach(action -> 
moreAttrsToGet.addAll(action.moreAttrsToGet(profile, any)));
+        profile.getActions().
+                forEach(action -> 
moreAttrsToGet.addAll(action.moreAttrsToGet(profile, any)));
+        profile.getPropagationActions().
+                forEach(action -> 
moreAttrsToGet.addAll(action.moreAttrsToGet(Optional.empty(), provision)));
         List<ConnectorObject> connObjs = outboundMatcher.match(
                 profile.getConnector(),
                 any,
                 profile.getTask().getResource(),
                 provision,
-                Optional.of(moreAttrsToGet.toArray(String[]::new)));
+                moreAttrsToGet.toArray(String[]::new));
         LOG.debug("Match(es) found for {} as {}: {}", any, 
provision.getObjectClass(), connObjs);
 
         if (connObjs.size() > 1) {
@@ -507,7 +510,7 @@ public abstract class AbstractPushResultHandler extends 
AbstractSyncopeResultHan
                             any,
                             profile.getTask().getResource(),
                             provision,
-                            
Optional.of(moreAttrsToGet.toArray(String[]::new)));
+                            moreAttrsToGet.toArray(String[]::new));
                 }
             } catch (IgnoreProvisionException e) {
                 throw e;
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
index b99933fbdf..542b68b3f6 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -83,8 +83,8 @@ public class DefaultUserPushResultHandler extends 
AbstractPushResultHandler impl
         PropagationByResource<Pair<String, String>> propByLinkedAccount = new 
PropagationByResource<>();
         ((User) 
any).getLinkedAccounts(profile.getTask().getResource().getKey()).
                 forEach(account -> propByLinkedAccount.add(
-                ResourceOperation.CREATE,
-                Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
+                        ResourceOperation.CREATE,
+                        Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
 
         PropagationReporter reporter = 
taskExecutor.execute(propagationManager.getUserCreateTasks(
                 before.getKey(),
@@ -118,8 +118,8 @@ public class DefaultUserPushResultHandler extends 
AbstractPushResultHandler impl
         PropagationByResource<Pair<String, String>> propByLinkedAccount = new 
PropagationByResource<>();
         ((User) 
any).getLinkedAccounts(profile.getTask().getResource().getKey()).
                 forEach(account -> propByLinkedAccount.add(
-                ResourceOperation.UPDATE,
-                Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
+                        ResourceOperation.UPDATE,
+                        Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
 
         List<PropagationTaskInfo> taskInfos = 
propagationManager.getUpdateTasks(
                 null,
@@ -152,8 +152,8 @@ public class DefaultUserPushResultHandler extends 
AbstractPushResultHandler impl
         PropagationByResource<Pair<String, String>> propByLinkedAccount = new 
PropagationByResource<>();
         ((User) 
any).getLinkedAccounts(profile.getTask().getResource().getKey()).
                 forEach(account -> propByLinkedAccount.add(
-                ResourceOperation.DELETE,
-                Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
+                        ResourceOperation.DELETE,
+                        Pair.of(account.getResource().getKey(), 
account.getConnObjectKeyValue())));
 
         List<PropagationTaskInfo> taskInfos = 
propagationManager.getDeleteTasks(
                 any.getType().getKind(),
@@ -222,11 +222,10 @@ public class DefaultUserPushResultHandler extends 
AbstractPushResultHandler impl
         // Try to read remote object BEFORE any actual operation
         Optional<ConnectorObject> connObj = 
MappingUtils.getConnObjectKeyItem(provision).
                 flatMap(connObjectKeyItem -> 
outboundMatcher.matchByConnObjectKeyValue(
-                profile.getConnector(),
-                connObjectKeyItem,
-                account.getConnObjectKeyValue(),
-                provision,
-                Optional.empty()));
+                        profile.getConnector(),
+                        connObjectKeyItem,
+                        account.getConnObjectKeyValue(),
+                        provision));
         LOG.debug("Match found for linked account {} as {}: {}", account, 
provision.getObjectClass(), connObj);
 
         ConnectorObject beforeObj = connObj.orElse(null);
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
index 5fe2d98e3c..f8a422df20 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
@@ -25,7 +25,6 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Stream;
 import org.apache.syncope.common.lib.to.Item;
 import org.apache.syncope.common.lib.to.Provision;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -64,8 +63,6 @@ public class OutboundMatcher {
 
     protected final AnyUtilsFactory anyUtilsFactory;
 
-    protected final Map<String, PropagationActions> perContextActions = new 
ConcurrentHashMap<>();
-
     protected final Map<String, PushCorrelationRule> 
perContextPushCorrelationRules = new ConcurrentHashMap<>();
 
     public OutboundMatcher(
@@ -137,14 +134,14 @@ public class OutboundMatcher {
                         connector,
                         rule.get().getFilter(any, taskInfo.getResource(), 
provision),
                         provision,
-                        Optional.of(moreAttrsToGet.toArray(String[]::new))));
+                        moreAttrsToGet.toArray(String[]::new)));
             } else {
                 
MappingUtils.getConnObjectKeyItem(provision).flatMap(connObjectKeyItem -> 
matchByConnObjectKeyValue(
                         connector,
                         connObjectKeyItem,
                         connObjectKeyValue,
                         provision,
-                        
Optional.of(moreAttrsToGet.toArray(String[]::new)))).ifPresent(result::add);
+                        
moreAttrsToGet.toArray(String[]::new))).ifPresent(result::add);
             }
         } catch (RuntimeException e) {
             LOG.error("Could not match {} with any existing {}", any, 
provision.getObjectClass(), e);
@@ -153,37 +150,13 @@ public class OutboundMatcher {
         return result;
     }
 
-    protected List<PropagationActions> getPropagationActions(final 
ExternalResource resource) {
-        List<PropagationActions> result = new ArrayList<>();
-
-        resource.getPropagationActions().forEach(impl -> {
-            try {
-                result.add(ImplementationManager.build(
-                        AuthContextUtils.getDomain(),
-                        impl,
-                        () -> perContextActions.get(impl.getKey()),
-                        instance -> perContextActions.put(impl.getKey(), 
instance)));
-            } catch (Exception e) {
-                LOG.error("While building {}", impl, e);
-            }
-        });
-
-        return result;
-    }
-
     @Transactional(readOnly = true)
     public List<ConnectorObject> match(
             final Connector connector,
             final Any any,
             final ExternalResource resource,
             final Provision provision,
-            final Optional<String[]> moreAttrsToGet) {
-
-        Stream<String> matgFromPropagationActions = 
getPropagationActions(resource).stream().
-                flatMap(a -> a.moreAttrsToGet(Optional.empty(), 
provision).stream());
-        Optional<String[]> effectiveMATG = Optional.of(Stream.concat(
-                moreAttrsToGet.stream().flatMap(Stream::of),
-                matgFromPropagationActions).toArray(String[]::new));
+            final String... moreAttrsToGet) {
 
         Optional<PushCorrelationRule> rule = rule(resource, provision);
 
@@ -194,7 +167,7 @@ public class OutboundMatcher {
                         connector,
                         rule.get().getFilter(any, resource, provision),
                         provision,
-                        effectiveMATG));
+                        moreAttrsToGet));
             } else {
                 Optional<Item> connObjectKeyItem = 
MappingUtils.getConnObjectKeyItem(provision);
                 Optional<String> connObjectKeyValue = 
mappingManager.getConnObjectKeyValue(any, resource, provision);
@@ -205,7 +178,7 @@ public class OutboundMatcher {
                             connObjectKeyItem.get(),
                             connObjectKeyValue.get(),
                             provision,
-                            effectiveMATG).
+                            moreAttrsToGet).
                             ifPresent(result::add);
                 }
             }
@@ -220,7 +193,7 @@ public class OutboundMatcher {
             final Connector connector,
             final Filter filter,
             final Provision provision,
-            final Optional<String[]> moreAttrsToGet) {
+            final String... moreAttrsToGet) {
 
         List<ConnectorObject> objs = new ArrayList<>();
         try {
@@ -237,7 +210,7 @@ public class OutboundMatcher {
                     return true;
                 }
             }, MappingUtils.buildOperationOptions(
-                    provision.getMapping().getItems().stream(), 
moreAttrsToGet.orElse(null)));
+                    provision.getMapping().getItems().stream(), 
moreAttrsToGet));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
             throw toe;
@@ -254,7 +227,7 @@ public class OutboundMatcher {
             final Item connObjectKeyItem,
             final String connObjectKeyValue,
             final Provision provision,
-            final Optional<String[]> moreAttrsToGet) {
+            final String... moreAttrsToGet) {
 
         ConnectorObject obj = null;
         try {
@@ -263,7 +236,7 @@ public class OutboundMatcher {
                     AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), 
connObjectKeyValue),
                     provision.isIgnoreCaseMatch(),
                     MappingUtils.buildOperationOptions(
-                            provision.getMapping().getItems().stream(), 
moreAttrsToGet.orElse(null)));
+                            provision.getMapping().getItems().stream(), 
moreAttrsToGet));
         } catch (TimeoutException toe) {
             LOG.debug("Request timeout", toe);
             throw toe;
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 6f60b26493..e5ad81f7cc 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -48,6 +48,7 @@ import 
org.apache.syncope.core.provisioning.api.ProvisionSorter;
 import org.apache.syncope.core.provisioning.api.job.JobExecutionContext;
 import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
 import org.apache.syncope.core.provisioning.api.job.StoppableJobDelegate;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyPushResultHandler;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
@@ -81,7 +82,9 @@ public class PushJobDelegate
 
     protected final Map<String, MutablePair<Integer, String>> handled = new 
ConcurrentHashMap<>();
 
-    protected final Map<String, PushActions> perContextActions = new 
ConcurrentHashMap<>();
+    protected final Map<String, PushActions> perContextOPushActions = new 
ConcurrentHashMap<>();
+
+    protected final Map<String, PropagationActions> 
perContextPropagationActions = new ConcurrentHashMap<>();
 
     protected PushResultHandlerDispatcher dispatcher;
 
@@ -139,7 +142,10 @@ public class PushJobDelegate
         return 
ctx.getBeanFactory().createBean(DefaultGroupPushResultHandler.class);
     }
 
-    protected List<PushActions> getPushActions(final String domain, final 
List<? extends Implementation> impls) {
+    protected List<PushActions> getPushActions(
+            final String domain,
+            final List<? extends Implementation> impls) {
+
         List<PushActions> result = new ArrayList<>();
 
         impls.forEach(impl -> {
@@ -147,8 +153,8 @@ public class PushJobDelegate
                 result.add(ImplementationManager.build(
                         domain,
                         impl,
-                        () -> perContextActions.get(impl.getKey()),
-                        instance -> perContextActions.put(impl.getKey(), 
instance)));
+                        () -> perContextOPushActions.get(impl.getKey()),
+                        instance -> perContextOPushActions.put(impl.getKey(), 
instance)));
             } catch (Exception e) {
                 LOG.warn("While building {}", impl, e);
             }
@@ -157,6 +163,27 @@ public class PushJobDelegate
         return result;
     }
 
+    protected List<PropagationActions> getPropagationActions(
+            final String domain,
+            final List<? extends Implementation> impls) {
+
+        List<PropagationActions> result = new ArrayList<>();
+
+        impls.forEach(impl -> {
+            try {
+                result.add(ImplementationManager.build(
+                        domain,
+                        impl,
+                        () -> perContextPropagationActions.get(impl.getKey()),
+                        instance -> 
perContextPropagationActions.put(impl.getKey(), instance)));
+            } catch (Exception e) {
+                LOG.error("While building {}", impl, e);
+            }
+        });
+
+        return result;
+    }
+
     @Override
     protected void init(
             final TaskType taskType,
@@ -175,6 +202,8 @@ public class PushJobDelegate
                 getPushActions(context.domain(), task.getActions()),
                 AuthContextUtils.getUsername(),
                 context.dryRun());
+        profile.getPropagationActions().addAll(
+                getPropagationActions(context.domain(), 
task.getResource().getPropagationActions()));
     }
 
     @Override

Reply via email to