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


The following commit(s) were added to refs/heads/master by this push:
     new 3bf77aa102 [SYNCOPE-1970] Expiring Implementation caches by OpenJPA 
RemoteCommitListener
3bf77aa102 is described below

commit 3bf77aa1027543a1240e51137e7dea8d3afe76f4
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Thu Jun 25 16:55:58 2026 +0200

    [SYNCOPE-1970] Expiring Implementation caches by OpenJPA 
RemoteCommitListener
---
 .../syncope/core/logic/AbstractAnyLogic.java       |  3 +-
 .../apache/syncope/core/logic/CommandLogic.java    |  8 ++-
 .../org/apache/syncope/core/logic/SchemaLogic.java |  2 +
 .../DefaultPlainAttrValidationManager.java         |  2 +
 ...ava => ConnectorManagerCacheEntryListener.java} |  6 +-
 .../jpa/dao/repo/ImplementationRepoExtImpl.java    |  5 +-
 .../ImplementationManagerCacheEntryListener.java   | 79 ++++++++++++++++++++++
 .../jpa/spring/DomainEntityManagerFactoryBean.java | 37 ++++++++--
 .../spring/DomainRoutingEntityManagerFactory.java  | 11 ++-
 .../neo4j/dao/repo/ImplementationRepoExtImpl.java  |  7 +-
 .../provisioning/java/DefaultMappingManager.java   | 16 +++--
 .../java/data/AttributableDataBinder.java          |  2 +
 .../java/data/ImplementationDataBinderImpl.java    |  5 +-
 .../provisioning/java/data/TaskDataBinderImpl.java |  3 +-
 .../core/provisioning/java/job/LiveSyncJob.java    |  2 +-
 .../provisioning/java/job/MacroJobDelegate.java    |  4 +-
 .../core/provisioning/java/job/TaskJob.java        |  2 +-
 .../provisioning/java/job/report/ReportJob.java    |  1 +
 .../notification/DefaultNotificationManager.java   |  1 +
 .../AbstractPropagationTaskExecutor.java           |  1 +
 .../pushpull/AbstractProvisioningJobDelegate.java  |  3 +-
 .../java/pushpull/AbstractPullExecutor.java        |  5 +-
 .../provisioning/java/pushpull/InboundMatcher.java | 13 ++--
 .../java/pushpull/LiveSyncJobDelegate.java         |  4 +-
 .../java/pushpull/OutboundMatcher.java             |  3 +
 .../java/pushpull/PullJobDelegate.java             | 11 +--
 .../java/pushpull/PushJobDelegate.java             |  7 +-
 .../java/pushpull/SinglePullJobDelegate.java       |  3 +-
 .../java/pushpull/SinglePushJobDelegate.java       |  3 +-
 .../pushpull/stream/StreamPullJobDelegate.java     |  3 +-
 .../pushpull/stream/StreamPushJobDelegate.java     |  3 +-
 .../core/provisioning/java/utils/MappingUtils.java |  2 +
 .../implementation/ImplementationManager.java      | 48 ++++++++-----
 .../core/spring/policy/DefaultRuleProvider.java    |  3 +
 .../spring/security/DefaultPasswordGenerator.java  |  1 +
 .../spring/implementation/GroovySandboxTest.java   |  3 +-
 .../spring/implementation/PasswordRuleTest.java    |  2 +
 .../syncope/core/logic/oidc/OIDCUserManager.java   |  6 +-
 .../apache/syncope/core/logic/SAML2SP4UILogic.java |  1 +
 .../core/logic/saml2/SAML2SP4UIUserManager.java    |  6 +-
 40 files changed, 253 insertions(+), 74 deletions(-)

diff --git 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index b933876ace..00bc9e77c3 100644
--- 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@ -39,6 +39,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 
 abstract class AbstractAnyLogic<TO extends AnyTO, C extends AnyCR, U extends 
