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 2018/08/02 15:24:41 UTC

[8/9] syncope git commit: [SYNCOPE-1348] Batch is in, now need to kick Bulk out

[SYNCOPE-1348] Batch is in, now need to kick Bulk out


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/d9250efa
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/d9250efa
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/d9250efa

Branch: refs/heads/master
Commit: d9250efade3793edeb35be10f72890a98090438c
Parents: d2ff6e4
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 2 17:23:43 2018 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 2 17:23:53 2018 +0200

----------------------------------------------------------------------
 .../syncope/client/lib/SyncopeClient.java       |   9 +
 .../client/lib/SyncopeClientFactoryBean.java    |   3 +-
 .../lib/batch/BatchClientFactoryBean.java       |  89 +++++
 .../client/lib/batch/BatchClientProxyImpl.java  |  83 +++++
 .../lib/batch/BatchOfflineHTTPConduit.java      |  77 +++++
 .../syncope/client/lib/batch/BatchRequest.java  | 102 ++++++
 .../syncope/client/lib/batch/BatchResponse.java |  95 +++++
 .../syncope/common/lib/SyncopeConstants.java    |   5 +-
 .../syncope/common/rest/api/Preference.java     |   3 +-
 .../syncope/common/rest/api/RESTHeaders.java    |  36 +-
 .../common/rest/api/batch/BatchItem.java        |  57 +++
 .../rest/api/batch/BatchPayloadGenerator.java   |  78 +++++
 .../common/rest/api/batch/BatchPayloadLine.java |  53 +++
 .../rest/api/batch/BatchPayloadLineReader.java  | 193 +++++++++++
 .../rest/api/batch/BatchPayloadParser.java      | 208 +++++++++++
 .../common/rest/api/batch/BatchRequestItem.java |  54 +++
 .../rest/api/batch/BatchResponseItem.java       |  34 ++
 .../rest/api/service/AccessTokenService.java    |  13 +-
 .../rest/api/service/AnyObjectService.java      |  13 +-
 .../common/rest/api/service/AnyService.java     |  29 +-
 .../rest/api/service/AnyTypeClassService.java   |  15 +-
 .../common/rest/api/service/AnyTypeService.java |  11 +-
 .../rest/api/service/ApplicationService.java    |  17 +-
 .../rest/api/service/ConfigurationService.java  |  12 +-
 .../api/service/ConnectorHistoryService.java    |   8 +-
 .../rest/api/service/ConnectorService.java      |  29 +-
 .../common/rest/api/service/DomainService.java  |  15 +-
 .../rest/api/service/DynRealmService.java       |  15 +-
 .../rest/api/service/ExecutableService.java     |  16 +-
 .../common/rest/api/service/GroupService.java   |  17 +-
 .../rest/api/service/ImplementationService.java |  16 +-
 .../common/rest/api/service/LoggerService.java  |  18 +-
 .../rest/api/service/MailTemplateService.java   |  15 +-
 .../rest/api/service/NotificationService.java   |  19 +-
 .../common/rest/api/service/PolicyService.java  |  15 +-
 .../common/rest/api/service/RealmService.java   |  15 +-
 .../rest/api/service/ReconciliationService.java |  12 +-
 .../api/service/RelationshipTypeService.java    |  15 +-
 .../rest/api/service/RemediationService.java    |  17 +-
 .../common/rest/api/service/ReportService.java  |  17 +-
 .../rest/api/service/ReportTemplateService.java |   9 +-
 .../api/service/ResourceHistoryService.java     |   8 +-
 .../rest/api/service/ResourceService.java       |  33 +-
 .../common/rest/api/service/RoleService.java    |  19 +-
 .../common/rest/api/service/SchemaService.java  |  15 +-
 .../api/service/SecurityQuestionService.java    |  19 +-
 .../common/rest/api/service/SyncopeService.java |  76 +++-
 .../common/rest/api/service/TaskService.java    |  19 +-
 .../rest/api/service/UserSelfService.java       |  27 +-
 .../common/rest/api/service/UserService.java    |  17 +-
 .../rest/api/service/UserWorkflowService.java   |  16 +-
 .../rest/api/service/WorkflowService.java       |  11 +-
 .../core/persistence/api/dao/BatchDAO.java      |  32 ++
 .../core/persistence/api/entity/Batch.java      |  32 ++
 .../core/persistence/jpa/dao/JPABatchDAO.java   |  62 ++++
 .../core/persistence/jpa/entity/JPABatch.java   |  68 ++++
 .../jpa/entity/JPAEntityFactory.java            |   3 +
 .../main/resources/domains/MasterContent.xml    |   4 +
 .../jpa/inner/ImplementationTest.java           |   4 +-
 .../core/persistence/jpa/inner/TaskTest.java    |   2 +-
 .../test/resources/domains/MasterContent.xml    |   4 +
 .../java/data/AccessTokenDataBinderImpl.java    |   6 +-
 .../java/job/ExpiredBatchCleanup.java           |  39 +++
 core/rest-cxf/pom.xml                           |  11 +
 .../core/rest/cxf/batch/BatchItemRequest.java   | 145 ++++++++
 .../core/rest/cxf/batch/BatchItemResponse.java  | 310 +++++++++++++++++
 .../core/rest/cxf/batch/BatchProcess.java       | 144 ++++++++
 .../rest/cxf/service/AbstractServiceImpl.java   |   7 +-
 .../rest/cxf/service/SyncopeServiceImpl.java    | 129 +++++++
 .../src/main/resources/restCXFContext.xml       |   7 +-
 .../rest/api/service/CamelRouteService.java     |  14 +-
 .../rest/api/service/OIDCClientService.java     |   8 +-
 .../rest/api/service/OIDCProviderService.java   |  21 +-
 .../rest/api/service/SAML2IdPService.java       |  14 +-
 .../common/rest/api/service/SAML2SPService.java |  14 +-
 .../rest/api/service/SCIMConfService.java       |   8 +-
 fit/core-reference/pom.xml                      |   2 +-
 .../src/main/resources/jboss/restCXFContext.xml |   7 +-
 .../apache/syncope/fit/core/BatchITCase.java    | 345 +++++++++++++++++++
 pom.xml                                         |   2 +-
 80 files changed, 2898 insertions(+), 363 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
