You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2020/05/19 13:23:44 UTC

[syncope] branch master updated: [SYNCOPE-1564] Fixing YAML support

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

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


The following commit(s) were added to refs/heads/master by this push:
     new d08d0a6  [SYNCOPE-1564] Fixing YAML support
d08d0a6 is described below

commit d08d0a69e14cc890712745dc2d57c23fd3c91618
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon May 18 14:01:07 2020 +0200

    [SYNCOPE-1564] Fixing YAML support
---
 client/idrepo/lib/pom.xml                          |   8 +-
 .../client/lib/SyncopeClientFactoryBean.java       |  43 ++-
 .../syncope/common/lib/to/client/ClientAppTO.java  |  29 +-
 .../syncope/common/lib/to/client/OIDCRPTO.java     |   3 +-
 .../syncope/common/lib/to/client/SAML2SPTO.java    |   3 +-
 common/idrepo/lib/pom.xml                          |   6 +
 .../org/apache/syncope/common/lib/BaseBean.java    |   9 +
 .../common/lib/to/AbstractStartEndBean.java        |   3 +-
 core/idrepo/rest-cxf/pom.xml                       |  39 +++
 .../syncope/core/rest/cxf/CheckDomainFilter.java   |   2 +-
 .../syncope/core/rest/cxf/RESTCXFContext.java      |   9 +-
 ...dObjectMapper.java => SyncopeObjectMapper.java} |   9 +-
 .../syncope/core/rest/cxf/SyncopeYAMLMapper.java   |  16 +-
 .../syncope/core/rest/cxf/batch/BatchProcess.java  |   2 +-
 .../core/rest/cxf/service/AbstractServiceImpl.java |   1 -
 .../rest/cxf/service/AnyObjectServiceTest.java     | 293 +++++++++++++++++++++
 .../core/rest/cxf/service/RESTCXFTestContext.java  | 115 ++++++++
 .../java/pushpull/stream/CSVStreamConnector.java   |   5 +-
 .../org/apache/syncope/fit/AbstractITCase.java     |   5 +-
 .../org/apache/syncope/fit/core/AuditITCase.java   |   2 +-
 .../org/apache/syncope/fit/core/BatchITCase.java   |  23 +-
 .../apache/syncope/fit/core/BpmnProcessITCase.java |   5 +
 .../apache/syncope/fit/core/ClientAppITCase.java   |   1 -
 .../apache/syncope/fit/core/DynRealmITCase.java    |   2 +-
 .../syncope/fit/core/LinkedAccountITCase.java      |  10 +-
 .../org/apache/syncope/fit/core/OpenAPIITCase.java |   2 +-
 .../apache/syncope/fit/core/UserRequestITCase.java |  26 +-
 .../apache/syncope/fit/core/WAClientAppITCase.java |   5 +-
 28 files changed, 598 insertions(+), 78 deletions(-)

diff --git a/client/idrepo/lib/pom.xml b/client/idrepo/lib/pom.xml
index 8adedb7..52dc00d 100644
--- a/client/idrepo/lib/pom.xml
+++ b/client/idrepo/lib/pom.xml
@@ -54,8 +54,12 @@ under the License.
     <dependency>
       <groupId>com.fasterxml.jackson.jaxrs</groupId>
       <artifactId>jackson-jaxrs-json-provider</artifactId>
-    </dependency>  
- 
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.jaxrs</groupId>
+      <artifactId>jackson-jaxrs-yaml-provider</artifactId>
+    </dependency>
+
     <dependency>
       <groupId>joda-time</groupId>
       <artifactId>joda-time</artifactId>
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 572bc1a..ee1fdf4 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
@@ -19,19 +19,19 @@
 package org.apache.syncope.client.lib;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
 import com.fasterxml.jackson.datatype.joda.JodaModule;
 import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
-import java.util.ArrayList;
+import com.fasterxml.jackson.jaxrs.yaml.JacksonJaxbYAMLProvider;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-
 import javax.ws.rs.core.MediaType;
 import javax.xml.bind.Marshaller;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.configuration.jsse.TLSClientParameters;
-import org.apache.cxf.feature.Feature;
 import org.apache.cxf.ext.logging.LoggingFeature;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
