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 ad896bfb85 Complete JCache support for Core (#1545)
ad896bfb85 is described below

commit ad896bfb85d91ec5f158a149832507e09e39ba76
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Wed Sep 23 09:02:45 2026 +0200

    Complete JCache support for Core (#1545)
---
 .github/workflows/crosschecks.yml                  |  2 +-
 .../resources/archetype-resources/core/pom.xml     |  4 +
 .../src/main/resources/archetype-resources/pom.xml |  5 ++
 core/cache/caffeine/pom.xml                        | 55 +++++++++++++
 core/cache/ehcache/pom.xml                         | 55 +++++++++++++
 core/cache/hazelcast/pom.xml                       | 55 +++++++++++++
 core/cache/infinispan/pom.xml                      | 55 +++++++++++++
 core/cache/pom.xml                                 | 94 ++++++++++++++++++++++
 .../core/persistence/jpa/JPADomainRegistry.java    | 14 ++--
 .../core/persistence/jpa/PersistenceContext.java   | 35 ++++----
 .../persistence/jpa/PersistenceProperties.java     | 30 +++----
 .../core/persistence/jpa/entity/JPARole.java       |  3 +-
 .../jpa/entity/anyobject/JPAAnyObject.java         |  6 +-
 .../persistence/jpa/entity/group/JPAGroup.java     |  3 +-
 .../persistence/jpa/entity/task/JPAMacroTask.java  |  3 +-
 .../core/persistence/jpa/entity/user/JPAUser.java  | 21 +++--
 .../jpa/spring/CommonEntityManagerFactoryConf.java |  2 +-
 .../jpa/spring/DomainEntityManagerFactoryBean.java | 63 ++++++---------
 .../spring/DomainRoutingEntityManagerFactory.java  | 32 +++++---
 .../persistence/jpa/PersistenceTestContext.java    | 12 ++-
 .../neo4j/entity/task/Neo4jMacroTask.java          |  3 +-
 .../persistence/neo4j/entity/user/Neo4jUser.java   | 15 ++--
 core/pom.xml                                       |  1 +
 .../spring/security/DefaultCredentialChecker.java  |  8 +-
 .../security/throttle/AbstractThrottler.java       |  7 +-
 .../security/throttle/AuthenticationThrottler.java | 59 +++++++-------
 .../throttle/PasswordResetRequestThrottler.java    | 64 +++++++--------
 .../throttle/AuthenticationThrottlerTest.java      | 10 ++-
 core/starter/pom.xml                               |  4 -
 core/starter/src/main/resources/core.properties    |  2 -
 .../core/workflow/java/WorkflowTestContext.java    |  7 ++
 docker/core/pom.xml                                | 39 +++++++++
 docker/core/src/main/resources/Dockerfile          |  3 +-
 .../core/src/main/resources/core-docker.properties |  2 -
 docker/core/src/main/resources/startup.sh          | 22 +++++
 .../docker-compose/docker-compose-all.yml          |  2 +-
 .../resources/docker-compose/docker-compose-ha.yml |  4 +-
 .../docker-compose/docker-compose-mariadb.yml      |  2 +-
 .../docker-compose/docker-compose-mysql.yml        |  2 +-
 .../docker-compose/docker-compose-oracle.yml       |  2 +-
 .../docker-compose/docker-compose-postgresql.yml   |  2 +-
 .../syncope/templates/syncope-deployment.yaml      |  2 +-
 fit/core-reference/pom.xml                         | 21 +++--
 .../src/main/resources/core-payara.properties      | 18 -----
 src/main/asciidoc/getting-started/obtain.adoc      | 11 ++-
 .../concepts/attributerepositories.adoc            | 14 ++--
 .../concepts/authenticationmodules.adoc            | 36 ++++-----
 .../reference-guide/concepts/authprofile.adoc      | 10 +--
 .../concepts/clientapplications.adoc               |  6 +-
 .../asciidoc/reference-guide/concepts/domains.adoc |  2 +-
 .../concepts/passwordmanagement.adoc               | 10 +--
 .../reference-guide/concepts/policies.adoc         |  8 +-
 .../configuration/highavailability.adoc            | 87 ++++++++++++++++----
 .../asciidoc/reference-guide/usage/actuator.adoc   | 12 +--
 .../reference-guide/usage/customization.adoc       |  2 +-
 .../asciidoc/reference-guide/usage/metrics.adoc    |  2 +-
 56 files changed, 740 insertions(+), 310 deletions(-)

diff --git a/.github/workflows/crosschecks.yml 
b/.github/workflows/crosschecks.yml
index d0deecd39b..2038b8a375 100644
--- a/.github/workflows/crosschecks.yml
+++ b/.github/workflows/crosschecks.yml
@@ -31,7 +31,7 @@ jobs:
       fail-fast: false
       matrix:
         language: ['java']
-        java: [ '25', '27-ea' ]
+        java: [ '25', '28-ea' ]
         os: [ubuntu-latest, windows-latest, macos-latest]
 
     steps:
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml 
b/archetype/src/main/resources/archetype-resources/core/pom.xml
index 39282e92af..6d0b2de0e3 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -47,6 +47,10 @@ under the License.
       <groupId>org.apache.syncope.core</groupId>
       <artifactId>syncope-core-starter</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-caffeine</artifactId>      
+    </dependency>
     <dependency>
       <groupId>org.apache.syncope.core.idm</groupId>
       <artifactId>syncope-core-idm-rest-cxf</artifactId>
diff --git a/archetype/src/main/resources/archetype-resources/pom.xml 
b/archetype/src/main/resources/archetype-resources/pom.xml
index 25a71bc0e9..ae9d094b4e 100644
--- a/archetype/src/main/resources/archetype-resources/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/pom.xml
@@ -67,6 +67,11 @@ under the License.
         <artifactId>syncope-core-starter</artifactId>
         <version>${syncope.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.syncope.core.cache</groupId>
+        <artifactId>syncope-core-cache-caffeine</artifactId>      
+        <version>${syncope.version}</version>
+      </dependency>
       <dependency>
         <groupId>org.apache.syncope.core.idm</groupId>
         <artifactId>syncope-core-idm-rest-cxf</artifactId>
diff --git a/core/cache/caffeine/pom.xml b/core/cache/caffeine/pom.xml
new file mode 100644
index 0000000000..ceeb6d677a
--- /dev/null
+++ b/core/cache/caffeine/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.core</groupId>
+    <artifactId>syncope-core-cache</artifactId>
+    <version>5.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Core Cache Caffeine</name>
+  <description>Apache Syncope Core Cache Caffeine</description>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-caffeine</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.github.ben-manes.caffeine</groupId>
+      <artifactId>jcache</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/core/cache/ehcache/pom.xml b/core/cache/ehcache/pom.xml
new file mode 100644
index 0000000000..d2d9e3a2c0
--- /dev/null
+++ b/core/cache/ehcache/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.core</groupId>
+    <artifactId>syncope-core-cache</artifactId>
+    <version>5.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Core Cache Ehcache</name>
+  <description>Apache Syncope Core Cache Ehcache</description>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-ehcache</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.ehcache</groupId>
+      <artifactId>ehcache</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/core/cache/hazelcast/pom.xml b/core/cache/hazelcast/pom.xml
new file mode 100644
index 0000000000..d6873a57b0
--- /dev/null
+++ b/core/cache/hazelcast/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.core</groupId>
+    <artifactId>syncope-core-cache</artifactId>
+    <version>5.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Core Cache Hazelcast</name>
+  <description>Apache Syncope Core Cache Hazelcast</description>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-hazelcast</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.hazelcast</groupId>
+      <artifactId>hazelcast</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/core/cache/infinispan/pom.xml b/core/cache/infinispan/pom.xml
new file mode 100644
index 0000000000..c6906fea85
--- /dev/null
+++ b/core/cache/infinispan/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope.core</groupId>
+    <artifactId>syncope-core-cache</artifactId>
+    <version>5.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Core Cache Infinispan</name>
+  <description>Apache Syncope Core Cache Infinispan</description>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-infinispan</artifactId>
+  <packaging>jar</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.infinispan</groupId>
+      <artifactId>infinispan-jcache</artifactId>    
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/core/cache/pom.xml b/core/cache/pom.xml
new file mode 100644
index 0000000000..10c81c8228
--- /dev/null
+++ b/core/cache/pom.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.syncope</groupId>
+    <artifactId>syncope-core</artifactId>
+    <version>5.0.0-SNAPSHOT</version>
+  </parent>
+
+  <name>Apache Syncope Core Cache</name>
+  <description>Apache Syncope Core Cache</description>
+  <groupId>org.apache.syncope.core</groupId>
+  <artifactId>syncope-core-cache</artifactId>
+  <packaging>pom</packaging>
+  
+  <properties>
+    <rootpom.basedir>${basedir}/../..</rootpom.basedir>
+  </properties>
+  
+  <build>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-assembly-plugin</artifactId>
+          <executions>
+            <execution>
+              <phase>package</phase>
+              <goals>
+                <goal>single</goal>
+              </goals>
+              <configuration>
+                <inlineDescriptors>
+                  <inlineDescriptor>
+                    <id>wrap</id>
+                    <formats>
+                      <format>zip</format>
+                    </formats>
+                    <includeBaseDirectory>false</includeBaseDirectory>
+                    <dependencySets>
+                      <dependencySet>
+                        <outputDirectory>/</outputDirectory>
+                        <useProjectArtifact>false</useProjectArtifact>
+                        <unpack>false</unpack>
+                        <scope>runtime</scope>
+                      </dependencySet>
+                    </dependencySets>
+                    <files>
+                      <file>
+                        <source>${rootpom.basedir}/LICENSE</source>
+                        <outputDirectory>META-INF</outputDirectory>
+                      </file>
+                      <file>
+                        <source>${rootpom.basedir}/NOTICE</source>
+                        <outputDirectory>META-INF</outputDirectory>
+                      </file>
+                    </files>
+                  </inlineDescriptor>
+                </inlineDescriptors>
+              </configuration>
+            </execution>
+          </executions>
+        </plugin>
+      </plugins>
+    </pluginManagement>
+  </build>
+  
+  <modules>
+    <module>caffeine</module>
+    <module>hazelcast</module>
+    <module>ehcache</module>
+    <module>infinispan</module>
+  </modules>
+</project>
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java
index e9dbe5dac1..df1fa5e3d5 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/JPADomainRegistry.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.persistence.jpa;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 import java.io.ByteArrayInputStream;
+import javax.cache.CacheManager;
 import javax.sql.DataSource;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
 import org.apache.syncope.core.persistence.api.DomainHolder;
@@ -33,9 +34,12 @@ import org.springframework.jndi.JndiObjectFactoryBean;
 
 public class JPADomainRegistry implements DomainRegistry<JPADomain> {
 
+    protected final CacheManager cacheManager;
+
     protected final ConfigurableApplicationContext ctx;
 
-    public JPADomainRegistry(final ConfigurableApplicationContext ctx) {
+    public JPADomainRegistry(final CacheManager cacheManager, final 
ConfigurableApplicationContext ctx) {
+        this.cacheManager = cacheManager;
         this.ctx = ctx;
     }
 
@@ -78,12 +82,12 @@ public class JPADomainRegistry implements 
DomainRegistry<JPADomain> {
                         addPropertyValue("jndiName", 
"java:comp/env/jdbc/syncope" + domain.getKey() + "DataSource").
                         addPropertyValue("defaultObject", new 
HikariDataSource(hikariConfig)).
                         getBeanDefinition());
-        DataSource initedDataSource = beanFactory().getBean(domain.getKey() + 
"DataSource", DataSource.class);
+        DataSource initedDataSource = ctx.getBean(domain.getKey() + 
"DataSource", DataSource.class);
 
         domainHolder().getDomains().put(domain.getKey(), initedDataSource);
 
         // DomainRoutingEntityManagerFactory#domain
-        
beanFactory().getBean(DomainRoutingEntityManagerFactory.class).domain(domain, 
initedDataSource);
+        ctx.getBean(DomainRoutingEntityManagerFactory.class).domain(domain, 
initedDataSource, cacheManager);
 
         // domainContentXML
         beanFactory().registerBeanDefinition(domain.getKey() + "ContentXML",
@@ -109,7 +113,7 @@ public class JPADomainRegistry implements 
DomainRegistry<JPADomain> {
         beanFactory().removeBeanDefinition(domain + "ContentXML");
 
         // DomainRoutingEntityManagerFactory#remove
-        
beanFactory().getBean(DomainRoutingEntityManagerFactory.class).remove(domain);
+        ctx.getBean(DomainRoutingEntityManagerFactory.class).remove(domain);
 
         // domainDataSourceInitializer
         unregisterSingleton(domain.toLowerCase() + "DataSourceInitializer");
@@ -121,6 +125,6 @@ public class JPADomainRegistry implements 
DomainRegistry<JPADomain> {
         unregisterSingleton(domain + "DataSource");
         beanFactory().removeBeanDefinition(domain + "DataSource");
 
-        beanFactory().getBean(DomainHolder.class).getDomains().remove(domain);
+        ctx.getBean(DomainHolder.class).getDomains().remove(domain);
     }
 }
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
index 8a8f871f70..374ad73776 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
@@ -20,10 +20,11 @@ package org.apache.syncope.core.persistence.jpa;
 
 import jakarta.persistence.EntityManager;
 import jakarta.persistence.EntityManagerFactory;
+import jakarta.persistence.PersistenceConfiguration;
 import jakarta.persistence.ValidationMode;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
+import javax.cache.CacheManager;
 import javax.sql.DataSource;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
@@ -188,6 +189,8 @@ import 
org.apache.syncope.core.persistence.jpa.spring.MultiJarAwarePersistenceUn
 import org.apache.syncope.core.persistence.jpa.spring.SyncopeJPARepository;
 import org.apache.syncope.core.provisioning.api.ConnectorManager;
 import org.apache.syncope.core.spring.security.SecurityProperties;
+import org.hibernate.cache.jcache.ConfigSettings;
+import org.hibernate.cfg.AvailableSettings;
 import org.springframework.beans.factory.annotation.Qualifier;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -217,7 +220,8 @@ public class PersistenceContext {
     public CommonEntityManagerFactoryConf commonEMFConf(
             final PersistenceProperties props,
             @Qualifier("MasterDataSource")
-            final JndiObjectFactoryBean masterDataSource) {
+            final JndiObjectFactoryBean masterDataSource,
+            final CacheManager cacheManager) {
 
         CommonEntityManagerFactoryConf commonEMFConf = new 
CommonEntityManagerFactoryConf();
         
commonEMFConf.setPackagesToScan("org.apache.syncope.core.persistence.jpa.entity");
@@ -225,15 +229,14 @@ public class PersistenceContext {
         commonEMFConf.setPersistenceUnitPostProcessors(new 
MultiJarAwarePersistenceUnitPostProcessor());
         Map<String, Object> jpaPropertyMap = new HashMap<>();
 
-        
jpaPropertyMap.put("jakarta.persistence.schema-generation.database.action", 
"create");
+        jpaPropertyMap.put(PersistenceConfiguration.SCHEMAGEN_DATABASE_ACTION, 
"update");
+
+        jpaPropertyMap.put(AvailableSettings.USE_SECOND_LEVEL_CACHE, "true");
+        jpaPropertyMap.put(AvailableSettings.USE_QUERY_CACHE, "true");
+        jpaPropertyMap.put(AvailableSettings.CACHE_REGION_FACTORY, 
DomainJCacheRegionFactory.class.getName());
+        jpaPropertyMap.put(ConfigSettings.CACHE_MANAGER, cacheManager);
 
-        jpaPropertyMap.put("hibernate.cache.use_second_level_cache", "true");
-        jpaPropertyMap.put("hibernate.cache.use_query_cache", "true");
-        jpaPropertyMap.put("hibernate.cache.region.factory_class", "jcache");
-        jpaPropertyMap.put("hibernate.javax.cache.provider", 
props.getCacheProvider());
-        Optional.ofNullable(props.getCacheURI()).
-                ifPresent(cacheURI -> 
jpaPropertyMap.put("hibernate.javax.cache.uri", cacheURI));
-        jpaPropertyMap.put("hibernate.cache.region.factory_class", 
DomainJCacheRegionFactory.class.getName());
+        jpaPropertyMap.putAll(props.getAdditionalJPAProperties());
 
         commonEMFConf.setJpaPropertyMap(jpaPropertyMap);
 
@@ -250,11 +253,12 @@ public class PersistenceContext {
             final JndiObjectFactoryBean masterDataSource,
             final CommonEntityManagerFactoryConf commonEMFConf,
             final @Lazy ConnectorManager connectorManager,
-            final @Lazy ExternalResourceDAO resourceDAO) {
+            final @Lazy ExternalResourceDAO resourceDAO,
+            final CacheManager cacheManager) {
 
         DomainRoutingEntityManagerFactory emf = new 
DomainRoutingEntityManagerFactory(
                 commonEMFConf, connectorManager, resourceDAO);
-        emf.master(props, masterDataSource);
+        emf.master(props, masterDataSource, cacheManager);
         return emf;
     }
 
@@ -299,8 +303,11 @@ public class PersistenceContext {
 
     @ConditionalOnMissingBean
     @Bean
-    public DomainRegistry<JPADomain> domainRegistry(final 
ConfigurableApplicationContext ctx) {
-        return new JPADomainRegistry(ctx);
+    public DomainRegistry<JPADomain> domainRegistry(
+            final CacheManager cacheManager,
+            final ConfigurableApplicationContext ctx) {
+
+        return new JPADomainRegistry(cacheManager, ctx);
     }
 
     @ConditionalOnMissingBean
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
index adc5c4e3c3..5c20252192 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceProperties.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa;
 
+import java.util.HashMap;
+import java.util.Map;
 import 
org.apache.syncope.core.persistence.common.AbstractPersistenceProperties;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
@@ -28,27 +30,9 @@ public class PersistenceProperties extends 
AbstractPersistenceProperties<DomainP
 
     public static final String DB_TYPE = "db-type";
 
-    private String cacheProvider = 
"com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider";
-
-    private String cacheURI;
-
     private String viewsXML = "classpath:META-INF/views.xml";
 
-    public String getCacheProvider() {
-        return cacheProvider;
-    }
-
-    public void setCacheProvider(final String cacheProvider) {
-        this.cacheProvider = cacheProvider;
-    }
-
-    public String getCacheURI() {
-        return cacheURI;
-    }
-
-    public void setCacheURI(final String cacheURI) {
-        this.cacheURI = cacheURI;
-    }
+    private Map<String, String> additionalJPAProperties = new HashMap<>();
 
     public String getViewsXML() {
         return viewsXML;
@@ -57,4 +41,12 @@ public class PersistenceProperties extends 
AbstractPersistenceProperties<DomainP
     public void setViewsXML(final String viewsXML) {
         this.viewsXML = viewsXML;
     }
+
+    public Map<String, String> getAdditionalJPAProperties() {
+        return additionalJPAProperties;
+    }
+
+    public void setAdditionalJPAProperties(final Map<String, String> 
additionalJPAProperties) {
+        this.additionalJPAProperties = additionalJPAProperties;
+    }
 }
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
index 5d89e319a3..9c9a02c372 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARole.java
@@ -60,8 +60,7 @@ public class JPARole extends AbstractProvidedKeyEntity 
implements Role {
             @JoinColumn(name = "realm_id"),
             uniqueConstraints =
             @UniqueConstraint(columnNames = { "role_id", "realm_id" }))
-    @Valid
-    private List<JPARealm> realms = new ArrayList<>();
+    private List<@Valid JPARealm> realms = new ArrayList<>();
 
     @Override
     public Set<String> getEntitlements() {
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
index bc931c30aa..7814485e4a 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/anyobject/JPAAnyObject.java
@@ -91,12 +91,10 @@ public class JPAAnyObject
     private List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"leftEnd")
-    @Valid
-    private List<JPAARelationship> relationships = new ArrayList<>();
+    private List<@Valid JPAARelationship> relationships = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"leftEnd")
-    @Valid
-    private List<JPAAMembership> memberships = new ArrayList<>();
+    private List<@Valid JPAAMembership> memberships = new ArrayList<>();
 
     @Override
     public String getName() {
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
index d934f05216..99effe74f5 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
@@ -93,8 +93,7 @@ public class JPAGroup
     private List<JPAGroupTypeExtension> typeExtensions = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"leftEnd")
-    @Valid
-    private List<JPAGRelationship> relationships = new ArrayList<>();
+    private List<@Valid JPAGRelationship> relationships = new ArrayList<>();
 
     @Override
     public String getName() {
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
index 31ad23b78b..e46a7d2ee6 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAMacroTask.java
@@ -65,8 +65,7 @@ public class JPAMacroTask extends AbstractSchedTask 
implements MacroTask {
 
     @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, 
orphanRemoval = true, mappedBy = "macroTask")
     @OrderBy("idx")
-    @Valid
-    private List<JPAFormPropertyDef> formPropertyDefs = new ArrayList<>();
+    private List<@Valid JPAFormPropertyDef> formPropertyDefs = new 
ArrayList<>();
 
     @ManyToOne(fetch = FetchType.EAGER)
     private JPAImplementation macroActions;
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index aa4fb93a9f..a0a40775d7 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -158,12 +158,10 @@ public class JPAUser
     protected List<JPAAnyTypeClass> auxClasses = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"leftEnd")
-    @Valid
-    protected List<JPAURelationship> relationships = new ArrayList<>();
+    protected List<@Valid JPAURelationship> relationships = new ArrayList<>();
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"leftEnd")
-    @Valid
-    protected List<JPAUMembership> memberships = new ArrayList<>();
+    protected List<@Valid JPAUMembership> memberships = new ArrayList<>();
 
     @ManyToOne(fetch = FetchType.EAGER)
     protected JPASecurityQuestion securityQuestion;
@@ -175,8 +173,7 @@ public class JPAUser
     protected String mfa;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = 
"owner")
-    @Valid
-    protected List<JPALinkedAccount> linkedAccounts = new ArrayList<>();
+    protected List<@Valid JPALinkedAccount> linkedAccounts = new ArrayList<>();
 
     protected Encryptor encryptor() {
         return 
ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance();
@@ -231,11 +228,11 @@ public class JPAUser
                 value,
                 Optional.ofNullable(cipherAlgorithm).
                         orElseGet(() -> CipherAlgorithm.valueOf(
-                        
ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get(
-                                AuthContextUtils.getDomain(),
-                                StandardConfParams.PASSWORD_CIPHER_ALGORITHM,
-                                CipherAlgorithm.AES.name(),
-                                String.class))));
+                                
ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get(
+                                        AuthContextUtils.getDomain(),
+                                        
StandardConfParams.PASSWORD_CIPHER_ALGORITHM,
+                                        CipherAlgorithm.AES.name(),
+                                        String.class))));
     }
 
     @Override
@@ -519,7 +516,7 @@ public class JPAUser
     public Optional<? extends LinkedAccount> getLinkedAccount(final String 
resource, final String connObjectKeyValue) {
         return linkedAccounts.stream().
                 filter(account -> 
account.getResource().getKey().equals(resource)
-                && account.getConnObjectKeyValue().equals(connObjectKeyValue)).
+                        && 
account.getConnObjectKeyValue().equals(connObjectKeyValue)).
                 findFirst();
     }
 
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
index 1cd004a735..d5fe7b8c45 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
@@ -33,7 +33,7 @@ import 
org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
 
 /**
  * Container for common configuration options among all EntityManagerFactory 
entities (one for each domain).
- * Acts as a commodity place for fetching each domain's {@link DataSource}..
+ * Acts as a commodity place for fetching each domain's {@link DataSource}.
  */
 public class CommonEntityManagerFactoryConf implements 
DomainHolder<DataSource> {
 
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 e1789bf58a..2f6549796c 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
@@ -18,15 +18,10 @@
  */
 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.CacheManager;
 import javax.cache.configuration.FactoryBuilder;
 import javax.cache.configuration.MutableCacheEntryListenerConfiguration;
-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;
@@ -43,11 +38,11 @@ public class DomainEntityManagerFactoryBean extends 
LocalContainerEntityManagerF
 
     private static final long serialVersionUID = 49152547930966545L;
 
-    protected final List<CacheEntryListener<Object, Object>> 
cacheEntryListeners = new ArrayList<>();
+    protected final CacheManager cacheManager;
 
-    protected ConnectorManagerCacheEntryListener 
connectorManagerCacheEntryListener;
-
-    protected ImplementationManagerCacheEntryListener 
implementationManagerCacheEntryListener;
+    public DomainEntityManagerFactoryBean(final CacheManager cacheManager) {
+        this.cacheManager = cacheManager;
+    }
 
     public void setCommonEntityManagerFactoryConf(final 
CommonEntityManagerFactoryConf commonEMFConf) {
         super.setJpaPropertyMap(commonEMFConf.getJpaPropertyMap());
@@ -62,42 +57,32 @@ public class DomainEntityManagerFactoryBean extends 
LocalContainerEntityManagerF
     }
 
     public void setConnectorManagerCacheEntryListener(
+            final String domain,
             final ConnectorManagerCacheEntryListener 
connectorManagerCacheEntryListener) {
 
-        this.connectorManagerCacheEntryListener = 
connectorManagerCacheEntryListener;
+        Optional.ofNullable(cacheManager.getCache(
+                RegionNameQualifier.INSTANCE.qualify(domain, 
JPAConnInstance.class.getName()))).
+                ifPresent(cache -> cache.registerCacheEntryListener(
+                        new MutableCacheEntryListenerConfiguration<Object, 
Object>(
+                                
FactoryBuilder.factoryOf(connectorManagerCacheEntryListener),
+                                null, false, false)));
+        Optional.ofNullable(cacheManager.getCache(
+                RegionNameQualifier.INSTANCE.qualify(domain, 
JPAExternalResource.class.getName()))).
+                ifPresent(cache -> cache.registerCacheEntryListener(
+                        new MutableCacheEntryListenerConfiguration<Object, 
Object>(
+                                
FactoryBuilder.factoryOf(connectorManagerCacheEntryListener),
+                                null, false, false)));
     }
 
     public void setImplementationManagerCacheEntryListener(
+            final String domain,
             final ImplementationManagerCacheEntryListener 
implementationManagerCacheEntryListener) {
 
-        this.implementationManagerCacheEntryListener = 
implementationManagerCacheEntryListener;
-    }
-
-    @Override
-    protected void postProcessEntityManagerFactory(final EntityManagerFactory 
emf, final PersistenceUnitInfo pui) {
-        super.postProcessEntityManagerFactory(emf, pui);
-
-        Optional.ofNullable(Caching.getCachingProvider().getCacheManager().
-                getCache(RegionNameQualifier.INSTANCE.qualify(
-                        pui.getPersistenceUnitName(), 
JPAConnInstance.class.getName()))).
-                ifPresent(cache -> cache.registerCacheEntryListener(
-                new MutableCacheEntryListenerConfiguration<Object, Object>(
-                        
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(connectorManagerCacheEntryListener),
-                        null, false, false)));
-
-        Optional.ofNullable(Caching.getCachingProvider().getCacheManager().
-                getCache(RegionNameQualifier.INSTANCE.qualify(
-                        pui.getPersistenceUnitName(), 
JPAImplementation.class.getName()))).
+        Optional.ofNullable(cacheManager.getCache(
+                RegionNameQualifier.INSTANCE.qualify(domain, 
JPAImplementation.class.getName()))).
                 ifPresent(cache -> cache.registerCacheEntryListener(
-                new MutableCacheEntryListenerConfiguration<Object, Object>(
-                        
FactoryBuilder.factoryOf(implementationManagerCacheEntryListener),
-                        null, false, false)));
+                        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 c7481ae408..f9c83a2a6c 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
@@ -37,6 +37,7 @@ import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Consumer;
 import java.util.function.Function;
+import javax.cache.CacheManager;
 import javax.sql.DataSource;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
 import org.apache.syncope.common.lib.SyncopeConstants;
@@ -90,23 +91,27 @@ public class DomainRoutingEntityManagerFactory implements 
EntityManagerFactory,
 
     public void master(
             final PersistenceProperties props,
-            final JndiObjectFactoryBean dataSource) {
+            final JndiObjectFactoryBean dataSource,
+            final CacheManager cacheManager) {
 
         HibernateJpaVendorAdapter vendorAdapter = new 
HibernateJpaVendorAdapter();
         vendorAdapter.setShowSql(false);
         vendorAdapter.setGenerateDdl(true);
         
vendorAdapter.setDatabasePlatform(props.getDomain().getFirst().getDatabasePlatform());
 
-        DomainEntityManagerFactoryBean emf = new 
DomainEntityManagerFactoryBean();
+        DomainEntityManagerFactoryBean emf = new 
DomainEntityManagerFactoryBean(cacheManager);
         emf.setPersistenceUnitName(SyncopeConstants.MASTER_DOMAIN);
         emf.setMappingResources(props.getDomain().getFirst().getOrm());
         emf.setDataSource(Objects.requireNonNull((DataSource) 
dataSource.getObject()));
         emf.setJpaVendorAdapter(vendorAdapter);
         emf.setCommonEntityManagerFactoryConf(commonEMFConf);
-        emf.setConnectorManagerCacheEntryListener(new 
ConnectorManagerCacheEntryListener(
-                this, connectorManager, resourceDAO, 
SyncopeConstants.MASTER_DOMAIN));
-        emf.setImplementationManagerCacheEntryListener(new 
ImplementationManagerCacheEntryListener(
-                this, SyncopeConstants.MASTER_DOMAIN));
+        emf.setConnectorManagerCacheEntryListener(
+                SyncopeConstants.MASTER_DOMAIN,
+                new ConnectorManagerCacheEntryListener(
+                        this, connectorManager, resourceDAO, 
SyncopeConstants.MASTER_DOMAIN));
+        emf.setImplementationManagerCacheEntryListener(
+                SyncopeConstants.MASTER_DOMAIN,
+                new ImplementationManagerCacheEntryListener(this, 
SyncopeConstants.MASTER_DOMAIN));
 
         addToJpaPropertyMap(
                 emf,
@@ -121,23 +126,26 @@ public class DomainRoutingEntityManagerFactory implements 
EntityManagerFactory,
 
     public void domain(
             final JPADomain domain,
-            final DataSource dataSource) {
+            final DataSource dataSource,
+            final CacheManager cacheManager) {
 
         HibernateJpaVendorAdapter vendorAdapter = new 
HibernateJpaVendorAdapter();
         vendorAdapter.setShowSql(false);
         vendorAdapter.setGenerateDdl(true);
         vendorAdapter.setDatabasePlatform(domain.getDatabasePlatform());
 
-        DomainEntityManagerFactoryBean emf = new 
DomainEntityManagerFactoryBean();
+        DomainEntityManagerFactoryBean emf = new 
DomainEntityManagerFactoryBean(cacheManager);
         emf.setPersistenceUnitName(domain.getKey());
         emf.setMappingResources(domain.getOrm());
         emf.setDataSource(dataSource);
         emf.setJpaVendorAdapter(vendorAdapter);
         emf.setCommonEntityManagerFactoryConf(commonEMFConf);
-        emf.setConnectorManagerCacheEntryListener(new 
ConnectorManagerCacheEntryListener(
-                this, connectorManager, resourceDAO, domain.getKey()));
-        emf.setImplementationManagerCacheEntryListener(new 
ImplementationManagerCacheEntryListener(
-                this, domain.getKey()));
+        emf.setConnectorManagerCacheEntryListener(
+                domain.getKey(),
+                new ConnectorManagerCacheEntryListener(this, connectorManager, 
resourceDAO, domain.getKey()));
+        emf.setImplementationManagerCacheEntryListener(
+                domain.getKey(),
+                new ImplementationManagerCacheEntryListener(this, 
domain.getKey()));
 
         addToJpaPropertyMap(emf, vendorAdapter, domain.getDbSchema(), 
domain.getKey());
 
diff --git 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
index 89753aef2f..0fa5bc7e13 100644
--- 
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
+++ 
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/PersistenceTestContext.java
@@ -20,6 +20,8 @@ package org.apache.syncope.core.persistence.jpa;
 
 import jakarta.persistence.EntityManagerFactory;
 import java.io.IOException;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
 import javax.sql.DataSource;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
@@ -126,6 +128,11 @@ public class PersistenceTestContext {
         return new DefaultEncryptorManager(new DefaultCredentialChecker("", 
"", "", "", false), securityProperties);
     }
 
+    @Bean
+    public CacheManager cacheManager() {
+        return Caching.getCachingProvider().getCacheManager();
+    }
+
     @Bean
     public DomainRoutingEntityManagerFactory entityManagerFactory(
             final PersistenceProperties props,
@@ -133,7 +140,8 @@ public class PersistenceTestContext {
             final JndiObjectFactoryBean masterDataSource,
             final CommonEntityManagerFactoryConf commonEMFConf,
             final @Lazy ConnectorManager connectorManager,
-            final @Lazy ExternalResourceDAO resourceDAO) {
+            final @Lazy ExternalResourceDAO resourceDAO,
+            final CacheManager cacheManager) {
 
         DomainRoutingEntityManagerFactory emf = new 
DomainRoutingEntityManagerFactory(
                 commonEMFConf, connectorManager, resourceDAO) {
@@ -145,7 +153,7 @@ public class PersistenceTestContext {
                         delegates.get(SyncopeConstants.MASTER_DOMAIN));
             }
         };
-        emf.master(props, masterDataSource);
+        emf.master(props, masterDataSource, cacheManager);
         return emf;
     }
 }
diff --git 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
index 7c1174705d..4c8318e81a 100644
--- 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
+++ 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jMacroTask.java
@@ -74,8 +74,7 @@ public class Neo4jMacroTask extends Neo4jSchedTask implements 
MacroTask {
             commands, Neo4jMacroTaskCommandRelationship.builder());
 
     @Relationship(type = MACRO_TASK_FORM_PROPERTY_DEF_REL, direction = 
Relationship.Direction.INCOMING)
-    @Valid
-    private SortedSet<Neo4jFormPropertyDefRelationship> formPropertyDefs = new 
TreeSet<>();
+    private SortedSet<@Valid Neo4jFormPropertyDefRelationship> 
formPropertyDefs = new TreeSet<>();
 
     @Transient
     private List<Neo4jFormPropertyDef> sortedFormPropertyDefs = new 
SortedSetList<>(
diff --git 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java
 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java
index e8a006974b..b57855ec00 100644
--- 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java
+++ 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/user/Neo4jUser.java
@@ -145,8 +145,7 @@ public class Neo4jUser
     protected String mfa;
 
     @Relationship(direction = Relationship.Direction.INCOMING)
-    @Valid
-    protected List<Neo4jLinkedAccount> linkedAccounts = new ArrayList<>();
+    protected List<@Valid Neo4jLinkedAccount> linkedAccounts = new 
ArrayList<>();
 
     protected Encryptor encryptor() {
         return 
ApplicationContextProvider.getApplicationContext().getBean(EncryptorManager.class).getInstance();
@@ -206,11 +205,11 @@ public class Neo4jUser
                 value,
                 Optional.ofNullable(cipherAlgorithm).
                         orElseGet(() -> CipherAlgorithm.valueOf(
-                        
ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get(
-                                AuthContextUtils.getDomain(),
-                                StandardConfParams.PASSWORD_CIPHER_ALGORITHM,
-                                CipherAlgorithm.AES.name(),
-                                String.class))));
+                                
ApplicationContextProvider.getBeanFactory().getBean(ConfParamOps.class).get(
+                                        AuthContextUtils.getDomain(),
+                                        
StandardConfParams.PASSWORD_CIPHER_ALGORITHM,
+                                        CipherAlgorithm.AES.name(),
+                                        String.class))));
     }
 
     @Override
@@ -469,7 +468,7 @@ public class Neo4jUser
     public Optional<? extends LinkedAccount> getLinkedAccount(final String 
resource, final String connObjectKeyValue) {
         return linkedAccounts.stream().
                 filter(account -> 
account.getResource().getKey().equals(resource)
-                && account.getConnObjectKeyValue().equals(connObjectKeyValue)).
+                        && 
account.getConnObjectKeyValue().equals(connObjectKeyValue)).
                 findFirst();
     }
 
diff --git a/core/pom.xml b/core/pom.xml
index af9ddb0d4c..73bca90cab 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -85,6 +85,7 @@ under the License.
     <module>persistence-jpa</module>
     <module>persistence-neo4j</module>
     <module>spring</module>
+    <module>cache</module>
     <module>provisioning-api</module>
     <module>provisioning-java</module>
     <module>workflow-api</module>
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
index df72ccd74d..ae317dd8ea 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/DefaultCredentialChecker.java
@@ -32,19 +32,19 @@ public class DefaultCredentialChecker {
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultCredentialChecker.class);
 
     private static final String DEFAULT_AES_KEY_ERROR_MESSAGE =
-            "⚠ The default AES key property is being used. "
+            "⚠️ WARNING: The default AES key property is being used. "
             + "This must be changed to avoid a security breach!";
 
     private static final String DEFAULT_JWS_KEY_ERROR_MESSAGE =
-            "⚠ The default JWKS key property is being used. "
+            "⚠️ WARNING: The default JWKS key property is being used. "
             + "This must be changed to avoid a security breach!";
 
     private static final String DEFAULT_ADMIN_PASSWORD_ERROR_MESSAGE =
-            "⚠ The default adminPassword property is being used. "
+            "⚠️ WARNING: The default adminPassword property is being used. "
             + "This must be changed to avoid a security breach!";
 
     private static final String DEFAULT_ANON_KEY_ERROR_MESSAGE =
-            "⚠ The default anonymousKey property is being used. "
+            "⚠️ WARNING: The default anonymousKey property is being used. "
             + "This must be changed to avoid a security breach!";
 
     private final boolean defaultAesKeyInUse;
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java
index 711df6540b..80593895e6 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AbstractThrottler.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.spring.security.throttle;
 import java.util.ArrayDeque;
 import java.util.Deque;
 import java.util.concurrent.TimeUnit;
-import java.util.function.LongSupplier;
 import javax.cache.Cache;
 import org.apache.syncope.core.spring.security.SecurityProperties;
 
@@ -29,8 +28,6 @@ abstract class AbstractThrottler {
 
     protected final SecurityProperties.ThrottleProperties throttle;
 
-    protected final LongSupplier clock = System::currentTimeMillis;
-
     protected final Cache<String, ThrottlerAttempts> attempts;
 
     protected AbstractThrottler(
@@ -48,6 +45,10 @@ abstract class AbstractThrottler {
                 && throttle.getLockSeconds() > 0;
     }
 
+    protected long now() {
+        return System.currentTimeMillis();
+    }
+
     protected Deque<Long> prune(final Deque<Long> attempts, final long now) {
         Deque<Long> failures = new ArrayDeque<>(attempts);
         long threshold = now - 
TimeUnit.SECONDS.toMillis(throttle.getWindowSeconds());
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java
index f725e0afba..0155bb66d7 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottler.java
@@ -49,25 +49,26 @@ public class AuthenticationThrottler extends 
AbstractThrottler {
         }
 
         String key = key(domain, username);
-        long now = clock.getAsLong();
-        Long retryAfter = attempts.invoke(key, (entry, args) -> {
-            if (!entry.exists()) {
-                return null;
-            }
-
-            ThrottlerAttempts state = entry.getValue();
-            if (state.blockedUntil() > now) {
-                return retryAfterSeconds(state.blockedUntil(), now);
-            }
+        long now = now();
 
-            Deque<Long> failures = prune(state.failures(), now);
-            if (failures.isEmpty()) {
-                entry.remove();
+        Long retryAfter = null;
+        synchronized (attempts) {
+            ThrottlerAttempts state = attempts.get(key);
+            if (state == null) {
+                retryAfter = null;
             } else {
-                entry.setValue(new ThrottlerAttempts(failures, 
state.blockedUntil()));
+                if (state.blockedUntil() > now) {
+                    retryAfter = retryAfterSeconds(state.blockedUntil(), now);
+                } else {
+                    Deque<Long> failures = prune(state.failures(), now);
+                    if (failures.isEmpty()) {
+                        attempts.remove(key);
+                    } else {
+                        attempts.put(key, new ThrottlerAttempts(failures, 
state.blockedUntil()));
+                    }
+                }
             }
-            return null;
-        });
+        }
         if (retryAfter != null) {
             throw new AuthenticationThrottleException(retryAfter);
         }
@@ -82,23 +83,27 @@ public class AuthenticationThrottler extends 
AbstractThrottler {
             return;
         }
 
-        long now = clock.getAsLong();
-        Long retryAfter = attempts.invoke(key(domain, username), (entry, args) 
-> {
-            ThrottlerAttempts state = entry.exists()
-                    ? entry.getValue()
-                    : new ThrottlerAttempts();
+        String key = key(domain, username);
+        long now = now();
+
+        Long retryAfter = null;
+        synchronized (attempts) {
+            ThrottlerAttempts state = attempts.get(key);
+            if (state == null) {
+                state = new ThrottlerAttempts();
+            }
+
             Deque<Long> failures = prune(state.failures(), now);
             failures.addLast(now);
 
             if (failures.size() >= throttle.getMaxAttempts()) {
                 long blockedUntil = now + 
TimeUnit.SECONDS.toMillis(throttle.getLockSeconds());
-                entry.setValue(new ThrottlerAttempts(failures, blockedUntil));
-                return retryAfterSeconds(blockedUntil, now);
+                attempts.put(key, new ThrottlerAttempts(failures, 
blockedUntil));
+                retryAfter = retryAfterSeconds(blockedUntil, now);
+            } else {
+                attempts.put(key, new ThrottlerAttempts(failures, 
state.blockedUntil()));
             }
-
-            entry.setValue(new ThrottlerAttempts(failures, 
state.blockedUntil()));
-            return null;
-        });
+        }
         if (retryAfter != null) {
             throw new AuthenticationThrottleException(retryAfter);
         }
diff --git 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java
 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java
index 9795cfae42..d811b78c78 100644
--- 
a/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java
+++ 
b/core/spring/src/main/java/org/apache/syncope/core/spring/security/throttle/PasswordResetRequestThrottler.java
@@ -60,48 +60,48 @@ public class PasswordResetRequestThrottler extends 
AbstractThrottler {
             return;
         }
 
-        long now = clock.getAsLong();
         String attemptKey = key(domain, username, clientAddress);
         String attemptKeyId = attemptKeyId(attemptKey);
-        PasswordResetThrottleException blocked = attempts.invoke(
-                attemptKey,
-                (entry, args) -> {
-                    ThrottlerAttempts state = entry.exists()
-                    ? entry.getValue()
-                    : new ThrottlerAttempts();
+        long now = now();
 
-                    if (state.blockedUntil() > now) {
-                        PasswordResetThrottleException exception = 
blocked(state.blockedUntil(), now);
-                        LOG.debug(
-                                "Password reset request throttled for attempt 
key [{}]; retry after [{}] seconds",
-                                attemptKeyId,
-                                exception.getRetryAfterSeconds());
-                        return exception;
-                    }
+        PasswordResetThrottleException blocked = null;
+        synchronized (attempts) {
+            ThrottlerAttempts state = attempts.get(attemptKey);
+            if (state == null) {
+                state = new ThrottlerAttempts();
+            }
 
-                    Deque<Long> failures = prune(state.failures(), now);
-                    failures.addLast(now);
-                    if (failures.size() > throttle.getMaxAttempts()) {
-                        long blockedUntil = now + 
TimeUnit.SECONDS.toMillis(throttle.getLockSeconds());
-                        entry.setValue(new ThrottlerAttempts(failures, 
blockedUntil));
-                        LOG.warn(
-                                "Password reset request throttling activated 
for attempt key [{}]; "
-                                + "attempts [{}], max attempts [{}], lock 
seconds [{}]",
-                                attemptKeyId,
-                                failures.size(),
-                                throttle.getMaxAttempts(),
-                                throttle.getLockSeconds());
-                        return blocked(blockedUntil, now);
-                    }
+            if (state.blockedUntil() > now) {
+                blocked = blocked(state.blockedUntil(), now);
+                LOG.debug(
+                        "Password reset request throttled for attempt key 
[{}]; retry after [{}] seconds",
+                        attemptKeyId,
+                        blocked.getRetryAfterSeconds());
+            } else {
+                Deque<Long> failures = prune(state.failures(), now);
+                failures.addLast(now);
 
-                    entry.setValue(new ThrottlerAttempts(failures, 
state.blockedUntil()));
+                if (failures.size() > throttle.getMaxAttempts()) {
+                    long blockedUntil = now + 
TimeUnit.SECONDS.toMillis(throttle.getLockSeconds());
+                    attempts.put(attemptKey, new ThrottlerAttempts(failures, 
blockedUntil));
+                    LOG.warn(
+                            "Password reset request throttling activated for 
attempt key [{}]; "
+                            + "attempts [{}], max attempts [{}], lock seconds 
[{}]",
+                            attemptKeyId,
+                            failures.size(),
+                            throttle.getMaxAttempts(),
+                            throttle.getLockSeconds());
+                    blocked = blocked(blockedUntil, now);
+                } else {
+                    attempts.put(attemptKey, new ThrottlerAttempts(failures, 
state.blockedUntil()));
                     LOG.trace(
                             "Password reset request failure recorded for 
attempt key [{}]; attempts [{}/{}]",
                             attemptKeyId,
                             failures.size(),
                             throttle.getMaxAttempts());
-                    return null;
-                });
+                }
+            }
+        }
         if (blocked != null) {
             throw blocked;
         }
diff --git 
a/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java
 
b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java
index 8f2a0c1795..77bb80215a 100644
--- 
a/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java
+++ 
b/core/spring/src/test/java/org/apache/syncope/core/spring/security/throttle/AuthenticationThrottlerTest.java
@@ -32,7 +32,6 @@ import javax.cache.expiry.TouchedExpiryPolicy;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.core.spring.security.SecurityProperties;
 import org.junit.jupiter.api.Test;
-import org.springframework.test.util.ReflectionTestUtils;
 
 class AuthenticationThrottlerTest {
 
@@ -61,8 +60,13 @@ class AuthenticationThrottlerTest {
             cache.clear();
         }
 
-        AuthenticationThrottler throttler = new 
AuthenticationThrottler(securityProperties, cache);
-        ReflectionTestUtils.setField(throttler, "clock", clock);
+        AuthenticationThrottler throttler = new 
AuthenticationThrottler(securityProperties, cache) {
+
+            @Override
+            protected long now() {
+                return clock.getAsLong();
+            }
+        };
         return throttler;
     }
 
diff --git a/core/starter/pom.xml b/core/starter/pom.xml
index c973d6c024..c22ba64a8d 100644
--- a/core/starter/pom.xml
+++ b/core/starter/pom.xml
@@ -58,10 +58,6 @@ under the License.
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-cache</artifactId>
     </dependency>
-    <dependency>
-      <groupId>com.github.ben-manes.caffeine</groupId>
-      <artifactId>jcache</artifactId>
-    </dependency>
 
     <dependency>
       <groupId>org.springframework.boot</groupId>
diff --git a/core/starter/src/main/resources/core.properties 
b/core/starter/src/main/resources/core.properties
index 27c75d568e..55b64fe55a 100644
--- a/core/starter/src/main/resources/core.properties
+++ b/core/starter/src/main/resources/core.properties
@@ -45,8 +45,6 @@ service.discovery.address=http://localhost:8080/syncope/rest/
 # acceptable values: POSTGRESQL | MYSQL | MARIADB | ORACLE
 persistence.db-type=POSTGRESQL
 
-persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
-
 persistence.domain[0].key=Master
 persistence.domain[0].jdbcDriver=org.postgresql.Driver
 
persistence.domain[0].jdbcURL=jdbc:postgresql://localhost:5432/syncope?stringtype=unspecified
diff --git 
a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java
 
b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java
index 664db44d1d..678bf5c6c0 100644
--- 
a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java
+++ 
b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/WorkflowTestContext.java
@@ -23,6 +23,8 @@ import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
 import java.time.OffsetDateTime;
+import javax.cache.CacheManager;
+import javax.cache.Caching;
 import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
 import org.apache.syncope.common.keymaster.client.api.DomainOps;
 import org.apache.syncope.common.keymaster.client.api.model.JPADomain;
@@ -128,4 +130,9 @@ public class WorkflowTestContext {
     public ConnectorManager connectorManager() {
         return mock(ConnectorManager.class);
     }
+
+    @Bean
+    public CacheManager cacheManager() {
+        return Caching.getCachingProvider().getCacheManager();
+    }
 }
diff --git a/docker/core/pom.xml b/docker/core/pom.xml
index 8d61db9d16..77d4e3e905 100644
--- a/docker/core/pom.xml
+++ b/docker/core/pom.xml
@@ -111,6 +111,40 @@ under the License.
       <version>${project.version}</version>
     </dependency>
 
+    <!-- JCache providers -->
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-caffeine</artifactId>
+      <version>${project.version}</version>
+      <classifier>wrap</classifier>
+      <type>zip</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-hazelcast</artifactId>
+      <version>${project.version}</version>
+      <classifier>wrap</classifier>
+      <type>zip</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-ehcache</artifactId>
+      <version>${project.version}</version>
+      <classifier>wrap</classifier>
+      <type>zip</type>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-infinispan</artifactId>
+      <version>${project.version}</version>
+      <classifier>wrap</classifier>
+      <type>zip</type>
+      <scope>test</scope>
+    </dependency>
+
     <!-- JDBC drivers -->
     <dependency>
       <groupId>org.postgresql</groupId>
@@ -172,6 +206,11 @@ under the License.
                 <copy 
file="${settings.localRepository}/com/mysql/mysql-connector-j/${jdbc.mysql.version}/mysql-connector-j-${jdbc.mysql.version}.jar"
 todir="${project.build.directory}/tmp/lib" overwrite="true"/>
                 <copy 
file="${settings.localRepository}/org/mariadb/jdbc/mariadb-java-client/${jdbc.mariadb.version}/mariadb-java-client-${jdbc.mariadb.version}.jar"
 todir="${project.build.directory}/tmp/lib" overwrite="true"/>
                 <copy 
file="${settings.localRepository}/com/oracle/database/jdbc/ojdbc11/${jdbc.oracle.version}/ojdbc11-${jdbc.oracle.version}.jar"
 todir="${project.build.directory}/tmp/lib" overwrite="true"/>
+
+                <copy 
file="${settings.localRepository}/org/apache/syncope/core/cache/syncope-core-cache-caffeine/${project.version}/syncope-core-cache-caffeine-${project.version}-wrap.zip"
 
tofile="${project.build.directory}/tmp/lib/syncope-core-cache-caffeine-wrap.zip"
 overwrite="true"/>
+                <copy 
file="${settings.localRepository}/org/apache/syncope/core/cache/syncope-core-cache-ehcache/${project.version}/syncope-core-cache-ehcache-${project.version}-wrap.zip"
 
tofile="${project.build.directory}/tmp/lib/syncope-core-cache-ehcache-wrap.zip" 
overwrite="true"/>
+                <copy 
file="${settings.localRepository}/org/apache/syncope/core/cache/syncope-core-cache-hazelcast/${project.version}/syncope-core-cache-hazelcast-${project.version}-wrap.zip"
 
tofile="${project.build.directory}/tmp/lib/syncope-core-cache-hazelcast-wrap.zip"
 overwrite="true"/>
+                <copy 
file="${settings.localRepository}/org/apache/syncope/core/cache/syncope-core-cache-infinispan/${project.version}/syncope-core-cache-infinispan-${project.version}-wrap.zip"
 
tofile="${project.build.directory}/tmp/lib/syncope-core-cache-infinispan-wrap.zip"
 overwrite="true"/>
               </target>
             </configuration>
             <goals>
diff --git a/docker/core/src/main/resources/Dockerfile 
b/docker/core/src/main/resources/Dockerfile
index 62c065d62f..e99b9a6e46 100644
--- a/docker/core/src/main/resources/Dockerfile
+++ b/docker/core/src/main/resources/Dockerfile
@@ -20,13 +20,14 @@ LABEL 
org.opencontainers.image.authors="[email protected]"
 
 RUN apk update && apk add bash
 
-RUN mkdir /opt/syncope /opt/syncope/bin /opt/syncope/bundles /opt/syncope/conf 
/opt/syncope/lib /opt/syncope/log
+RUN mkdir /opt/syncope /opt/syncope/bin /opt/syncope/bundles /opt/syncope/conf 
/opt/syncope/lib /opt/syncope/log /opt/syncope/cache
 
 COPY *.properties *.xml saml.keystore.jks /opt/syncope/conf/
 
 COPY bundles/*.jar /opt/syncope/bundles/
 
 COPY lib/mariadb*.jar lib/mysql*jar lib/ojdbc*jar lib/postgresql*jar 
/opt/syncope/lib/
+COPY lib/syncope-core-cache-*zip /opt/syncope/cache
 COPY lib/syncope-docker-core-*jar /opt/syncope/lib/syncope.jar
 
 ENV SPRING_PROFILES_ACTIVE=docker
diff --git a/docker/core/src/main/resources/core-docker.properties 
b/docker/core/src/main/resources/core-docker.properties
index 2749e0994c..cbadd111ae 100644
--- a/docker/core/src/main/resources/core-docker.properties
+++ b/docker/core/src/main/resources/core-docker.properties
@@ -24,8 +24,6 @@ security.productionMode=${PRODUCTION_MODE:true}
 security.anonymousUser=${ANONYMOUS_USER:anonymous}
 security.anonymousKey=${ANONYMOUS_KEY:anonymousKey}
 
-persistence.cacheProvider=${HIBERNATE_JCACHE_PROVIDER}
-
 spring.lifecycle.timeout-per-shutdown-phase=30s
 
 spring.threads.virtual.enabled=true
diff --git a/docker/core/src/main/resources/startup.sh 
b/docker/core/src/main/resources/startup.sh
index 3a552db7b0..09191582b6 100755
--- a/docker/core/src/main/resources/startup.sh
+++ b/docker/core/src/main/resources/startup.sh
@@ -17,4 +17,26 @@
 # specific language governing permissions and limitations
 # under the License.
 
+cd /opt/syncope/cache
+if [ $JCACHE = "ehcache" ]; then
+  mkdir ehcache && cd ehcache && unzip ../syncope-core-cache-ehcache-wrap.zip
+  LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/ehcache"
+elif [[ $JCACHE == hazelcast* ]]; then
+  mkdir hazelcast && cd hazelcast && unzip 
../syncope-core-cache-hazelcast-wrap.zip
+  LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/hazelcast"
+
+  PROVIDER_TYPE="member"
+  if [ $JCACHE = "hazelcast_client" ]; then
+    PROVIDER_TYPE="client"
+  fi
+  JAVA_OPTS="$JAVA_OPTS -Dhazelcast.jcache.provider.type=$PROVIDER_TYPE 
-Dhazelcast.logging.type=slf4j --add-modules java.se --add-exports 
java.base/jdk.internal.ref=ALL-UNNAMED --add-opens 
java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED 
--add-opens java.management/sun.management=ALL-UNNAMED --add-opens 
jdk.management/com.sun.management.internal=ALL-UNNAMED"
+elif [ $JCACHE = "infinispan" ]; then
+  mkdir infinispan && cd infinispan && unzip 
../syncope-core-cache-infinispan-wrap.zip
+  LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/infinispan"
+else
+  mkdir caffeine && cd caffeine && unzip 
../syncope-core-cache-caffeine-wrap.zip
+  LOADER_PATH="$LOADER_PATH,/opt/syncope/cache/caffeine"
+fi
+cd -
+
 exec java $JAVA_OPTS -jar /opt/syncope/lib/syncope.jar
diff --git a/docker/src/main/resources/docker-compose/docker-compose-all.yml 
b/docker/src/main/resources/docker-compose/docker-compose-all.yml
index 75f0feef1b..a69b11424f 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-all.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml
@@ -48,7 +48,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-ha.yml 
b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
index 1897c0723d..840b412d72 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-ha.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-ha.yml
@@ -35,13 +35,13 @@ services:
        - "18080:8080"
      restart: always
      environment:
+       JCACHE: hazelcast_member
        SPRING_PROFILES_ACTIVE: docker,postgresql,saml2
        DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified
        DB_USER: syncope
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: tcp(Addresses=syncope1;syncope2)
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
@@ -57,13 +57,13 @@ services:
        - "18081:8080"
      restart: always
      environment:
+       JCACHE: hazelcast_member
        SPRING_PROFILES_ACTIVE: docker,postgresql
        DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified
        DB_USER: syncope
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: tcp(Addresses=syncope1;syncope2)
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
diff --git 
a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml 
b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
index cc2e5df6c2..866a91ea0e 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-mariadb.yml
@@ -43,7 +43,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml 
b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
index eee9de85be..d464753b63 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-mysql.yml
@@ -43,7 +43,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
diff --git a/docker/src/main/resources/docker-compose/docker-compose-oracle.yml 
b/docker/src/main/resources/docker-compose/docker-compose-oracle.yml
index 00d72705bd..4255ae7746 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-oracle.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-oracle.yml
@@ -42,7 +42,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
diff --git 
a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml 
b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
index 4fbaf7e1ad..3329f76dc5 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-postgresql.yml
@@ -40,7 +40,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD}
diff --git 
a/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml
 
b/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml
index 74efb05717..ebde7fd2fd 100644
--- 
a/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml
+++ 
b/docker/src/main/resources/kubernetes/syncope/templates/syncope-deployment.yaml
@@ -25,7 +25,7 @@ data:
  DB_PASSWORD: {{.Values.syncopeEnvironment.userCreds}}
  DB_POOL_MAX: "{{.Values.syncopeEnvironment.dbPoolMax}}"
  DB_POOL_MIN: "{{.Values.syncopeEnvironment.dbPoolMin}}"
- HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+ JCACHE: caffeine
 ---
 apiVersion: extensions/v1beta1
 kind: Deployment
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 3650a0e83a..6525cf5be8 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -64,6 +64,12 @@ under the License.
       <version>${project.version}</version>
     </dependency>
 
+    <dependency>
+      <groupId>org.apache.syncope.core.cache</groupId>
+      <artifactId>syncope-core-cache-caffeine</artifactId>      
+      <version>${project.version}</version>
+    </dependency>
+
     <dependency>
       <groupId>org.apache.syncope.core</groupId>
       <artifactId>syncope-core-self-keymaster-starter</artifactId>
@@ -1223,10 +1229,6 @@ under the License.
     <profile>
       <id>payara-it</id>
 
-      <properties>
-        <spring.profiles.active>embedded,payara</spring.profiles.active>
-      </properties>
-
       <dependencies>
         <dependency>
           <groupId>org.apache.syncope.core</groupId>
@@ -1241,13 +1243,16 @@ under the License.
               <groupId>org.apache.tomcat.embed</groupId>
               <artifactId>tomcat-embed-el</artifactId>
             </exclusion>
-            <exclusion>
-              <groupId>com.github.ben-manes.caffeine</groupId>
-              <artifactId>jcache</artifactId>
-            </exclusion>
           </exclusions>
         </dependency>
 
+        <dependency>
+          <groupId>org.apache.syncope.core.cache</groupId>
+          <artifactId>syncope-core-cache-caffeine</artifactId>      
+          <version>${project.version}</version>
+          <scope>provided</scope>
+        </dependency>
+
         <dependency>
           <groupId>org.glassfish</groupId>
           <artifactId>jakarta.faces</artifactId>
diff --git a/fit/core-reference/src/main/resources/core-payara.properties 
b/fit/core-reference/src/main/resources/core-payara.properties
deleted file mode 100644
index db449da933..0000000000
--- a/fit/core-reference/src/main/resources/core-payara.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements.  See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership.  The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License.  You may obtain a copy of the License at
-#
-#   http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied.  See the License for the
-# specific language governing permissions and limitations
-# under the License.
-
-persistence.cacheProvider=com.hazelcast.cache.HazelcastCachingProvider
diff --git a/src/main/asciidoc/getting-started/obtain.adoc 
b/src/main/asciidoc/getting-started/obtain.adoc
index 69e109a68d..8f28619353 100644
--- a/src/main/asciidoc/getting-started/obtain.adoc
+++ b/src/main/asciidoc/getting-started/obtain.adoc
@@ -102,10 +102,9 @@ Environment variables:
 * `DB_PASSWORD`: password for internal storage authentication
 * `DB_POOL_MAX`: internal storage connection pool: ceiling
 * `DB_POOL_MIN`: internal storage connection pool: floor
-* `HIBERNATE_JCACHE_PROVIDER`: configure multiple instances, with high 
availability; valid values are the ones accepted by
-Hibernate for
-https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching-provider-jcache-cache-manager[JCache
 CacheManager^]
-including the default value 
`com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider` (single 
instance)
+* `JCACHE`: configure 
https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching-provider-jcache-cache-manager[JCache
 provider^]
+for Hibernate, defaults to https://github.com/ben-manes/caffeine[Caffeine^] if 
no value is provided; allowed values:
+`caffeine|ehcache|hazelcast_client|hazelcast_member|infinispan`
 
 ===== Console
 
@@ -180,7 +179,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: http://localhost:8080/syncope/rest/keymaster
        KEYMASTER_USERNAME: ${ANONYMOUS_USER}
        KEYMASTER_PASSWORD: ${ANONYMOUS_KEY}
@@ -265,7 +264,7 @@ services:
        DB_PASSWORD: syncope
        DB_POOL_MAX: 20
        DB_POOL_MIN: 5
-       HIBERNATE_JCACHE_PROVIDER: 
com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
+       JCACHE: caffeine
        KEYMASTER_ADDRESS: keymaster:2181
        KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-}
        KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-}
diff --git 
a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc 
b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc
index 328ce57ac7..65679d6c03 100644
--- a/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/attributerepositories.adoc
@@ -23,12 +23,12 @@ certain <<policies-attribute-release,Attribute Release 
Policy>>.
 
 Some attribute repositories are provided:
 
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-JDBC.html[Database^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-LDAP.html[LDAP^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Stub.html[Stub^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Syncope.html[Syncope^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-AzureAD.html[Azure
 Active Directory^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution-Okta.html[Okta^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-JDBC.html[Database^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-LDAP.html[LDAP^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Stub.html[Stub^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Syncope.html[Syncope^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-AzureAD.html[Azure
 Active Directory^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution-Okta.html[Okta^]
 
 [TIP]
 ====
@@ -51,4 +51,4 @@ class.
 
 [NOTE]
 Attribute Repositories are dynamically translated into 
-https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution.html[CAS 
Attribute Resolution^] configuration.
+https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution.html[CAS 
Attribute Resolution^] configuration.
diff --git 
a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc 
b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
index 4623370f6c..880826b3a3 100644
--- a/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authenticationmodules.adoc
@@ -24,24 +24,24 @@ technology or repository, in the context of a certain 
<<policies-authentication,
 Several authentication modules are provided:
 
 * Principal Authentication:
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/Database-Authentication.html[Database^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/JAAS-Authentication.html[JAAS^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/LDAP-Authentication.html[LDAP^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/SPNEGO-Authentication.html[SPNEGO^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/Syncope-Authentication.html[Syncope^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/Azure-ActiveDirectory-Authentication.html[Azure
 Active Directory^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/Okta-Authentication.html[Okta^]
-    ** 
https://apereo.github.io/cas/7.3.x/authentication/X509-Authentication.html[X509^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-Generic-OpenID-Connect.html[OpenID
 Connect^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-OAuth20.html[OAuth2^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-SAML.htmll[SAML^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-Apple.html[Apple
 Signin^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-Azure-AD.html[Azure
 Active Directory (OIDC)^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-Google-OpenID-Connect.html[Google
 OpenID^]
-    ** 
https://apereo.github.io/cas/7.3.x/integration/Delegate-Authentication-Keycloak.html[Keycloak^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/Database-Authentication.html[Database^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/JAAS-Authentication.html[JAAS^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/LDAP-Authentication.html[LDAP^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/SPNEGO-Authentication.html[SPNEGO^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/Syncope-Authentication.html[Syncope^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/Azure-ActiveDirectory-Authentication.html[Azure
 Active Directory^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/Okta-Authentication.html[Okta^]
+    ** 
https://apereo.github.io/cas/8.0.x/authentication/X509-Authentication.html[X509^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-Generic-OpenID-Connect.html[OpenID
 Connect^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-OAuth20.html[OAuth2^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-SAML.htmll[SAML^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-Apple.html[Apple
 Signin^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-Azure-AD.html[Azure
 Active Directory (OIDC)^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-Google-OpenID-Connect.html[Google
 OpenID^]
+    ** 
https://apereo.github.io/cas/8.0.x/integration/Delegate-Authentication-Keycloak.html[Keycloak^]
 * MFA:
-    ** 
https://apereo.github.io/cas/7.3.x/mfa/DuoSecurity-Authentication.html[Duo 
Security^]
-    ** 
https://apereo.github.io/cas/7.3.x/mfa/GoogleAuthenticator-Authentication.html[Google
 Authenticator^]
+    ** 
https://apereo.github.io/cas/8.0.x/mfa/DuoSecurity-Authentication.html[Duo 
Security^]
+    ** 
https://apereo.github.io/cas/8.0.x/mfa/GoogleAuthenticator-Authentication.html[Google
 Authenticator^]
 
 [TIP]
 ====
@@ -64,4 +64,4 @@ class.
 
 [NOTE]
 Authentication Modules are dynamically translated into
-https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Components.html#authentication-handlers[CAS
 Authentication Handlers^].
+https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Components.html#authentication-handlers[CAS
 Authentication Handlers^].
diff --git a/src/main/asciidoc/reference-guide/concepts/authprofile.adoc 
b/src/main/asciidoc/reference-guide/concepts/authprofile.adoc
index d7e230e841..250bc1882a 100644
--- a/src/main/asciidoc/reference-guide/concepts/authprofile.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/authprofile.adoc
@@ -21,8 +21,8 @@
 When users log into the <<client-applications,web applications>> integrated 
with <<web-access,WA>>, the following
 information is tracked for <<admin-console,administration>> and 
<<enduser-application,self-management>>:
 
-* 
https://apereo.github.io/cas/7.3.x/authentication/Surrogate-Authentication.html[Surrogate
 Authentication^]
-* 
https://apereo.github.io/cas/7.3.x/mfa/GoogleAuthenticator-Authentication.html[Google
 Authenticator Authentication^]
-* 
https://apereo.github.io/cas/7.3.x/mfa/Multifactor-TrustedDevice-Authentication.html[Multifactor
 Authentication Trusted Devices^]
-* 
https://apereo.github.io/cas/7.3.x/mfa/FIDO2-WebAuthn-Authentication.html[FIDO2 
WebAuthn (Passkey) Multifactor Authentication^]
-* 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-Consent.html[Attribute
 Consent^]
+* 
https://apereo.github.io/cas/8.0.x/authentication/Surrogate-Authentication.html[Surrogate
 Authentication^]
+* 
https://apereo.github.io/cas/8.0.x/mfa/GoogleAuthenticator-Authentication.html[Google
 Authenticator Authentication^]
+* 
https://apereo.github.io/cas/8.0.x/mfa/Multifactor-TrustedDevice-Authentication.html[Multifactor
 Authentication Trusted Devices^]
+* 
https://apereo.github.io/cas/8.0.x/mfa/FIDO2-WebAuthn-Authentication.html[FIDO2 
WebAuthn (Passkey) Multifactor Authentication^]
+* 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-Consent.html[Attribute
 Consent^]
diff --git a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc 
b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
index 4a1156f077..f96f626504 100644
--- a/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/clientapplications.adoc
@@ -34,17 +34,17 @@ When defining a client application, the following 
parameters shall be specified:
 . name - regular expression to match requests
 . description - optional textual description
 . username attribute provider, mapping to
-https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-PrincipalId-Attribute.html[CAS
 Attribute-based Principal Id^]
+https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-PrincipalId-Attribute.html[CAS
 Attribute-based Principal Id^]
 . <<policies-authentication,authentication policy>>
 . <<policies-access,access policy>>
 . <<policies-attribute-release,attribute release policy>>
 . <<policies-ticket-expiration,ticket expiration policy>>
 . additional properties
 . logout type, mapping to
-https://apereo.github.io/cas/7.3.x/installation/Logout-Single-Signout.html#slo-requests[the
 equivalent CAS setting^]
+https://apereo.github.io/cas/8.0.x/installation/Logout-Single-Signout.html#slo-requests[the
 equivalent CAS setting^]
 
 More parameters are required to be specified depending on the actual client 
application type.
 
 [NOTE]
 Client Applications are dynamically translated into
-https://apereo.github.io/cas/7.3.x/services/Service-Management.html[CAS 
Services^].
+https://apereo.github.io/cas/8.0.x/services/Service-Management.html[CAS 
Services^].
diff --git a/src/main/asciidoc/reference-guide/concepts/domains.adoc 
b/src/main/asciidoc/reference-guide/concepts/domains.adoc
index 3eea561038..379cbe2964 100644
--- a/src/main/asciidoc/reference-guide/concepts/domains.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/domains.adoc
@@ -27,7 +27,7 @@ External Resources, Policies, Tasks, etc. from different 
domains (e.g. tenants)
 By default, a single `Master` domain is defined, which also bears the 
configuration for additional domains.
 
 Every domain besides `Master` is mapped one-to-one with a
-https://apereo.github.io/cas/7.3.x/multitenancy/Multitenancy-Overview.html[CAS 
tenant^] having the same identifier; this
+https://apereo.github.io/cas/8.0.x/multitenancy/Multitenancy-Overview.html[CAS 
tenant^] having the same identifier; this
 allows for <<web-access>> configuration to relate only to the given domain's 
database instance.
 
 [.text-center]
diff --git a/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc 
b/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc
index 5807ed49ca..e44ef78fc6 100644
--- a/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/passwordmanagement.adoc
@@ -23,10 +23,10 @@ according to rules defined by a single configured password 
management module (on
 
 Some password management modules are provided:
 
-* 
https://apereo.github.io/cas/7.3.x/password_management/Password-Management-LDAP.html[LDAP^]
-* 
https://apereo.github.io/cas/7.3.x/password_management/Password-Management-JDBC.html[JDBC^]
-* 
https://apereo.github.io/cas/7.3.x/password_management/Password-Management-REST.html[REST^]
-* 
https://apereo.github.io/cas/7.3.x/password_management/Password-Management-ApacheSyncope.html[Syncope^]
+* 
https://apereo.github.io/cas/8.0.x/password_management/Password-Management-LDAP.html[LDAP^]
+* 
https://apereo.github.io/cas/8.0.x/password_management/Password-Management-JDBC.html[JDBC^]
+* 
https://apereo.github.io/cas/8.0.x/password_management/Password-Management-REST.html[REST^]
+* 
https://apereo.github.io/cas/8.0.x/password_management/Password-Management-ApacheSyncope.html[Syncope^]
 
 [TIP]
 ====
@@ -49,4 +49,4 @@ class.
 
 [NOTE]
 Password Management is dynamically translated into
-https://apereo.github.io/cas/7.3.x/password_management/Password-Management.html[CAS
 Password Management^] configuration.
+https://apereo.github.io/cas/8.0.x/password_management/Password-Management.html[CAS
 Password Management^] configuration.
diff --git a/src/main/asciidoc/reference-guide/concepts/policies.adoc 
b/src/main/asciidoc/reference-guide/concepts/policies.adoc
index dd84c70dfb..2d3ae151b2 100644
--- a/src/main/asciidoc/reference-guide/concepts/policies.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/policies.adoc
@@ -329,7 +329,7 @@ endif::[]
 
 [NOTE]
 Access Policy instances are dynamically translated into
-https://apereo.github.io/cas/7.3.x/services/Configuring-Service-Access-Strategy.html#configure-service-access-strategy[CAS
 Service Access Strategy^].
+https://apereo.github.io/cas/8.0.x/services/Configuring-Service-Access-Strategy.html#configure-service-access-strategy[CAS
 Service Access Strategy^].
 
 [[policies-attribute-release]]
 ==== Attribute Release
@@ -341,7 +341,7 @@ values.
 
 [NOTE]
 Attribute Release Policy instances are dynamically translated into
-https://apereo.github.io/cas/7.3.x/integration/Attribute-Release-Policies.html#attribute-release-policies[CAS
 Attribute Release Policy^].
+https://apereo.github.io/cas/8.0.x/integration/Attribute-Release-Policies.html#attribute-release-policies[CAS
 Attribute Release Policy^].
 
 [[policies-authentication]]
 ==== Authentication
@@ -368,7 +368,7 @@ be considered successful.
 
 [NOTE]
 Authentication Policy instances are dynamically translated into
-https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Policy.html#authentication-policy[CAS
 Authentication Policy^].
+https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Policy.html#authentication-policy[CAS
 Authentication Policy^].
 
 [[policies-propagation]]
 ==== Propagation
@@ -487,4 +487,4 @@ Ticket Expiration policies control the duration of various 
types of WA sessions.
 
 [NOTE]
 Ticket Expiration Policy instances are dynamically translated into
-https://apereo.github.io/cas/7.3.x/ticketing/Configuring-Ticket-Expiration-Policy.html[their
 CAS equivalent^].
+https://apereo.github.io/cas/8.0.x/ticketing/Configuring-Ticket-Expiration-Policy.html[their
 CAS equivalent^].
diff --git 
a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc 
b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc
index 101ff82ae8..5000cfc488 100644
--- a/src/main/asciidoc/reference-guide/configuration/highavailability.adoc
+++ b/src/main/asciidoc/reference-guide/configuration/highavailability.adoc
@@ -21,31 +21,86 @@
 ==== Core HA
 
 When deploying multiple Syncope <<core>> instances with a single database or 
database cluster, it is of
-fundamental importance that the 
https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching[second-level 
cache^]
+fundamental importance that the 
https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching[second-level 
cache^]
 is correctly configured for Hibernate ORM.
 Such configuration, in fact, allows the Hibernate ORM cache to remain 
synchronized across multiple JVMs, thus
 enforcing data consistency across all Syncope Core instances.
 
-The default configuration in `core.properties` is
+The supported https://github.com/jsr107/jsr107spec[JCache^] provider is 
configured according to the implementation found
+in the classpath.
 
-[source]
-....
-persistence.cacheProvider=com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
-....
+[WARNING]
+Ensure to define only one of the following providers to avoid conflicts.
 
-which is suited for single JVM installations; with multiple instances, more 
options are available depending on the
-configured https://github.com/jsr107/jsr107spec[JCache^] implementation, such 
as, for example,
-https://docs.hazelcast.com/hazelcast/5.6/jcache/overview[Hazelcast^],
-https://www.ehcache.org/documentation/3.0/107.html[Ehcache],
-https://infinispan.org/docs/stable/titles/hibernate/hibernate.html[Infinispan^]
 or
-https://ignite.apache.org/[Apache Ignite^].
+[core-ha-caffeine]
+===== Caffeine
 
-Additional, implementation-specific configuration files can be set up via 
`persistence.cacheURI`.
+By default, the following dependency is set in `core/pom.xml`.
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-caffeine</artifactId> 
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+[WARNING]
+https://github.com/ben-manes/caffeine[Caffeine^] does support only single JVM 
installations.
+
+[core-ha-ehcache]
+===== Ehcache
+
+Set the following dependency is in `core/pom.xml` to enable 
https://www.ehcache.org/documentation/3.11/107.html[Ehcache^]:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-caffeine</artifactId> 
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+[TIP]
+Adding `ehcache.xml` in the classpath will allow to fine-tune Ehcache's 
https://www.ehcache.org/documentation/3.11/xml.html[configuration^].
+
+[core-ha-hazelcast]
+===== Hazelcast
+
+Set the following dependency is in `core/pom.xml` to enable 
https://docs.hazelcast.com/hazelcast/5.7/jcache/overview[Hazelcast^]:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-hazelcast</artifactId> 
+  <version>${syncope.version}</version>
+</dependency>
+----
+
+[TIP]
+Adding `hazelcast.xml` in the classpath will allow to fine-tune Hazelcast's 
https://docs.hazelcast.com/hazelcast/5.7/configuration/understanding-configuration[configuration^].
+
+[core-ha-infinispan]
+===== Infinispan
+
+Set the following dependency is in `core/pom.xml` to enable 
https://infinispan.org/docs/stable/titles/embedding/embedding.html[Infinispan^]:
+
+[source,xml,subs="verbatim,attributes"]
+----
+<dependency>
+  <groupId>org.apache.syncope.core.cache</groupId>
+  <artifactId>syncope-core-cache-infinispan</artifactId> 
+  <version>${syncope.version}</version>
+</dependency>
+----
 
 ==== WA HA
 
 When multiple Syncope <<web-access>> instances are needed to support 
authentication and authorization requirements, 
-a distributed 
https://apereo.github.io/cas/7.3.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry[Ticket
 Registry^]
+a distributed 
https://apereo.github.io/cas/8.0.x/ticketing/Configuring-Ticketing-Components.html#ticket-registry[Ticket
 Registry^]
 is required, such has the ones based on
-https://apereo.github.io/cas/7.3.x/ticketing/Hazelcast-Ticket-Registry.html[Hazelcast^]
-or 
https://apereo.github.io/cas/7.3.x/ticketing/Ignite-Ticket-Registry.html[Apache 
Ignite^].
+https://apereo.github.io/cas/8.0.x/ticketing/Hazelcast-Ticket-Registry.html[Hazelcast^]
+or 
https://apereo.github.io/cas/8.0.x/ticketing/Ignite-Ticket-Registry.html[Apache 
Ignite^].
diff --git a/src/main/asciidoc/reference-guide/usage/actuator.adoc 
b/src/main/asciidoc/reference-guide/usage/actuator.adoc
index 478b348d1e..10b8eeda8d 100644
--- a/src/main/asciidoc/reference-guide/usage/actuator.adoc
+++ b/src/main/asciidoc/reference-guide/usage/actuator.adoc
@@ -40,7 +40,7 @@ The pre-defined `health` endpoint is typically used for 
liveness and readiness p
 |===
 
 | `entityCache`
-a| Allows to work with 
https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching-statistics[JPA
 cache statistics^]
+a| Allows to work with 
https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching-statistics[JPA
 cache statistics^]
 
 * `GET` - shows JPA cache statistics
 * `POST {ENABLE,DISABLE,RESET}` - performs the requested operation onto JPA 
cache
@@ -60,15 +60,15 @@ a| Allows to work with the various jobs defined after 
<<tasks>> and <<reports>>.
 |===
 
 | `ssoSessions`
-| 
https://apereo.github.io/cas/7.3.x/authentication/Configuring-SSO.html#actuator-endpoints[More
 details^]
+| 
https://apereo.github.io/cas/8.0.x/authentication/Configuring-SSO.html#actuator-endpoints[More
 details^]
 | `registeredServices`
-| 
https://apereo.github.io/cas/7.3.x/services/Service-Management.html#actuator-endpoints[More
 details^]
+| 
https://apereo.github.io/cas/8.0.x/services/Service-Management.html#actuator-endpoints[More
 details^]
 | `authenticationHandlers`
-| 
https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Components.html#actuator-endpoints[More
 details^]
+| 
https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Components.html#actuator-endpoints[More
 details^]
 | `authenticationPolicies`
-| 
https://apereo.github.io/cas/7.3.x/authentication/Configuring-Authentication-Policy.html#actuator-endpoints[More
 details^]
+| 
https://apereo.github.io/cas/8.0.x/authentication/Configuring-Authentication-Policy.html#actuator-endpoints[More
 details^]
 | `resolveAttributes`
-| 
https://apereo.github.io/cas/7.3.x/integration/Attribute-Resolution.html#actuator-endpoints[More
 details^]
+| 
https://apereo.github.io/cas/8.0.x/integration/Attribute-Resolution.html#actuator-endpoints[More
 details^]
 
 |===
 
diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc 
b/src/main/asciidoc/reference-guide/usage/customization.adoc
index f46742a142..1280b54186 100644
--- a/src/main/asciidoc/reference-guide/usage/customization.adoc
+++ b/src/main/asciidoc/reference-guide/usage/customization.adoc
@@ -275,7 +275,7 @@ components:
 ===== Customize Hibernate ORM settings
 
 Hibernate ORM is at the core of the <<persistence,persistence>> layer; its 
configuration can be tweaked under several
-aspects - including 
https://docs.hibernate.org/orm/7.3/userguide/html_single/#caching[caching^] for
+aspects - including 
https://docs.hibernate.org/orm/7.4/userguide/html_single/#caching[caching^] for
 example, to best suit the various environments.
 
 The main configuration classes are:
diff --git a/src/main/asciidoc/reference-guide/usage/metrics.adoc 
b/src/main/asciidoc/reference-guide/usage/metrics.adoc
index 01baccff3d..c3137f6945 100644
--- a/src/main/asciidoc/reference-guide/usage/metrics.adoc
+++ b/src/main/asciidoc/reference-guide/usage/metrics.adoc
@@ -52,4 +52,4 @@ This can be enabled by adding the following dependency to 
`wa/pom.xml`:
 </dependency>
 ----
 
-For further options and configuration, refer to 
https://apereo.github.io/cas/7.3.x/monitoring/Configuring-Metrics.html[CAS 
documentation^].
+For further options and configuration, refer to 
https://apereo.github.io/cas/8.0.x/monitoring/Configuring-Metrics.html[CAS 
documentation^].

Reply via email to