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

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


The following commit(s) were added to refs/heads/3_0_X by this push:
     new 37b5caa144 [SYNCOPE-1763] Upgrading to CXF 3.6.1 + reviewing client 
usage (#473)
37b5caa144 is described below

commit 37b5caa144fcfa3704a2c2f0f6816f246c61e803
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Thu Jun 15 13:35:28 2023 +0200

    [SYNCOPE-1763] Upgrading to CXF 3.6.1 + reviewing client usage (#473)
---
 .../actuate/SyncopeCoreHealthIndicator.java        |  21 +++-
 .../syncope/client/lib/SyncopeAnonymousClient.java |  11 +-
 .../apache/syncope/client/lib/SyncopeClient.java   |  47 ++++----
 .../client/lib/SyncopeClientFactoryBean.java       |  21 ++++
 .../client/self/SelfKeymasterClientContext.java    |   2 +-
 .../keymaster/client/self/SelfKeymasterOps.java    |  43 +++++--
 core/idrepo/rest-cxf/pom.xml                       |   6 +-
 .../core/rest/cxf/service/SyncopeServiceImpl.java  |   7 +-
 core/self-keymaster-starter/pom.xml                |  60 ++++++++++
 .../src/test/resources/core-debug.properties       |  50 ++++++++
 .../src/test/resources/log4j2.xml                  | 130 +++++++++++++++++++++
 .../docker-compose/docker-compose-all.yml          |   2 +-
 .../client/enduser/rest/UserRequestRestClient.java |  11 +-
 .../common/lib/to/WorkflowTaskExecInput.java       |   2 -
 .../syncope/core/logic/UserRequestLogic.java       |   2 +-
 .../rest/api/service/UserRequestService.java       |   1 +
 .../org/apache/syncope/fit/core/BatchITCase.java   |  20 +---
 .../org/apache/syncope/fit/core/RESTITCase.java    |   1 +
 .../apache/syncope/fit/core/UserRequestITCase.java |  41 ++++---
 pom.xml                                            |   2 +-
 .../sra/actuate/SyncopeCoreHealthIndicator.java    |  21 +++-
 .../apache/syncope/wa/bootstrap/WARestClient.java  |  36 ++++--
 .../actuate/SyncopeCoreHealthIndicator.java        |   2 +-
 .../wa/starter/audit/WAAuditTrailManager.java      |   6 +-
 .../wa/starter/events/WAEventRepository.java       |   8 +-
 .../gauth/WAGoogleMfaAuthCredentialRepository.java |   5 +-
 .../gauth/WAGoogleMfaAuthTokenRepository.java      |   2 +-
 .../starter/oidc/WAOIDCJWKSGeneratorService.java   |  11 +-
 .../pac4j/saml/WASAML2ClientKeystoreGenerator.java |  20 ++--
 .../pac4j/saml/WASAML2ClientMetadataGenerator.java |  27 +++--
 .../pac4j/saml/WASAML2MetadataResolver.java        |  17 ++-
 .../idp/metadata/WASamlIdPMetadataGenerator.java   |  15 +--
 .../idp/metadata/WASamlIdPMetadataLocator.java     |  51 ++++----
 .../wa/starter/services/WAServiceRegistry.java     | 108 ++++++++---------
 .../WASurrogateAuthenticationService.java          |  22 ++--
 .../wa/starter/u2f/WAU2FDeviceRepository.java      |  51 ++++----
 .../webauthn/WAWebAuthnCredentialRepository.java   |  37 +++---
 .../syncope/wa/starter/WAServiceRegistryTest.java  |  13 +--
 .../wa/starter/audit/WAAuditTrailManagerTest.java  |  10 +-
 .../wa/starter/events/WAEventRepositoryTest.java   |  10 +-
 .../pac4j/saml/WASAML2ClientCustomizerTest.java    |  10 +-
 .../saml/WASAML2ClientKeystoreGeneratorTest.java   |  10 +-
 .../saml/WASAML2ClientMetadataGeneratorTest.java   |   9 +-
 .../pac4j/saml/WASAML2MetadataResolverTest.java    |   9 +-
 .../WASurrogateAuthenticationServiceTest.java      |   7 +-
 45 files changed, 640 insertions(+), 357 deletions(-)

diff --git 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
index 3ac380f22a..b340bcb420 100644
--- 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/actuate/SyncopeCoreHealthIndicator.java
@@ -41,6 +41,8 @@ public class SyncopeCoreHealthIndicator implements 
HealthIndicator {
 
     protected final boolean useGZIPCompression;
 
+    private UserSelfService service;
+
     public SyncopeCoreHealthIndicator(
             final ServiceOps serviceOps,
             final String anonymousUser,
@@ -53,16 +55,25 @@ public class SyncopeCoreHealthIndicator implements 
HealthIndicator {
         this.useGZIPCompression = useGZIPCompression;
     }
 
+    protected UserSelfService service() {
+        synchronized (this) {
+            if (service == null) {
+                service = new SyncopeClientFactoryBean().
+                        
setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
+                        setUseCompression(useGZIPCompression).
+                        create(new 
AnonymousAuthenticationHandler(anonymousUser, anonymousKey)).
+                        getService(UserSelfService.class);
+            }
+        }
+        return service;
+    }
+
     @Override
     public Health health() {
         Health.Builder builder = new Health.Builder();
 
         try {
-            new SyncopeClientFactoryBean().
-                    
setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
-                    setUseCompression(useGZIPCompression).
-                    create(new AnonymousAuthenticationHandler(anonymousUser, 
anonymousKey)).
-                    getService(UserSelfService.class).read();
+            service().read();
             builder.status(Status.UP);
         } catch (Exception e) {
             LOG.debug("When attempting to connect to Syncope Core", e);
diff --git 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java
 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java
index 69d6cb2521..d2465fb561 100644
--- 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java
+++ 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeAnonymousClient.java
@@ -29,6 +29,7 @@ import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.configuration.jsse.TLSClientParameters;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 import org.apache.syncope.common.lib.info.NumbersInfo;
 import org.apache.syncope.common.lib.info.PlatformInfo;
 import org.apache.syncope.common.lib.info.SystemInfo;
@@ -44,9 +45,17 @@ public class SyncopeAnonymousClient extends SyncopeClient {
             final RestClientExceptionMapper exceptionMapper,
             final AnonymousAuthenticationHandler anonymousAuthHandler,
             final boolean useCompression,
+            final HTTPClientPolicy httpClientPolicy,
             final TLSClientParameters tlsClientParameters) {
 
-        super(mediaType, restClientFactory, exceptionMapper, 
anonymousAuthHandler, useCompression, tlsClientParameters);
+        super(
+                mediaType,
+                restClientFactory,
+                exceptionMapper,
+                anonymousAuthHandler,
+                useCompression,
+                httpClientPolicy,
+                tlsClientParameters);
         this.anonymousAuthHandler = anonymousAuthHandler;
     }
 
diff --git 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
index 0c7c4d2d6f..da6127d59d 100644
--- 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
+++ 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
@@ -24,6 +24,7 @@ import java.io.IOException;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import javax.ws.rs.core.EntityTag;
 import javax.ws.rs.core.HttpHeaders;
@@ -38,7 +39,8 @@ import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
 import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
 import org.apache.cxf.transport.http.HTTPConduit;
-import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
+import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 import org.apache.syncope.client.lib.batch.BatchRequest;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import 
org.apache.syncope.common.lib.search.AnyObjectFiqlSearchConditionBuilder;
@@ -76,6 +78,8 @@ public class SyncopeClient {
 
     protected final boolean useCompression;
 
+    protected final HTTPClientPolicy httpClientPolicy;
+
     protected final TLSClientParameters tlsClientParameters;
 
     public SyncopeClient(
@@ -84,6 +88,7 @@ public class SyncopeClient {
             final RestClientExceptionMapper exceptionMapper,
             final AuthenticationHandler authHandler,
             final boolean useCompression,
+            final HTTPClientPolicy httpClientPolicy,
             final TLSClientParameters tlsClientParameters) {
 
         this.mediaType = mediaType;
@@ -93,6 +98,7 @@ public class SyncopeClient {
         }
         this.exceptionMapper = exceptionMapper;
         this.useCompression = useCompression;
+        this.httpClientPolicy = httpClientPolicy;
         this.tlsClientParameters = tlsClientParameters;
 
         init(authHandler);
@@ -279,30 +285,31 @@ public class SyncopeClient {
      * @return service instance of the given reference class
      */
     public <T> T getService(final Class<T> serviceClass) {
+        T serviceInstance;
         synchronized (restClientFactory) {
             restClientFactory.setServiceClass(serviceClass);
-            T serviceInstance = restClientFactory.create(serviceClass);
-
-            Client client = WebClient.client(serviceInstance);
-            client.type(mediaType).accept(mediaType);
-            if (serviceInstance instanceof AnyService || serviceInstance 
instanceof ExecutableService) {
-                client.accept(RESTHeaders.MULTIPART_MIXED);
-            }
+            serviceInstance = restClientFactory.create(serviceClass);
+        }
 
-            ClientConfiguration config = WebClient.getConfig(client);
-            config.getRequestContext().put(HEADER_SPLIT_PROPERTY, true);
-            
config.getRequestContext().put(URLConnectionHTTPConduit.HTTPURL_CONNECTION_METHOD_REFLECTION,
 true);
-            if (useCompression) {
-                config.getInInterceptors().add(new GZIPInInterceptor());
-                config.getOutInterceptors().add(new GZIPOutInterceptor());
-            }
-            if (tlsClientParameters != null) {
-                HTTPConduit httpConduit = (HTTPConduit) config.getConduit();
-                httpConduit.setTlsClientParameters(tlsClientParameters);
-            }
+        Client client = WebClient.client(serviceInstance);
+        client.type(mediaType).accept(mediaType);
+        if (serviceInstance instanceof AnyService || serviceInstance 
instanceof ExecutableService) {
+            client.accept(RESTHeaders.MULTIPART_MIXED);
+        }
 
-            return serviceInstance;
+        ClientConfiguration config = WebClient.getConfig(client);
+        config.getRequestContext().put(HEADER_SPLIT_PROPERTY, true);
+        config.getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.TRUE);
+        if (useCompression) {
+            config.getInInterceptors().add(new GZIPInInterceptor());
+            config.getOutInterceptors().add(new GZIPOutInterceptor());
         }
+
+        HTTPConduit httpConduit = (HTTPConduit) config.getConduit();
+        
Optional.ofNullable(httpClientPolicy).ifPresent(httpConduit::setClient);
+        
Optional.ofNullable(tlsClientParameters).ifPresent(httpConduit::setTlsClientParameters);
+
+        return serviceInstance;
     }
 
     public Triple<Map<String, Set<String>>, List<String>, UserTO> self() {
diff --git 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
index e8d67f34d4..05b941e78d 100644
--- 
a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
+++ 
b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
@@ -29,6 +29,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.configuration.jsse.TLSClientParameters;
 import org.apache.cxf.ext.logging.LoggingFeature;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.transports.http.configuration.ConnectionType;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 import org.apache.syncope.common.lib.jackson.SyncopeJsonMapper;
 import org.apache.syncope.common.lib.jackson.SyncopeXmlMapper;
 import org.apache.syncope.common.lib.jackson.SyncopeYAMLMapper;
@@ -82,6 +84,8 @@ public class SyncopeClientFactoryBean {
 
     private boolean useCompression;
 
+    private HTTPClientPolicy httpClientPolicy;
+
     private TLSClientParameters tlsClientParameters;
 
     private JAXRSClientFactoryBean restClientFactoryBean;
@@ -102,6 +106,12 @@ public class SyncopeClientFactoryBean {
         return new RestClientExceptionMapper();
     }
 
+    protected static HTTPClientPolicy defaultHTTPClientPolicy() {
+        HTTPClientPolicy policy = new HTTPClientPolicy();
+        policy.setConnection(ConnectionType.CLOSE);
+        return policy;
+    }
+
     protected JAXRSClientFactoryBean defaultRestClientFactoryBean() {
         JAXRSClientFactoryBean defaultRestClientFactoryBean = new 
JAXRSClientFactoryBean();
         defaultRestClientFactoryBean.setHeaders(new HashMap<>());
@@ -210,6 +220,15 @@ public class SyncopeClientFactoryBean {
         return useCompression;
     }
 
+    public SyncopeClientFactoryBean setHttpClientPolicy(final HTTPClientPolicy 
httpClientPolicy) {
+        this.httpClientPolicy = httpClientPolicy;
+        return this;
+    }
+
+    public HTTPClientPolicy getHttpClientPolicy() {
+        return 
Optional.ofNullable(httpClientPolicy).orElseGet(SyncopeClientFactoryBean::defaultHTTPClientPolicy);
+    }
+
     /**
      * Sets the client TLS configuration.
      *
@@ -272,6 +291,7 @@ public class SyncopeClientFactoryBean {
                 getExceptionMapper(),
                 handler,
                 useCompression,
+                getHttpClientPolicy(),
                 tlsClientParameters);
     }
 
@@ -289,6 +309,7 @@ public class SyncopeClientFactoryBean {
                 getExceptionMapper(),
                 new AnonymousAuthenticationHandler(username, password),
                 useCompression,
+                getHttpClientPolicy(),
                 tlsClientParameters);
     }
 }
diff --git 
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
 
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
index 29199d5590..de44e9523e 100644
--- 
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
+++ 
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterClientContext.java
@@ -66,7 +66,7 @@ public class SelfKeymasterClientContext {
         restClientFactoryBean.setPassword(props.getPassword());
         restClientFactoryBean.setThreadSafe(true);
         restClientFactoryBean.setInheritHeaders(true);
-        restClientFactoryBean.setFeatures(List.of(new LoggingFeature()));
+        restClientFactoryBean.getFeatures().add(new LoggingFeature());
         restClientFactoryBean.setProviders(List.of(
                 new 
JacksonJsonProvider(JsonMapper.builder().findAndAddModules().build()),
                 new SelfKeymasterClientExceptionMapper()));
diff --git 
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
 
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
index 2b9f6e32df..514c715f08 100644
--- 
a/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
+++ 
b/common/keymaster/self/client-self/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterOps.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.keymaster.client.self;
 
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import javax.ws.rs.client.CompletionStageRxInvoker;
 import javax.ws.rs.core.MediaType;
@@ -28,29 +30,48 @@ import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
 import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transports.http.configuration.ConnectionType;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 
 abstract class SelfKeymasterOps {
 
+    protected final Map<Class<?>, Object> services = 
Collections.synchronizedMap(new HashMap<>());
+
     private final JAXRSClientFactoryBean clientFactory;
 
+    protected HTTPClientPolicy httpClientPolicy;
+
     protected SelfKeymasterOps(final JAXRSClientFactoryBean clientFactory) {
         this.clientFactory = clientFactory;
+
+        httpClientPolicy = new HTTPClientPolicy();
+        httpClientPolicy.setConnection(ConnectionType.CLOSE);
     }
 
-    protected <T> T client(final Class<T> serviceClass, final Map<String, 
String> headers) {
+    @SuppressWarnings("unchecked")
+    public <T> T client(final Class<T> serviceClass, final Map<String, String> 
headers) {
         T service;
-        synchronized (clientFactory) {
-            clientFactory.setServiceClass(serviceClass);
-            clientFactory.setHeaders(headers);
-            service = clientFactory.create(serviceClass);
-        }
+        if (services.containsKey(serviceClass)) {
+            service = (T) services.get(serviceClass);
+        } else {
+            synchronized (clientFactory) {
+                clientFactory.setServiceClass(serviceClass);
+                clientFactory.setHeaders(headers);
+                service = clientFactory.create(serviceClass);
+            }
+            services.put(serviceClass, service);
 
-        Client client = WebClient.client(service);
-        
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
+            Client client = WebClient.client(service);
+            
client.type(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON);
 
-        ClientConfiguration config = WebClient.getConfig(client);
-        config.getInInterceptors().add(new GZIPInInterceptor());
-        config.getOutInterceptors().add(new GZIPOutInterceptor());
+            ClientConfiguration config = WebClient.getConfig(client);
+            config.getInInterceptors().add(new GZIPInInterceptor());
+            config.getOutInterceptors().add(new GZIPOutInterceptor());
+
+            HTTPConduit httpConduit = (HTTPConduit) config.getConduit();
+            httpConduit.setClient(httpClientPolicy);
+        }
 
         return service;
     }
diff --git a/core/idrepo/rest-cxf/pom.xml b/core/idrepo/rest-cxf/pom.xml
index 6011d8a460..8df08ed83f 100644
--- a/core/idrepo/rest-cxf/pom.xml
+++ b/core/idrepo/rest-cxf/pom.xml
@@ -43,7 +43,7 @@ under the License.
       <artifactId>jakarta.servlet-api</artifactId> 
       <scope>provided</scope>
     </dependency>
-    
+
     <dependency>
       <groupId>jakarta.persistence</groupId>
       <artifactId>jakarta.persistence-api</artifactId>
@@ -72,6 +72,10 @@ under the License.
       <artifactId>jackson-jaxrs-yaml-provider</artifactId>
     </dependency>
       
+    <dependency>
+      <groupId>jakarta.xml.ws</groupId>
+      <artifactId>jakarta.xml.ws-api</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.apache.cxf</groupId>
       <artifactId>cxf-rt-frontend-jaxrs</artifactId>
diff --git 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java
 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java
index 956f26acf0..db1d78107f 100644
--- 
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java
+++ 
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/SyncopeServiceImpl.java
@@ -22,6 +22,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.time.OffsetDateTime;
 import java.util.List;
+import java.util.Optional;
 import javax.ws.rs.InternalServerErrorException;
 import javax.ws.rs.NotFoundException;
 import javax.ws.rs.core.HttpHeaders;
@@ -169,10 +170,8 @@ public class SyncopeServiceImpl extends AbstractService 
implements SyncopeServic
         MediaType mediaType = 
MediaType.valueOf(messageContext.getHttpServletRequest().getContentType());
         String boundary = 
mediaType.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER);
 
-        Batch batch = batchDAO.find(boundary);
-        if (batch == null) {
-            throw new NotFoundException("Batch " + boundary);
-        }
+        Batch batch = Optional.ofNullable(batchDAO.find(boundary)).
+                orElseThrow(() -> new NotFoundException("Batch " + boundary));
 
         if (batch.getResults() == null) {
             return Response.accepted().
diff --git a/core/self-keymaster-starter/pom.xml 
b/core/self-keymaster-starter/pom.xml
index a23ec27431..5a415aed9b 100644
--- a/core/self-keymaster-starter/pom.xml
+++ b/core/self-keymaster-starter/pom.xml
@@ -128,7 +128,67 @@ under the License.
           </plugin>
         </plugins>
       </build>
+    </profile>
+
+    <profile>
+      <id>debug</id>
+
+      <properties>
+        <maven.build.cache.skipCache>true</maven.build.cache.skipCache>
+        <skipTests>true</skipTests>
+      </properties>
+
+      <dependencies>
+        <dependency>
+          <groupId>org.springframework.boot</groupId>
+          <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+
+        <dependency>
+          <groupId>com.h2database</groupId>
+          <artifactId>h2</artifactId>
+        </dependency>
+      </dependencies>
 
+      <build>
+        <defaultGoal>clean package</defaultGoal>
+
+        <plugins>
+          <plugin>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-maven-plugin</artifactId>
+            <configuration>
+              
<mainClass>org.apache.syncope.core.starter.SyncopeCoreApplication</mainClass>
+              <jvmArguments>
+                
-Dsyncope.connid.location=file:${basedir}/../provisioning-java/target/bundles
+                -XX:HotswapAgent=fatjar
+                -Xdebug 
-Xrunjdwp:transport=dt_socket,address=8002,server=y,suspend=n
+              </jvmArguments>
+              <profiles>
+                <profile>debug</profile>
+              </profiles>
+            </configuration>
+            <executions>
+              <execution>
+                <phase>package</phase>
+                <goals>
+                  <goal>run</goal>
+                </goals>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+
+        <resources>
+          <resource>
+            <directory>${basedir}/src/test/resources</directory>
+            <filtering>true</filtering>
+            <includes>
+              <include>*.properties</include>
+            </includes>
+          </resource>
+        </resources>        
+      </build>
     </profile>
   </profiles>  
 </project>
diff --git 
a/core/self-keymaster-starter/src/test/resources/core-debug.properties 
b/core/self-keymaster-starter/src/test/resources/core-debug.properties
new file mode 100644
index 0000000000..6876fce530
--- /dev/null
+++ b/core/self-keymaster-starter/src/test/resources/core-debug.properties
@@ -0,0 +1,50 @@
+# 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.
+
+server.port=9080
+service.discovery.address=http://localhost:9080/syncope/rest/
+
+logging.config=file://${project.build.testOutputDirectory}/log4j2.xml
+
+management.endpoints.web.exposure.include=health,info,beans,env,loggers,entityCache
+
+keymaster.address=http://localhost:9080/syncope/rest/keymaster
+keymaster.username=${anonymousUser}
+keymaster.password=${anonymousKey}
+
+# H2
+spring.h2.console.enabled=true
+spring.h2.console.path=/h2
+
+security.adminUser=${adminUser}
+security.anonymousUser=${anonymousUser}
+security.jwsKey=${jwsKey}
+security.secretKey=${secretKey}
+
+persistence.domain[0].key=Master
+persistence.domain[0].jdbcDriver=org.h2.Driver
+persistence.domain[0].jdbcURL=jdbc:h2:mem:syncopedb;DB_CLOSE_DELAY=-1
+persistence.domain[0].dbUsername=sa
+persistence.domain[0].dbPassword=
+persistence.domain[0].databasePlatform=org.apache.openjpa.jdbc.sql.H2Dictionary
+persistence.domain[0].auditSql=audit.sql
+persistence.domain[0].poolMaxActive=20
+persistence.domain[0].poolMinIdle=5
+
+provisioning.quartz.delegate=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
+provisioning.quartz.sql=tables_h2.sql
+provisioning.quartz.waitForJobsToCompleteOnShutdown=false
diff --git a/core/self-keymaster-starter/src/test/resources/log4j2.xml 
b/core/self-keymaster-starter/src/test/resources/log4j2.xml
new file mode 100644
index 0000000000..91ab9b6611
--- /dev/null
+++ b/core/self-keymaster-starter/src/test/resources/log4j2.xml
@@ -0,0 +1,130 @@
+<?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.
+-->
+<configuration status="WARN">
+
+  <appenders>
+
+    <Console name="console" target="SYSTEM_OUT" follow="true">
+      <PatternLayout pattern="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd 
HH:mm:ss.SSS}} %notEmpty{[operation.id=%X{operation.id}] 
}%highlight{${LOG_LEVEL_PATTERN:-%5p}}{FATAL=red blink, ERROR=red, WARN=yellow 
bold, INFO=green, DEBUG=green bold, TRACE=blue} [%11.11t] 
%style{%-60.60c{60}}{cyan} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
+    </Console>
+
+  </appenders>
+  
+  <loggers>
+    
+    <asyncLogger name="org.apache.syncope.core.persistence" additivity="false" 
level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.springframework.orm" additivity="false" 
level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.openjpa" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.ibatis" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    
+    <asyncLogger name="org.apache.syncope.core.rest" additivity="false" 
level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.springframework.web" additivity="false" 
level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.http" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.cxf" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    
+    <asyncLogger name="org.identityconnectors" additivity="false" 
level="DEBUG">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="net.tirasa.connid" additivity="false" level="DEBUG">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger 
name="org.apache.syncope.core.provisioning.api.ConnIdBundleManager" 
additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    
+    <asyncLogger name="org.apache.syncope" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.syncope.core.provisioning" 
additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.syncope.core.logic" additivity="false" 
level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.springframework" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.quartz" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.flowable" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="liquibase" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.fop" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.wss4j" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.apache.xml" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.opensaml" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="io.swagger" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.reflections" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="org.elasticsearch" additivity="false" level="INFO">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="io.netty" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="com.zaxxer.hikari" additivity="false" level="ERROR">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    
+    <!-- To enable when setting 'mail.debug=true' in mail.properties -->
+    <!--<asyncLogger 
name="org.apache.syncope.core.provisioning.java.job.notification" 
additivity="false" level="DEBUG">
+      <appender-ref ref="console"/>
+    </asyncLogger>
+    <asyncLogger name="javax.mail" additivity="false" level="DEBUG">
+      <appender-ref ref="console"/>
+    </asyncLogger>-->
+    
+    <root level="INFO">
+      <appender-ref ref="console"/>
+    </root>
+    
+  </loggers>
+</configuration>
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 f8daa397d7..f1f95dcdf9 100644
--- a/docker/src/main/resources/docker-compose/docker-compose-all.yml
+++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml
@@ -24,7 +24,7 @@ version: '3.3'
 
 services:
    keymaster:
-     image: zookeeper:3.8.0
+     image: zookeeper:3.8.1
      restart: always
 
    db:
diff --git 
a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserRequestRestClient.java
 
b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserRequestRestClient.java
index 7c59747df5..2d64f52971 100644
--- 
a/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserRequestRestClient.java
+++ 
b/ext/flowable/client-enduser/src/main/java/org/apache/syncope/client/enduser/rest/UserRequestRestClient.java
@@ -22,6 +22,8 @@ import java.util.List;
 import java.util.Optional;
 import javax.ws.rs.core.GenericType;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.UserRequest;
@@ -87,7 +89,14 @@ public class UserRequestRestClient extends BaseRestClient {
     }
 
     public void startRequest(final String bpmnProcess, final String user) {
-        getService(UserRequestService.class).startRequest(bpmnProcess, user, 
null);
+        UserRequestService service = getService(UserRequestService.class);
+        WebClient.getConfig(WebClient.client(service)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.FALSE);
+
+        service.startRequest(bpmnProcess, user, null);
+
+        WebClient.getConfig(WebClient.client(service)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.TRUE);
     }
 
     public UserRequestForm claimForm(final String taskKey) {
diff --git 
a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowTaskExecInput.java
 
b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowTaskExecInput.java
index b90381854e..65f3a7ac5b 100644
--- 
a/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowTaskExecInput.java
+++ 
b/ext/flowable/common-lib/src/main/java/org/apache/syncope/common/lib/to/WorkflowTaskExecInput.java
@@ -21,7 +21,6 @@ package org.apache.syncope.common.lib.to;
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
-import javax.ws.rs.PathParam;
 
 public class WorkflowTaskExecInput implements Serializable {
 
@@ -35,7 +34,6 @@ public class WorkflowTaskExecInput implements Serializable {
         return userKey;
     }
 
-    @PathParam("userKey")
     public void setUserKey(final String userKey) {
         this.userKey = userKey;
     }
diff --git 
a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
 
b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
index add8fae463..91cdccabc3 100644
--- 
a/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
+++ 
b/ext/flowable/logic/src/main/java/org/apache/syncope/core/logic/UserRequestLogic.java
@@ -112,7 +112,7 @@ public class UserRequestLogic extends 
AbstractTransactionalLogic<EntityTO> {
             final WorkflowTaskExecInput inputVariables) {
 
         // check if BPMN process exists
-        bpmnProcessManager.exportProcess(bpmnProcess, BpmnProcessFormat.XML, 
NullOutputStream.NULL_OUTPUT_STREAM);
+        bpmnProcessManager.exportProcess(bpmnProcess, BpmnProcessFormat.XML, 
NullOutputStream.INSTANCE);
 
         return userRequestHandler.start(bpmnProcess, user, inputVariables);
     }
diff --git 
a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
 
b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
index 348647cdc9..16302c0fb8 100644
--- 
a/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
+++ 
b/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/UserRequestService.java
@@ -79,6 +79,7 @@ public interface UserRequestService extends JAXRSService {
     @POST
     @Path("start/{bpmnProcess}")
     @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, 
MediaType.APPLICATION_XML })
     UserRequest startRequest(
             @NotNull @PathParam("bpmnProcess") String bpmnProcess,
             @QueryParam(JAXRSService.PARAM_USER) String user,
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java
index 07108a2c7f..ea7b169748 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BatchITCase.java
@@ -151,7 +151,7 @@ public class BatchITCase extends AbstractITCase {
         assertEquals(RESTHeaders.APPLICATION_YAML, 
resItems.get(0).getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
         ProvisioningResult<UserTO> user = YAML_MAPPER.readValue(
                 resItems.get(0).getContent(), new TypeReference<>() {
-            });
+        });
         assertNotNull(user.getEntity().getKey());
 
         assertEquals(Response.Status.CREATED.getStatusCode(), 
resItems.get(1).getStatus());
@@ -163,7 +163,7 @@ public class BatchITCase extends AbstractITCase {
 
         ProvisioningResult<GroupTO> group = XML_MAPPER.readValue(
                 resItems.get(1).getContent(), new TypeReference<>() {
-            });
+        });
         assertNotNull(group.getEntity().getKey());
 
         assertEquals(Response.Status.NO_CONTENT.getStatusCode(), 
resItems.get(2).getStatus());
@@ -185,7 +185,7 @@ public class BatchITCase extends AbstractITCase {
         assertEquals(MediaType.APPLICATION_JSON, 
resItems.get(5).getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
         group = JSON_MAPPER.readValue(
                 resItems.get(5).getContent(), new TypeReference<>() {
-            });
+        });
         assertNotNull(group);
     }
 
@@ -321,18 +321,8 @@ public class BatchITCase extends AbstractITCase {
         assertEquals(Response.Status.ACCEPTED.getStatusCode(), 
response.getStatus());
         
assertTrue(response.getMediaType().toString().startsWith(RESTHeaders.MULTIPART_MIXED));
 
-        for (int i = 0; i < 10 && response.getStatus() == 
Response.Status.ACCEPTED.getStatusCode(); i++) {
-            // wait a bit...
-            try {
-                Thread.sleep(2000);
-            } catch (InterruptedException e) {
-            }
-
-            // check results
-            response = batchResponse.poll();
-        }
-        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
-        
assertTrue(response.getMediaType().toString().startsWith(RESTHeaders.MULTIPART_MIXED));
+        await().atMost(10, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).
+                until(() -> batchResponse.poll().getStatus() == 
Response.Status.OK.getStatusCode());
 
         check(batchResponse.getItems());
 
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RESTITCase.java 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RESTITCase.java
index 76fc82a3b1..ee88fc586d 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RESTITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RESTITCase.java
@@ -178,6 +178,7 @@ public class RESTITCase extends AbstractITCase {
                 factory.getExceptionMapper(),
                 new BasicAuthenticationHandler(ADMIN_UNAME, ADMIN_PWD),
                 false,
+                null,
                 null);
 
         // perform operation
diff --git 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
index 5b3b7cfc84..b3491bbce4 100644
--- 
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
+++ 
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserRequestITCase.java
@@ -32,6 +32,7 @@ import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.MediaType;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.transport.http.asyncclient.AsyncHTTPConduit;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.lib.SyncopeClientException;
@@ -46,6 +47,7 @@ import 
org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.rest.api.beans.UserRequestQuery;
 import org.apache.syncope.common.rest.api.service.UserRequestService;
 import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -64,6 +66,15 @@ public class UserRequestITCase extends AbstractITCase {
                 
IOUtils.toString(UserRequestITCase.class.getResourceAsStream("/assignPrinterRequest.bpmn20.xml")));
         BPMN_PROCESS_SERVICE.set("verifyAddedVariables",
                 
IOUtils.toString(UserRequestITCase.class.getResourceAsStream("/verifyAddedVariables.bpmn20.xml")));
+
+        WebClient.getConfig(WebClient.client(USER_REQUEST_SERVICE)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.FALSE);
+    }
+
+    @AfterAll
+    public static void reset() {
+        WebClient.getConfig(WebClient.client(USER_REQUEST_SERVICE)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.TRUE);
     }
 
     @BeforeEach
@@ -198,21 +209,27 @@ public class UserRequestITCase extends AbstractITCase {
         SyncopeClient client = CLIENT_FACTORY.create(user.getUsername(), 
"password123");
 
         // start request as user
-        UserRequest req = 
client.getService(UserRequestService.class).startRequest("assignPrinterRequest",
 null, null);
+        UserRequestService service = 
client.getService(UserRequestService.class);
+        WebClient.getConfig(WebClient.client(service)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.FALSE);
+
+        UserRequest req = service.startRequest("assignPrinterRequest", null, 
null);
         assertNotNull(req);
+        WebClient.getConfig(WebClient.client(service)).
+                getRequestContext().put(AsyncHTTPConduit.USE_ASYNC, 
Boolean.TRUE);
 
         // check (as admin) that a new form is available
         forms = USER_REQUEST_SERVICE.listForms(new 
UserRequestQuery.Builder().build());
         assertEquals(preForms + 1, forms.getTotalCount());
 
         // get (as user) the form, claim and submit
-        PagedResult<UserRequestForm> userForms = 
client.getService(UserRequestService.class).
-                listForms(new 
UserRequestQuery.Builder().user(user.getKey()).build());
+        PagedResult<UserRequestForm> userForms = service.listForms(
+                new UserRequestQuery.Builder().user(user.getKey()).build());
         assertEquals(1, userForms.getTotalCount());
 
         UserRequestForm form = userForms.getResult().get(0);
         assertEquals("assignPrinterRequest", form.getBpmnProcess());
-        form = 
client.getService(UserRequestService.class).claimForm(form.getTaskId());
+        form = service.claimForm(form.getTaskId());
 
         
assertFalse(form.getProperty("printer").get().getDropdownValues().isEmpty());
         form.getProperty("printer").ifPresent(printer -> 
printer.setValue("8559d14d-58c2-46eb-a2d4-a7d35161e8f8"));
@@ -220,15 +237,14 @@ public class UserRequestITCase extends AbstractITCase {
         
assertFalse(form.getProperty("printMode").get().getEnumValues().isEmpty());
         form.getProperty("printMode").ifPresent(printMode -> 
printMode.setValue("color"));
 
-        client.getService(UserRequestService.class).submitForm(form);
+        service.submitForm(form);
 
-        userForms = client.getService(UserRequestService.class).listForms(
-                new UserRequestQuery.Builder().user(user.getKey()).build());
+        userForms = service.listForms(new 
UserRequestQuery.Builder().user(user.getKey()).build());
         assertEquals(0, userForms.getTotalCount());
 
         // check that user can see the ongoing request
-        PagedResult<UserRequest> requests = 
client.getService(UserRequestService.class).
-                listRequests(new 
UserRequestQuery.Builder().user(user.getKey()).build());
+        PagedResult<UserRequest> requests = service.listRequests(
+                new UserRequestQuery.Builder().user(user.getKey()).build());
         assertEquals(1, requests.getTotalCount());
         assertEquals("assignPrinterRequest", 
requests.getResult().get(0).getBpmnProcess());
 
@@ -247,8 +263,8 @@ public class UserRequestITCase extends AbstractITCase {
         forms = USER_REQUEST_SERVICE.listForms(new 
UserRequestQuery.Builder().build());
         assertEquals(preForms, forms.getTotalCount());
 
-        assertTrue(client.getService(UserRequestService.class).
-                listRequests(new 
UserRequestQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
+        assertTrue(service.listRequests(
+                new 
UserRequestQuery.Builder().user(user.getKey()).build()).getResult().isEmpty());
 
         // check that relationship was made effective by approval
         relationships = USER_SERVICE.read(user.getKey()).getRelationships();
@@ -259,8 +275,7 @@ public class UserRequestITCase extends AbstractITCase {
 
     @Test
     public void addVariablesToUserRequestAtStart() {
-        PagedResult<UserRequestForm> forms =
-                USER_REQUEST_SERVICE.listForms(new 
UserRequestQuery.Builder().build());
+        PagedResult<UserRequestForm> forms = 
USER_REQUEST_SERVICE.listForms(new UserRequestQuery.Builder().build());
         int preForms = forms.getTotalCount();
 
         UserTO user = 
createUser(UserITCase.getUniqueSample("[email protected]")).getEntity();
diff --git a/pom.xml b/pom.xml
index d6399153d6..c09c25af45 100644
--- a/pom.xml
+++ b/pom.xml
@@ -409,7 +409,7 @@ under the License.
     <connid.okta.version>3.0.0</connid.okta.version>
     <connid.cmd.version>0.5</connid.cmd.version>
 
-    <cxf.version>3.5.6</cxf.version>
+    <cxf.version>3.6.1</cxf.version>
     <bouncycastle.version>1.70</bouncycastle.version>
     <nimbus-jose-jwt.version>9.31</nimbus-jose-jwt.version>
 
diff --git 
a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
 
b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
index 6c2af78f60..94f9c0095d 100644
--- 
a/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
+++ 
b/sra/src/main/java/org/apache/syncope/sra/actuate/SyncopeCoreHealthIndicator.java
@@ -41,6 +41,8 @@ public class SyncopeCoreHealthIndicator implements 
HealthIndicator {
 
     protected final boolean useGZIPCompression;
 
+    private SRARouteService service;
+
     public SyncopeCoreHealthIndicator(
             final ServiceOps serviceOps,
             final String anonymousUser,
@@ -53,16 +55,25 @@ public class SyncopeCoreHealthIndicator implements 
HealthIndicator {
         this.useGZIPCompression = useGZIPCompression;
     }
 
+    protected SRARouteService service() {
+        synchronized (this) {
+            if (service == null) {
+                service = new SyncopeClientFactoryBean().
+                        
setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
+                        setUseCompression(useGZIPCompression).
+                        create(new 
AnonymousAuthenticationHandler(anonymousUser, anonymousKey)).
+                        getService(SRARouteService.class);
+            }
+        }
+        return service;
+    }
+
     @Override
     public Health health() {
         Health.Builder builder = new Health.Builder();
 
         try {
-            new SyncopeClientFactoryBean().
-                    
setAddress(serviceOps.get(NetworkService.Type.CORE).getAddress()).
-                    setUseCompression(useGZIPCompression).
-                    create(new AnonymousAuthenticationHandler(anonymousUser, 
anonymousKey)).
-                    getService(SRARouteService.class).list();
+            service().list();
             builder.status(Status.UP);
         } catch (Exception e) {
             LOG.debug("When attempting to connect to Syncope Core", e);
diff --git 
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java 
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
index 88ee068169..8d48df5a02 100644
--- 
a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
+++ 
b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WARestClient.java
@@ -19,6 +19,9 @@
 package org.apache.syncope.wa.bootstrap;
 
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Optional;
 import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClient;
@@ -33,15 +36,17 @@ import org.springframework.context.ApplicationContext;
 
 public class WARestClient {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WARestClient.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WARestClient.class);
 
-    private final String anonymousUser;
+    protected final String anonymousUser;
 
-    private final String anonymousKey;
+    protected final String anonymousKey;
 
-    private final boolean useGZIPCompression;
+    protected final boolean useGZIPCompression;
 
-    private final String serviceDiscoveryAddress;
+    protected final String serviceDiscoveryAddress;
+
+    protected final Map<Class<?>, Object> services = 
Collections.synchronizedMap(new HashMap<>());
 
     private SyncopeClient client;
 
@@ -57,7 +62,7 @@ public class WARestClient {
         this.serviceDiscoveryAddress = serviceDiscoveryAddress;
     }
 
-    private Optional<NetworkService> getCore() {
+    protected Optional<NetworkService> getCore() {
         try {
             ApplicationContext ctx = 
ApplicationContextProvider.getApplicationContext();
             if (ctx == null) {
@@ -84,7 +89,7 @@ public class WARestClient {
         return Optional.empty();
     }
 
-    public SyncopeClient getSyncopeClient() {
+    protected SyncopeClient getSyncopeClient() {
         synchronized (this) {
             if (client == null) {
                 getCore().ifPresent(core -> {
@@ -103,6 +108,23 @@ public class WARestClient {
         }
     }
 
+    @SuppressWarnings("unchecked")
+    public <T> T getService(final Class<T> serviceClass) {
+        if (!isReady()) {
+            throw new IllegalStateException("Syncope core is not yet ready");
+        }
+
+        T service;
+        if (services.containsKey(serviceClass)) {
+            service = (T) services.get(serviceClass);
+        } else {
+            service = getSyncopeClient().getService(serviceClass);
+            services.put(serviceClass, service);
+        }
+
+        return service;
+    }
+
     public boolean isReady() {
         try {
             return getCore().isPresent();
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java
index d2bf0a475e..a2aeeaaf9c 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/actuate/SyncopeCoreHealthIndicator.java
@@ -41,7 +41,7 @@ public class SyncopeCoreHealthIndicator implements 
HealthIndicator {
         Health.Builder builder = new Health.Builder();
 
         try {
-            
waRestClient.getSyncopeClient().getService(UserSelfService.class).read();
+            waRestClient.getService(UserSelfService.class).read();
             builder.status(Status.UP);
         } catch (Exception e) {
             LOG.debug("When attempting to connect to Syncope Core", e);
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
index f4119a8065..25005f3d1c 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManager.java
@@ -23,7 +23,6 @@ import java.time.OffsetDateTime;
 import java.util.Map;
 import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.audit.AuditEntry;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
@@ -43,8 +42,7 @@ public class WAAuditTrailManager extends 
AbstractAuditTrailManager {
 
     @Override
     protected void saveAuditRecord(final AuditActionContext audit) {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to store audit record");
             return;
         }
@@ -71,7 +69,7 @@ public class WAAuditTrailManager extends 
AbstractAuditTrailManager {
                     audit.getActionPerformed(),
                     result);
             auditEntry.setLogger(auditLogger);
-            syncopeClient.getService(AuditService.class).create(auditEntry);
+            waRestClient.getService(AuditService.class).create(auditEntry);
         } catch (JsonProcessingException e) {
             LOG.error("During serialization", e);
         }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
index f72ce18562..6355ba084f 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/WAEventRepository.java
@@ -27,7 +27,6 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.audit.AuditEntry;
 import org.apache.syncope.common.lib.types.AuditElements;
 import org.apache.syncope.common.lib.types.AuditLoggerName;
@@ -63,13 +62,12 @@ public class WAEventRepository extends 
AbstractCasEventRepository {
 
     @Override
     public CasEvent saveInternal(final CasEvent event) {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to store audit record");
             return null;
         }
 
-        LOG.info("Saving Cas events");
+        LOG.debug("Saving WA events");
         try {
             Map<String, String> properties = new HashMap<>();
             if (event.getGeoLocation() != null) {
@@ -97,7 +95,7 @@ public class WAEventRepository extends 
AbstractCasEventRepository {
                     String.valueOf(event.getId()),
                     AuditElements.Result.SUCCESS);
             auditEntry.setLogger(auditLogger);
-            syncopeClient.getService(AuditService.class).create(auditEntry);
+            waRestClient.getService(AuditService.class).create(auditEntry);
         } catch (JsonProcessingException e) {
             LOG.error("During serialization", e);
         }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
index e785ea29dd..85951b6eab 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthCredentialRepository.java
@@ -69,7 +69,7 @@ public class WAGoogleMfaAuthCredentialRepository extends 
BaseGoogleAuthenticator
     }
 
     protected GoogleMfaAuthAccountService service() {
-        return 
waRestClient.getSyncopeClient().getService(GoogleMfaAuthAccountService.class);
+        return waRestClient.getService(GoogleMfaAuthAccountService.class);
     }
 
     @Override
@@ -143,8 +143,7 @@ public class WAGoogleMfaAuthCredentialRepository extends 
BaseGoogleAuthenticator
                 name(otta.getName()).
                 id(otta.getId()).
                 build();
-        waRestClient.getSyncopeClient().
-                
getService(GoogleMfaAuthAccountService.class).create(otta.getUsername(), 
account);
+        service().create(otta.getUsername(), account);
         return mapGoogleMfaAuthAccount(account);
     }
 
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
index 74d33363b9..edb1d8b2e5 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/gauth/WAGoogleMfaAuthTokenRepository.java
@@ -41,7 +41,7 @@ public class WAGoogleMfaAuthTokenRepository extends 
BaseOneTimeTokenRepository<G
     }
 
     protected GoogleMfaAuthTokenService service() {
-        return 
waRestClient.getSyncopeClient().getService(GoogleMfaAuthTokenService.class);
+        return waRestClient.getService(GoogleMfaAuthTokenService.class);
     }
 
     @Override
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
index 93202ce310..04e36a99ec 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/oidc/WAOIDCJWKSGeneratorService.java
@@ -60,10 +60,7 @@ public class WAOIDCJWKSGeneratorService implements 
OidcJsonWebKeystoreGeneratorS
 
     @Override
     public JsonWebKeySet store(final JsonWebKeySet jsonWebKeySet) throws 
Exception {
-        if (!waRestClient.isReady()) {
-            throw new IllegalStateException("Syncope core is not yet ready");
-        }
-        OIDCJWKSService service = 
waRestClient.getSyncopeClient().getService(OIDCJWKSService.class);
+        OIDCJWKSService service = 
waRestClient.getService(OIDCJWKSService.class);
         OIDCJWKSTO to = new OIDCJWKSTO();
         
to.setJson(jsonWebKeySet.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE));
         service.set(to);
@@ -77,11 +74,7 @@ public class WAOIDCJWKSGeneratorService implements 
OidcJsonWebKeystoreGeneratorS
 
     @Override
     public Resource generate() {
-        if (!waRestClient.isReady()) {
-            throw new IllegalStateException("Syncope core is not yet ready");
-        }
-
-        OIDCJWKSService service = 
waRestClient.getSyncopeClient().getService(OIDCJWKSService.class);
+        OIDCJWKSService service = 
waRestClient.getService(OIDCJWKSService.class);
         OIDCJWKSTO jwksTO = null;
         try {
             jwksTO = service.get();
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
index 3d071354d4..8de963dddf 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGenerator.java
@@ -35,15 +35,15 @@ import org.slf4j.LoggerFactory;
 
 public class WASAML2ClientKeystoreGenerator extends BaseSAML2KeystoreGenerator 
{
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASAML2ClientKeystoreGenerator.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASAML2ClientKeystoreGenerator.class);
 
-    private final WARestClient restClient;
+    protected final WARestClient waRestClient;
 
-    private final SAML2Client saml2Client;
+    protected final SAML2Client saml2Client;
 
-    WASAML2ClientKeystoreGenerator(final WARestClient restClient, final 
SAML2Client saml2Client) {
+    public WASAML2ClientKeystoreGenerator(final WARestClient waRestClient, 
final SAML2Client saml2Client) {
         super(saml2Client.getConfiguration());
-        this.restClient = restClient;
+        this.waRestClient = waRestClient;
         this.saml2Client = saml2Client;
     }
 
@@ -65,8 +65,7 @@ public class WASAML2ClientKeystoreGenerator extends 
BaseSAML2KeystoreGenerator {
 
             SAML2SPEntityTO entityTO;
             try {
-                entityTO = 
restClient.getSyncopeClient().getService(SAML2SPEntityService.class).
-                        get(saml2Client.getName());
+                entityTO = 
waRestClient.getService(SAML2SPEntityService.class).get(saml2Client.getName());
                 entityTO.setKeystore(encodedKeystore);
             } catch (Exception e) {
                 LOG.debug("SP Entity {} not found, creating new", 
saml2Client.getName(), e);
@@ -78,19 +77,18 @@ public class WASAML2ClientKeystoreGenerator extends 
BaseSAML2KeystoreGenerator {
             }
 
             LOG.debug("Storing SP Entity {}", entityTO);
-            
restClient.getSyncopeClient().getService(SAML2SPEntityService.class).set(entityTO);
+            waRestClient.getService(SAML2SPEntityService.class).set(entityTO);
         }
     }
 
     @Override
     public InputStream retrieve() throws Exception {
         try {
-            SAML2SPEntityTO spEntity =
-                    
restClient.getSyncopeClient().getService(SAML2SPEntityService.class).get(saml2Client.getName());
+            SAML2SPEntityTO spEntity = 
waRestClient.getService(SAML2SPEntityService.class).get(saml2Client.getName());
 
             LOG.debug("Retrieved keystore {}", spEntity.getKeystore());
             return new 
ByteArrayInputStream(Base64.getDecoder().decode(spEntity.getKeystore()));
-        } catch (final Exception e) {
+        } catch (Exception e) {
             String message = "Unable to fetch SAML2 SP keystore for " + 
saml2Client.getName();
             LOG.error(message, e);
             throw new Exception(message);
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
index c4d1541eda..b28e02d7df 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGenerator.java
@@ -33,20 +33,25 @@ import org.springframework.core.io.Resource;
 
 public class WASAML2ClientMetadataGenerator extends BaseSAML2MetadataGenerator 
{
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASAML2ClientMetadataGenerator.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASAML2ClientMetadataGenerator.class);
 
-    private final WARestClient restClient;
+    protected final WARestClient waRestClient;
 
-    private final SAML2Client saml2Client;
+    protected final SAML2Client saml2Client;
 
-    WASAML2ClientMetadataGenerator(final WARestClient restClient, final 
SAML2Client saml2Client) {
-        this.restClient = restClient;
+    public WASAML2ClientMetadataGenerator(final WARestClient waRestClient, 
final SAML2Client saml2Client) {
+        this.waRestClient = waRestClient;
         this.saml2Client = saml2Client;
     }
 
+    @Override
+    public boolean storeMetadata(final String metadata, final Resource 
resource, final boolean force) throws Exception {
+        return true;
+    }
+
     @Override
     protected AbstractBatchMetadataResolver createMetadataResolver(final 
Resource metadataResource) {
-        return new WASAML2MetadataResolver(restClient, saml2Client);
+        return new WASAML2MetadataResolver(waRestClient, saml2Client);
     }
 
     @Override
@@ -56,8 +61,7 @@ public class WASAML2ClientMetadataGenerator extends 
BaseSAML2MetadataGenerator {
 
         SAML2SPEntityTO entityTO;
         try {
-            entityTO = 
restClient.getSyncopeClient().getService(SAML2SPEntityService.class).
-                    get(saml2Client.getName());
+            entityTO = 
waRestClient.getService(SAML2SPEntityService.class).get(saml2Client.getName());
             entityTO.setMetadata(encodedMetadata);
         } catch (Exception e) {
             LOG.debug("SP Entity {} not found, creating new", 
saml2Client.getName(), e);
@@ -69,13 +73,8 @@ public class WASAML2ClientMetadataGenerator extends 
BaseSAML2MetadataGenerator {
         }
 
         LOG.debug("Storing SP Entity {}", entityTO);
-        
restClient.getSyncopeClient().getService(SAML2SPEntityService.class).set(entityTO);
+        waRestClient.getService(SAML2SPEntityService.class).set(entityTO);
 
         return super.buildMetadataResolver(metadataResource);
     }
-
-    @Override
-    public boolean storeMetadata(final String metadata, final Resource 
resource, final boolean force) throws Exception {
-        return true;
-    }
 }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
index d09321b5bb..05a95a0fef 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolver.java
@@ -30,14 +30,14 @@ import org.slf4j.LoggerFactory;
 
 public class WASAML2MetadataResolver extends AbstractReloadingMetadataResolver 
{
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASAML2MetadataResolver.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASAML2MetadataResolver.class);
 
-    private final WARestClient restClient;
+    protected final WARestClient waRestClient;
 
-    private final SAML2Client saml2Client;
+    protected final SAML2Client saml2Client;
 
-    WASAML2MetadataResolver(final WARestClient restClient, final SAML2Client 
saml2Client) {
-        this.restClient = restClient;
+    public WASAML2MetadataResolver(final WARestClient waRestClient, final 
SAML2Client saml2Client) {
+        this.waRestClient = waRestClient;
         this.saml2Client = saml2Client;
     }
 
@@ -49,11 +49,10 @@ public class WASAML2MetadataResolver extends 
AbstractReloadingMetadataResolver {
     @Override
     protected byte[] fetchMetadata() throws ResolverException {
         try {
-            SAML2SPEntityTO metadataTO = restClient.getSyncopeClient().
-                    
getService(SAML2SPEntityService.class).get(saml2Client.getName());
+            SAML2SPEntityTO metadataTO = 
waRestClient.getService(SAML2SPEntityService.class).get(saml2Client.getName());
             return Base64.getDecoder().decode(metadataTO.getMetadata());
-        } catch (final Exception e) {
-            final String message = "Unable to fetch SP metadata for " + 
saml2Client.getName();
+        } catch (Exception e) {
+            String message = "Unable to fetch SP metadata for " + 
saml2Client.getName();
             LOG.error(message, e);
             throw new ResolverException(message);
         }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataGenerator.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataGenerator.java
index e71481b70c..9bd161d7d6 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataGenerator.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataGenerator.java
@@ -22,7 +22,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 import java.util.Optional;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2IdPEntityTO;
 import org.apache.syncope.common.rest.api.service.SAML2IdPEntityService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -35,9 +34,9 @@ import org.slf4j.LoggerFactory;
 
 public class WASamlIdPMetadataGenerator extends BaseSamlIdPMetadataGenerator {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASamlIdPMetadataGenerator.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASamlIdPMetadataGenerator.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
     public WASamlIdPMetadataGenerator(
             final SamlIdPMetadataGeneratorConfigurationContext 
samlIdPMetadataGeneratorConfigurationContext,
@@ -47,14 +46,6 @@ public class WASamlIdPMetadataGenerator extends 
BaseSamlIdPMetadataGenerator {
         this.waRestClient = waRestClient;
     }
 
-    private SyncopeClient getSyncopeClient() {
-        if (!waRestClient.isReady()) {
-            LOG.info("Syncope client is not yet ready");
-            throw new IllegalStateException("Syncope core is not yet ready to 
access requests");
-        }
-        return waRestClient.getSyncopeClient();
-    }
-
     @Override
     protected SamlIdPMetadataDocument finalizeMetadataDocument(
             final SamlIdPMetadataDocument doc,
@@ -87,7 +78,7 @@ public class WASamlIdPMetadataGenerator extends 
BaseSamlIdPMetadataGenerator {
                     
doc.getEncryptionCertificate().getBytes(StandardCharsets.UTF_8)));
         }
 
-        
getSyncopeClient().getService(SAML2IdPEntityService.class).set(entityTO);
+        waRestClient.getService(SAML2IdPEntityService.class).set(entityTO);
 
         return doc;
     }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataLocator.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataLocator.java
index 06fead6735..bc7d2c054f 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataLocator.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/saml/idp/metadata/WASamlIdPMetadataLocator.java
@@ -22,7 +22,6 @@ import com.github.benmanes.caffeine.cache.Cache;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 import java.util.Optional;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.SAML2IdPEntityTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
@@ -37,9 +36,9 @@ import org.slf4j.LoggerFactory;
 
 public class WASamlIdPMetadataLocator extends AbstractSamlIdPMetadataLocator {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASamlIdPMetadataLocator.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASamlIdPMetadataLocator.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
     public WASamlIdPMetadataLocator(
             final CipherExecutor<String, String> metadataCipherExecutor,
@@ -50,6 +49,25 @@ public class WASamlIdPMetadataLocator extends 
AbstractSamlIdPMetadataLocator {
         this.waRestClient = waRestClient;
     }
 
+    protected SAML2IdPEntityTO fetchFromCore(final 
Optional<SamlRegisteredService> registeredService) {
+        SAML2IdPEntityService idpEntityService = 
waRestClient.getService(SAML2IdPEntityService.class);
+
+        SAML2IdPEntityTO result = null;
+        try {
+            result = idpEntityService.get(registeredService.
+                    map(SamlRegisteredService::getName).
+                    orElse(SAML2IdPEntityService.DEFAULT_OWNER));
+        } catch (SyncopeClientException e) {
+            if (e.getType() == ClientExceptionType.NotFound && 
registeredService.isPresent()) {
+                result = 
idpEntityService.get(SAML2IdPEntityService.DEFAULT_OWNER);
+            } else {
+                throw e;
+            }
+        }
+
+        return result;
+    }
+
     @Override
     public SamlIdPMetadataDocument fetchInternal(final 
Optional<SamlRegisteredService> registeredService) {
         try {
@@ -101,31 +119,4 @@ public class WASamlIdPMetadataLocator extends 
AbstractSamlIdPMetadataLocator {
 
         return null;
     }
-
-    private SAML2IdPEntityTO fetchFromCore(final 
Optional<SamlRegisteredService> registeredService) {
-        SAML2IdPEntityService idpEntityService = 
getSyncopeClient().getService(SAML2IdPEntityService.class);
-
-        SAML2IdPEntityTO result = null;
-        try {
-            result = idpEntityService.get(registeredService.
-                    map(SamlRegisteredService::getName).
-                    orElse(SAML2IdPEntityService.DEFAULT_OWNER));
-        } catch (SyncopeClientException e) {
-            if (e.getType() == ClientExceptionType.NotFound && 
registeredService.isPresent()) {
-                result = 
idpEntityService.get(SAML2IdPEntityService.DEFAULT_OWNER);
-            } else {
-                throw e;
-            }
-        }
-
-        return result;
-    }
-
-    private SyncopeClient getSyncopeClient() {
-        if (!waRestClient.isReady()) {
-            LOG.info("Syncope client is not yet ready");
-            throw new IllegalStateException("Syncope core is not yet ready to 
access requests");
-        }
-        return waRestClient.getSyncopeClient();
-    }
 }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
index a6a2e9843d..efdcf0f752 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/services/WAServiceRegistry.java
@@ -21,9 +21,7 @@ package org.apache.syncope.wa.starter.services;
 import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
-import java.util.Optional;
 import java.util.stream.Collectors;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.types.ClientAppType;
 import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -41,9 +39,9 @@ public class WAServiceRegistry extends 
AbstractServiceRegistry {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(WAServiceRegistry.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
-    private final RegisteredServiceMapper registeredServiceMapper;
+    protected final RegisteredServiceMapper registeredServiceMapper;
 
     public WAServiceRegistry(
             final WARestClient restClient,
@@ -73,87 +71,81 @@ public class WAServiceRegistry extends 
AbstractServiceRegistry {
 
     @Override
     public Collection<RegisteredService> load() {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to fetch application 
definitions");
             return List.of();
-        } else {
-            LOG.info("Loading application definitions");
-            return 
waRestClient.getSyncopeClient().getService(WAClientAppService.class).list().stream().
-                    map(registeredServiceMapper::toRegisteredService).
-                    filter(Objects::nonNull).
-                    collect(Collectors.toList());
         }
+
+        LOG.info("Loading application definitions");
+        return 
waRestClient.getService(WAClientAppService.class).list().stream().
+                map(registeredServiceMapper::toRegisteredService).
+                filter(Objects::nonNull).
+                collect(Collectors.toList());
     }
 
     @Override
     public RegisteredService findServiceById(final long id) {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to fetch application 
definitions");
             return null;
-        } else {
-            LOG.info("Searching for application definition by id {}", id);
-            return 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                    getService(WAClientAppService.class).read(id, null));
         }
+
+        LOG.info("Searching for application definition by id {}", id);
+        return registeredServiceMapper.toRegisteredService(
+                waRestClient.getService(WAClientAppService.class).read(id, 
null));
     }
 
-    @Override
     @SuppressWarnings("unchecked")
+    @Override
     public <T extends RegisteredService> T findServiceById(final long id, 
final Class<T> clazz) {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to fetch application 
definitions");
             return null;
-        } else {
-            LOG.info("Searching for application definition by id {} and type 
{}", id, clazz);
-            if (clazz.isInstance(OidcRegisteredService.class)) {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(id, 
ClientAppType.OIDCRP));
-            } else if (clazz.isInstance(SamlRegisteredService.class)) {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(id, 
ClientAppType.SAML2SP));
-            } else {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(id, 
ClientAppType.CASSP));
-            }
         }
+
+        LOG.info("Searching for application definition by id {} and type {}", 
id, clazz);
+        if (clazz.isInstance(OidcRegisteredService.class)) {
+            return (T) registeredServiceMapper.toRegisteredService(
+                    waRestClient.getService(WAClientAppService.class).read(id, 
ClientAppType.OIDCRP));
+        }
+        if (clazz.isInstance(SamlRegisteredService.class)) {
+            return (T) registeredServiceMapper.toRegisteredService(
+                    waRestClient.getService(WAClientAppService.class).read(id, 
ClientAppType.SAML2SP));
+        }
+        return (T) registeredServiceMapper.toRegisteredService(
+                waRestClient.getService(WAClientAppService.class).read(id, 
ClientAppType.CASSP));
     }
 
-    @Override
     @SuppressWarnings("unchecked")
+    @Override
     public <T extends RegisteredService> T findServiceByExactServiceName(final 
String name, final Class<T> clazz) {
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        if (syncopeClient == null) {
+        if (!waRestClient.isReady()) {
             LOG.debug("Syncope client is not yet ready to fetch application 
definitions");
             return null;
-        } else {
-            LOG.info("Searching for application definition by name {} and type 
{}", name, clazz);
-            if (clazz.isInstance(OidcRegisteredService.class)) {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(name, 
ClientAppType.OIDCRP));
-            } else if (clazz.isInstance(SamlRegisteredService.class)) {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(name, 
ClientAppType.SAML2SP));
-            } else {
-                return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                        getService(WAClientAppService.class).read(name, 
ClientAppType.CASSP));
-            }
         }
+
+        LOG.info("Searching for application definition by name {} and type 
{}", name, clazz);
+        if (clazz.isInstance(OidcRegisteredService.class)) {
+            return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.
+                    getService(WAClientAppService.class).read(name, 
ClientAppType.OIDCRP));
+        }
+        if (clazz.isInstance(SamlRegisteredService.class)) {
+            return (T) 
registeredServiceMapper.toRegisteredService(waRestClient.
+                    getService(WAClientAppService.class).read(name, 
ClientAppType.SAML2SP));
+        }
+        return (T) registeredServiceMapper.toRegisteredService(waRestClient.
+                getService(WAClientAppService.class).read(name, 
ClientAppType.CASSP));
     }
 
     @Override
     public RegisteredService findServiceByExactServiceName(final String name) {
-        return Optional.ofNullable(waRestClient.getSyncopeClient()).
-                map(syncopeClient -> {
-                    LOG.info("Searching for application definition by name 
{}", name);
-                    return 
registeredServiceMapper.toRegisteredService(waRestClient.getSyncopeClient().
-                            getService(WAClientAppService.class).read(name, 
null));
-                }).
-                orElseGet(() -> {
-                    LOG.debug("Syncope client is not yet ready to fetch 
application definitions");
-                    return null;
-                });
+        if (!waRestClient.isReady()) {
+            LOG.debug("Syncope client is not yet ready to fetch application 
definitions");
+            return null;
+        }
+
+        LOG.info("Searching for application definition by name {}", name);
+        return registeredServiceMapper.toRegisteredService(
+                waRestClient.getService(WAClientAppService.class).read(name, 
null));
     }
 }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
index 0c44fb9f25..2a8ef9910f 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationService.java
@@ -32,22 +32,21 @@ import org.slf4j.LoggerFactory;
 
 public class WASurrogateAuthenticationService implements 
SurrogateAuthenticationService {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WASurrogateAuthenticationService.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WASurrogateAuthenticationService.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
     public WASurrogateAuthenticationService(final WARestClient waRestClient) {
         this.waRestClient = waRestClient;
     }
 
     @Override
-    public boolean canImpersonate(
-            final String surrogate, final Principal principal, final 
Optional<Service> service) {
-
+    public boolean canImpersonate(final String surrogate, final Principal 
principal, final Optional<Service> service) {
         try {
             LOG.debug("Checking impersonation attempt by {} for {}", 
principal, surrogate);
-            return getImpersonationService().read(principal.getId()).stream().
-                    anyMatch(acct -> surrogate.equals(acct.getImpersonated()));
+
+            return waRestClient.getService(ImpersonationService.class).read(
+                    principal.getId()).stream().anyMatch(acct -> 
surrogate.equals(acct.getImpersonated()));
         } catch (final Exception e) {
             LOG.info("Could not authorize account {} for owner {}", surrogate, 
principal.getId());
         }
@@ -56,16 +55,9 @@ public class WASurrogateAuthenticationService implements 
SurrogateAuthentication
 
     @Override
     public Collection<String> getImpersonationAccounts(final String username) {
-        return getImpersonationService().read(username).
+        return 
waRestClient.getService(ImpersonationService.class).read(username).
                 stream().
                 map(ImpersonationAccount::getImpersonated).
                 collect(Collectors.toList());
     }
-
-    private ImpersonationService getImpersonationService() {
-        if (!waRestClient.isReady()) {
-            throw new IllegalStateException("Syncope core is not yet ready");
-        }
-        return 
waRestClient.getSyncopeClient().getService(ImpersonationService.class);
-    }
 }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
index 6210ca349e..178392603c 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/u2f/WAU2FDeviceRepository.java
@@ -42,9 +42,23 @@ public class WAU2FDeviceRepository extends 
BaseU2FDeviceRepository {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(WAU2FDeviceRepository.class);
 
-    private final WARestClient waRestClient;
+    protected static U2FDeviceRegistration parseRegistrationRecord(final 
String owner, final U2FDevice device) {
+        try {
+            return U2FDeviceRegistration.builder().
+                    id(device.getId()).
+                    username(owner).
+                    record(device.getRecord()).
+                    
createdDate(device.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()).
+                    build();
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    protected final WARestClient waRestClient;
 
-    private final OffsetDateTime expirationDate;
+    protected final OffsetDateTime expirationDate;
 
     public WAU2FDeviceRepository(
             final CasConfigurationProperties casProperties,
@@ -57,23 +71,13 @@ public class WAU2FDeviceRepository extends 
BaseU2FDeviceRepository {
         this.expirationDate = expirationDate;
     }
 
-    private static U2FDeviceRegistration parseRegistrationRecord(final String 
owner, final U2FDevice device) {
-        try {
-            return U2FDeviceRegistration.builder().
-                    id(device.getId()).
-                    username(owner).
-                    record(device.getRecord()).
-                    
createdDate(device.getIssueDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()).
-                    build();
-        } catch (final Exception e) {
-            LOG.error(e.getMessage(), e);
-        }
-        return null;
+    protected U2FRegistrationService service() {
+        return waRestClient.getService(U2FRegistrationService.class);
     }
 
     @Override
     public Collection<? extends U2FDeviceRegistration> 
getRegisteredDevices(final String owner) {
-        return getU2FService().
+        return service().
                 search(new 
U2FDeviceQuery.Builder().owner(owner).expirationDate(expirationDate).build()).getResult().
                 stream().
                 map(device -> parseRegistrationRecord(owner, device)).
@@ -83,7 +87,7 @@ public class WAU2FDeviceRepository extends 
BaseU2FDeviceRepository {
 
     @Override
     public Collection<? extends U2FDeviceRegistration> getRegisteredDevices() {
-        return getU2FService().search(new 
U2FDeviceQuery.Builder().expirationDate(expirationDate).build()).getResult().
+        return service().search(new 
U2FDeviceQuery.Builder().expirationDate(expirationDate).build()).getResult().
                 stream().
                 map(device -> parseRegistrationRecord("", device)).
                 filter(Objects::nonNull).
@@ -98,13 +102,13 @@ public class WAU2FDeviceRepository extends 
BaseU2FDeviceRepository {
                 record(registration.getRecord()).
                 id(registration.getId()).
                 build();
-        getU2FService().create(registration.getUsername(), record);
+        service().create(registration.getUsername(), record);
         return parseRegistrationRecord(registration.getUsername(), record);
     }
 
     @Override
     public void deleteRegisteredDevice(final U2FDeviceRegistration 
registration) {
-        getU2FService().delete(new 
U2FDeviceQuery.Builder().id(registration.getId()).build());
+        service().delete(new 
U2FDeviceQuery.Builder().id(registration.getId()).build());
     }
 
     @Override
@@ -124,18 +128,11 @@ public class WAU2FDeviceRepository extends 
BaseU2FDeviceRepository {
 
     @Override
     public void clean() {
-        getU2FService().delete(new 
U2FDeviceQuery.Builder().expirationDate(expirationDate).build());
+        service().delete(new 
U2FDeviceQuery.Builder().expirationDate(expirationDate).build());
     }
 
     @Override
     public void removeAll() {
-        getU2FService().delete(new U2FDeviceQuery.Builder().build());
-    }
-
-    private U2FRegistrationService getU2FService() {
-        if (!waRestClient.isReady()) {
-            throw new IllegalStateException("Syncope core is not yet ready");
-        }
-        return 
waRestClient.getSyncopeClient().getService(U2FRegistrationService.class);
+        service().delete(new U2FDeviceQuery.Builder().build());
     }
 }
diff --git 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
index 6b3e8ac2f2..bec625d1e9 100644
--- 
a/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
+++ 
b/wa/starter/src/main/java/org/apache/syncope/wa/starter/webauthn/WAWebAuthnCredentialRepository.java
@@ -40,9 +40,9 @@ import org.slf4j.LoggerFactory;
 
 public class WAWebAuthnCredentialRepository extends 
BaseWebAuthnCredentialRepository {
 
-    private static final Logger LOG = 
LoggerFactory.getLogger(WAWebAuthnCredentialRepository.class);
+    protected static final Logger LOG = 
LoggerFactory.getLogger(WAWebAuthnCredentialRepository.class);
 
-    private final WARestClient waRestClient;
+    protected final WARestClient waRestClient;
 
     public WAWebAuthnCredentialRepository(final CasConfigurationProperties 
properties,
             final WARestClient waRestClient) {
@@ -50,23 +50,29 @@ public class WAWebAuthnCredentialRepository extends 
BaseWebAuthnCredentialReposi
         this.waRestClient = waRestClient;
     }
 
+    protected WebAuthnRegistrationService service() {
+        return waRestClient.getService(WebAuthnRegistrationService.class);
+    }
+
     @Override
-    public boolean removeRegistrationByUsername(final String username,
+    public boolean removeRegistrationByUsername(
+            final String username,
             final CredentialRegistration credentialRegistration) {
+
         String id = 
credentialRegistration.getCredential().getCredentialId().getHex();
-        getService().delete(username, id);
+        service().delete(username, id);
         return true;
     }
 
     @Override
     public boolean removeAllRegistrations(final String username) {
-        getService().delete(username);
+        service().delete(username);
         return true;
     }
 
     @Override
     public Stream<? extends CredentialRegistration> stream() {
-        return getService().list().
+        return service().list().
                 stream().
                 map(WebAuthnAccount::getCredentials).
                 flatMap(Collection::stream).
@@ -91,13 +97,13 @@ public class WAWebAuthnCredentialRepository extends 
BaseWebAuthnCredentialReposi
                     })).
                     collect(Collectors.toList());
 
-            WebAuthnAccount account = getService().read(username);
+            WebAuthnAccount account = service().read(username);
             if (account != null) {
                 account.getCredentials().addAll(credentials);
-                getService().update(username, account);
+                service().update(username, account);
             } else {
                 account = new 
WebAuthnAccount.Builder().credentials(credentials).build();
-                getService().create(username, account);
+                service().create(username, account);
             }
         } catch (final Exception e) {
             LOG.error(e.getMessage(), e);
@@ -107,7 +113,7 @@ public class WAWebAuthnCredentialRepository extends 
BaseWebAuthnCredentialReposi
     @Override
     public Collection<CredentialRegistration> getRegistrationsByUsername(final 
String username) {
         try {
-            return getService().read(username).getCredentials().stream().
+            return service().read(username).getCredentials().stream().
                     map(Unchecked.function(record -> {
                         String json = 
getCipherExecutor().decode(record.getJson());
                         return WebAuthnUtils.getObjectMapper()
@@ -115,22 +121,15 @@ public class WAWebAuthnCredentialRepository extends 
BaseWebAuthnCredentialReposi
                                 });
                     })).
                     collect(Collectors.toList());
-        } catch (final SyncopeClientException e) {
+        } catch (SyncopeClientException e) {
             if (e.getType() == ClientExceptionType.NotFound) {
                 LOG.info("Could not locate account for {}", username);
             } else {
                 LOG.error(e.getMessage(), e);
             }
-        } catch (final Exception e) {
+        } catch (Exception e) {
             LOG.error(e.getMessage(), e);
         }
         return List.of();
     }
-
-    private WebAuthnRegistrationService getService() {
-        if (!waRestClient.isReady()) {
-            throw new IllegalStateException("Syncope core is not yet ready");
-        }
-        return 
waRestClient.getSyncopeClient().getService(WebAuthnRegistrationService.class);
-    }
 }
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
index 635338055f..88477ebc44 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/WAServiceRegistryTest.java
@@ -26,7 +26,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.auth.OIDCAuthModuleConf;
 import org.apache.syncope.common.lib.policy.AccessPolicyTO;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
@@ -152,12 +151,11 @@ public class WAServiceRegistryTest extends AbstractTest {
     @Test
     public void addClientApp() {
         // 1. start with no client apps defined on mocked Core
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        assertNotNull(syncopeClient);
+        assertTrue(waRestClient.isReady());
 
         SyncopeCoreTestingServer.CLIENT_APPS.clear();
 
-        WAClientAppService service = 
syncopeClient.getService(WAClientAppService.class);
+        WAClientAppService service = 
waRestClient.getService(WAClientAppService.class);
         assertTrue(service.list().isEmpty());
 
         // 2. add one client app on mocked Core, nothing on WA yet
@@ -232,8 +230,7 @@ public class WAServiceRegistryTest extends AbstractTest {
     @Test
     public void delegatedAuthentication() {
         // 1. start with 1 client app and 1 auth module defined on mocked Core
-        SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
-        assertNotNull(syncopeClient);
+        assertTrue(waRestClient.isReady());
 
         OIDCAuthModuleConf oidcAuthModuleConf = new OIDCAuthModuleConf();
         oidcAuthModuleConf.setClientId("clientId");
@@ -244,11 +241,11 @@ public class WAServiceRegistryTest extends AbstractTest {
 
         SyncopeCoreTestingServer.AUTH_MODULES.clear();
         SyncopeCoreTestingServer.AUTH_MODULES.add(authModuleTO);
-        AuthModuleService authModuleService = 
syncopeClient.getService(AuthModuleService.class);
+        AuthModuleService authModuleService = 
waRestClient.getService(AuthModuleService.class);
         assertEquals(1, authModuleService.list().size());
 
         SyncopeCoreTestingServer.CLIENT_APPS.clear();
-        WAClientAppService waClientAppService = 
syncopeClient.getService(WAClientAppService.class);
+        WAClientAppService waClientAppService = 
waRestClient.getService(WAClientAppService.class);
         assertTrue(waClientAppService.list().isEmpty());
 
         WAClientApp waClientApp = new WAClientApp();
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
index 7e620b9a3d..48a90f02ef 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/WAAuditTrailManagerTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.Date;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.audit.AuditEntry;
 import org.apache.syncope.common.rest.api.service.AuditService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -37,14 +36,13 @@ public class WAAuditTrailManagerTest extends AbstractTest {
     private static AuditService LOGGER_SERVICE;
 
     private static WARestClient getWaRestClient() {
-        WARestClient restClient = mock(WARestClient.class);
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
         LOGGER_SERVICE = mock(AuditService.class);
 
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
-        
when(syncopeClient.getService(AuditService.class)).thenReturn(LOGGER_SERVICE);
+        WARestClient waRestClient = mock(WARestClient.class);
+        when(waRestClient.isReady()).thenReturn(Boolean.TRUE);
+        
when(waRestClient.getService(AuditService.class)).thenReturn(LOGGER_SERVICE);
 
-        return restClient;
+        return waRestClient;
     }
 
     @Test
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
index 1c6a67da02..b9b570080d 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/WAEventRepositoryTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.util.Map;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.audit.AuditEntry;
 import org.apache.syncope.common.rest.api.service.AuditService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -38,14 +37,13 @@ public class WAEventRepositoryTest extends AbstractTest {
     private static AuditService AUDIT_SERVICE;
 
     private static WARestClient getWaRestClient() {
-        WARestClient restClient = mock(WARestClient.class);
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
         AUDIT_SERVICE = mock(AuditService.class);
 
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
-        
when(syncopeClient.getService(AuditService.class)).thenReturn(AUDIT_SERVICE);
+        WARestClient waRestClient = mock(WARestClient.class);
+        when(waRestClient.isReady()).thenReturn(Boolean.TRUE);
+        
when(waRestClient.getService(AuditService.class)).thenReturn(AUDIT_SERVICE);
 
-        return restClient;
+        return waRestClient;
     }
 
     @Test
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
index 4b63324aab..94b4db1552 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientCustomizerTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
 
 import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -49,13 +48,10 @@ public class WASAML2ClientCustomizerTest extends 
BaseWASAML2ClientTest {
         when(service.get(anyString())).thenReturn(entityTO);
         doNothing().when(service).set(any(SAML2SPEntityTO.class));
 
-        WARestClient restClient = mock(WARestClient.class);
+        WARestClient waRestClient = mock(WARestClient.class);
+        
when(waRestClient.getService(SAML2SPEntityService.class)).thenReturn(service);
 
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
-        
when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(service);
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
-
-        WASAML2ClientCustomizer customizer = new 
WASAML2ClientCustomizer(restClient);
+        WASAML2ClientCustomizer customizer = new 
WASAML2ClientCustomizer(waRestClient);
         SAML2Client client = getSAML2Client();
         customizer.customize(client);
         client.init();
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
index 29d012081b..43457616b8 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientKeystoreGeneratorTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
 
 import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -39,8 +38,6 @@ import org.springframework.core.io.ClassPathResource;
 public class WASAML2ClientKeystoreGeneratorTest extends BaseWASAML2ClientTest {
 
     private static WARestClient getWaRestClient() throws Exception {
-        WARestClient restClient = mock(WARestClient.class);
-
         SAML2SPEntityTO keystoreTO = new SAML2SPEntityTO.Builder()
                 .key("CAS")
                 .keystore(getKeystoreAsString())
@@ -51,10 +48,9 @@ public class WASAML2ClientKeystoreGeneratorTest extends 
BaseWASAML2ClientTest {
         when(service.get(anyString())).thenReturn(keystoreTO);
         doNothing().when(service).set(any(SAML2SPEntityTO.class));
 
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
-        
when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(service);
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
-        return restClient;
+        WARestClient waRestClient = mock(WARestClient.class);
+        
when(waRestClient.getService(SAML2SPEntityService.class)).thenReturn(service);
+        return waRestClient;
     }
 
     @Test
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
index 794f9285e1..3e1f19340c 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2ClientMetadataGeneratorTest.java
@@ -29,7 +29,6 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -42,7 +41,6 @@ import org.springframework.core.io.ClassPathResource;
 public class WASAML2ClientMetadataGeneratorTest extends BaseWASAML2ClientTest {
 
     private static WARestClient getWaRestClient() throws IOException {
-        WARestClient restClient = mock(WARestClient.class);
         SAML2SPEntityTO metadataTO = new SAML2SPEntityTO.Builder()
                 .key("Syncope")
                 .metadata(IOUtils.toString(new 
ClassPathResource("sp-metadata.xml").getInputStream(),
@@ -53,10 +51,9 @@ public class WASAML2ClientMetadataGeneratorTest extends 
BaseWASAML2ClientTest {
         when(saml2SPMetadataService.get(anyString())).thenReturn(metadataTO);
         
doNothing().when(saml2SPMetadataService).set(any(SAML2SPEntityTO.class));
 
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
-        
when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(saml2SPMetadataService);
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
-        return restClient;
+        WARestClient waRestClient = mock(WARestClient.class);
+        
when(waRestClient.getService(SAML2SPEntityService.class)).thenReturn(saml2SPMetadataService);
+        return waRestClient;
     }
 
     @Test
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
index 3b128deffb..c30758ecea 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/pac4j/saml/WASAML2MetadataResolverTest.java
@@ -28,7 +28,6 @@ import static org.mockito.Mockito.when;
 import java.io.File;
 import java.util.Base64;
 import org.apache.commons.io.IOUtils;
-import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.to.SAML2SPEntityTO;
 import org.apache.syncope.common.rest.api.service.SAML2SPEntityService;
 import org.apache.syncope.wa.bootstrap.WARestClient;
@@ -43,7 +42,6 @@ public class WASAML2MetadataResolverTest extends 
BaseWASAML2ClientTest {
         SAML2Client client = getSAML2Client();
         String keystoreFile = File.createTempFile("keystore", 
"jks").getCanonicalPath();
         client.getConfiguration().setKeystoreResourceFilepath(keystoreFile);
-        WARestClient restClient = mock(WARestClient.class);
 
         SAML2SPEntityTO metadataTO = new SAML2SPEntityTO.Builder()
                 .key("Syncope")
@@ -55,11 +53,10 @@ public class WASAML2MetadataResolverTest extends 
BaseWASAML2ClientTest {
         when(saml2SPMetadataService.get(anyString())).thenReturn(metadataTO);
         
doNothing().when(saml2SPMetadataService).set(any(SAML2SPEntityTO.class));
 
-        SyncopeClient syncopeClient = mock(SyncopeClient.class);
-        
when(syncopeClient.getService(SAML2SPEntityService.class)).thenReturn(saml2SPMetadataService);
-        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+        WARestClient waRestClient = mock(WARestClient.class);
+        
when(waRestClient.getService(SAML2SPEntityService.class)).thenReturn(saml2SPMetadataService);
 
-        WASAML2MetadataResolver resolver = new 
WASAML2MetadataResolver(restClient, client);
+        WASAML2MetadataResolver resolver = new 
WASAML2MetadataResolver(waRestClient, client);
         assertNotNull(resolver.fetchMetadata());
     }
 }
diff --git 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
index fd99d9e45b..cea5f004cb 100644
--- 
a/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
+++ 
b/wa/starter/src/test/java/org/apache/syncope/wa/starter/surrogate/WASurrogateAuthenticationServiceTest.java
@@ -43,11 +43,10 @@ public class WASurrogateAuthenticationServiceTest extends 
AbstractTest {
     @Test
     public void verifyImpersonation() {
         String owner = "syncope-principal";
-        ImpersonationAccount account = new 
ImpersonationAccount.Builder().impersonated("impersonatee").
-                build();
+        ImpersonationAccount account = new ImpersonationAccount.Builder().
+                impersonated("impersonatee").build();
 
-        ImpersonationService impersonationService = 
waRestClient.getSyncopeClient().
-                getService(ImpersonationService.class);
+        ImpersonationService impersonationService = 
waRestClient.getService(ImpersonationService.class);
 
         impersonationService.create(owner, account);
 

Reply via email to