@@ -75,6 +75,8 @@ public class SyncopeClientFactoryBean {
 
     private JAXBElementProvider<?> jaxbProvider;
 
+    private JacksonJaxbYAMLProvider yamlProvider;
+
     private RestClientExceptionMapper exceptionMapper;
 
     private String address;
@@ -92,7 +94,7 @@ public class SyncopeClientFactoryBean {
     protected static JacksonJaxbJsonProvider defaultJsonProvider() {
         ObjectMapper objectMapper = new ObjectMapper();
         objectMapper.registerModule(new JodaModule());
-        objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
         return new JacksonJaxbJsonProvider(objectMapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
     }
 
@@ -115,6 +117,13 @@ public class SyncopeClientFactoryBean {
         return defaultJAXBProvider;
     }
 
+    protected static JacksonJaxbYAMLProvider defaultYamlProvider() {
+        YAMLMapper yamlMapper = new YAMLMapper();
+        yamlMapper.registerModule(new JodaModule());
+        yamlMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        return new JacksonJaxbYAMLProvider(yamlMapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
+    }
+
     protected static RestClientExceptionMapper defaultExceptionMapper() {
         return new RestClientExceptionMapper();
     }
@@ -135,16 +144,14 @@ public class SyncopeClientFactoryBean {
         defaultRestClientFactoryBean.setThreadSafe(true);
         defaultRestClientFactoryBean.setInheritHeaders(true);
 
-        List<Feature> features = new ArrayList<>();
-        features.add(new LoggingFeature());
-        defaultRestClientFactoryBean.setFeatures(features);
+        defaultRestClientFactoryBean.setFeatures(List.of(new LoggingFeature()));
 
-        List<Object> providers = new ArrayList<>(4);
-        providers.add(new DateParamConverterProvider());
-        providers.add(getJaxbProvider());
-        providers.add(getJsonProvider());
-        providers.add(getExceptionMapper());
-        defaultRestClientFactoryBean.setProviders(providers);
+        defaultRestClientFactoryBean.setProviders(List.of(
+                new DateParamConverterProvider(),
+                getJsonProvider(),
+                getJaxbProvider(),
+                getYamlProvider(),
+                getExceptionMapper()));
 
         return defaultRestClientFactoryBean;
     }
@@ -168,6 +175,16 @@ public class SyncopeClientFactoryBean {
         return this;
     }
 
+    public JacksonJaxbYAMLProvider getYamlProvider() {
+        return yamlProvider == null
+                ? defaultYamlProvider()
+                : yamlProvider;
+    }
+
+    public void setYamlProvider(final JacksonJaxbYAMLProvider yamlProvider) {
+        this.yamlProvider = yamlProvider;
+    }
+
     public RestClientExceptionMapper getExceptionMapper() {
         return Optional.ofNullable(exceptionMapper).orElseGet(SyncopeClientFactoryBean::defaultExceptionMapper);
     }
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
index ed8a6f0..465b9e7 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/ClientAppTO.java
@@ -18,27 +18,33 @@
  */
 package org.apache.syncope.common.lib.to.client;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonPropertyOrder;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import io.swagger.v3.oas.annotations.media.Schema;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.syncope.common.lib.BaseBean;
-import org.apache.syncope.common.lib.to.EntityTO;
+import javax.ws.rs.PathParam;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.syncope.common.lib.to.EntityTO;
 
 @XmlRootElement(name = "clientApp")
 @XmlType
 @XmlSeeAlso({ OIDCRPTO.class, SAML2SPTO.class })
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@class")
-@JsonPropertyOrder(value = { "@class", "key", "name", "description", "authPolicy", "accessPolicy", "attReleasePolicy" })
+@JsonPropertyOrder(value = { "@class", "key", "description" })
 @Schema(subTypes = { OIDCRPTO.class, SAML2SPTO.class }, discriminatorProperty = "@class")
-public abstract class ClientAppTO extends BaseBean implements EntityTO {
+public abstract class ClientAppTO implements EntityTO {
 
     private static final long serialVersionUID = 6577639976115661357L;
 
+    @XmlTransient
+    @JsonProperty("@class")
+    private String discriminator;
+
     private String key;
 
     private String name;
@@ -53,6 +59,13 @@ public abstract class ClientAppTO extends BaseBean implements EntityTO {
 
     private String attrReleasePolicy;
 
+    @Schema(name = "@class", required = true)
+    public abstract String getDiscriminator();
+
+    public void setDiscriminator(final String discriminator) {
+        // do nothing
+    }
+
     public String getAttrReleasePolicy() {
         return attrReleasePolicy;
     }
@@ -82,6 +95,7 @@ public abstract class ClientAppTO extends BaseBean implements EntityTO {
         return key;
     }
 
+    @PathParam("key")
     @Override
     public void setKey(final String key) {
         this.key = key;
@@ -111,9 +125,6 @@ public abstract class ClientAppTO extends BaseBean implements EntityTO {
         this.description = description;
     }
 
-    @Schema(name = "@class", required = true)
-    public abstract String getDiscriminator();
-
     @Override
     public int hashCode() {
         return new HashCodeBuilder()
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
index 987c534..e5e92ee 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/OIDCRPTO.java
@@ -58,8 +58,7 @@ public class OIDCRPTO extends ClientAppTO {
 
     @XmlTransient
     @JsonProperty("@class")
-    @Schema(name = "@class", required = true,
-            example = "org.apache.syncope.common.lib.to.client.OIDCRPTO")
+    @Schema(name = "@class", required = true, example = "org.apache.syncope.common.lib.to.client.OIDCRPTO")
     @Override
     public String getDiscriminator() {
         return getClass().getName();
diff --git a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
index d2cfac5..955222a 100644
--- a/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
+++ b/common/am/lib/src/main/java/org/apache/syncope/common/lib/to/client/SAML2SPTO.java
@@ -62,8 +62,7 @@ public class SAML2SPTO extends ClientAppTO {
 
     @XmlTransient
     @JsonProperty("@class")
-    @Schema(name = "@class", required = true,
-            example = "org.apache.syncope.common.lib.to.client.SAML2SPTO")
+    @Schema(name = "@class", required = true, example = "org.apache.syncope.common.lib.to.client.SAML2SPTO")
     @Override
     public String getDiscriminator() {
         return getClass().getName();
diff --git a/common/idrepo/lib/pom.xml b/common/idrepo/lib/pom.xml
index b5c3a37..6057ed3 100644
--- a/common/idrepo/lib/pom.xml
+++ b/common/idrepo/lib/pom.xml
@@ -54,6 +54,12 @@ under the License.
     </dependency>
     
     <dependency>
+      <groupId>com.fasterxml.jackson.dataformat</groupId>
+      <artifactId>jackson-dataformat-yaml</artifactId>
+      <version>${jackson.version}</version>
+    </dependency>
+
+    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
     </dependency>
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java
index edfbeff..f3d603b 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java
@@ -19,7 +19,16 @@
 package org.apache.syncope.common.lib;
 
 import java.io.Serializable;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.lib.to.AbstractStartEndBean;
+import org.apache.syncope.common.lib.to.AccessTokenTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.RealmTO;
 
+@XmlType
+@XmlSeeAlso({ AbstractStartEndBean.class, AccessTokenTO.class, AuditEntry.class, AnyTO.class, RealmTO.class })
 public abstract class BaseBean implements Serializable {
 
     private static final long serialVersionUID = 8243277089603485380L;
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
index b5a6811..aad8578 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractStartEndBean.java
@@ -21,13 +21,14 @@ package org.apache.syncope.common.lib.to;
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.util.Date;
 import java.util.Optional;
-
+import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.syncope.common.lib.BaseBean;
 
 @XmlType
+@XmlSeeAlso({ ExecTO.class, ReportTO.class, TaskTO.class })
 public class AbstractStartEndBean extends BaseBean {
 
     private static final long serialVersionUID = 2399577415544539917L;
diff --git a/core/idrepo/rest-cxf/pom.xml b/core/idrepo/rest-cxf/pom.xml
index a092cd8..f1028e6 100644
--- a/core/idrepo/rest-cxf/pom.xml
+++ b/core/idrepo/rest-cxf/pom.xml
@@ -135,6 +135,45 @@ under the License.
       <groupId>org.webjars</groupId>
       <artifactId>highlightjs</artifactId>
     </dependency>
+
+    <!-- TEST -->
+    <dependency>
+      <groupId>org.apache.cxf</groupId>
+      <artifactId>cxf-rt-transports-local</artifactId>
+      <version>${cxf.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.bval</groupId>
+      <artifactId>bval-jsr</artifactId>
+      <scope>test</scope>
+    </dependency>      
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <version>${slf4j.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-junit-jupiter</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/CheckDomainFilter.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/CheckDomainFilter.java
index ea23ee1..5b3062d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/CheckDomainFilter.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/CheckDomainFilter.java
@@ -45,7 +45,7 @@ public class CheckDomainFilter implements ContainerRequestFilter {
 
     @Override
     public void filter(final ContainerRequestContext reqContext) throws IOException {
-        final String domain = reqContext.getHeaderString(RESTHeaders.DOMAIN);
+        String domain = reqContext.getHeaderString(RESTHeaders.DOMAIN);
         if (domain != null && !SyncopeConstants.MASTER_DOMAIN.equals(domain)) {
             if (!domainHolder.getDomains().containsKey(domain)) {
                 String message = "Domain '" + domain + "' not available";
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
index 9affd05..eb2f55d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/RESTCXFContext.java
@@ -91,8 +91,7 @@ public class RESTCXFContext {
         documentDepthProperties.setInnerElementCountThreshold(500);
         jaxbProvider.setDepthProperties(documentDepthProperties);
 
-        jaxbProvider.setCollectionWrapperMap(
-                Map.of("org.apache.syncope.common.lib.policy.PolicyTO", "policies"));
+        jaxbProvider.setCollectionWrapperMap(Map.of("org.apache.syncope.common.lib.policy.PolicyTO", "policies"));
 
         return jaxbProvider;
     }
@@ -100,13 +99,15 @@ public class RESTCXFContext {
     @Bean
     public JacksonJaxbJsonProvider jsonProvider() {
         JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
-        jsonProvider.setMapper(new UnwrappedObjectMapper());
+        jsonProvider.setMapper(new SyncopeObjectMapper());
         return jsonProvider;
     }
 
     @Bean
     public JacksonJaxbYAMLProvider yamlProvider() {
-        return new JacksonJaxbYAMLProvider();
+        JacksonJaxbYAMLProvider yamlProvider = new JacksonJaxbYAMLProvider();
+        yamlProvider.setMapper(new SyncopeYAMLMapper());
+        return yamlProvider;
     }
 
     @Bean
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/UnwrappedObjectMapper.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeObjectMapper.java
similarity index 91%
rename from core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/UnwrappedObjectMapper.java
rename to core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeObjectMapper.java
index 9ab783d..42d121d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/UnwrappedObjectMapper.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeObjectMapper.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.rest.cxf;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import com.fasterxml.jackson.datatype.joda.JodaModule;
 import java.io.File;
 import java.io.IOException;
@@ -32,15 +33,15 @@ import java.util.Map;
  * Jackson ObjectMapper that unwraps singleton map values and enable default
  * typing for handling abstract types serialization.
  */
-public class UnwrappedObjectMapper extends ObjectMapper {
+public class SyncopeObjectMapper extends ObjectMapper {
 
     private static final long serialVersionUID = -317191546835195103L;
 
-    public UnwrappedObjectMapper() {
+    public SyncopeObjectMapper() {
         super();
 
         registerModule(new JodaModule());
-        configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
     }
 
     /**
@@ -50,7 +51,7 @@ public class UnwrappedObjectMapper extends ObjectMapper {
      * @param value the potential Map to unwrap
      * @return the unwrapped map or the original value
      */
-    private static Object unwrapMap(final Object value) {
+    private Object unwrapMap(final Object value) {
         if (value instanceof Map) {
             Map<?, ?> map = (Map<?, ?>) value;
             if (map.size() == 1) {
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeYAMLMapper.java
similarity index 61%
copy from common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java
copy to core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeYAMLMapper.java
index edfbeff..b9f2ba3 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/BaseBean.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/SyncopeYAMLMapper.java
@@ -16,12 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib;
+package org.apache.syncope.core.rest.cxf;
 
-import java.io.Serializable;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.fasterxml.jackson.datatype.joda.JodaModule;
 
-public abstract class BaseBean implements Serializable {
+public class SyncopeYAMLMapper extends YAMLMapper {
 
-    private static final long serialVersionUID = 8243277089603485380L;
+    private static final long serialVersionUID = 1022020055828974308L;
 
+    public SyncopeYAMLMapper() {
+        super();
+
+        registerModule(new JodaModule());
+        configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+    }
 }
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java
index a76d418..e6426fd 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchProcess.java
@@ -92,7 +92,7 @@ public class BatchProcess implements Runnable {
 
         List<BatchResponseItem> batchResponseItems = new ArrayList<>(batchRequestItems.size());
 
-        batchRequestItems.forEach((BatchRequestItem reqItem) -> {
+        batchRequestItems.forEach(reqItem -> {
             LOG.debug("Batch item:\n{}", reqItem);
 
             AbstractHTTPDestination dest = destinationRegistry.getDestinationForPath(reqItem.getRequestURI(), true);
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index f61260d..ae3c903 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@ -243,5 +243,4 @@ public abstract class AbstractServiceImpl implements JAXRSService {
 
         return result;
     }
-
 }
diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java
new file mode 100644
index 0000000..16d309c
--- /dev/null
+++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/AnyObjectServiceTest.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import com.fasterxml.jackson.jaxrs.yaml.JacksonJaxbYAMLProvider;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.UUID;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.endpoint.Server;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.jaxrs.ext.search.SearchBean;
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchContext;
+import org.apache.cxf.jaxrs.ext.search.SearchContextProvider;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.impl.UriBuilderImpl;
+import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider;
+import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
+import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
+import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
+import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
+import org.apache.cxf.transport.local.LocalConduit;
+import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.EntityTOUtils;
+import org.apache.syncope.common.lib.request.AnyObjectCR;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.rest.api.DateParamConverterProvider;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.AnyObjectService;
+import org.apache.syncope.core.logic.AnyObjectLogic;
+import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
+import org.apache.syncope.core.rest.cxf.AddETagFilter;
+import org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+import org.springframework.test.util.ReflectionTestUtils;
+
+//@SpringJUnitConfig(locations = { "classpath:restCXFTest.xml" })
+@SpringJUnitConfig(classes = { RESTCXFTestContext.class })
+public class AnyObjectServiceTest {
+
+    private static final String LOCAL_ADDRESS = "local://anyObjects";
+
+    private static Server SERVER;
+
+    @Autowired
+    private JAXBElementProvider<?> jaxbProvider;
+
+    @Autowired
+    private DateParamConverterProvider dateParamConverterProvider;
+
+    @Autowired
+    private JacksonJaxbJsonProvider jsonProvider;
+
+    @Autowired
+    private JacksonJaxbYAMLProvider yamlProvider;
+
+    @Autowired
+    private RestServiceExceptionMapper exceptionMapper;
+
+    @Autowired
+    private JAXRSBeanValidationInInterceptor validationInInterceptor;
+
+    @Autowired
+    private GZIPInInterceptor gzipInInterceptor;
+
+    @Autowired
+    private GZIPOutInterceptor gzipOutInterceptor;
+
+    @Autowired
+    private SearchContextProvider searchContextProvider;
+
+    @Autowired
+    private AddETagFilter addETagFilter;
+
+    @BeforeEach
+    public void setup() {
+        if (SERVER == null) {
+            AnyObjectDAO anyObjectDAO = mock(AnyObjectDAO.class);
+
+            AnyObjectLogic logic = mock(AnyObjectLogic.class);
+            when(logic.search(any(SearchCond.class), anyInt(), anyInt(), anyList(), anyString(), anyBoolean())).
+                    thenAnswer(ic -> {
+                        AnyObjectTO printer1 = new AnyObjectTO();
+                        printer1.setKey(UUID.randomUUID().toString());
+                        printer1.setName("printer1");
+                        printer1.setType("PRINTER");
+                        printer1.getPlainAttrs().add(new Attr.Builder("location").value("here").build());
+
+                        AnyObjectTO printer2 = new AnyObjectTO();
+                        printer2.setKey(UUID.randomUUID().toString());
+                        printer2.setName("printer2");
+                        printer2.setType("PRINTER");
+                        printer2.getPlainAttrs().add(new Attr.Builder("location").value("there").build());
+
+                        return Pair.of(2, Arrays.asList(printer1, printer2));
+                    });
+            when(logic.create(any(AnyObjectCR.class), anyBoolean())).thenAnswer(ic -> {
+                AnyObjectTO anyObjectTO = new AnyObjectTO();
+                EntityTOUtils.toAnyTO(ic.getArgument(0), anyObjectTO);
+                anyObjectTO.setKey(UUID.randomUUID().toString());
+
+                ProvisioningResult<AnyObjectTO> result = new ProvisioningResult<>();
+                result.setEntity(anyObjectTO);
+                return result;
+            });
+
+            SearchCondVisitor searchCondVisitor = mock(SearchCondVisitor.class);
+            when(searchCondVisitor.getQuery()).thenReturn(new SearchCond());
+
+            @SuppressWarnings("unchecked")
+            SearchCondition<SearchBean> sc = mock(SearchCondition.class);
+            doNothing().when(sc).accept(any());
+            SearchContext searchContext = mock(SearchContext.class);
+            when(searchContext.getCondition(anyString(), eq(SearchBean.class))).thenReturn(sc);
+
+            UriInfo uriInfo = mock(UriInfo.class);
+            when(uriInfo.getAbsolutePathBuilder()).thenReturn(new UriBuilderImpl());
+            when(uriInfo.getQueryParameters()).thenReturn(new MetadataMap<>());
+
+            MessageContext messageContext = mock(MessageContext.class);
+            MockHttpServletRequest httpRequest = new MockHttpServletRequest();
+            httpRequest.addHeader(RESTHeaders.NULL_PRIORITY_ASYNC, "false");
+            when(messageContext.getHttpServletRequest()).thenReturn(httpRequest);
+            when(messageContext.getHttpServletResponse()).thenReturn(new MockHttpServletResponse());
+
+            Request request = mock(Request.class);
+            when(request.evaluatePreconditions(any(Date.class))).thenReturn(Response.notModified());
+            when(messageContext.getRequest()).thenReturn(request);
+
+            AnyObjectServiceImpl service = new AnyObjectServiceImpl();
+            ReflectionTestUtils.setField(service, "anyObjectDAO", anyObjectDAO);
+            ReflectionTestUtils.setField(service, "logic", logic);
+            ReflectionTestUtils.setField(service, "searchCondVisitor", searchCondVisitor);
+            ReflectionTestUtils.setField(service, "searchContext", searchContext);
+            ReflectionTestUtils.setField(service, "uriInfo", uriInfo);
+            ReflectionTestUtils.setField(service, "messageContext", messageContext);
+
+            JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
+            sf.setAddress(LOCAL_ADDRESS);
+            sf.setResourceClasses(AnyObjectService.class);
+            sf.setResourceProvider(
+                    AnyObjectService.class,
+                    new SingletonResourceProvider(service, true));
+
+            sf.setInInterceptors(Arrays.asList(gzipInInterceptor, validationInInterceptor));
+            sf.setOutInterceptors(Arrays.asList(gzipOutInterceptor));
+
+            sf.setProviders(Arrays.asList(dateParamConverterProvider, jaxbProvider, jsonProvider, yamlProvider,
+                    exceptionMapper, searchContextProvider, addETagFilter));
+
+            SERVER = sf.create();
+        }
+
+        assertNotNull(SERVER);
+    }
+
+    private WebClient client(final MediaType mediaType) {
+        WebClient client = WebClient.create(LOCAL_ADDRESS, Arrays.asList(
+                dateParamConverterProvider, jsonProvider, jaxbProvider, yamlProvider));
+        WebClient.getConfig(client).getRequestContext().put(LocalConduit.DIRECT_DISPATCH, Boolean.TRUE);
+        return client.accept(mediaType).type(mediaType).path("anyObjects");
+    }
+
+    private InputStream list(final MediaType mediaType) {
+        Response response = client(mediaType).
+                query("fiql", "$type==PRINTER").
+                query("page", "1").
+                query("size", "10").
+                get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+
+        return (InputStream) response.getEntity();
+    }
+
+    private void checkList(final PagedResult<AnyObjectTO> list) {
+        assertEquals(2, list.getTotalCount());
+        assertEquals(2, list.getResult().size());
+
+        assertEquals("printer1", list.getResult().get(0).getName());
+        assertEquals("PRINTER", list.getResult().get(0).getType());
+
+        assertEquals("there", list.getResult().get(1).getPlainAttr("location").get().getValues().get(0));
+    }
+
+    @Test
+    public void jsonList() throws IOException {
+        InputStream in = list(MediaType.APPLICATION_JSON_TYPE);
+
+        PagedResult<AnyObjectTO> list = new ObjectMapper().
+                readValue(IOUtils.toString(in), new TypeReference<PagedResult<AnyObjectTO>>() {
+                });
+        checkList(list);
+    }
+
+    @Test
+    public void xmlList() throws IOException, JAXBException {
+        InputStream in = list(MediaType.APPLICATION_XML_TYPE);
+
+        JAXBContext context = JAXBContext.newInstance(PagedResult.class, AnyObjectTO.class);
+        @SuppressWarnings("unchecked")
+        PagedResult<AnyObjectTO> list = (PagedResult<AnyObjectTO>) context.createUnmarshaller().unmarshal(in);
+        checkList(list);
+    }
+
+    @Test
+    public void yamlList() throws IOException {
+        InputStream in = list(RESTHeaders.APPLICATION_YAML_TYPE);
+
+        PagedResult<AnyObjectTO> list = new YAMLMapper().
+                readValue(IOUtils.toString(in), new TypeReference<PagedResult<AnyObjectTO>>() {
+                });
+        checkList(list);
+    }
+
+    private void create(final MediaType mediaType) {
+        AnyObjectCR newPrinter = new AnyObjectCR();
+        newPrinter.setName("newPrinter");
+        newPrinter.setType("PRINTER");
+        newPrinter.getPlainAttrs().add(new Attr.Builder("location").value("new").build());
+
+        Response response = client(mediaType).post(newPrinter);
+        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
+        assertNotNull(response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+    }
+
+    @Test
+    public void jsonCreate() {
+        create(MediaType.APPLICATION_JSON_TYPE);
+    }
+
+    @Test
+    public void xmlCreate() {
+        create(MediaType.APPLICATION_XML_TYPE);
+    }
+
+    @Test
+    public void yamlCreate() {
+        create(RESTHeaders.APPLICATION_YAML_TYPE);
+    }
+}
diff --git a/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RESTCXFTestContext.java b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RESTCXFTestContext.java
new file mode 100644
index 0000000..2fa4ba1
--- /dev/null
+++ b/core/idrepo/rest-cxf/src/test/java/org/apache/syncope/core/rest/cxf/service/RESTCXFTestContext.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.rest.cxf.service;
+
+import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
+import com.fasterxml.jackson.jaxrs.yaml.JacksonJaxbYAMLProvider;
+import java.util.Map;
+import org.apache.cxf.jaxrs.ext.search.SearchContextProvider;
+import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
+import org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor;
+import org.apache.cxf.staxutils.DocumentDepthProperties;
+import org.apache.cxf.transport.common.gzip.GZIPInInterceptor;
+import org.apache.cxf.transport.common.gzip.GZIPOutInterceptor;
+import org.apache.cxf.validation.BeanValidationProvider;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.rest.api.DateParamConverterProvider;
+import org.apache.syncope.core.rest.cxf.AddETagFilter;
+import org.apache.syncope.core.rest.cxf.RestServiceExceptionMapper;
+import org.apache.syncope.core.rest.cxf.SyncopeObjectMapper;
+import org.apache.syncope.core.rest.cxf.SyncopeYAMLMapper;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RESTCXFTestContext {
+
+    @Bean
+    public DateParamConverterProvider dateParamConverterProvider() {
+        return new DateParamConverterProvider();
+    }
+
+    @Bean
+    public JAXBElementProvider<?> jaxbProvider() {
+        JAXBElementProvider<?> jaxbProvider = new JAXBElementProvider<>();
+        jaxbProvider.setNamespacePrefixes(Map.of(SyncopeConstants.NS, SyncopeConstants.NS_PREFIX));
+
+        DocumentDepthProperties documentDepthProperties = new DocumentDepthProperties();
+        documentDepthProperties.setInnerElementCountThreshold(500);
+        jaxbProvider.setDepthProperties(documentDepthProperties);
+
+        jaxbProvider.setCollectionWrapperMap(Map.of("org.apache.syncope.common.lib.policy.PolicyTO", "policies"));
+
+        return jaxbProvider;
+    }
+
+    @Bean
+    public JacksonJaxbJsonProvider jsonProvider() {
+        JacksonJaxbJsonProvider jsonProvider = new JacksonJaxbJsonProvider();
+        jsonProvider.setMapper(new SyncopeObjectMapper());
+        return jsonProvider;
+    }
+
+    @Bean
+    public JacksonJaxbYAMLProvider yamlProvider() {
+        JacksonJaxbYAMLProvider yamlProvider = new JacksonJaxbYAMLProvider();
+        yamlProvider.setMapper(new SyncopeYAMLMapper());
+        return yamlProvider;
+    }
+
+    @Bean
+    public BeanValidationProvider validationProvider() {
+        return new BeanValidationProvider();
+    }
+
+    @Bean
+    public JAXRSBeanValidationInInterceptor validationInInterceptor() {
+        JAXRSBeanValidationInInterceptor validationInInterceptor = new JAXRSBeanValidationInInterceptor();
+        validationInInterceptor.setProvider(validationProvider());
+        return validationInInterceptor;
+    }
+
+    @Bean
+    public GZIPInInterceptor gzipInInterceptor() {
+        return new GZIPInInterceptor();
+    }
+
+    @Bean
+    public GZIPOutInterceptor gzipOutInterceptor() {
+        GZIPOutInterceptor gzipOutInterceptor = new GZIPOutInterceptor();
+        gzipOutInterceptor.setThreshold(0);
+        gzipOutInterceptor.setForce(true);
+        return gzipOutInterceptor;
+    }
+
+    @Bean
+    public RestServiceExceptionMapper restServiceExceptionMapper() {
+        return new RestServiceExceptionMapper();
+    }
+
+    @Bean
+    public SearchContextProvider searchContextProvider() {
+        return new SearchContextProvider();
+    }
+
+    @Bean
+    public AddETagFilter addETagFilter() {
+        return new AddETagFilter();
+    }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
index 8d70cea..7314465 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.java.pushpull.stream;
 import com.fasterxml.jackson.databind.MappingIterator;
 import com.fasterxml.jackson.databind.SequenceWriter;
 import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvParser;
 import com.fasterxml.jackson.dataformat.csv.CsvSchema;
 import java.io.IOException;
 import java.io.InputStream;
@@ -104,7 +105,9 @@ public class CSVStreamConnector implements Connector, AutoCloseable {
     public MappingIterator<Map<String, String>> reader() throws IOException {
         synchronized (this) {
             if (reader == null) {
-                reader = new CsvMapper().readerFor(Map.class).with(schemaBuilder.build()).readValues(in);
+                reader = new CsvMapper().
+                        enable(CsvParser.Feature.SKIP_EMPTY_LINES).
+                        readerFor(Map.class).with(schemaBuilder.build()).readValues(in);
             }
         }
         return reader;
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index 01936ca..ca562eb 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
@@ -158,7 +159,9 @@ public abstract class AbstractITCase {
 
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractITCase.class);
 
-    protected static final ObjectMapper MAPPER = new ObjectMapper();
+    protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    protected static final YAMLMapper YAML_MAPPER = new YAMLMapper();
 
     protected static final String ADMIN_UNAME = "admin";
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
index 3fb543b..a3362bf 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
@@ -225,7 +225,7 @@ public class AuditITCase extends AbstractITCase {
         entries = query(query, MAX_WAIT_SECONDS);
         assertEquals(pre + 1, entries.size());
 
-        ConnInstanceTO restore = MAPPER.readValue(entries.get(0).getBefore(), ConnInstanceTO.class);
+        ConnInstanceTO restore = OBJECT_MAPPER.readValue(entries.get(0).getBefore(), ConnInstanceTO.class);
         connectorService.update(restore);
 
         ldapConn = connectorService.read(connectorKey, null);
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 8b9f682..57bba98 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
@@ -72,17 +72,17 @@ public class BatchITCase extends AbstractITCase {
     private static String requestBody(final String boundary) throws JsonProcessingException, JAXBException {
         List<BatchRequestItem> reqItems = new ArrayList<>();
 
-        // 1. create user as JSON
+        // 1. create user as YAML
         UserCR userCR = UserITCase.getUniqueSample("batch@syncope.apache.org");
         assertNotEquals("/odd", userCR.getRealm());
-        String createUserPayload = MAPPER.writeValueAsString(userCR);
+        String createUserPayload = YAML_MAPPER.writeValueAsString(userCR);
 
         BatchRequestItem createUser = new BatchRequestItem();
         createUser.setMethod(HttpMethod.POST);
         createUser.setRequestURI("/users");
         createUser.setHeaders(new HashMap<>());
-        createUser.getHeaders().put(HttpHeaders.ACCEPT, List.of(MediaType.APPLICATION_JSON));
-        createUser.getHeaders().put(HttpHeaders.CONTENT_TYPE, List.of(MediaType.APPLICATION_JSON));
+        createUser.getHeaders().put(HttpHeaders.ACCEPT, List.of(RESTHeaders.APPLICATION_YAML));
+        createUser.getHeaders().put(HttpHeaders.CONTENT_TYPE, List.of(RESTHeaders.APPLICATION_YAML));
         createUser.getHeaders().put(HttpHeaders.CONTENT_LENGTH, List.of(createUserPayload.length()));
         createUser.setContent(createUserPayload);
         reqItems.add(createUser);
@@ -109,7 +109,7 @@ public class BatchITCase extends AbstractITCase {
         UserUR userUR = new UserUR();
         userUR.setKey(userCR.getUsername());
         userUR.setRealm(new StringReplacePatchItem.Builder().value("/odd").build());
-        String updateUserPayload = MAPPER.writeValueAsString(userUR);
+        String updateUserPayload = OBJECT_MAPPER.writeValueAsString(userUR);
 
         BatchRequestItem updateUser = new BatchRequestItem();
         updateUser.setMethod(HttpMethod.PATCH);
@@ -154,8 +154,8 @@ public class BatchITCase extends AbstractITCase {
         assertNotNull(resItems.get(0).getHeaders().get(HttpHeaders.ETAG));
         assertNotNull(resItems.get(0).getHeaders().get(RESTHeaders.DOMAIN));
         assertNotNull(resItems.get(0).getHeaders().get(RESTHeaders.RESOURCE_KEY));
-        assertEquals(MediaType.APPLICATION_JSON, resItems.get(0).getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
-        ProvisioningResult<UserTO> user = MAPPER.readValue(
+        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<ProvisioningResult<UserTO>>() {
         });
         assertNotNull(user.getEntity().getKey());
@@ -191,7 +191,8 @@ public class BatchITCase extends AbstractITCase {
         assertEquals(Response.Status.OK.getStatusCode(), resItems.get(5).getStatus());
         assertNotNull(resItems.get(5).getHeaders().get(RESTHeaders.DOMAIN));
         assertEquals(MediaType.APPLICATION_JSON, resItems.get(5).getHeaders().get(HttpHeaders.CONTENT_TYPE).get(0));
-        group = MAPPER.readValue(resItems.get(5).getContent(), new TypeReference<ProvisioningResult<GroupTO>>() {
+        group = OBJECT_MAPPER.readValue(
+                resItems.get(5).getContent(), new TypeReference<ProvisioningResult<GroupTO>>() {
         });
         assertNotNull(group);
     }
@@ -268,15 +269,17 @@ public class BatchITCase extends AbstractITCase {
     private static BatchRequest batchRequest() {
         BatchRequest batchRequest = adminClient.batch();
 
-        // 1. create user as JSON
+        // 1. create user as YAML
         UserService batchUserService = batchRequest.getService(UserService.class);
+        Client client = WebClient.client(batchUserService).reset();
+        client.type(RESTHeaders.APPLICATION_YAML).accept(RESTHeaders.APPLICATION_YAML);
         UserCR userCR = UserITCase.getUniqueSample("batch@syncope.apache.org");
         assertNotEquals("/odd", userCR.getRealm());
         batchUserService.create(userCR);
 
         // 2. create group as XML
         GroupService batchGroupService = batchRequest.getService(GroupService.class);
-        Client client = WebClient.client(batchGroupService).reset();
+        client = WebClient.client(batchGroupService).reset();
         client.type(MediaType.APPLICATION_XML).accept(MediaType.APPLICATION_XML);
         GroupCR groupCR = GroupITCase.getBasicSample("batch");
         batchGroupService.create(groupCR);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
index c5e9625..ddcd22e 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/BpmnProcessITCase.java
@@ -21,6 +21,7 @@ package org.apache.syncope.fit.core;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.io.IOException;
@@ -28,6 +29,7 @@ import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.lib.to.BpmnProcess;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.FlowableDetector;
@@ -41,7 +43,9 @@ public class BpmnProcessITCase extends AbstractITCase {
 
     @BeforeAll
     public static void findDefault() {
+        assumeFalse(clientFactory.getContentType() == SyncopeClientFactoryBean.ContentType.YAML);
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+
         bpmnProcessService.list().stream().
                 filter(BpmnProcess::isUserWorkflow).findAny().
                 ifPresent(process -> userWorkflowKey = process.getKey());
@@ -50,6 +54,7 @@ public class BpmnProcessITCase extends AbstractITCase {
 
     @BeforeEach
     public void check() {
+        assumeFalse(clientFactory.getContentType() == SyncopeClientFactoryBean.ContentType.YAML);
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
     }
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java
index f0caecf..10a0194 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ClientAppITCase.java
@@ -151,5 +151,4 @@ public class ClientAppITCase extends AbstractITCase {
             assertNotNull(e);
         }
     }
-
 }
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
index e878067..fe31d97 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -239,7 +239,7 @@ public class DynRealmITCase extends AbstractITCase {
                 send();
         assertEquals(HttpStatus.OK_200, response.getStatus());
 
-        return (ArrayNode) MAPPER.readTree(response.getContent()).
+        return (ArrayNode) OBJECT_MAPPER.readTree(response.getContent()).
                 get("hits").get("hits").get(0).get("_source").get("dynRealms");
     }
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
index 54f8325..29b8f5a 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
@@ -459,7 +459,7 @@ public class LinkedAccountITCase extends AbstractITCase {
         WebClient webClient = WebClient.create(BUILD_TOOLS_ADDRESS + "/rest/users").
                 accept(MediaType.APPLICATION_JSON_TYPE).type(MediaType.APPLICATION_JSON_TYPE);
 
-        ObjectNode user = MAPPER.createObjectNode();
+        ObjectNode user = OBJECT_MAPPER.createObjectNode();
         user.put("username", "linkedaccount1");
         user.put("password", "Password123");
         user.put("firstName", "Pasquale");
@@ -471,7 +471,7 @@ public class LinkedAccountITCase extends AbstractITCase {
         String user1Key = StringUtils.substringAfterLast(response.getHeaderString(HttpHeaders.LOCATION), "/");
         assertNotNull(user1Key);
 
-        user = MAPPER.createObjectNode();
+        user = OBJECT_MAPPER.createObjectNode();
         user.put("username", "vivaldi");
         user.put("password", "Password123");
         user.put("firstName", "Giovannino");
@@ -483,7 +483,7 @@ public class LinkedAccountITCase extends AbstractITCase {
         String user2Key = StringUtils.substringAfterLast(response.getHeaderString(HttpHeaders.LOCATION), "/");
         assertNotNull(user2Key);
 
-        user = MAPPER.createObjectNode();
+        user = OBJECT_MAPPER.createObjectNode();
         user.put("username", "not.vivaldi");
         user.put("password", "Password123");
         user.put("email", "not.vivaldi@syncope.org");
@@ -537,12 +537,12 @@ public class LinkedAccountITCase extends AbstractITCase {
             response = webClient.path(user1Key).delete();
             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
-            user = MAPPER.createObjectNode();
+            user = OBJECT_MAPPER.createObjectNode();
             user.put("username", "linkedaccount2");
             response = webClient.replacePath(user2Key).put(user.toString());
             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
 
-            user = MAPPER.createObjectNode();
+            user = OBJECT_MAPPER.createObjectNode();
             user.put("status", "INACTIVE");
             response = webClient.replacePath(user3Key).put(user.toString());
             assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java
index dcb7264..f810b4d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/OpenAPIITCase.java
@@ -39,7 +39,7 @@ public class OpenAPIITCase extends AbstractITCase {
         Response response = webClient.get();
         assertEquals(200, response.getStatus());
 
-        JsonNode tree = MAPPER.readTree((InputStream) response.getEntity());
+        JsonNode tree = OBJECT_MAPPER.readTree((InputStream) response.getEntity());
         assertNotNull(tree);
 
         JsonNode info = tree.get("info");
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 e1a89ad..0f845b1 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
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.io.IOException;
@@ -30,6 +31,7 @@ import javax.ws.rs.core.MediaType;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.UserRequestForm;
@@ -42,12 +44,14 @@ import org.apache.syncope.common.rest.api.service.UserRequestService;
 import org.apache.syncope.fit.AbstractITCase;
 import org.apache.syncope.fit.FlowableDetector;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 public class UserRequestITCase extends AbstractITCase {
 
     @BeforeAll
     public static void loadBpmnProcesses() throws IOException {
+        assumeFalse(clientFactory.getContentType() == SyncopeClientFactoryBean.ContentType.YAML);
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
 
         WebClient.client(bpmnProcessService).type(MediaType.APPLICATION_XML_TYPE);
@@ -59,10 +63,14 @@ public class UserRequestITCase extends AbstractITCase {
                 IOUtils.toString(UserRequestITCase.class.getResourceAsStream("/verifyAddedVariables.bpmn20.xml")));
     }
 
-    @Test
-    public void twoLevelsApproval() {
+    @BeforeEach
+    public void check() {
+        assumeFalse(clientFactory.getContentType() == SyncopeClientFactoryBean.ContentType.YAML);
         assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
+    }
 
+    @Test
+    public void twoLevelsApproval() {
         UserTO user = createUser(UserITCase.getUniqueSample("twoLevelsApproval@tirasa.net")).getEntity();
         assertNotNull(user);
         assertFalse(user.getMembership("ebf97068-aa4b-4a85-9f01-680e8c4cf227").isPresent());
@@ -141,8 +149,6 @@ public class UserRequestITCase extends AbstractITCase {
 
     @Test
     public void cancel() {
-        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
-
         PagedResult<UserRequestForm> forms =
                 userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
@@ -175,8 +181,6 @@ public class UserRequestITCase extends AbstractITCase {
 
     @Test
     public void userSelection() {
-        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
-
         PagedResult<UserRequestForm> forms =
                 userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
@@ -247,11 +251,9 @@ public class UserRequestITCase extends AbstractITCase {
         assertTrue(relationships.stream().
                 anyMatch(relationship -> "8559d14d-58c2-46eb-a2d4-a7d35161e8f8".equals(relationship.getOtherEndKey())));
     }
-    
+
     @Test
     public void addVariablesToUserRequestAtStart() {
-        assumeTrue(FlowableDetector.isFlowableEnabledForUserWorkflow(syncopeService));
-
         PagedResult<UserRequestForm> forms =
                 userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         int preForms = forms.getTotalCount();
@@ -263,7 +265,7 @@ public class UserRequestITCase extends AbstractITCase {
 
         WorkflowTaskExecInput testInput = new WorkflowTaskExecInput();
         testInput.getVariables().put("providedVariable", "test");
-        
+
         // start request as user
         UserRequest req = client.getService(UserRequestService.class).start("verifyAddedVariables", null, testInput);
         assertNotNull(req);
@@ -280,10 +282,10 @@ public class UserRequestITCase extends AbstractITCase {
         UserRequestForm form = userForms.getResult().get(0);
         form = userRequestService.claimForm(form.getTaskId());
         assertEquals(form.getProperty("providedVariable").get().getValue(), "test");
-        
+
         // cancel request
         userRequestService.cancel(req.getExecutionId(), "nothing in particular");
-       
+
         // no more forms available
         forms = userRequestService.getForms(new UserRequestFormQuery.Builder().build());
         assertEquals(preForms, forms.getTotalCount());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java
index b3c0029..4078360 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/WAClientAppITCase.java
@@ -22,10 +22,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
 import java.util.List;
 import org.apache.syncope.client.lib.AnonymousAuthenticationHandler;
 import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
 import org.apache.syncope.common.lib.policy.AccessPolicyTO;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import org.apache.syncope.common.lib.policy.AuthPolicyTO;
@@ -47,6 +49,8 @@ public class WAClientAppITCase extends AbstractITCase {
 
     @BeforeAll
     public static void setup() {
+        assumeTrue(clientFactory.getContentType() == SyncopeClientFactoryBean.ContentType.JSON);
+
         SyncopeClient anonymous = clientFactory.create(
                 new AnonymousAuthenticationHandler(ANONYMOUS_UNAME, ANONYMOUS_KEY));
         waClientAppService = anonymous.getService(WAClientAppService.class);
@@ -154,5 +158,4 @@ public class WAClientAppITCase extends AbstractITCase {
         authModuleTO = authModuleService.read("be456831-593d-4003-b273-4c3fb61700df");
         assertTrue(authModuleTO.getItems().isEmpty());
     }
-
 }