index 7675f0a..a614dd1 100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.lib;
 
+import org.apache.syncope.client.lib.batch.BatchRequest;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
@@ -358,4 +359,12 @@ public class SyncopeClient {
     public <T> EntityTag getLatestEntityTag(final T service) {
         return WebClient.client(service).getResponse().getEntityTag();
     }
+
+    public BatchRequest batch() {
+        return new BatchRequest(
+                mediaType,
+                restClientFactory.getAddress(),
+                restClientFactory.getProviders(),
+                getJWT());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
index d0ee76a..90105d0 100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
@@ -34,7 +34,6 @@ import org.apache.cxf.ext.logging.LoggingFeature;
 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
 import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
 import org.apache.cxf.staxutils.DocumentDepthProperties;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.rest.api.DateParamConverterProvider;
 import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -48,7 +47,7 @@ public class SyncopeClientFactoryBean {
     public enum ContentType {
 
         JSON(MediaType.APPLICATION_JSON_TYPE),
-        YAML(SyncopeConstants.APPLICATION_YAML_TYPE),
+        YAML(RESTHeaders.APPLICATION_YAML_TYPE),
         XML(MediaType.APPLICATION_XML_TYPE);
 
         private final MediaType mediaType;

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientFactoryBean.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientFactoryBean.java b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientFactoryBean.java
new file mode 100644
index 0000000..a8858dc
--- /dev/null
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientFactoryBean.java
@@ -0,0 +1,89 @@
+/*
+ * 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.client.lib.batch;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.cxf.endpoint.ConduitSelector;
+import org.apache.cxf.endpoint.Endpoint;
+import org.apache.cxf.endpoint.UpfrontConduitSelector;
+import org.apache.cxf.jaxrs.client.ClientProxyImpl;
+import org.apache.cxf.jaxrs.client.ClientState;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
+
+public class BatchClientFactoryBean extends JAXRSClientFactoryBean {
+
+    private final List<BatchRequestItem> batchRequestItems = new ArrayList<>();
+
+    private ClassLoader proxyLoader;
+
+    private boolean inheritHeaders;
+
+    @Override
+    public void setClassLoader(final ClassLoader loader) {
+        this.proxyLoader = loader;
+    }
+
+    @Override
+    public void setInheritHeaders(final boolean inheritHeaders) {
+        this.inheritHeaders = inheritHeaders;
+    }
+
+    @Override
+    protected ConduitSelector getConduitSelector(final Endpoint ep) {
+        ConduitSelector cs = getConduitSelector();
+        if (cs == null) {
+            try {
+                cs = new UpfrontConduitSelector(new BatchOfflineHTTPConduit(bus, ep.getEndpointInfo()));
+            } catch (IOException e) {
+                throw new IllegalArgumentException("Could not create " + BatchOfflineHTTPConduit.class.getName(), e);
+            }
+        }
+        cs.setEndpoint(ep);
+        return cs;
+    }
+
+    @Override
+    protected ClientProxyImpl createClientProxy(
+            final ClassResourceInfo cri,
+            final boolean isRoot,
+            final ClientState actualState,
+            final Object[] varValues) {
+
+        if (actualState == null) {
+            return new BatchClientProxyImpl(
+                    this, URI.create(getAddress()), proxyLoader, cri, isRoot, inheritHeaders, varValues);
+        } else {
+            return new BatchClientProxyImpl(
+                    this, actualState, proxyLoader, cri, isRoot, inheritHeaders, varValues);
+        }
+    }
+
+    public boolean add(final BatchRequestItem item) {
+        return this.batchRequestItems.add(item);
+    }
+
+    public List<BatchRequestItem> getBatchRequestItems() {
+        return batchRequestItems;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientProxyImpl.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientProxyImpl.java b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientProxyImpl.java
new file mode 100644
index 0000000..3e736e0
--- /dev/null
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchClientProxyImpl.java
@@ -0,0 +1,83 @@
+/*
+ * 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.client.lib.batch;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.ClientProxyImpl;
+import org.apache.cxf.jaxrs.client.ClientState;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.message.Message;
+import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
+
+public class BatchClientProxyImpl extends ClientProxyImpl {
+
+    private final BatchClientFactoryBean factory;
+
+    public BatchClientProxyImpl(
+            final BatchClientFactoryBean factory,
+            final URI baseURI,
+            final ClassLoader loader,
+            final ClassResourceInfo cri,
+            final boolean isRoot,
+            final boolean inheritHeaders,
+            final Object... varValues) {
+
+        super(baseURI, loader, cri, isRoot, inheritHeaders, varValues);
+        this.factory = factory;
+    }
+
+    public BatchClientProxyImpl(
+            final BatchClientFactoryBean factory,
+            final ClientState initialState,
+            final ClassLoader loader,
+            final ClassResourceInfo cri,
+            final boolean isRoot,
+            final boolean inheritHeaders,
+            final Object... varValues) {
+
+        super(initialState, loader, cri, isRoot, inheritHeaders, varValues);
+        this.factory = factory;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected Object[] preProcessResult(final Message message) throws Exception {
+        BatchRequestItem bri = new BatchRequestItem();
+        bri.setMethod((String) message.get(Message.HTTP_REQUEST_METHOD));
+        bri.setRequestURI(StringUtils.substringAfter(
+                (String) message.getContextualProperty(Message.REQUEST_URI),
+                getState().getBaseURI().toASCIIString()));
+        bri.setHeaders((Map<String, List<Object>>) message.get(Message.PROTOCOL_HEADERS));
+
+        BatchOfflineHTTPConduit conduit = (BatchOfflineHTTPConduit) message.getExchange().getConduit(message);
+        bri.setContent(conduit.getOutputStream().toString(StandardCharsets.UTF_8.name()));
+
+        factory.add(bri);
+        return null;
+    }
+
+    @Override
+    protected Object handleResponse(final Message outMessage, final Class<?> serviceCls) throws Throwable {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchOfflineHTTPConduit.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchOfflineHTTPConduit.java b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchOfflineHTTPConduit.java
new file mode 100644
index 0000000..e4587be
--- /dev/null
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchOfflineHTTPConduit.java
@@ -0,0 +1,77 @@
+/*
+ * 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.client.lib.batch;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.cxf.Bus;
+import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.service.model.EndpointInfo;
+import org.apache.cxf.transport.http.Address;
+import org.apache.cxf.transport.http.HTTPConduit;
+import org.apache.cxf.transport.http.auth.DefaultBasicAuthSupplier;
+import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
+import org.apache.cxf.ws.addressing.EndpointReferenceType;
+
+public class BatchOfflineHTTPConduit extends HTTPConduit {
+
+    private ByteArrayOutputStream baos;
+
+    public BatchOfflineHTTPConduit(final Bus bus, final EndpointInfo ei) throws IOException {
+        this(bus, ei, null);
+    }
+
+    public BatchOfflineHTTPConduit(
+            final Bus bus,
+            final EndpointInfo ei,
+            final EndpointReferenceType t) throws IOException {
+
+        super(bus, ei, t);
+        this.proxyAuthSupplier = new DefaultBasicAuthSupplier();
+        this.proxyAuthorizationPolicy = new ProxyAuthorizationPolicy();
+    }
+
+    @Override
+    protected void setupConnection(
+            final Message message, final Address address,
+            final HTTPClientPolicy csPolicy) throws IOException {
+    }
+
+    @Override
+    public HTTPClientPolicy getClient(final Message message) {
+        return new HTTPClientPolicy();
+    }
+
+    @Override
+    protected OutputStream createOutputStream(
+            final Message message,
+            final boolean needToCacheRequest,
+            final boolean isChunking,
+            final int chunkThreshold) throws IOException {
+
+        baos = new ByteArrayOutputStream();
+        return baos;
+    }
+
+    public ByteArrayOutputStream getOutputStream() {
+        return baos;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchRequest.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchRequest.java b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchRequest.java
new file mode 100644
index 0000000..580c95a
--- /dev/null
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchRequest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.client.lib.batch;
+
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.rest.api.Preference;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadGenerator;
+import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BatchRequest {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BatchRequest.class);
+
+    private final MediaType mediaType;
+
+    private final String jwt;
+
+    private final String address;
+
+    private final List<?> providers;
+
+    private BatchClientFactoryBean bcfb;
+
+    public BatchRequest(
+            final MediaType mediaType,
+            final String address,
+            final List<?> providers,
+            final String jwt) {
+
+        this.mediaType = mediaType;
+        this.jwt = jwt;
+        this.address = address;
+        this.providers = providers;
+        initBatchClientFactoryBean();
+    }
+
+    private void initBatchClientFactoryBean() {
+        this.bcfb = new BatchClientFactoryBean();
+        this.bcfb.setAddress(address);
+        this.bcfb.setProviders(providers);
+    }
+
+    public <T> T getService(final Class<T> serviceClass) {
+        bcfb.setServiceClass(serviceClass);
+        T serviceInstance = bcfb.create(serviceClass);
+
+        Client client = WebClient.client(serviceInstance);
+        client.type(mediaType).accept(mediaType);
+
+        return serviceInstance;
+    }
+
+    public List<BatchRequestItem> getItems() {
+        return bcfb.getBatchRequestItems();
+    }
+
+    public BatchResponse commit() {
+        return commit(false);
+    }
+
+    public BatchResponse commit(final boolean async) {
+        String boundary = "--batch_" + UUID.randomUUID().toString();
+
+        WebClient webClient = WebClient.create(bcfb.getAddress()).path("batch").
+                header(HttpHeaders.AUTHORIZATION, "Bearer " + jwt).
+                type(RESTHeaders.multipartMixedWith(boundary.substring(2)));
+        if (async) {
+            webClient.header(RESTHeaders.PREFER, Preference.RESPOND_ASYNC);
+        }
+
+        String body = BatchPayloadGenerator.generate(bcfb.getBatchRequestItems(), boundary);
+        LOG.debug("Batch request body:\n{}", body);
+
+        initBatchClientFactoryBean();
+
+        return new BatchResponse(boundary, jwt, webClient.post(body));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchResponse.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchResponse.java b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchResponse.java
new file mode 100644
index 0000000..89cd0e7
--- /dev/null
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/batch/BatchResponse.java
@@ -0,0 +1,95 @@
+/*
+ * 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.client.lib.batch;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.helpers.IOUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.batch.BatchPayloadParser;
+import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BatchResponse {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BatchResponse.class);
+
+    private final String boundary;
+
+    private final String jwt;
+
+    private final URI monitor;
+
+    private Response response;
+
+    public BatchResponse(final String boundary, final String jwt, final Response response) {
+        this.boundary = boundary;
+        this.jwt = jwt;
+        this.monitor = response.getLocation();
+        this.response = response;
+    }
+
+    /**
+     * Gives the last Response received from the Batch service.
+     *
+     * @return the last Response received from the Batch service
+     */
+    public Response getResponse() {
+        return response;
+    }
+
+    /**
+     * If asynchronous processing was requested, query the monitor URI.
+     *
+     * @return the last Response received from the Batch service
+     */
+    public Response poll() {
+        if (monitor != null) {
+            response = WebClient.create(monitor).
+                    header(HttpHeaders.AUTHORIZATION, "Bearer " + jwt).
+                    type(RESTHeaders.multipartMixedWith(boundary.substring(2))).get();
+        }
+
+        return response;
+    }
+
+    /**
+     * Parses the latest Response received into a list of {@link BatchResponseItem}s.
+     *
+     * @return the Batch Response parsed as list of {@link BatchResponseItem}s
+     * @throws IOException if there are issues when reading the response body
+     */
+    public List<BatchResponseItem> getItems() throws IOException {
+        String body = IOUtils.toString((InputStream) response.getEntity(), StandardCharsets.UTF_8.name());
+        LOG.debug("Batch response body:\n{}", body);
+
+        return BatchPayloadParser.parse(
+                new ByteArrayInputStream(body.getBytes()),
+                response.getMediaType(),
+                new BatchResponseItem());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
index 517e775..b8a7ee4 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/SyncopeConstants.java
@@ -21,7 +21,6 @@ package org.apache.syncope.common.lib;
 import java.util.Collections;
 import java.util.Set;
 import java.util.regex.Pattern;
-import javax.ws.rs.core.MediaType;
 
 public final class SyncopeConstants {
 
@@ -64,9 +63,9 @@ public final class SyncopeConstants {
 
     public static final Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
 
-    public static final String APPLICATION_YAML = "application/yaml";
+    public static final String DOUBLE_DASH = "--";
 
-    public static final MediaType APPLICATION_YAML_TYPE = new MediaType("application", "yaml");
+    public static final String CRLF = "\r\n";
 
     private SyncopeConstants() {
         // private constructor for utility class

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
index 8d21e54..a2e6fb2 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/Preference.java
@@ -28,7 +28,8 @@ public enum Preference {
 
     NONE(""),
     RETURN_CONTENT("return-content"),
-    RETURN_NO_CONTENT("return-no-content");
+    RETURN_NO_CONTENT("return-no-content"),
+    RESPOND_ASYNC("respond-async");
 
     private final String literal;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
index 40cd8ba..f993317 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.common.rest.api;
 
+import javax.ws.rs.core.MediaType;
+
 /**
  * Custom HTTP headers in use with REST services.
  */
@@ -51,13 +53,41 @@ public final class RESTHeaders {
     public static final String ERROR_INFO = "X-Application-Error-Info";
 
     /**
-     * Mediatype for PNG images, not defined in <tt>javax.ws.rs.core.MediaType</tt>.
-     *
-     * @see javax.ws.rs.core.MediaType
+     * Mediatype for PNG images, not defined in {@link javax.ws.rs.core.MediaType}.
      */
     public static final String MEDIATYPE_IMAGE_PNG = "image/png";
 
     /**
+     * Mediatype for YAML, not defined in {@link javax.ws.rs.core.MediaType}.
+     */
+    public static final String APPLICATION_YAML = "application/yaml";
+
+    /**
+     * Mediatype for YAML, not defined in {@link javax.ws.rs.core.MediaType}.
+     */
+    public static final MediaType APPLICATION_YAML_TYPE = new MediaType("application", "yaml");
+
+    /**
+     * Mediatype for multipart/mixed, not defined in {@link javax.ws.rs.core.MediaType}.
+     */
+    public static final String MULTIPART_MIXED = "multipart/mixed";
+
+    /**
+     * The boundary parameter name for multipart, not defined in {@link javax.ws.rs.core.MediaType}.
+     */
+    public static final String BOUNDARY_PARAMETER = "boundary";
+
+    /**
+     * Builds Content-Type string for multipart/mixed and the given boundary.
+     * 
+     * @param boundary multipart boundary value
+     * @return multipart/mixed Content-Type string, with given boundary
+     */
+    public static String multipartMixedWith(final String boundary) {
+        return MULTIPART_MIXED + ";" + BOUNDARY_PARAMETER + "=" + boundary;
+    }
+
+    /**
      * Allows the client to specify a preference for the result to be returned from the server.
      * <a href="http://msdn.microsoft.com/en-us/library/hh537533.aspx">More information</a>.
      *

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchItem.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchItem.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchItem.java
new file mode 100644
index 0000000..561ac6a
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchItem.java
@@ -0,0 +1,57 @@
+/*
+ * 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.common.rest.api.batch;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public abstract class BatchItem implements Serializable {
+
+    private static final long serialVersionUID = -1393976266651766259L;
+
+    private final Map<String, List<Object>> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+
+    private String content;
+
+    public Map<String, List<Object>> getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(final Map<String, List<Object>> headers) {
+        this.headers.clear();
+        this.headers.putAll(headers);
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(final String content) {
+        this.content = content;
+    }
+
+    @Override
+    public String toString() {
+        return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadGenerator.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadGenerator.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadGenerator.java
new file mode 100644
index 0000000..18d5f64
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadGenerator.java
@@ -0,0 +1,78 @@
+/*
+ * 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.common.rest.api.batch;
+
+import java.util.List;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeConstants;
+
+public final class BatchPayloadGenerator {
+
+    private static final String HTTP_1_1 = "HTTP/1.1";
+
+    public static <T extends BatchItem> String generate(final List<T> items, final String boundary) {
+        StringBuilder payload = new StringBuilder();
+
+        items.forEach(item -> {
+            payload.append(boundary).append(SyncopeConstants.CRLF);
+            payload.append(HttpHeaders.CONTENT_TYPE).append(": ").append("application/http").append('\n');
+            payload.append("Content-Transfer-Encoding: binary").append('\n');
+            payload.append(SyncopeConstants.CRLF);
+
+            if (item instanceof BatchRequestItem) {
+                BatchRequestItem bri = BatchRequestItem.class.cast(item);
+                payload.append(bri.getMethod()).append(' ').append(bri.getRequestURI());
+                if (bri.getQueryString() != null) {
+                    payload.append('?').append(bri.getQueryString());
+                }
+                payload.append(' ').append(HTTP_1_1).append('\n');
+            }
+
+            if (item instanceof BatchResponseItem) {
+                BatchResponseItem bri = BatchResponseItem.class.cast(item);
+                payload.append(HTTP_1_1).append(' ').
+                        append(bri.getStatus()).append(' ').
+                        append(Response.Status.fromStatusCode(bri.getStatus()).getReasonPhrase()).
+                        append('\n');
+            }
+
+            if (item.getHeaders() != null && !item.getHeaders().isEmpty()) {
+                item.getHeaders().forEach((key, values) -> {
+                    values.forEach(value -> {
+                        payload.append(key).append(": ").append(value).append('\n');
+                    });
+                });
+                payload.append(SyncopeConstants.CRLF);
+            }
+
+            if (item.getContent() != null) {
+                payload.append(item.getContent()).append('\n');
+            }
+        });
+
+        payload.append(boundary).append(SyncopeConstants.DOUBLE_DASH).append('\n');
+
+        return payload.toString();
+    }
+
+    private BatchPayloadGenerator() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLine.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLine.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLine.java
new file mode 100644
index 0000000..aa2cf41
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLine.java
@@ -0,0 +1,53 @@
+/*
+ * 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.common.rest.api.batch;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+public class BatchPayloadLine {
+
+    private final int lineNumber;
+
+    private final String content;
+
+    public BatchPayloadLine(final String content, final int lineNumber) {
+        this.content = content;
+        this.lineNumber = lineNumber;
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    @Override
+    public String toString() {
+        return content;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        return EqualsBuilder.reflectionEquals(this, obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return HashCodeBuilder.reflectionHashCode(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLineReader.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLineReader.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLineReader.java
new file mode 100644
index 0000000..b4a4d9f
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadLineReader.java
@@ -0,0 +1,193 @@
+/*
+ * 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.common.rest.api.batch;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+public class BatchPayloadLineReader implements AutoCloseable {
+
+    private static final byte CR = '\r';
+
+    private static final byte LF = '\n';
+
+    private static final int EOF = -1;
+
+    private static final int BUFFER_SIZE = 8192;
+
+    private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
+
+    private final ReadState readState = new ReadState();
+
+    private final InputStream in;
+
+    private final MediaType multipartMixed;
+
+    private final byte[] buffer = new byte[BUFFER_SIZE];
+
+    private Charset currentCharset = DEFAULT_CHARSET;
+
+    private String currentBoundary = null;
+
+    private int offset = 0;
+
+    private int limit = 0;
+
+    public BatchPayloadLineReader(final InputStream in, final MediaType multipartMixed) {
+        this.in = in;
+        this.multipartMixed = multipartMixed;
+    }
+
+    @Override
+    public void close() throws IOException {
+        in.close();
+    }
+
+    private boolean isBoundary(final String currentLine) {
+        return (currentBoundary + SyncopeConstants.CRLF).equals(currentLine)
+                || (currentBoundary + SyncopeConstants.DOUBLE_DASH + SyncopeConstants.CRLF).equals(currentLine);
+    }
+
+    private int fillBuffer() throws IOException {
+        limit = in.read(buffer, 0, buffer.length);
+        offset = 0;
+
+        return limit;
+    }
+
+    private String readLine() throws IOException {
+        if (limit == EOF) {
+            return null;
+        }
+
+        ByteBuffer innerBuffer = ByteBuffer.allocate(BUFFER_SIZE);
+        // EOF will be considered as line ending
+        boolean foundLineEnd = false;
+
+        while (!foundLineEnd) {
+            // Is buffer refill required?
+            if (limit == offset && fillBuffer() == EOF) {
+                foundLineEnd = true;
+            }
+
+            if (!foundLineEnd) {
+                byte currentChar = buffer[offset++];
+                if (!innerBuffer.hasRemaining()) {
+                    innerBuffer.flip();
+                    ByteBuffer tmp = ByteBuffer.allocate(innerBuffer.limit() * 2);
+                    tmp.put(innerBuffer);
+                    innerBuffer = tmp;
+                }
+                innerBuffer.put(currentChar);
+
+                if (currentChar == LF) {
+                    foundLineEnd = true;
+                } else if (currentChar == CR) {
+                    foundLineEnd = true;
+
+                    // Check next byte. Consume \n if available
+                    // Is buffer refill required?
+                    if (limit == offset) {
+                        fillBuffer();
+                    }
+
+                    // Check if there is at least one character
+                    if (limit != EOF && buffer[offset] == LF) {
+                        innerBuffer.put(LF);
+                        offset++;
+                    }
+                }
+            }
+        }
+
+        if (innerBuffer.position() == 0) {
+            return null;
+        } else {
+            String currentLine = new String(innerBuffer.array(), 0, innerBuffer.position(),
+                    readState.isReadBody() ? currentCharset : DEFAULT_CHARSET);
+
+            if (currentLine.startsWith(HttpHeaders.CONTENT_TYPE)) {
+                String charsetString = multipartMixed.getParameters().get(MediaType.CHARSET_PARAMETER);
+                currentCharset = charsetString == null
+                        ? DEFAULT_CHARSET
+                        : Charset.forName(charsetString);
+
+                currentBoundary = SyncopeConstants.DOUBLE_DASH + multipartMixed.getParameters().
+                        get(RESTHeaders.BOUNDARY_PARAMETER);
+            } else if (SyncopeConstants.CRLF.equals(currentLine)) {
+                readState.foundLinebreak();
+            } else if (isBoundary(currentLine)) {
+                readState.foundBoundary();
+            }
+
+            return currentLine;
+        }
+    }
+
+    public List<BatchPayloadLine> read() throws IOException {
+        List<BatchPayloadLine> result = new ArrayList<>();
+
+        String currentLine = readLine();
+        if (currentLine != null) {
+            currentBoundary = currentLine.trim();
+            int counter = 1;
+            result.add(new BatchPayloadLine(currentLine, counter++));
+
+            while ((currentLine = readLine()) != null) {
+                result.add(new BatchPayloadLine(currentLine, counter++));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Read state indicator (whether currently the {@code body} or {@code header} part is read).
+     */
+    private static class ReadState {
+
+        private int state = 0;
+
+        public void foundLinebreak() {
+            state++;
+        }
+
+        public void foundBoundary() {
+            state = 0;
+        }
+
+        public boolean isReadBody() {
+            return state >= 2;
+        }
+
+        @Override
+        public String toString() {
+            return String.valueOf(state);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadParser.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadParser.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadParser.java
new file mode 100644
index 0000000..a558db1
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchPayloadParser.java
@@ -0,0 +1,208 @@
+/*
+ * 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.common.rest.api.batch;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.MediaType;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.SerializationUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class BatchPayloadParser {
+
+    private static final Logger LOG = LoggerFactory.getLogger(BatchPayloadParser.class);
+
+    private static final Pattern PATTERN_LAST_CRLF = Pattern.compile("(.*)\\r\\n\\s*", Pattern.DOTALL);
+
+    private static final Pattern PATTERN_HEADER_LINE = Pattern.compile("((?:\\w|[!#$%\\&'*+\\-.^`|~])+):\\s?(.*)\\s*");
+
+    private static final Pattern PATTERN_BLANK_LINE = Pattern.compile("\\s*\r?\n\\s*");
+
+    private static final String[] HTTP_METHODS = {
+        HttpMethod.DELETE,
+        HttpMethod.PATCH,
+        HttpMethod.POST,
+        HttpMethod.PUT
+    };
+
+    private static BatchPayloadLine removeEndingCRLF(final BatchPayloadLine line) {
+        Matcher matcher = PATTERN_LAST_CRLF.matcher(line.toString());
+        return matcher.matches()
+                ? new BatchPayloadLine(matcher.group(1), line.getLineNumber())
+                : line;
+    }
+
+    private static void removeEndingCRLFFromList(final List<BatchPayloadLine> lines) {
+        if (!lines.isEmpty()) {
+            BatchPayloadLine lastLine = lines.remove(lines.size() - 1);
+            lines.add(removeEndingCRLF(lastLine));
+        }
+    }
+
+    private static List<List<BatchPayloadLine>> split(final List<BatchPayloadLine> lines, final String boundary) {
+        List<List<BatchPayloadLine>> messageParts = new ArrayList<>();
+        List<BatchPayloadLine> currentPart = new ArrayList<>();
+        boolean isEndReached = false;
+
+        String quotedBoundary = Pattern.quote(boundary);
+        Pattern boundaryDelimiterPattern = Pattern.compile("--" + quotedBoundary + "--\\s*");
+        Pattern boundaryPattern = Pattern.compile("--" + quotedBoundary + "\\s*");
+
+        for (BatchPayloadLine line : lines) {
+            if (boundaryDelimiterPattern.matcher(line.toString()).matches()) {
+                removeEndingCRLFFromList(currentPart);
+                messageParts.add(currentPart);
+                isEndReached = true;
+            } else if (boundaryPattern.matcher(line.toString()).matches()) {
+                removeEndingCRLFFromList(currentPart);
+                messageParts.add(currentPart);
+                currentPart = new ArrayList<>();
+            } else {
+                currentPart.add(line);
+            }
+
+            if (isEndReached) {
+                break;
+            }
+        }
+
+        // Remove preamble
+        if (!messageParts.isEmpty()) {
+            messageParts.remove(0);
+        }
+
+        if (!isEndReached) {
+            int lineNumber = lines.isEmpty() ? 0 : lines.get(0).getLineNumber();
+            throw new IllegalArgumentException("Missing close boundary delimiter around line " + lineNumber);
+        }
+
+        return messageParts;
+    }
+
+    private static void consumeHeaders(final List<BatchPayloadLine> bodyPart, final BatchItem item) {
+        Map<String, List<Object>> headers = new HashMap<>();
+
+        boolean isHeader = true;
+        for (Iterator<BatchPayloadLine> itor = bodyPart.iterator(); itor.hasNext() && isHeader;) {
+            BatchPayloadLine currentLine = itor.next();
+
+            Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine.toString());
+            if (headerMatcher.matches() && headerMatcher.groupCount() == 2) {
+                itor.remove();
+            } else {
+                isHeader = false;
+            }
+        }
+        consumeBlankLine(bodyPart);
+
+        isHeader = true;
+        for (Iterator<BatchPayloadLine> itor = bodyPart.iterator(); itor.hasNext() && isHeader;) {
+            BatchPayloadLine currentLine = itor.next();
+
+            if (currentLine.toString().contains("HTTP/1.1")) {
+                itor.remove();
+
+                if (ArrayUtils.contains(HTTP_METHODS, StringUtils.substringBefore(currentLine.toString(), " "))
+                        && item instanceof BatchRequestItem) {
+
+                    BatchRequestItem bri = BatchRequestItem.class.cast(item);
+                    String[] parts = currentLine.toString().split(" ");
+                    bri.setMethod(parts[0]);
+                    String[] target = parts[1].split("\\?");
+                    bri.setRequestURI(target[0]);
+                    if (target.length > 1) {
+                        bri.setQueryString(target[1]);
+                    }
+                } else if (item instanceof BatchResponseItem) {
+                    BatchResponseItem bri = BatchResponseItem.class.cast(item);
+                    bri.setStatus(Integer.valueOf(StringUtils.substringBefore(
+                            StringUtils.substringAfter(currentLine.toString(), " "), " ").trim()));
+                }
+            } else {
+                Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine.toString());
+                if (headerMatcher.matches() && headerMatcher.groupCount() == 2) {
+                    itor.remove();
+
+                    String headerName = headerMatcher.group(1).trim();
+                    String headerValue = headerMatcher.group(2).trim();
+
+                    List<Object> header = headers.get(headerName);
+                    if (header == null) {
+                        header = new ArrayList<>();
+                        headers.put(headerName, header);
+                    }
+                    header.addAll(Stream.of(headerValue.split(",")).map(String::trim).collect(Collectors.toList()));
+                } else {
+                    isHeader = false;
+                }
+            }
+        }
+        consumeBlankLine(bodyPart);
+
+        item.setHeaders(headers);
+    }
+
+    private static void consumeBlankLine(final List<BatchPayloadLine> bodyPart) {
+        if (!bodyPart.isEmpty() && PATTERN_BLANK_LINE.matcher(bodyPart.get(0).toString()).matches()) {
+            bodyPart.remove(0);
+        }
+    }
+
+    public static <T extends BatchItem> List<T> parse(
+            final InputStream in,
+            final MediaType multipartMixed,
+            final T template) throws IOException {
+
+        List<BatchPayloadLine> lines;
+        try (BatchPayloadLineReader lineReader = new BatchPayloadLineReader(in, multipartMixed)) {
+            lines = lineReader.read();
+        }
+
+        return split(lines, multipartMixed.getParameters().get(RESTHeaders.BOUNDARY_PARAMETER)).stream().
+                map(bodyPart -> {
+                    LOG.debug("Body part:\n{}", bodyPart);
+
+                    T item = SerializationUtils.clone(template);
+
+                    consumeHeaders(bodyPart, item);
+                    item.setContent(
+                            bodyPart.stream().map(BatchPayloadLine::toString).collect(Collectors.joining()));
+
+                    return item;
+                }).collect(Collectors.toList());
+    }
+
+    private BatchPayloadParser() {
+        // private constructor for static utility class
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchRequestItem.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchRequestItem.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchRequestItem.java
new file mode 100644
index 0000000..834b0fe
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchRequestItem.java
@@ -0,0 +1,54 @@
+/*
+ * 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.common.rest.api.batch;
+
+public class BatchRequestItem extends BatchItem {
+
+    private static final long serialVersionUID = -986002485818968262L;
+
+    private String method;
+
+    private String requestURI;
+
+    private String queryString;
+
+    public String getMethod() {
+        return method;
+    }
+
+    public void setMethod(final String method) {
+        this.method = method;
+    }
+
+    public String getRequestURI() {
+        return requestURI;
+    }
+
+    public void setRequestURI(final String requestURI) {
+        this.requestURI = requestURI;
+    }
+
+    public String getQueryString() {
+        return queryString;
+    }
+
+    public void setQueryString(final String queryString) {
+        this.queryString = queryString;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchResponseItem.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchResponseItem.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchResponseItem.java
new file mode 100644
index 0000000..e989b65
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/batch/BatchResponseItem.java
@@ -0,0 +1,34 @@
+/*
+ * 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.common.rest.api.batch;
+
+public class BatchResponseItem extends BatchItem {
+
+    private static final long serialVersionUID = -2163506313221985565L;
+
+    private int status;
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(final int status) {
+        this.status = status;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AccessTokenService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AccessTokenService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AccessTokenService.java
index e9ae07b..e26b2e8 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AccessTokenService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AccessTokenService.java
@@ -35,7 +35,6 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AccessTokenTO;
 import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -67,7 +66,7 @@ public interface AccessTokenService extends JAXRSService {
     })
     @POST
     @Path("login")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response login();
 
     /**
@@ -89,7 +88,7 @@ public interface AccessTokenService extends JAXRSService {
                         description = "Expiration of the refreshed JWT") }))
     @POST
     @Path("refresh")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response refresh();
 
     /**
@@ -101,7 +100,7 @@ public interface AccessTokenService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @POST
     @Path("logout")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void logout();
 
     /**
@@ -114,8 +113,8 @@ public interface AccessTokenService extends JAXRSService {
         @SecurityRequirement(name = "BasicAuthentication"),
         @SecurityRequirement(name = "Bearer") })
     @GET
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     PagedResult<AccessTokenTO> list(@BeanParam AccessTokenQuery query);
 
     /**
@@ -130,6 +129,6 @@ public interface AccessTokenService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(@PathParam("key") String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
index e8169b1..f9b6287 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyObjectService.java
@@ -38,7 +38,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.PagedResult;
@@ -105,8 +104,8 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
                         description = "Allows the server to inform the "
                         + "client about the fact that a specified preference was applied") }))
     @POST
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response create(@NotNull AnyObjectTO anyObjectTO);
 
     /**
@@ -148,8 +147,8 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
                 + " date of the entity") })
     @PATCH
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response update(@NotNull AnyObjectPatch anyObjectPatch);
 
     /**
@@ -191,7 +190,7 @@ public interface AnyObjectService extends AnyService<AnyObjectTO> {
                 + " date of the entity") })
     @PUT
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response update(@NotNull AnyObjectTO anyObjectTO);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
index 73cd69f..7bb78d8 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyService.java
@@ -39,7 +39,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AssociationPatch;
 import org.apache.syncope.common.lib.patch.DeassociationPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -68,7 +67,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      */
     @GET
     @Path("{key}/{schemaType}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Set<AttrTO> read(@NotNull @PathParam("key") String key, @NotNull @PathParam("schemaType") SchemaType schemaType);
 
     /**
@@ -84,7 +83,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      */
     @GET
     @Path("{key}/{schemaType}/{schema}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     AttrTO read(
             @NotNull @PathParam("key") String key,
             @NotNull @PathParam("schemaType") SchemaType schemaType,
@@ -98,7 +97,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      */
     @GET
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     TO read(@NotNull @PathParam("key") String key);
 
     /**
@@ -108,7 +107,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
      * @return paged list of any objects matching the given query
      */
     @GET
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     PagedResult<TO> search(@BeanParam AnyQuery anyQuery);
 
     /**
@@ -123,8 +122,8 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
             @Schema(type = "string"))
     @PUT
     @Path("{key}/{schemaType}/{schema}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response update(
             @NotNull @PathParam("key") String key,
             @NotNull @PathParam("schemaType") SchemaType schemaType,
@@ -141,7 +140,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{key}/{schemaType}/{schema}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(
             @NotNull @PathParam("key") String key,
             @NotNull @PathParam("schemaType") SchemaType schemaType,
@@ -184,7 +183,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
                 + " date of the entity") })
     @DELETE
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response delete(@NotNull @PathParam("key") String key);
 
     /**
@@ -227,8 +226,8 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
                 + " date of the entity") })
     @POST
     @Path("{key}/deassociate/{action}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response deassociate(@NotNull DeassociationPatch patch);
 
     /**
@@ -271,8 +270,8 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
                 + " date of the entity") })
     @POST
     @Path("{key}/associate/{action}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response associate(@NotNull AssociationPatch patch);
 
     /**
@@ -303,7 +302,7 @@ public interface AnyService<TO extends AnyTO> extends JAXRSService {
                         + "client about the fact that a specified preference was applied")) })
     @POST
     @Path("bulk")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response bulk(@NotNull BulkAction bulkAction);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeClassService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeClassService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeClassService.java
index 7752def..6b90a1b 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeClassService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeClassService.java
@@ -40,7 +40,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
@@ -60,7 +59,7 @@ public interface AnyTypeClassService extends JAXRSService {
      * @return list of all anyTypeClasss.
      */
     @GET
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     List<AnyTypeClassTO> list();
 
     /**
@@ -71,7 +70,7 @@ public interface AnyTypeClassService extends JAXRSService {
      */
     @GET
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     AnyTypeClassTO read(@NotNull @PathParam("key") String key);
 
     /**
@@ -90,8 +89,8 @@ public interface AnyTypeClassService extends JAXRSService {
                         @Schema(type = "string"),
                         description = "URL of the entity created") }))
     @POST
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response create(@NotNull AnyTypeClassTO anyTypeClassTO);
 
     /**
@@ -105,8 +104,8 @@ public interface AnyTypeClassService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
     @Path("{key}")
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void update(@NotNull AnyTypeClassTO anyTypeClassTO);
 
     /**
@@ -118,6 +117,6 @@ public interface AnyTypeClassService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(@NotNull @PathParam("key") String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeService.java
index 53b8b87..e5db506 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AnyTypeService.java
@@ -40,7 +40,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
@@ -60,7 +59,7 @@ public interface AnyTypeService extends JAXRSService {
      * @return list of all anyTypes.
      */
     @GET
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     List<AnyTypeTO> list();
 
     /**
@@ -71,7 +70,7 @@ public interface AnyTypeService extends JAXRSService {
      */
     @GET
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     AnyTypeTO read(@NotNull @PathParam("key") String key);
 
     /**
@@ -90,7 +89,7 @@ public interface AnyTypeService extends JAXRSService {
                         @Schema(type = "string"),
                         description = "URL of the entity created") }))
     @POST
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response create(@NotNull AnyTypeTO anyTypeTO);
 
     /**
@@ -104,7 +103,7 @@ public interface AnyTypeService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
     @Path("{key}")
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void update(@NotNull AnyTypeTO anyTypeTO);
 
     /**
@@ -116,6 +115,6 @@ public interface AnyTypeService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(@NotNull @PathParam("key") String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
index cec7938..e8e8b19 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ApplicationService.java
@@ -40,7 +40,6 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.ApplicationTO;
 import org.apache.syncope.common.lib.to.PrivilegeTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
@@ -61,7 +60,7 @@ public interface ApplicationService extends JAXRSService {
      * @return list of all applications.
      */
     @GET
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     List<ApplicationTO> list();
 
     /**
@@ -72,7 +71,7 @@ public interface ApplicationService extends JAXRSService {
      */
     @GET
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     ApplicationTO read(@NotNull @PathParam("key") String key);
 
     /**
@@ -83,7 +82,7 @@ public interface ApplicationService extends JAXRSService {
      */
     @GET
     @Path("privileges/{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     PrivilegeTO readPrivilege(@NotNull @PathParam("key") String key);
 
     /**
@@ -102,8 +101,8 @@ public interface ApplicationService extends JAXRSService {
                         @Schema(type = "string"),
                         description = "URL of the entity created") }))
     @POST
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     Response create(@NotNull ApplicationTO applicationTO);
 
     /**
@@ -117,8 +116,8 @@ public interface ApplicationService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
     @Path("{key}")
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void update(@NotNull ApplicationTO applicationTO);
 
     /**
@@ -130,6 +129,6 @@ public interface ApplicationService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{key}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(@NotNull @PathParam("key") String key);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9250efa/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConfigurationService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConfigurationService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConfigurationService.java
index e2193aa..0287154 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConfigurationService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConfigurationService.java
@@ -37,8 +37,8 @@ import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**
  * REST operations for configuration.
@@ -65,7 +65,7 @@ public interface ConfigurationService extends JAXRSService {
      * @return all configuration parameters
      */
     @GET
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     List<AttrTO> list();
 
     /**
@@ -76,7 +76,7 @@ public interface ConfigurationService extends JAXRSService {
      */
     @GET
     @Path("{schema}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     AttrTO get(@NotNull @PathParam("schema") String schema);
 
     /**
@@ -90,8 +90,8 @@ public interface ConfigurationService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @PUT
     @Path("{schema}")
-    @Consumes({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void set(@NotNull AttrTO value);
 
     /**
@@ -103,6 +103,6 @@ public interface ConfigurationService extends JAXRSService {
             @ApiResponse(responseCode = "204", description = "Operation was successful"))
     @DELETE
     @Path("{schema}")
-    @Produces({ MediaType.APPLICATION_JSON, SyncopeConstants.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
     void delete(@NotNull @PathParam("schema") String schema);
 }