AnyUR> extends AbstractLogic<TO> {
 
@@ -72,7 +73,7 @@ abstract class AbstractAnyLogic<TO extends AnyTO, C extends 
AnyCR, U extends Any
         realm.getActions().forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
-                        impl,
+                        AuthContextUtils.getDomain(), impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
             } catch (Exception e) {
diff --git 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
index 8b128f080c..48e3192802 100644
--- 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
+++ 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
@@ -41,6 +41,7 @@ import 
org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.search.SyncopePage;
 import org.apache.syncope.core.provisioning.api.macro.Command;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.Pageable;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -72,7 +73,7 @@ public class CommandLogic extends AbstractLogic<EntityTO> {
                 map(command -> {
                     try {
                         return new CommandTO.Builder(command.getKey()).
-                                
args(ImplementationManager.emptyArgs(command)).build();
+                                
args(ImplementationManager.emptyArgs(AuthContextUtils.getDomain(), 
command)).build();
                     } catch (Exception e) {
                         LOG.error("Could not get arg class for {}", command, 
e);
                         return null;
@@ -92,7 +93,7 @@ public class CommandLogic extends AbstractLogic<EntityTO> {
 
         try {
             return new CommandTO.Builder(impl.getKey()).
-                    args(ImplementationManager.emptyArgs(impl)).build();
+                    
args(ImplementationManager.emptyArgs(AuthContextUtils.getDomain(), 
impl)).build();
         } catch (Exception e) {
             SyncopeClientException sce = 
SyncopeClientException.build(ClientExceptionType.InvalidImplementation);
             sce.getElements().add("Could not build " + impl.getKey());
@@ -109,6 +110,7 @@ public class CommandLogic extends AbstractLogic<EntityTO> {
         Command<CommandArgs> runnable;
         try {
             runnable = (Command<CommandArgs>) ImplementationManager.build(
+                    AuthContextUtils.getDomain(),
                     impl,
                     () -> perContextCommands.get(impl.getKey()),
                     instance -> perContextCommands.put(impl.getKey(), 
instance));
@@ -135,7 +137,7 @@ public class CommandLogic extends AbstractLogic<EntityTO> {
 
         try {
             return runnable.run(command.getArgs() == null
-                    ? ImplementationManager.emptyArgs(impl)
+                    ? 
ImplementationManager.emptyArgs(AuthContextUtils.getDomain(), impl)
                     : command.getArgs()).message();
         } catch (Exception e) {
             LOG.error("While running {} on {}", command.getKey(), 
command.getArgs(), e);
diff --git 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
index db052c13e5..8b4a93c89a 100644
--- 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
+++ 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
@@ -50,6 +50,7 @@ import 
org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.persistence.api.entity.Schema;
 import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -225,6 +226,7 @@ public class SchemaLogic extends 
AbstractTransactionalLogic<SchemaTO> {
 
         try {
             DropdownValueProvider provider = ImplementationManager.build(
+                    AuthContextUtils.getDomain(),
                     schema.getDropdownValueProvider(),
                     () -> 
perContextDropdownValueProviders.get(schema.getDropdownValueProvider().getKey()),
                     instance -> perContextDropdownValueProviders.put(
diff --git 
a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java
 
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java
index 7dcf20929c..ca9eb3d0a6 100644
--- 
a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java
+++ 
b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/attrvalue/DefaultPlainAttrValidationManager.java
@@ -25,6 +25,7 @@ import 
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValueValidator
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,6 +44,7 @@ public class DefaultPlainAttrValidationManager implements 
PlainAttrValidationMan
         if (schema.getValidator() != null) {
             try {
                 validator = ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         schema.getValidator(),
                         () -> 
perContextValidators.get(schema.getValidator().getKey()),
                         instance -> 
perContextValidators.put(schema.getValidator().getKey(), instance));
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerCacheEntryListener.java
similarity index 97%
rename from 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java
rename to 
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerCacheEntryListener.java
index 38582444fc..c5a02d1624 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerRemoteCommitListener.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ConnectorManagerCacheEntryListener.java
@@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory;
  * Takes care of connectors' Spring beans (un)registration in case HA is set 
up and the actual change is performed by
  * another node in the Hibernate cluster.
  */
-public class ConnectorManagerRemoteCommitListener
+public class ConnectorManagerCacheEntryListener
         implements CacheEntryCreatedListener<Object, Object>,
         CacheEntryUpdatedListener<Object, Object>,
         CacheEntryRemovedListener<Object, Object>,
@@ -47,7 +47,7 @@ public class ConnectorManagerRemoteCommitListener
 
     private static final long serialVersionUID = 5260753255454140460L;
 
-    protected static final Logger LOG = 
LoggerFactory.getLogger(ConnectorManagerRemoteCommitListener.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(ConnectorManagerCacheEntryListener.class);
 
     protected final EntityManagerFactory entityManagerFactory;
 
@@ -57,7 +57,7 @@ public class ConnectorManagerRemoteCommitListener
 
     protected final String domain;
 
-    public ConnectorManagerRemoteCommitListener(
+    public ConnectorManagerCacheEntryListener(
             final EntityManagerFactory entityManagerFactory,
             final ConnectorManager connectorManager,
             final ExternalResourceDAO resourceDAO,
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ImplementationRepoExtImpl.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ImplementationRepoExtImpl.java
index a7fc53f353..b547b92498 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ImplementationRepoExtImpl.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ImplementationRepoExtImpl.java
@@ -28,6 +28,7 @@ import 
org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 
 public class ImplementationRepoExtImpl implements ImplementationRepoExt {
 
@@ -70,7 +71,7 @@ public class ImplementationRepoExtImpl implements 
ImplementationRepoExt {
     public Implementation save(final Implementation implementation) {
         Implementation merged = entityManager.merge(implementation);
 
-        ImplementationManager.purge(merged.getKey());
+        ImplementationManager.purge(AuthContextUtils.getDomain(), 
merged.getKey());
 
         resourceDAO.findByProvisionSorter(merged).
                 forEach(resource -> 
entityCacheDAO.evict(JPAExternalResource.class, resource.getKey()));
@@ -86,6 +87,6 @@ public class ImplementationRepoExtImpl implements 
ImplementationRepoExt {
         }
 
         entityManager.remove(implementation);
-        ImplementationManager.purge(key);
+        ImplementationManager.purge(AuthContextUtils.getDomain(), key);
     }
 }
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ImplementationManagerCacheEntryListener.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ImplementationManagerCacheEntryListener.java
new file mode 100644
index 0000000000..85d05aff32
--- /dev/null
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/openjpa/ImplementationManagerCacheEntryListener.java
@@ -0,0 +1,79 @@
+/*
+ * 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.persistence.jpa.openjpa;
+
+import jakarta.persistence.EntityManagerFactory;
+import java.io.Serializable;
+import javax.cache.event.CacheEntryEvent;
+import javax.cache.event.CacheEntryListenerException;
+import javax.cache.event.CacheEntryRemovedListener;
+import javax.cache.event.CacheEntryUpdatedListener;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
+import org.apache.syncope.core.spring.implementation.ImplementationManager;
+
+/**
+ * Takes care of Implementation classes cache removal in case HA is set up and 
the actual change is performed by
+ * another node in the Hibernate cluster.
+ */
+public class ImplementationManagerCacheEntryListener
+        implements CacheEntryUpdatedListener<Object, Object>,
+        CacheEntryRemovedListener<Object, Object>, Serializable {
+
+    private static final long serialVersionUID = 5260753255454140460L;
+
+    protected final EntityManagerFactory entityManagerFactory;
+
+    protected final String domain;
+
+    public ImplementationManagerCacheEntryListener(
+            final EntityManagerFactory entityManagerFactory,
+            final String domain) {
+
+        this.entityManagerFactory = entityManagerFactory;
+        this.domain = domain;
+    }
+
+    @Override
+    public void onUpdated(final Iterable<CacheEntryEvent<? extends Object, ? 
extends Object>> events)
+            throws CacheEntryListenerException {
+
+        for (CacheEntryEvent<? extends Object, ? extends Object> event : 
events) {
+            String[] split = event.getKey().toString().split("#");
+            if (split.length > 1) {
+                if (JPAImplementation.class.getName().equals(split[0])) {
+                    ImplementationManager.purge(domain, split[1]);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onRemoved(final Iterable<CacheEntryEvent<? extends Object, ? 
extends Object>> events)
+            throws CacheEntryListenerException {
+
+        for (CacheEntryEvent<? extends Object, ? extends Object> event : 
events) {
+            String[] split = event.getKey().toString().split("#");
+            if (split.length > 1) {
+                if (JPAImplementation.class.getName().equals(split[0])) {
+                    ImplementationManager.purge(domain, split[1]);
+                }
+            }
+        }
+    }
+}
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
index 7fcc1575b0..e1789bf58a 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
@@ -20,13 +20,18 @@ package org.apache.syncope.core.persistence.jpa.spring;
 
 import jakarta.persistence.EntityManagerFactory;
 import jakarta.persistence.spi.PersistenceUnitInfo;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Optional;
 import javax.cache.Caching;
 import javax.cache.configuration.FactoryBuilder;
 import javax.cache.configuration.MutableCacheEntryListenerConfiguration;
-import 
org.apache.syncope.core.persistence.jpa.ConnectorManagerRemoteCommitListener;
+import javax.cache.event.CacheEntryListener;
+import 
org.apache.syncope.core.persistence.jpa.ConnectorManagerCacheEntryListener;
 import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance;
 import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource;
+import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
+import 
org.apache.syncope.core.persistence.jpa.openjpa.ImplementationManagerCacheEntryListener;
 import org.hibernate.cache.spi.support.RegionNameQualifier;
 import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
 
@@ -38,7 +43,11 @@ public class DomainEntityManagerFactoryBean extends 
LocalContainerEntityManagerF
 
     private static final long serialVersionUID = 49152547930966545L;
 
-    protected ConnectorManagerRemoteCommitListener 
connectorManagerRemoteCommitListener;
+    protected final List<CacheEntryListener<Object, Object>> 
cacheEntryListeners = new ArrayList<>();
+
+    protected ConnectorManagerCacheEntryListener 
connectorManagerCacheEntryListener;
+
+    protected ImplementationManagerCacheEntryListener 
implementationManagerCacheEntryListener;
 
     public void setCommonEntityManagerFactoryConf(final 
CommonEntityManagerFactoryConf commonEMFConf) {
         super.setJpaPropertyMap(commonEMFConf.getJpaPropertyMap());
@@ -52,10 +61,16 @@ public class DomainEntityManagerFactoryBean extends 
LocalContainerEntityManagerF
                 ifPresent(super::setPersistenceUnitPostProcessors);
     }
 
-    public void setConnectorManagerRemoteCommitListener(
-            final ConnectorManagerRemoteCommitListener 
connectorManagerRemoteCommitListener) {
+    public void setConnectorManagerCacheEntryListener(
+            final ConnectorManagerCacheEntryListener 
connectorManagerCacheEntryListener) {
+
+        this.connectorManagerCacheEntryListener = 
connectorManagerCacheEntryListener;
+    }
+
+    public void setImplementationManagerCacheEntryListener(
+            final ImplementationManagerCacheEntryListener 
implementationManagerCacheEntryListener) {
 
-        this.connectorManagerRemoteCommitListener = 
connectorManagerRemoteCommitListener;
+        this.implementationManagerCacheEntryListener = 
implementationManagerCacheEntryListener;
     }
 
     @Override
@@ -67,14 +82,22 @@ public class DomainEntityManagerFactoryBean extends 
LocalContainerEntityManagerF
                         pui.getPersistenceUnitName(), 
JPAConnInstance.class.getName()))).
                 ifPresent(cache -> cache.registerCacheEntryListener(
                 new MutableCacheEntryListenerConfiguration<Object, Object>(
-                        
FactoryBuilder.factoryOf(connectorManagerRemoteCommitListener),
+                        
FactoryBuilder.factoryOf(connectorManagerCacheEntryListener),
                         null, false, false)));
         Optional.ofNullable(Caching.getCachingProvider().getCacheManager().
                 getCache(RegionNameQualifier.INSTANCE.qualify(
                         pui.getPersistenceUnitName(), 
JPAExternalResource.class.getName()))).
                 ifPresent(cache -> cache.registerCacheEntryListener(
                 new MutableCacheEntryListenerConfiguration<Object, Object>(
-                        
FactoryBuilder.factoryOf(connectorManagerRemoteCommitListener),
+                        
FactoryBuilder.factoryOf(connectorManagerCacheEntryListener),
+                        null, false, false)));
+
+        Optional.ofNullable(Caching.getCachingProvider().getCacheManager().
+                getCache(RegionNameQualifier.INSTANCE.qualify(
+                        pui.getPersistenceUnitName(), 
JPAImplementation.class.getName()))).
+                ifPresent(cache -> cache.registerCacheEntryListener(
+                new MutableCacheEntryListenerConfiguration<Object, Object>(
+                        
FactoryBuilder.factoryOf(implementationManagerCacheEntryListener),
                         null, false, false)));
     }
 }
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
index 0483be9ff1..c7481ae408 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainRoutingEntityManagerFactory.java
@@ -41,8 +41,9 @@ import javax.sql.DataSource;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
-import 
org.apache.syncope.core.persistence.jpa.ConnectorManagerRemoteCommitListener;
+import 
org.apache.syncope.core.persistence.jpa.ConnectorManagerCacheEntryListener;
 import org.apache.syncope.core.persistence.jpa.PersistenceProperties;
+import 
org.apache.syncope.core.persistence.jpa.openjpa.ImplementationManagerCacheEntryListener;
 import org.apache.syncope.core.provisioning.api.ConnectorManager;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
@@ -102,8 +103,10 @@ public class DomainRoutingEntityManagerFactory implements 
EntityManagerFactory,
         emf.setDataSource(Objects.requireNonNull((DataSource) 
dataSource.getObject()));
         emf.setJpaVendorAdapter(vendorAdapter);
         emf.setCommonEntityManagerFactoryConf(commonEMFConf);
-        emf.setConnectorManagerRemoteCommitListener(new 
ConnectorManagerRemoteCommitListener(
+        emf.setConnectorManagerCacheEntryListener(new 
ConnectorManagerCacheEntryListener(
                 this, connectorManager, resourceDAO, 
SyncopeConstants.MASTER_DOMAIN));
+        emf.setImplementationManagerCacheEntryListener(new 
ImplementationManagerCacheEntryListener(
+                this, SyncopeConstants.MASTER_DOMAIN));
 
         addToJpaPropertyMap(
                 emf,
@@ -131,8 +134,10 @@ public class DomainRoutingEntityManagerFactory implements 
EntityManagerFactory,
         emf.setDataSource(dataSource);
         emf.setJpaVendorAdapter(vendorAdapter);
         emf.setCommonEntityManagerFactoryConf(commonEMFConf);
-        emf.setConnectorManagerRemoteCommitListener(new 
ConnectorManagerRemoteCommitListener(
+        emf.setConnectorManagerCacheEntryListener(new 
ConnectorManagerCacheEntryListener(
                 this, connectorManager, resourceDAO, domain.getKey()));
+        emf.setImplementationManagerCacheEntryListener(new 
ImplementationManagerCacheEntryListener(
+                this, domain.getKey()));
 
         addToJpaPropertyMap(emf, vendorAdapter, domain.getDbSchema(), 
domain.getKey());
 
diff --git 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java
 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java
index 417579becf..12e75febb7 100644
--- 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java
+++ 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ImplementationRepoExtImpl.java
@@ -40,6 +40,7 @@ import 
org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPoli
 import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy;
 import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.data.neo4j.core.Neo4jClient;
 import org.springframework.data.neo4j.core.Neo4jTemplate;
 import org.springframework.transaction.annotation.Transactional;
@@ -49,7 +50,7 @@ public class ImplementationRepoExtImpl extends AbstractDAO 
implements Implementa
     protected final ExternalResourceDAO resourceDAO;
 
     protected final PolicyDAO policyDAO;
-    
+
     protected final RealmDAO realmDAO;
 
     protected final EntityCacheDAO entityCacheDAO;
@@ -115,7 +116,7 @@ public class ImplementationRepoExtImpl extends AbstractDAO 
implements Implementa
     public Implementation save(final Implementation implementation) {
         Implementation saved = 
neo4jTemplate.save(nodeValidator.validate(implementation));
 
-        ImplementationManager.purge(saved.getKey());
+        ImplementationManager.purge(AuthContextUtils.getDomain(), 
saved.getKey());
 
         cache.put(EntityCacheKey.of(saved.getKey()), (Neo4jImplementation) 
saved);
 
@@ -151,7 +152,7 @@ public class ImplementationRepoExtImpl extends AbstractDAO 
implements Implementa
 
             neo4jTemplate.deleteById(key, Neo4jImplementation.class);
 
-            ImplementationManager.purge(key);
+            ImplementationManager.purge(AuthContextUtils.getDomain(), key);
         });
     }
 }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
index eaa5fd93dd..9227b9dfb0 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
@@ -83,6 +83,7 @@ import 
org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
 import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
 import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.FrameworkUtil;
 import org.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeBuilder;
@@ -851,7 +852,9 @@ public class DefaultMappingManager implements 
MappingManager {
 
         IntValues transformed = new IntValues(schemaType, values);
         if (transform) {
-            for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+            for (ItemTransformer transformer
+                    : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
+
                 transformed = transformer.beforePropagation(
                         item, any, transformed.attrSchemaType(), 
transformed.values());
             }
@@ -930,7 +933,9 @@ public class DefaultMappingManager implements 
MappingManager {
 
         IntValues transformed = new IntValues(schemaType, values);
         if (transform) {
-            for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+            for (ItemTransformer transformer
+                    : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
+
                 transformed = transformer.beforePropagation(
                         item, realm, transformed.attrSchemaType(), 
transformed.values());
             }
@@ -1038,7 +1043,8 @@ public class DefaultMappingManager implements 
MappingManager {
         List<Object> values = null;
         if (attr != null) {
             values = attr.getValue();
-            for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+            for (ItemTransformer transformer
+                    : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
                 values = transformer.beforePull(item, anyTO, values);
             }
         }
@@ -1163,7 +1169,9 @@ public class DefaultMappingManager implements 
MappingManager {
         List<Object> values = null;
         if (attr != null) {
             values = attr.getValue();
-            for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+            for (ItemTransformer transformer
+                    : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
+
                 values = transformer.beforePull(item, realmTO, values);
             }
         }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
index 8480cade31..b4bdcf2f85 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
@@ -41,6 +41,7 @@ import 
org.apache.syncope.core.provisioning.api.MappingManager;
 import org.apache.syncope.core.provisioning.api.PropagationByResource;
 import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.Uid;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -144,6 +145,7 @@ abstract class AttributableDataBinder {
                             List<String> dropdownValues = List.of();
                             try {
                                 DropdownValueProvider provider = 
ImplementationManager.build(
+                                        AuthContextUtils.getDomain(),
                                         schema.getDropdownValueProvider(),
                                         () -> dropdownValueProviders.get(
                                                 
schema.getDropdownValueProvider().getKey()),
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
index 36027f13ce..16544b6cbb 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
@@ -35,6 +35,7 @@ import 
org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -128,7 +129,7 @@ public class ImplementationDataBinderImpl implements 
ImplementationDataBinder {
 
         Class<?> clazz = null;
         try {
-            clazz = ImplementationManager.getClass(implementation).getLeft();
+            clazz = 
ImplementationManager.getClass(AuthContextUtils.getDomain(), 
implementation).getLeft();
         } catch (Exception e) {
             LOG.error("Could not get Class", e);
             sce.getElements().add("No " + implementation.getKey() + " class 
found");
@@ -146,7 +147,7 @@ public class ImplementationDataBinderImpl implements 
ImplementationDataBinder {
 
         if (implementation.getEngine() == ImplementationEngine.GROOVY) {
             try {
-                ImplementationManager.build(implementation);
+                ImplementationManager.build(AuthContextUtils.getDomain(), 
implementation);
             } catch (Exception e) {
                 LOG.error("While building Groovy class {}", 
implementation.getKey(), e);
 
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index b015989cfe..df5be8b3bb 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -306,7 +306,7 @@ public class TaskDataBinderImpl extends 
AbstractExecutableDatabinder implements
                     try {
                         CommandArgs args = command.getArgs();
                         if (args == null) {
-                            args = ImplementationManager.emptyArgs(impl);
+                            args = 
ImplementationManager.emptyArgs(AuthContextUtils.getDomain(), impl);
                         }
 
                         MacroTaskCommand macroTaskCommand = 
entityFactory.newEntity(MacroTaskCommand.class);
@@ -677,6 +677,7 @@ public class TaskDataBinderImpl extends 
AbstractExecutableDatabinder implements
         } else {
             try {
                 actions = Optional.of(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         task.getMacroActions(),
                         () -> 
perContextMacroActions.get(task.getMacroActions().getKey()),
                         instance -> 
perContextMacroActions.put(task.getMacroActions().getKey(), instance)));
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java
index 9f51e0a4a2..5a64eda427 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java
@@ -49,7 +49,7 @@ public class LiveSyncJob extends TaskJob {
         if (impl == null) {
             LOG.error("Could not find Implementation '{}', aborting", implKey);
         } else {
-            delegate = ImplementationManager.build(impl);
+            delegate = ImplementationManager.build(context.getDomain(), impl);
             delegate.execute(
                     TaskType.LIVE_SYNC,
                     taskKey,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
index 98b6f636f3..ba2e801c4f 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
@@ -274,6 +274,7 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> im
         } else {
             try {
                 actions = Optional.of(ImplementationManager.build(
+                        context.getDomain(),
                         task.getMacroActions(),
                         () -> 
perContextActions.get(task.getMacroActions().getKey()),
                         instance -> 
perContextActions.put(task.getMacroActions().getKey(), instance)));
@@ -296,6 +297,7 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> im
             Command<CommandArgs> runnable;
             try {
                 runnable = (Command<CommandArgs>) ImplementationManager.build(
+                        context.getDomain(),
                         command.getCommand(),
                         () -> 
perContextCommands.get(command.getCommand().getKey()),
                         instance -> 
perContextCommands.put(command.getCommand().getKey(), instance));
@@ -306,7 +308,7 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> im
             CommandArgs args;
             if (command.getArgs() == null) {
                 try {
-                    args = 
ImplementationManager.emptyArgs(command.getCommand());
+                    args = 
ImplementationManager.emptyArgs(context.getDomain(), command.getCommand());
                 } catch (Exception e) {
                     throw new JobExecutionException("While getting empty args 
from " + command.getKey(), e);
                 }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
index e945c3ea74..0a78a73684 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java
@@ -67,7 +67,7 @@ public class TaskJob extends Job {
         if (impl == null) {
             LOG.error("Could not find Implementation '{}', aborting", implKey);
         } else {
-            delegate = ImplementationManager.build(impl);
+            delegate = ImplementationManager.build(context.getDomain(), impl);
             delegate.execute(
                     (TaskType) context.getData().get(JobManager.TASK_TYPE),
                     taskKey,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
index 31c9de5041..f6932af1b0 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/ReportJob.java
@@ -75,6 +75,7 @@ public class ReportJob extends Job {
             LOG.error("Could not find Implementation '{}', aborting", implKey);
         } else {
             delegate = ImplementationManager.buildReportJobDelegate(
+                    context.getDomain(),
                     impl,
                     () -> perContextReportJobDelegates.get(impl.getKey()),
                     instance -> 
perContextReportJobDelegates.put(impl.getKey(), instance)).
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
index 6af79f1031..4cb2cb709a 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
@@ -209,6 +209,7 @@ public class DefaultNotificationManager implements 
NotificationManager {
         
Optional.ofNullable(notification.getRecipientsProvider()).ifPresent(impl -> {
             try {
                 RecipientsProvider recipientsProvider = 
ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextRecipientsProvider.orElse(null),
                         instance -> perContextRecipientsProvider = 
Optional.of(instance));
diff --git 
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
index 87b69d8bd2..0203313d84 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
@@ -175,6 +175,7 @@ public abstract class AbstractPropagationTaskExecutor 
implements PropagationTask
         resource.getPropagationActions().forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
index 1328b35122..bd61bdb24c 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
@@ -113,10 +113,11 @@ public abstract class AbstractProvisioningJobDelegate<T 
extends ProvisioningTask
                 || task.getResource().getProvisioningTraceLevel().ordinal() >= 
TraceLevel.SUMMARY.ordinal();
     }
 
-    protected ProvisionSorter getProvisionSorter(final T task) {
+    protected ProvisionSorter getProvisionSorter(final String domain, final T 
task) {
         if (task.getResource().getProvisionSorter() != null) {
             try {
                 return ImplementationManager.build(
+                        domain,
                         task.getResource().getProvisionSorter(),
                         () -> perContextProvisionSorter.orElse(null),
                         instance -> perContextProvisionSorter = 
Optional.of(instance));
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java
index cae2c41632..c123d818b1 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullExecutor.java
@@ -95,12 +95,13 @@ abstract class AbstractPullExecutor<T extends 
ProvisioningTask<T>>
         return 
ctx.getBeanFactory().createBean(DefaultGroupPullResultHandler.class);
     }
 
-    protected List<InboundActions> getInboundActions(final List<? extends 
Implementation> impls) {
+    protected List<InboundActions> getInboundActions(final String domain, 
final List<? extends Implementation> impls) {
         List<InboundActions> result = new ArrayList<>();
 
         impls.forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        domain,
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
@@ -120,7 +121,7 @@ abstract class AbstractPullExecutor<T extends 
ProvisioningTask<T>>
 
         super.init(taskType, taskKey, context);
 
-        provisionSorter = getProvisionSorter(task);
+        provisionSorter = getProvisionSorter(context.getDomain(), task);
 
         latestSyncTokens.clear();
     }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
index a5f6930f25..41943f3f42 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
@@ -60,6 +60,7 @@ import 
org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule;
 import org.apache.syncope.core.provisioning.api.rules.InboundMatch;
 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.identityconnectors.framework.common.objects.Attribute;
 import org.identityconnectors.framework.common.objects.AttributeUtil;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
@@ -234,8 +235,8 @@ public class InboundMatcher {
             final boolean ignoreCaseMatch) {
 
         String finalConnObjectKeyValue = connObjectKeyValue;
-        for (ItemTransformer transformer
-                : MappingUtils.getItemTransformers(connObjectKeyItem, 
getTransformers(connObjectKeyItem))) {
+        for (ItemTransformer transformer : MappingUtils.getItemTransformers(
+                AuthContextUtils.getDomain(), connObjectKeyItem, 
getTransformers(connObjectKeyItem))) {
 
             List<Object> output = transformer.beforePull(
                     connObjectKeyItem,
@@ -365,7 +366,9 @@ public class InboundMatcher {
         if (correlationRule.isPresent()) {
             Implementation impl = correlationRule.get().getImplementation();
             try {
-                rule = ImplementationManager.buildInboundCorrelationRule(impl,
+                rule = ImplementationManager.buildInboundCorrelationRule(
+                        AuthContextUtils.getDomain(),
+                        impl,
                         () -> 
perContextInboundCorrelationRules.get(impl.getKey()),
                         instance -> 
perContextInboundCorrelationRules.put(impl.getKey(), instance));
             } catch (Exception e) {
@@ -452,8 +455,8 @@ public class InboundMatcher {
             return List.of();
         }
 
-        for (ItemTransformer transformer
-                : MappingUtils.getItemTransformers(connObjectKeyItem, 
getTransformers(connObjectKeyItem))) {
+        for (ItemTransformer transformer : MappingUtils.getItemTransformers(
+                AuthContextUtils.getDomain(), connObjectKeyItem, 
getTransformers(connObjectKeyItem))) {
 
             List<Object> output = transformer.beforePull(
                     connObjectKeyItem,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java
index 4726995046..0e05150ac5 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java
@@ -105,7 +105,7 @@ public class LiveSyncJobDelegate
                 orElseThrow(() -> new JobExecutionException(
                 "No " + LiveSyncDeltaMapper.class.getSimpleName() + " 
provided, aborting"));
         try {
-            mapper = ImplementationManager.build(impl);
+            mapper = ImplementationManager.build(context.getDomain(), impl);
         } catch (Exception e) {
             throw new JobExecutionException(
                     "Could not build " + 
IdMImplementationType.LIVE_SYNC_DELTA_MAPPER + " " + impl.getKey(), e);
@@ -140,7 +140,7 @@ public class LiveSyncJobDelegate
                 Optional.ofNullable(task.getResource().getInboundPolicy()).
                         map(InboundPolicy::getConflictResolutionAction).
                         orElse(ConflictResolutionAction.IGNORE),
-                getInboundActions(task.getActions()),
+                getInboundActions(context.getDomain(), task.getActions()),
                 executor,
                 context.isDryRun()) {
 
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 ebd0157255..5fe2d98e3c 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
@@ -43,6 +43,7 @@ import 
org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
 import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule;
 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.identityconnectors.framework.common.objects.AttributeBuilder;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 import org.identityconnectors.framework.common.objects.ObjectClass;
@@ -87,6 +88,7 @@ public class OutboundMatcher {
             Implementation impl = correlationRule.get().getImplementation();
             try {
                 rule = ImplementationManager.buildPushCorrelationRule(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> 
perContextPushCorrelationRules.get(impl.getKey()),
                         instance -> 
perContextPushCorrelationRules.put(impl.getKey(), instance));
@@ -157,6 +159,7 @@ public class OutboundMatcher {
         resource.getPropagationActions().forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
index 32ed2fb6bd..f0ab389bbe 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
@@ -53,8 +53,11 @@ public class PullJobDelegate
 
     protected Optional<ReconFilterBuilder> perContextReconFilterBuilder = 
Optional.empty();
 
-    protected ReconFilterBuilder getReconFilterBuilder(final PullTask task) 
throws ClassNotFoundException {
+    protected ReconFilterBuilder getReconFilterBuilder(final String domain, 
final PullTask task)
+            throws ClassNotFoundException {
+
         return ImplementationManager.build(
+                domain,
                 task.getReconFilterBuilder(),
                 () -> perContextReconFilterBuilder.orElse(null),
                 instance -> perContextReconFilterBuilder = 
Optional.of(instance));
@@ -75,7 +78,7 @@ public class PullJobDelegate
                 Optional.ofNullable(task.getResource().getInboundPolicy()).
                         map(InboundPolicy::getConflictResolutionAction).
                         orElse(ConflictResolutionAction.IGNORE),
-                getInboundActions(task.getActions()),
+                getInboundActions(context.getDomain(), task.getActions()),
                 executor,
                 context.isDryRun());
 
@@ -139,7 +142,7 @@ public class PullJobDelegate
                     case FILTERED_RECONCILIATION:
                         connector.filteredReconciliation(
                                 new ObjectClass(orgUnit.getObjectClass()),
-                                getReconFilterBuilder(task),
+                                getReconFilterBuilder(context.getDomain(), 
task),
                                 dispatcher,
                                 options);
                         break;
@@ -214,7 +217,7 @@ public class PullJobDelegate
 
                     case FILTERED_RECONCILIATION:
                         connector.filteredReconciliation(new 
ObjectClass(provision.getObjectClass()),
-                                getReconFilterBuilder(task),
+                                getReconFilterBuilder(context.getDomain(), 
task),
                                 dispatcher,
                                 options);
                         break;
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 aa092cdccf..fe0c2c266d 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
@@ -138,12 +138,13 @@ public class PushJobDelegate
         return 
ctx.getBeanFactory().createBean(DefaultGroupPushResultHandler.class);
     }
 
-    protected List<PushActions> getPushActions(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 -> {
             try {
                 result.add(ImplementationManager.build(
+                        domain,
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
@@ -170,7 +171,7 @@ public class PushJobDelegate
                 Optional.ofNullable(task.getResource().getPushPolicy()).
                         map(PushPolicy::getConflictResolutionAction).
                         orElse(ConflictResolutionAction.IGNORE),
-                getPushActions(task.getActions()),
+                getPushActions(context.getDomain(), task.getActions()),
                 executor,
                 context.isDryRun());
     }
@@ -226,7 +227,7 @@ public class PushJobDelegate
         }
 
         // ...then provisions for any types
-        ProvisionSorter provisionSorter = getProvisionSorter(task);
+        ProvisionSorter provisionSorter = 
getProvisionSorter(context.getDomain(), task);
 
         for (Provision provision : task.getResource().getProvisions().stream().
                 filter(provision -> provision.getMapping() != 
null).sorted(provisionSorter).
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
index ac89668562..7b4f97501f 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
@@ -48,6 +48,7 @@ import 
org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
 import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
 import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -113,7 +114,7 @@ public class SinglePullJobDelegate extends PullJobDelegate 
implements SyncopeSin
                     taskType,
                     task,
                     ConflictResolutionAction.FIRSTMATCH,
-                    getInboundActions(pullTaskTO.getActions().stream().
+                    getInboundActions(AuthContextUtils.getDomain(), 
pullTaskTO.getActions().stream().
                             
map(implementationDAO::findById).flatMap(Optional::stream).
                             toList()),
                     executor,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
index deaf21e1d9..f0354093ca 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
@@ -39,6 +39,7 @@ import 
org.apache.syncope.core.provisioning.api.pushpull.PushActions;
 import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -73,7 +74,7 @@ public class SinglePushJobDelegate extends PushJobDelegate 
implements SyncopeSin
                 taskType,
                 task,
                 ConflictResolutionAction.FIRSTMATCH,
-                getPushActions(pushTaskTO.getActions().stream().
+                getPushActions(AuthContextUtils.getDomain(), 
pushTaskTO.getActions().stream().
                         
map(implementationDAO::findById).flatMap(Optional::stream).
                         toList()),
                 executor,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
index de35611c03..ad42939663 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
@@ -51,6 +51,7 @@ import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandle
 import 
org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
 import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
 import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -185,7 +186,7 @@ public class StreamPullJobDelegate extends PullJobDelegate 
implements SyncopeStr
                     taskType,
                     task,
                     conflictResolutionAction,
-                    getInboundActions(pullTaskTO.getActions().stream().
+                    getInboundActions(AuthContextUtils.getDomain(), 
pullTaskTO.getActions().stream().
                             
map(implementationDAO::findById).flatMap(Optional::stream).
                             toList()),
                     executor,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
index 39accfb1da..a8ee30a3ce 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
@@ -44,6 +44,7 @@ import 
org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandle
 import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
 import 
org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
 import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.core.spring.security.SecureRandomUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -140,7 +141,7 @@ public class StreamPushJobDelegate extends PushJobDelegate 
implements SyncopeStr
                     taskType,
                     task,
                     ConflictResolutionAction.FIRSTMATCH,
-                    getPushActions(pushTaskTO.getActions().stream().
+                    getPushActions(AuthContextUtils.getDomain(), 
pushTaskTO.getActions().stream().
                             
map(implementationDAO::findById).flatMap(Optional::stream).
                             toList()),
                     executor,
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
index 9eb69b097a..fd2dcfd67e 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
@@ -68,6 +68,7 @@ public final class MappingUtils {
     }
 
     public static List<ItemTransformer> getItemTransformers(
+            final String domain,
             final Item item,
             final List<Implementation> transformers) {
 
@@ -89,6 +90,7 @@ public final class MappingUtils {
         transformers.forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        domain,
                         impl,
                         () -> PER_CONTEXT_ITEM_TRANSFORMERS.get(impl.getKey()),
                         instance -> 
PER_CONTEXT_ITEM_TRANSFORMERS.put(impl.getKey(), instance)));
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java
index 7ccdec209d..99a89b3490 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java
@@ -61,7 +61,7 @@ public final class ImplementationManager {
 
     private static final GroovyClassLoader GROOVY_CLASSLOADER;
 
-    private static final Map<String, Class<?>> CLASS_CACHE = 
Collections.synchronizedMap(new HashMap<>());
+    private static final Map<Pair<String, String>, Class<?>> CLASS_CACHE = 
Collections.synchronizedMap(new HashMap<>());
 
     static {
         CompilerConfiguration cc = new CompilerConfiguration();
@@ -73,6 +73,7 @@ public final class ImplementationManager {
 
     @SuppressWarnings("unchecked")
     public static Optional<ReportJobDelegate> buildReportJobDelegate(
+            final String domain,
             final Implementation impl,
             final Supplier<ReportJobDelegate> cacheGetter,
             final Consumer<ReportJobDelegate> cachePutter)
@@ -80,7 +81,7 @@ public final class ImplementationManager {
 
         switch (impl.getEngine()) {
             case GROOVY:
-                return Optional.of(build(impl, cacheGetter, cachePutter));
+                return Optional.of(build(domain, impl, cacheGetter, 
cachePutter));
 
             case JAVA:
             default:
@@ -101,6 +102,7 @@ public final class ImplementationManager {
 
     @SuppressWarnings("unchecked")
     public static Optional<AccountRule> buildAccountRule(
+            final String domain,
             final Implementation impl,
             final Supplier<AccountRule> cacheGetter,
             final Consumer<AccountRule> cachePutter)
@@ -108,7 +110,7 @@ public final class ImplementationManager {
 
         switch (impl.getEngine()) {
             case GROOVY:
-                return Optional.of(build(impl, cacheGetter, cachePutter));
+                return Optional.of(build(domain, impl, cacheGetter, 
cachePutter));
 
             case JAVA:
             default:
@@ -128,6 +130,7 @@ public final class ImplementationManager {
 
     @SuppressWarnings("unchecked")
     public static Optional<PasswordRule> buildPasswordRule(
+            final String domain,
             final Implementation impl,
             final Supplier<PasswordRule> cacheGetter,
             final Consumer<PasswordRule> cachePutter)
@@ -135,7 +138,7 @@ public final class ImplementationManager {
 
         switch (impl.getEngine()) {
             case GROOVY:
-                return Optional.of(build(impl, cacheGetter, cachePutter));
+                return Optional.of(build(domain, impl, cacheGetter, 
cachePutter));
 
             case JAVA:
             default:
@@ -155,6 +158,7 @@ public final class ImplementationManager {
 
     @SuppressWarnings("unchecked")
     public static Optional<InboundCorrelationRule> buildInboundCorrelationRule(
+            final String domain,
             final Implementation impl,
             final Supplier<InboundCorrelationRule> cacheGetter,
             final Consumer<InboundCorrelationRule> cachePutter)
@@ -162,7 +166,7 @@ public final class ImplementationManager {
 
         switch (impl.getEngine()) {
             case GROOVY:
-                return Optional.of(build(impl, cacheGetter, cachePutter));
+                return Optional.of(build(domain, impl, cacheGetter, 
cachePutter));
 
             case JAVA:
             default:
@@ -183,6 +187,7 @@ public final class ImplementationManager {
 
     @SuppressWarnings("unchecked")
     public static Optional<PushCorrelationRule> buildPushCorrelationRule(
+            final String domain,
             final Implementation impl,
             final Supplier<PushCorrelationRule> cacheGetter,
             final Consumer<PushCorrelationRule> cachePutter)
@@ -190,7 +195,7 @@ public final class ImplementationManager {
 
         switch (impl.getEngine()) {
             case GROOVY:
-                return Optional.of(build(impl, cacheGetter, cachePutter));
+                return Optional.of(build(domain, impl, cacheGetter, 
cachePutter));
 
             case JAVA:
             default:
@@ -229,12 +234,12 @@ public final class ImplementationManager {
         return null;
     }
 
-    public static CommandArgs emptyArgs(final Implementation impl) throws 
Exception {
+    public static CommandArgs emptyArgs(final String domain, final 
Implementation impl) throws Exception {
         if (!IdRepoImplementationType.COMMAND.equals(impl.getType())) {
             throw new IllegalArgumentException("This method can be only called 
on implementations");
         }
 
-        Class<Object> commandClass = getClass(impl).getLeft();
+        Class<Object> commandClass = getClass(domain, impl).getLeft();
 
         Class<? extends CommandArgs> commandArgsClass = 
findCommandArgsClass(commandClass);
         if (commandArgsClass != null
@@ -249,9 +254,12 @@ public final class ImplementationManager {
     }
 
     @SuppressWarnings("unchecked")
-    public static <T> Pair<Class<T>, Boolean> getClass(final Implementation 
impl) throws ClassNotFoundException {
-        if (CLASS_CACHE.containsKey(impl.getKey())) {
-            return Pair.of((Class<T>) CLASS_CACHE.get(impl.getKey()), true);
+    public static <T> Pair<Class<T>, Boolean> getClass(final String domain, 
final Implementation impl)
+            throws ClassNotFoundException {
+
+        Pair<String, String> cacheKey = Pair.of(domain, impl.getKey());
+        if (CLASS_CACHE.containsKey(cacheKey)) {
+            return Pair.of((Class<T>) CLASS_CACHE.get(cacheKey), true);
         }
 
         Class<?> clazz;
@@ -265,7 +273,7 @@ public final class ImplementationManager {
                 clazz = Class.forName(impl.getBody());
         }
 
-        CLASS_CACHE.put(impl.getKey(), clazz);
+        CLASS_CACHE.put(cacheKey, clazz);
         return Pair.of((Class<T>) clazz, false);
     }
 
@@ -289,8 +297,8 @@ public final class ImplementationManager {
     }
 
     @SuppressWarnings("unchecked")
-    public static <T> T build(final Implementation impl) throws 
ClassNotFoundException {
-        return createBean((Class<T>) getClass(impl).getLeft(), 
impl.getEngine());
+    public static <T> T build(final String domain, final Implementation impl) 
throws ClassNotFoundException {
+        return createBean((Class<T>) getClass(domain, impl).getLeft(), 
impl.getEngine());
     }
 
     @SuppressWarnings("unchecked")
@@ -319,16 +327,20 @@ public final class ImplementationManager {
         return instance;
     }
 
-    public static <T> T build(final Implementation impl, final Supplier<T> 
cacheGetter, final Consumer<T> cachePutter)
+    public static <T> T build(
+            final String domain,
+            final Implementation impl,
+            final Supplier<T> cacheGetter,
+            final Consumer<T> cachePutter)
             throws ClassNotFoundException {
 
-        Pair<Class<T>, Boolean> clazz = getClass(impl);
+        Pair<Class<T>, Boolean> clazz = getClass(domain, impl);
 
         return build(clazz.getLeft(), clazz.getRight(), impl.getEngine(), 
cacheGetter, cachePutter);
     }
 
-    public static Class<?> purge(final String implementation) {
-        return CLASS_CACHE.remove(implementation);
+    public static Class<?> purge(final String domain, final String 
implementation) {
+        return CLASS_CACHE.remove(Pair.of(domain, implementation));
     }
 
     private ImplementationManager() {
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleProvider.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleProvider.java
index aa459bb626..8190f7cba5 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleProvider.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/policy/DefaultRuleProvider.java
@@ -34,6 +34,7 @@ import 
org.apache.syncope.core.provisioning.api.rules.AccountRule;
 import org.apache.syncope.core.provisioning.api.rules.PasswordRule;
 import org.apache.syncope.core.provisioning.api.rules.RuleProvider;
 import org.apache.syncope.core.spring.implementation.ImplementationManager;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.annotation.Transactional;
@@ -82,6 +83,7 @@ public class DefaultRuleProvider implements RuleProvider {
         for (Implementation impl : policy.getRules()) {
             try {
                 ImplementationManager.buildAccountRule(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextAccountRules.get(impl.getKey()),
                         instance -> perContextAccountRules.put(impl.getKey(), 
instance)).
@@ -124,6 +126,7 @@ public class DefaultRuleProvider implements RuleProvider {
         for (Implementation impl : policy.getRules()) {
             try {
                 ImplementationManager.buildPasswordRule(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextPasswordRules.get(impl.getKey()),
                         instance -> perContextPasswordRules.put(impl.getKey(), 
instance)).
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
index 8ee5ad2f38..98e82b737a 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultPasswordGenerator.java
@@ -77,6 +77,7 @@ public class DefaultPasswordGenerator implements 
PasswordGenerator {
         for (Implementation impl : policy.getRules()) {
             try {
                 ImplementationManager.buildPasswordRule(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextPasswordRules.get(impl.getKey()),
                         instance -> perContextPasswordRules.put(impl.getKey(), 
instance)).
diff --git 
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
 
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
index fe2e0641bf..b301f9f2a7 100644
--- 
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
+++ 
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/GroovySandboxTest.java
@@ -30,6 +30,7 @@ import java.util.Map;
 import java.util.Objects;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.cxf.helpers.IOUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.provisioning.api.macro.MacroActions;
@@ -63,7 +64,7 @@ class GroovySandboxTest {
         when(impl.getBody()).thenReturn(IOUtils.toString(
                 
Objects.requireNonNull(getClass().getResourceAsStream(resource))));
 
-        return ImplementationManager.build(impl);
+        return ImplementationManager.build(SyncopeConstants.MASTER_DOMAIN, 
impl);
     }
 
     @EnabledOnOs(OS.LINUX)
diff --git 
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java
 
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java
index d7edb9723d..b0cd17778f 100644
--- 
a/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java
+++ 
b/core/spring/src/test/java/org/apache/syncope/core/spring/implementation/PasswordRuleTest.java
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.apache.syncope.core.spring.SpringTestConfiguration;
@@ -68,6 +69,7 @@ public class PasswordRuleTest {
                         }
                         try {
                             ImplementationManager.buildPasswordRule(
+                                    SyncopeConstants.MASTER_DOMAIN,
                                     impl,
                                     () -> null,
                                     instance -> {
diff --git 
a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
 
b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
index 33796a0a6a..23b7a9244e 100644
--- 
a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
+++ 
b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -52,6 +52,7 @@ import 
org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
 import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.annotation.Propagation;
@@ -115,6 +116,7 @@ public class OIDCUserManager {
         op.getActions().forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
@@ -141,7 +143,9 @@ public class OIDCUserManager {
                 values.addAll(oidcAttr.get().getValues());
 
                 List<Object> transformed = new ArrayList<>(values);
-                for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+                for (ItemTransformer transformer
+                        : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
+
                     transformed = transformer.beforePull(null, userTO, 
transformed);
                 }
                 values.clear();
diff --git 
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
 
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
index 69d025743e..d2c3244502 100644
--- 
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
+++ 
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogic.java
@@ -256,6 +256,7 @@ public class SAML2SP4UILogic extends 
AbstractSAML2SP4UILogic {
         if (impl != null) {
             try {
                 return Optional.of(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextRACP.get(impl.getKey()),
                         instance -> perContextRACP.put(impl.getKey(), 
instance)));
diff --git 
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
 
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
index 8aa5d1a492..810a3d7fa3 100644
--- 
a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
+++ 
b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
@@ -53,6 +53,7 @@ import 
org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
 import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
 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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.annotation.Propagation;
@@ -126,6 +127,7 @@ public class SAML2SP4UIUserManager {
         idp.getActions().forEach(impl -> {
             try {
                 result.add(ImplementationManager.build(
+                        AuthContextUtils.getDomain(),
                         impl,
                         () -> perContextActions.get(impl.getKey()),
                         instance -> perContextActions.put(impl.getKey(), 
instance)));
@@ -158,7 +160,9 @@ public class SAML2SP4UIUserManager {
                 values = samlAttr.get().getValues();
 
                 List<Object> transformed = new ArrayList<>(values);
-                for (ItemTransformer transformer : 
MappingUtils.getItemTransformers(item, getTransformers(item))) {
+                for (ItemTransformer transformer
+                        : 
MappingUtils.getItemTransformers(AuthContextUtils.getDomain(), item, 
getTransformers(item))) {
+
                     transformed = transformer.beforePull(null, userTO, 
transformed);
                 }
                 values.clear();

Reply via email to