You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2014/04/15 16:33:32 UTC

git commit: [OLINGO-246] provided batch integration; still missing internak batch reference support

Repository: olingo-odata4
Updated Branches:
  refs/heads/master 542541dd4 -> 68f27d57a


[OLINGO-246] provided batch integration; still missing internak batch reference support


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/68f27d57
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/68f27d57
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/68f27d57

Branch: refs/heads/master
Commit: 68f27d57a1d7dc10b1382f3b44b327ce052bc0b6
Parents: 542541d
Author: fmartelli <fa...@gmail.com>
Authored: Tue Apr 15 16:33:19 2014 +0200
Committer: fmartelli <fa...@gmail.com>
Committed: Tue Apr 15 16:33:19 2014 +0200

----------------------------------------------------------------------
 .../org/apache/olingo/fit/AbstractServices.java | 235 +++++-
 .../olingo/fit/utils/AbstractUtilities.java     |  22 +
 .../org/apache/olingo/fit/utils/Constants.java  |   5 +
 .../api/communication/header/HeaderName.java    |   2 +-
 .../request/cud/ODataEntityCreateRequest.java   |   3 +-
 .../request/cud/ODataEntityUpdateRequest.java   |   3 +-
 .../request/retrieve/ODataRetrieveRequest.java  |   4 +-
 .../request/batch/ODataBatchRequestImpl.java    |   5 +-
 .../request/batch/ODataBatchUtilities.java      |   3 +-
 .../cud/ODataEntityCreateRequestImpl.java       |   3 +-
 .../cud/ODataEntityUpdateRequestImpl.java       |   4 +-
 .../retrieve/AbstractODataRetrieveRequest.java  |   3 +-
 .../client/core/it/v3/BatchTestITCase.java      | 806 ++++++++++---------
 13 files changed, 688 insertions(+), 410 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
index 00e22f3..4f9d970 100644
--- a/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
+++ b/fit/src/main/java/org/apache/olingo/fit/AbstractServices.java
@@ -18,12 +18,12 @@
  */
 package org.apache.olingo.fit;
 
-import org.apache.olingo.commons.api.data.Container;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
 import org.apache.olingo.commons.api.data.Feed;
 import org.apache.olingo.commons.api.data.Link;
 import org.apache.olingo.commons.api.data.Property;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.core.data.AtomFeedImpl;
 import org.apache.olingo.commons.core.data.LinkImpl;
 import org.apache.olingo.fit.metadata.Metadata;
@@ -33,24 +33,28 @@ import org.apache.olingo.fit.utils.ConstantKey;
 import org.apache.olingo.fit.utils.Constants;
 import org.apache.olingo.fit.utils.DataBinder;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.util.AbstractMap;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import javax.mail.Header;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMultipart;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.DefaultValue;
@@ -65,12 +69,20 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.jaxrs.ext.multipart.Attachment;
+import org.apache.cxf.jaxrs.ext.multipart.Multipart;
+import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
+import org.apache.olingo.commons.api.data.Container;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.core.data.AtomEntryImpl;
 import org.apache.olingo.commons.core.data.AtomPropertyImpl;
 import org.apache.olingo.commons.core.data.AtomSerializer;
@@ -81,13 +93,13 @@ import org.apache.olingo.commons.core.data.PrimitiveValueImpl;
 import org.apache.olingo.fit.metadata.EntitySet;
 import org.apache.olingo.fit.metadata.EntityType;
 import org.apache.olingo.fit.metadata.NavigationProperty;
+import org.apache.olingo.fit.methods.MERGE;
+import org.apache.olingo.fit.methods.PATCH;
+import org.apache.olingo.fit.serializer.FITAtomDeserializer;
 import org.apache.olingo.fit.utils.Accept;
 import org.apache.olingo.fit.utils.FSManager;
 
 import org.apache.olingo.fit.utils.Commons;
-import org.apache.olingo.fit.methods.MERGE;
-import org.apache.olingo.fit.methods.PATCH;
-import org.apache.olingo.fit.serializer.FITAtomDeserializer;
 import org.apache.olingo.fit.utils.AbstractJSONUtilities;
 import org.apache.olingo.fit.utils.AbstractUtilities;
 import org.apache.olingo.fit.utils.AbstractXMLUtilities;
@@ -102,6 +114,10 @@ public abstract class AbstractServices {
    */
   protected static final Logger LOG = LoggerFactory.getLogger(AbstractServices.class);
 
+  private Pattern requestPatter = Pattern.compile("(.*) (http://.*) HTTP/.*");
+
+  private static final String boundary = "batch_243234_25424_ef_892u748";
+
   protected final ODataServiceVersion version;
 
   protected final AbstractXMLUtilities xml;
@@ -174,6 +190,197 @@ public abstract class AbstractServices {
     }
   }
 
+  @POST
+  @Path("/$batch")
+  @Consumes("multipart/mixed")
+  @Produces("application/octet-stream; boundary=" + boundary)
+  public Response batch(final @Multipart MultipartBody attachment) {
+    try {
+      return xml.createBatchResponse(exploreMultipart(attachment.getAllAttachments(), boundary), boundary);
+    } catch (IOException e) {
+      return xml.createFaultResponse(Accept.XML.toString(version), e);
+    }
+  }
+
+  private Response bodyPartRequest(final MimeBodyPart body) throws Exception {
+
+    @SuppressWarnings("unchecked")
+    final Enumeration<Header> en = (Enumeration<Header>) body.getAllHeaders();
+
+    Header header = en.nextElement();
+    final String request = header.getName() + ":" + header.getValue();
+    final Matcher matcher = requestPatter.matcher(request);
+
+    if (matcher.find()) {
+      final MultivaluedMap<String, String> headers = new MultivaluedHashMap<String, String>();
+
+      while (en.hasMoreElements()) {
+        header = en.nextElement();
+        headers.putSingle(header.getName(), header.getValue());
+      }
+
+      String method = matcher.group(1);
+      if ("PATCH".equals(method) || "MERGE".equals(method)) {
+        headers.putSingle("X-HTTP-METHOD", method);
+        method = "POST";
+      }
+
+      final String url = matcher.group(2);
+
+      final WebClient client = WebClient.create(url);
+
+      client.headers(headers);
+
+      return client.invoke(method, body.getDataHandler().getInputStream());
+    } else {
+      return null;
+    }
+  }
+
+  public InputStream exploreMultipart(final List<Attachment> attachments, final String boundary) throws IOException {
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    Response res = null;
+    try {
+      for (Attachment obj : attachments) {
+        bos.write(("--" + boundary).getBytes());
+        bos.write(Constants.CRLF);
+
+        final Object content = obj.getDataHandler().getContent();
+        if (content instanceof MimeMultipart) {
+          final String cboundary = "changeset_" + UUID.randomUUID().toString();
+          bos.write(("Content-Type: multipart/mixed;boundary=" + cboundary).getBytes());
+          bos.write(Constants.CRLF);
+          bos.write(Constants.CRLF);
+
+          final ByteArrayOutputStream chbos = new ByteArrayOutputStream();
+          String lastContebtID = null;
+          try {
+            for (int i = 0; i < ((MimeMultipart) content).getCount(); i++) {
+              final MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) content).getBodyPart(i);
+              lastContebtID = part.getContentID();
+              addChangesetItemIntro(chbos, lastContebtID, cboundary);
+
+              res = bodyPartRequest(new MimeBodyPart(part.getInputStream()));
+              if (res.getStatus() > 400) {
+                throw new Exception("Failure processing changeset");
+              }
+
+              addSingleBatchResponse(res, lastContebtID, chbos);
+            }
+            bos.write(chbos.toByteArray());
+            IOUtils.closeQuietly(chbos);
+
+            bos.write(("--" + cboundary + "--").getBytes());
+            bos.write(Constants.CRLF);
+          } catch (Exception e) {
+            LOG.warn("While processing changeset", e);
+            IOUtils.closeQuietly(chbos);
+
+            addChangesetItemIntro(bos, lastContebtID, cboundary);
+            if (res == null || res.getStatus() < 400) {
+              addErrorBatchResponse(e, "1", bos);
+            } else {
+              addSingleBatchResponse(res, lastContebtID, bos);
+            }
+
+            bos.write(("--" + cboundary + "--").getBytes());
+            bos.write(Constants.CRLF);
+          }
+        } else {
+          addItemIntro(bos);
+
+          res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()));
+
+          if (res.getStatus() > 400) {
+            throw new Exception("Failure processing changeset");
+          }
+
+          addSingleBatchResponse(res, bos);
+        }
+      }
+    } catch (Exception e) {
+      if (res == null || res.getStatus() < 400) {
+        addErrorBatchResponse(e, bos);
+      } else {
+        addSingleBatchResponse(res, bos);
+      }
+    }
+
+    bos.write(("--" + boundary + "--").getBytes());
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  private void addItemIntro(final ByteArrayOutputStream bos) throws IOException {
+    bos.write("Content-Type: application/http".getBytes());
+    bos.write(Constants.CRLF);
+    bos.write("Content-Transfer-Encoding: binary".getBytes());
+    bos.write(Constants.CRLF);
+    bos.write(Constants.CRLF);
+  }
+
+  private void addChangesetItemIntro(
+          final ByteArrayOutputStream bos, final String contentId, final String cboundary) throws IOException {
+    bos.write(("--" + cboundary).getBytes());
+    bos.write(Constants.CRLF);
+    bos.write(("Content-ID: " + contentId).getBytes());
+    bos.write(Constants.CRLF);
+    addItemIntro(bos);
+  }
+
+  private void addSingleBatchResponse(
+          final Response response, final ByteArrayOutputStream bos) throws IOException {
+    addSingleBatchResponse(response, null, bos);
+  }
+
+  private void addSingleBatchResponse(
+          final Response response, final String contentId, final ByteArrayOutputStream bos) throws IOException {
+    bos.write("HTTP/1.1 ".getBytes());
+    bos.write(String.valueOf(response.getStatusInfo().getStatusCode()).getBytes());
+    bos.write(" ".getBytes());
+    bos.write(response.getStatusInfo().getReasonPhrase().getBytes());
+    bos.write(Constants.CRLF);
+
+    for (Map.Entry<String, List<Object>> header : response.getHeaders().entrySet()) {
+      StringBuilder builder = new StringBuilder();
+      for (Object value : header.getValue()) {
+        if (builder.length() > 0) {
+          builder.append(", ");
+        }
+        builder.append(value.toString());
+      }
+      builder.insert(0, ": ").insert(0, header.getKey());
+      bos.write(builder.toString().getBytes());
+      bos.write(Constants.CRLF);
+    }
+
+    if (StringUtils.isNotBlank(contentId)) {
+      bos.write(("Content-ID: " + contentId).getBytes());
+      bos.write(Constants.CRLF);
+    }
+
+    bos.write(Constants.CRLF);
+
+    final Object entity = response.getEntity();
+    if (entity != null) {
+      bos.write(IOUtils.toByteArray((InputStream) entity));
+      bos.write(Constants.CRLF);
+    }
+
+    bos.write(Constants.CRLF);
+  }
+
+  private void addErrorBatchResponse(final Exception e, final ByteArrayOutputStream bos)
+          throws IOException {
+    addErrorBatchResponse(e, null, bos);
+  }
+
+  private void addErrorBatchResponse(final Exception e, final String contentId, final ByteArrayOutputStream bos)
+          throws IOException {
+    addSingleBatchResponse(xml.createFaultResponse(Accept.XML.toString(version), e), contentId, bos);
+  }
+
   @MERGE
   @Path("/{entitySetName}({entityId})")
   @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON})
@@ -386,7 +593,7 @@ public abstract class AbstractServices {
         } else {
           final Container<JSONEntryImpl> jcontainer =
                   mapper.readValue(IOUtils.toInputStream(entity), new TypeReference<JSONEntryImpl>() {
-                  });
+          });
 
           entry = (new DataBinder(version)).
                   getAtomEntry(jcontainer.getObject());
@@ -459,13 +666,13 @@ public abstract class AbstractServices {
               replaceAll("\"Salary\":[0-9]*,", "\"Salary\":0,").
               replaceAll("\"Title\":\".*\"", "\"Title\":\"[Sacked]\"").
               replaceAll("\\<d:Salary m:type=\"Edm.Int32\"\\>.*\\</d:Salary\\>",
-                      "<d:Salary m:type=\"Edm.Int32\">0</d:Salary>").
+              "<d:Salary m:type=\"Edm.Int32\">0</d:Salary>").
               replaceAll("\\<d:Title\\>.*\\</d:Title\\>", "<d:Title>[Sacked]</d:Title>");
 
       final FSManager fsManager = FSManager.instance(version);
       fsManager.putInMemory(IOUtils.toInputStream(newContent, "UTF-8"),
               fsManager.getAbsolutePath(Commons.getEntityBasePath("Person", entityId) + Constants.get(version,
-                              ConstantKey.ENTITY), utils.getKey()));
+              ConstantKey.ENTITY), utils.getKey()));
 
       return utils.getValue().createResponse(null, null, utils.getKey(), Response.Status.NO_CONTENT);
     } catch (Exception e) {
@@ -517,9 +724,9 @@ public abstract class AbstractServices {
         final Long newSalary = Long.valueOf(salaryMatcher.group(1)) + n;
         newContent = newContent.
                 replaceAll("\"Salary\":" + salaryMatcher.group(1) + ",",
-                        "\"Salary\":" + newSalary + ",").
+                "\"Salary\":" + newSalary + ",").
                 replaceAll("\\<d:Salary m:type=\"Edm.Int32\"\\>" + salaryMatcher.group(1) + "</d:Salary\\>",
-                        "<d:Salary m:type=\"Edm.Int32\">" + newSalary + "</d:Salary>");
+                "<d:Salary m:type=\"Edm.Int32\">" + newSalary + "</d:Salary>");
       }
 
       FSManager.instance(version).putInMemory(IOUtils.toInputStream(newContent, "UTF-8"),
@@ -651,7 +858,7 @@ public abstract class AbstractServices {
 
           mapper.writeValue(
                   writer, new JsonFeedContainer<JSONFeedImpl>(container.getContextURL(), container.getMetadataETag(),
-                          new DataBinder(version).getJsonFeed(container.getObject())));
+                  new DataBinder(version).getJsonFeed(container.getObject())));
         }
 
         return xml.createResponse(new ByteArrayInputStream(content.toByteArray()),
@@ -833,7 +1040,7 @@ public abstract class AbstractServices {
         final ObjectMapper mapper = Commons.getJsonMapper(version);
         mapper.writeValue(
                 writer, new JsonEntryContainer<JSONEntryImpl>(container.getContextURL(), container.getMetadataETag(),
-                        (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject())));
+                (new DataBinder(version)).getJsonEntry((AtomEntryImpl) container.getObject())));
       }
 
       return xml.createResponse(new ByteArrayInputStream(content.toByteArray()),

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
index 9c0ef94..8ef4472 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/AbstractUtilities.java
@@ -60,6 +60,21 @@ public abstract class AbstractUtilities {
 
   protected final static Pattern entityUriPattern = Pattern.compile(".*\\/.*\\(.*\\)");
 
+  /**
+   * Batch/Changeset content type.
+   */
+  public static final String MULTIPART_CONTENT_TYPE = "multipart/mixed";
+
+  /**
+   * Batch item content type.
+   */
+  public static final String ITEM_CONTENT_TYPE = "application/http";
+
+  /**
+   * Boundary key.
+   */
+  public static final String BOUNDARY = "boundary";
+
   public AbstractUtilities(final ODataServiceVersion version) throws Exception {
     this.version = version;
     this.fsManager = FSManager.instance(version);
@@ -399,6 +414,13 @@ public abstract class AbstractUtilities {
     return createResponse(entity, etag, accept, null);
   }
 
+  public Response createBatchResponse(final InputStream stream, final String boundary) {
+
+    final Response.ResponseBuilder builder = Response.accepted(stream);
+    builder.header(Constants.get(version, ConstantKey.ODATA_SERVICE_VERSION), version.toString() + ";");
+    return builder.build();
+  }
+
   public Response createResponse(
           final InputStream entity, final String etag, final Accept accept, final Response.Status status) {
     final Response.ResponseBuilder builder = Response.ok();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
index 5066cb0..975a537 100644
--- a/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/Constants.java
@@ -29,6 +29,11 @@ public class Constants {
 
   private final static Map<ConstantKey, String> constants = new EnumMap<ConstantKey, String>(ConstantKey.class);
 
+  /**
+   * CR/LF.
+   */
+  public static final byte[] CRLF = {13, 10};
+
   public static Charset encoding = Charset.forName("UTF-8");
 
   static {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/HeaderName.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/HeaderName.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/HeaderName.java
index 67b3043..a99c5ef 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/HeaderName.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/HeaderName.java
@@ -59,7 +59,7 @@ public enum HeaderName {
    * <li>multipart/mixed</li>
    * </ul>
    */
-  contentType("Content-Type", Arrays.asList(ODataServiceVersion.V30)),
+  contentType("Content-Type", Arrays.asList(ODataServiceVersion.V30, ODataServiceVersion.V40)),
   /**
    * This header is a custom HTTP header defined for protocol versioning purposes. This header MAY be present on any
    * request or response message.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityCreateRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityCreateRequest.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityCreateRequest.java
index 73471b9..ddd5975 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityCreateRequest.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityCreateRequest.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.client.api.communication.request.cud;
 
 import org.apache.olingo.client.api.communication.request.ODataBasicRequest;
+import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
 import org.apache.olingo.commons.api.format.ODataPubFormat;
@@ -29,5 +30,5 @@ import org.apache.olingo.commons.api.format.ODataPubFormat;
  * @param <E> concrete ODataEntity implementation
  */
 public interface ODataEntityCreateRequest<E extends CommonODataEntity>
-        extends ODataBasicRequest<ODataEntityCreateResponse<E>, ODataPubFormat> {
+        extends ODataBasicRequest<ODataEntityCreateResponse<E>, ODataPubFormat>, ODataBatchableRequest {
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityUpdateRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityUpdateRequest.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityUpdateRequest.java
index 3f02897..341028e 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityUpdateRequest.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/cud/ODataEntityUpdateRequest.java
@@ -19,6 +19,7 @@
 package org.apache.olingo.client.api.communication.request.cud;
 
 import org.apache.olingo.client.api.communication.request.ODataBasicRequest;
+import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
 import org.apache.olingo.commons.api.format.ODataPubFormat;
@@ -29,5 +30,5 @@ import org.apache.olingo.commons.api.format.ODataPubFormat;
  * @param <E> concrete ODataEntity implementation
  */
 public interface ODataEntityUpdateRequest<E extends CommonODataEntity>
-        extends ODataBasicRequest<ODataEntityUpdateResponse<E>, ODataPubFormat> {
+        extends ODataBasicRequest<ODataEntityUpdateResponse<E>, ODataPubFormat>, ODataBatchableRequest {
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/retrieve/ODataRetrieveRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/retrieve/ODataRetrieveRequest.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/retrieve/ODataRetrieveRequest.java
index 073f520..61e505c 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/retrieve/ODataRetrieveRequest.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/retrieve/ODataRetrieveRequest.java
@@ -19,11 +19,13 @@
 package org.apache.olingo.client.api.communication.request.retrieve;
 
 import org.apache.olingo.client.api.communication.request.ODataBasicRequest;
+import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.commons.api.format.Format;
 
 /**
  * This is an abstract representation of an OData retrieve query request returning one or more result item.
  */
-public interface ODataRetrieveRequest<V, T extends Format> extends ODataBasicRequest<ODataRetrieveResponse<V>, T> {
+public interface ODataRetrieveRequest<V, T extends Format>
+        extends ODataBasicRequest<ODataRetrieveResponse<V>, T>, ODataBatchableRequest {
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchRequestImpl.java
index 9ebf783..165a514 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchRequestImpl.java
@@ -151,7 +151,8 @@ public class ODataBatchRequestImpl extends AbstractODataStreamedRequest<ODataBat
   /**
    * Batch request payload management.
    */
-  public class BatchStreamManagerImpl extends AbstractODataStreamManager<ODataBatchResponse> {
+  public class BatchStreamManagerImpl extends AbstractODataStreamManager<ODataBatchResponse>
+          implements BatchStreamManager {
 
     /**
      * Batch request current item.
@@ -178,6 +179,7 @@ public class ODataBatchRequestImpl extends AbstractODataStreamedRequest<ODataBat
      *
      * @return ODataChangeset instance.
      */
+    @Override
     public ODataChangeset addChangeset() {
       closeCurrentItem();
 
@@ -197,6 +199,7 @@ public class ODataBatchRequestImpl extends AbstractODataStreamedRequest<ODataBat
      *
      * @return ODataRetrieve instance.
      */
+    @Override
     public ODataRetrieve addRetrieve() {
       closeCurrentItem();
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchUtilities.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchUtilities.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchUtilities.java
index 7474e13..1fc6d7b 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchUtilities.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/ODataBatchUtilities.java
@@ -148,8 +148,7 @@ public class ODataBatchUtilities {
         }
 
       } else {
-        for (int i = 0;
-                controller.isValidBatch() && controller.getBatchLineIterator().hasNext() && i < count; i++) {
+        for (int i = 0; controller.isValidBatch() && controller.getBatchLineIterator().hasNext() && i < count; i++) {
           currentLine = controller.getBatchLineIterator().nextLine();
         }
       }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
index 18485ae..cfd9089 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
@@ -25,7 +25,6 @@ import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.olingo.client.api.CommonODataClient;
-import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
 import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
@@ -44,7 +43,7 @@ import org.apache.olingo.commons.api.data.Entry;
  */
 public class ODataEntityCreateRequestImpl<E extends CommonODataEntity>
         extends AbstractODataBasicRequest<ODataEntityCreateResponse<E>, ODataPubFormat>
-        implements ODataEntityCreateRequest<E>, ODataBatchableRequest {
+        implements ODataEntityCreateRequest<E> {
 
   /**
    * Entity to be created.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
index 9a84de3..67be0d4 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
@@ -25,7 +25,6 @@ import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
 import org.apache.olingo.client.api.CommonODataClient;
-import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
 import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
@@ -39,11 +38,12 @@ import org.apache.olingo.commons.api.data.Entry;
 
 /**
  * This class implements an OData update request.
+ *
  * @param <E> concrete ODataEntity implementation
  */
 public class ODataEntityUpdateRequestImpl<E extends CommonODataEntity>
         extends AbstractODataBasicRequest<ODataEntityUpdateResponse<E>, ODataPubFormat>
-        implements ODataEntityUpdateRequest<E>, ODataBatchableRequest {
+        implements ODataEntityUpdateRequest<E> {
 
   /**
    * Changes to be applied.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/AbstractODataRetrieveRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/AbstractODataRetrieveRequest.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/AbstractODataRetrieveRequest.java
index 53e46c1..f1ae24b 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/AbstractODataRetrieveRequest.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/retrieve/AbstractODataRetrieveRequest.java
@@ -23,7 +23,6 @@ import java.net.URI;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.HttpClient;
 import org.apache.olingo.client.api.CommonODataClient;
-import org.apache.olingo.client.api.communication.request.ODataBatchableRequest;
 import org.apache.olingo.client.api.communication.request.retrieve.ODataRetrieveRequest;
 import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
 import org.apache.olingo.commons.api.format.Format;
@@ -36,7 +35,7 @@ import org.apache.olingo.client.core.communication.response.AbstractODataRespons
  */
 public abstract class AbstractODataRetrieveRequest<V, T extends Format>
         extends AbstractODataBasicRequest<ODataRetrieveResponse<V>, T>
-        implements ODataRetrieveRequest<V, T>, ODataBatchableRequest {
+        implements ODataRetrieveRequest<V, T> {
 
   /**
    * Private constructor.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/68f27d57/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java
index 8ee3efd..4365600 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/BatchTestITCase.java
@@ -18,389 +18,429 @@
  */
 package org.apache.olingo.client.core.it.v3;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Iterator;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import org.apache.http.HttpResponse;
+import org.apache.olingo.client.api.ODataBatchConstants;
+import org.apache.olingo.client.api.communication.request.ODataStreamManager;
+import org.apache.olingo.client.api.communication.request.batch.BatchStreamManager;
+import org.apache.olingo.client.api.communication.request.batch.ODataBatchRequest;
+import org.apache.olingo.client.api.communication.request.batch.ODataBatchResponseItem;
+import org.apache.olingo.client.api.communication.request.batch.ODataChangeset;
+import org.apache.olingo.client.api.communication.request.batch.ODataRetrieve;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
+import org.apache.olingo.client.api.communication.request.cud.v3.UpdateType;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.client.api.communication.response.ODataBatchResponse;
+import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
+import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse;
+import org.apache.olingo.client.api.communication.response.ODataResponse;
+import org.apache.olingo.client.api.uri.v3.URIBuilder;
+import org.apache.olingo.client.core.communication.request.AbstractODataStreamManager;
+import org.apache.olingo.client.core.communication.request.Wrapper;
+import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem;
+import org.apache.olingo.client.core.communication.request.batch.ODataRetrieveResponseItem;
+import org.apache.olingo.client.core.communication.request.retrieve.ODataEntityRequestImpl;
+import org.apache.olingo.client.core.communication.request.retrieve.ODataEntityRequestImpl.ODataEntityResponseImpl;
+import org.apache.olingo.client.core.uri.URIUtils;
+import org.apache.olingo.commons.api.domain.v3.ODataEntity;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.format.ODataPubFormat;
+import org.junit.Ignore;
+import org.junit.Test;
+
 public class BatchTestITCase extends AbstractTestITCase {
 
-//  private static String PREFIX = "!!PREFIX!!";
-//
-//  private static String SUFFIX = "!!SUFFIX!!";
-//
-//  private static int MAX = 10000;
-//
-//  @Test
-//  @Ignore
-//  public void stringStreaming() {
-//    final TestStreamManager streaming = new TestStreamManager();
-//
-//    new StreamingThread(streaming).start();
-//
-//    streaming.addObject((PREFIX + "\n").getBytes());
-//
-//    for (int i = 0; i <= MAX; i++) {
-//      streaming.addObject((i + ") send info\n").getBytes());
-//    }
-//
-//    streaming.addObject((SUFFIX).getBytes());
-//    streaming.finalizeBody();
-//  }
-//
-//  @Test
-//  @Ignore
-//  public void emptyBatchRequest() {
-//    // create your request
-//    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
-//
-//    final BatchStreamManager payload = request.execute();
-//    final ODataBatchResponse response = payload.getResponse();
-//
-//    assertEquals(202, response.getStatusCode());
-//    assertEquals("Accepted", response.getStatusMessage());
-//
-//    final Iterator<ODataBatchResponseItem> iter = response.getBody();
-//    assertFalse(iter.hasNext());
-//  }
-//
-//  @Test
-//  @Ignore
-//  public void changesetWithError() {
-//    // create your request
-//    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
-//
-//    final BatchStreamManager payload = request.execute();
-//    final ODataChangeset changeset = payload.addChangeset();
-//
-//    URIBuilder<?> targetURI;
-//    ODataEntityCreateRequest createReq;
-//
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
-//    for (int i = 1; i <= 2; i++) {
-//      // Create Customer into the changeset
-//      createReq = client.getCUDRequestFactory().getEntityCreateRequest(
-//              targetURI.build(),
-//              getSampleCustomerProfile(100 + i, "Sample customer", false));
-//      createReq.setFormat(ODataPubFormat.JSON);
-//      changeset.addRequest(createReq);
-//    }
-//
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("WrongEntitySet");
-//    createReq = client.getCUDRequestFactory().getEntityCreateRequest(
-//            targetURI.build(),
-//            getSampleCustomerProfile(105, "Sample customer", false));
-//    createReq.setFormat(ODataPubFormat.JSON);
-//    changeset.addRequest(createReq);
-//
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
-//    for (int i = 3; i <= 4; i++) {
-//      // Create Customer into the changeset
-//      createReq = client.getCUDRequestFactory().getEntityCreateRequest(
-//              targetURI.build(),
-//              getSampleCustomerProfile(100 + i, "Sample customer", false));
-//      createReq.setFormat(ODataPubFormat.ATOM);
-//      changeset.addRequest(createReq);
-//    }
-//
-//    final ODataBatchResponse response = payload.getResponse();
-//    assertEquals(202, response.getStatusCode());
-//    assertEquals("Accepted", response.getStatusMessage());
-//
-//    final Iterator<ODataBatchResponseItem> iter = response.getBody();
-//    final ODataChangesetResponseItem chgResponseItem = (ODataChangesetResponseItem) iter.next();
-//
-//    final ODataResponse res = chgResponseItem.next();
-//    assertEquals(404, res.getStatusCode());
-//    assertEquals("Not Found", res.getStatusMessage());
-//    assertEquals(Integer.valueOf(3), Integer.valueOf(
-//            res.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next()));
-//    assertFalse(chgResponseItem.hasNext());
-//  }
-//
-//  @Test
-//  @Ignore
-//  public void changesetWithReference() {
-//    // create your request
-//    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
-//    final BatchStreamManager streamManager = request.execute();
-//
-//    final ODataChangeset changeset = streamManager.addChangeset();
-//    ODataEntity customer = getSampleCustomerProfile(20, "sample customer", false);
-//
-//    URIBuilder<?> uriBuilder = client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("Customer");
-//
-//    // add create request
-//    final ODataEntityCreateRequest createReq =
-//            client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), customer);
-//
-//    changeset.addRequest(createReq);
-//
-//    // retrieve request reference
-//    int createRequestRef = changeset.getLastContentId();
-//
-//    // add update request: link CustomerInfo(17) to the new customer
-//    final ODataEntity customerChanges = client.getObjectFactory().newEntity(customer.getName());
-//    customerChanges.addLink(client.getObjectFactory().newEntityNavigationLink(
-//            "Info",
-//            client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("CustomerInfo").
-//            appendKeySegment(17).build()));
-//
-//    final ODataEntityUpdateRequest updateReq = client.getCUDRequestFactory().getEntityUpdateRequest(
-//            URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges);
-//
-//    changeset.addRequest(updateReq);
-//
-//    final ODataBatchResponse response = streamManager.getResponse();
-//    assertEquals(202, response.getStatusCode());
-//    assertEquals("Accepted", response.getStatusMessage());
-//
-//    // verify response payload ...
-//    final Iterator<ODataBatchResponseItem> iter = response.getBody();
-//
-//    final ODataBatchResponseItem item = iter.next();
-//    assertTrue(item instanceof ODataChangesetResponseItem);
-//
-//    final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
-//
-//    ODataResponse res = chgitem.next();
-//    assertEquals(201, res.getStatusCode());
-//    assertTrue(res instanceof ODataEntityCreateResponse);
-//
-//    customer = ((ODataEntityCreateResponse) res).getBody();
-//
-//    ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(
-//            URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString() + "/Info"));
-//
-//    assertEquals(Integer.valueOf(17),
-//            req.execute().getBody().getProperty("CustomerInfoId").getPrimitiveValue().<Integer>toCastValue());
-//
-//    res = chgitem.next();
-//    assertEquals(204, res.getStatusCode());
-//    assertTrue(res instanceof ODataEntityUpdateResponse);
-//
-//    // clean ...
-//    assertEquals(204, client.getCUDRequestFactory().getDeleteRequest(
-//            URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString())).execute().
-//            getStatusCode());
-//
-//    try {
-//      client.getRetrieveRequestFactory().getEntityRequest(
-//              URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString())).
-//              execute().getBody();
-//      fail();
-//    } catch (Exception e) {
-//      // ignore
-//    }
-//  }
-//
-//  @Test
-//  @Ignore
-//  public void batchRequest() {
-//    // create your request
-//    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
-//
-//    final BatchStreamManager streamManager = request.execute();
-//
-//    // -------------------------------------------
-//    // Add retrieve item
-//    // -------------------------------------------
-//    ODataRetrieve retrieve = streamManager.addRetrieve();
-//
-//    // prepare URI
-//    URIBuilder<?> targetURI = client.getURIBuilder(testStaticServiceRootURL);
-//    targetURI.appendEntitySetSegment("Customer").appendKeySegment(-10).
-//            expand("Logins").select("CustomerId,Logins/Username");
-//
-//    // create new request
-//    ODataEntityRequest queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
-//    queryReq.setFormat(ODataPubFormat.ATOM);
-//
-//    retrieve.setRequest(queryReq);
-//    // -------------------------------------------
-//
-//    // -------------------------------------------
-//    // Add changeset item
-//    // -------------------------------------------
-//    final ODataChangeset changeset = streamManager.addChangeset();
-//
-//    // Update Product into the changeset
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).
-//            appendEntitySetSegment("Product").appendKeySegment(-10);
-//    final URI editLink = targetURI.build();
-//
-//    final ODataEntity merge = client.getObjectFactory().newEntity(TEST_PRODUCT_TYPE);
-//    merge.setEditLink(editLink);
-//
-//    merge.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
-//            "Description", client.getPrimitiveValueBuilder().setText("new description from batch").build()));
-//
-//    final ODataEntityUpdateRequest changeReq =
-//            client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.MERGE, merge);
-//    changeReq.setFormat(ODataPubFormat.JSON_FULL_METADATA);
-//    changeReq.setIfMatch(getETag(editLink));
-//
-//    changeset.addRequest(changeReq);
-//
-//    // Create Customer into the changeset
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
-//    final ODataEntity original = getSampleCustomerProfile(1000, "Sample customer", false);
-//    final ODataEntityCreateRequest createReq =
-//            client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original);
-//    createReq.setFormat(ODataPubFormat.ATOM);
-//    changeset.addRequest(createReq);
-//    // -------------------------------------------
-//
-//    // -------------------------------------------
-//    // Add retrieve item
-//    // -------------------------------------------
-//    retrieve = streamManager.addRetrieve();
-//
-//    // prepare URI
-//    targetURI = client.getURIBuilder(testStaticServiceRootURL).
-//            appendEntitySetSegment("Product").appendKeySegment(-10);
-//
-//    // create new request
-//    queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
-//
-//    retrieve.setRequest(queryReq);
-//    // -------------------------------------------
-//
-//    final ODataBatchResponse response = streamManager.getResponse();
-//    assertEquals(202, response.getStatusCode());
-//    assertEquals("Accepted", response.getStatusMessage());
-//
-//    final Iterator<ODataBatchResponseItem> iter = response.getBody();
-//
-//    // retrive the first item (ODataRetrieve)
-//    ODataBatchResponseItem item = iter.next();
-//    assertTrue(item instanceof ODataRetrieveResponseItem);
-//
-//    ODataRetrieveResponseItem retitem = (ODataRetrieveResponseItem) item;
-//    ODataResponse res = retitem.next();
-//    assertTrue(res instanceof ODataEntityResponseImpl);
-//    assertEquals(200, res.getStatusCode());
-//    assertEquals("OK", res.getStatusMessage());
-//
-//    ODataEntityResponseImpl entres = (ODataEntityResponseImpl) res;
-//    ODataEntity entity = entres.getBody();
-//    assertEquals(new Integer(-10), entity.getProperty("CustomerId").getPrimitiveValue().<Integer>toCastValue());
-//
-//    // retrieve the second item (ODataChangeset)
-//    item = iter.next();
-//    assertTrue(item instanceof ODataChangesetResponseItem);
-//
-//    final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
-//    res = chgitem.next();
-//    assertTrue(res instanceof ODataEntityUpdateResponse);
-//    assertEquals(204, res.getStatusCode());
-//    assertEquals("No Content", res.getStatusMessage());
-//
-//    res = chgitem.next();
-//    assertTrue(res instanceof ODataEntityCreateResponse);
-//    assertEquals(201, res.getStatusCode());
-//    assertEquals("Created", res.getStatusMessage());
-//
-//    final ODataEntityCreateResponse createres = (ODataEntityCreateResponse) res;
-//    entity = createres.getBody();
-//    assertEquals(new Integer(1000), entity.getProperty("CustomerId").getPrimitiveValue().<Integer>toCastValue());
-//
-//    // retrive the third item (ODataRetrieve)
-//    item = iter.next();
-//    assertTrue(item instanceof ODataRetrieveResponseItem);
-//
-//    retitem = (ODataRetrieveResponseItem) item;
-//    res = retitem.next();
-//    assertTrue(res instanceof ODataEntityResponseImpl);
-//    assertEquals(200, res.getStatusCode());
-//    assertEquals("OK", res.getStatusMessage());
-//
-//    entres = (ODataEntityResponseImpl) res;
-//    entity = entres.getBody();
-//    assertEquals("new description from batch",
-//            entity.getProperty("Description").getPrimitiveValue().<String>toCastValue());
-//
-//    assertFalse(iter.hasNext());
-//  }
-//
-//  private static class TestStreamManager extends AbstractODataStreamManager<ODataBatchResponse> {
-//
-//    public TestStreamManager() {
-//      super(new Wrapper<Future<HttpResponse>>());
-//    }
-//
-//    public ODataStreamManager<ODataBatchResponse> addObject(byte[] src) {
-//      stream(src);
-//      return this;
-//    }
-//
-//    @Override
-//    protected ODataBatchResponse getResponse(long timeout, TimeUnit unit) {
-//      throw new UnsupportedOperationException("Not supported yet.");
-//    }
-//  };
-//
-//  /**
-//   * To be used for debug purposes.
-//   */
-//  private static class StreamingThread extends Thread {
-//
-//    private final TestStreamManager streaming;
-//
-//    public StreamingThread(final TestStreamManager streaming) {
-//      this.streaming = streaming;
-//    }
-//
-//    @Override
-//    public void run() {
-//      try {
-//        final StringBuilder builder = new StringBuilder();
-//
-//        byte[] buff = new byte[1024];
-//
-//        int len;
-//
-//        while ((len = streaming.getBody().read(buff)) >= 0) {
-//          builder.append(new String(buff, 0, len));
-//        }
-//
-//        assertTrue(builder.toString().startsWith(PREFIX));
-//        assertTrue(builder.toString().contains((MAX / 2) + ") send info"));
-//        assertTrue(builder.toString().contains((MAX / 3) + ") send info"));
-//        assertTrue(builder.toString().contains((MAX / 20) + ") send info"));
-//        assertTrue(builder.toString().contains((MAX / 30) + ") send info"));
-//        assertTrue(builder.toString().contains(MAX + ") send info"));
-//        assertTrue(builder.toString().endsWith(SUFFIX));
-//
-//      } catch (IOException e) {
-//        fail();
-//      }
-//    }
-//  }
-//
-//  private static class BatchStreamingThread extends Thread {
-//
-//    private final BatchStreamManager streaming;
-//
-//    public BatchStreamingThread(final BatchStreamManager streaming) {
-//      this.streaming = streaming;
-//    }
-//
-//    @Override
-//    public void run() {
-//      try {
-//        final StringBuilder builder = new StringBuilder();
-//
-//        byte[] buff = new byte[1024];
-//
-//        int len;
-//
-//        while ((len = streaming.getBody().read(buff)) >= 0) {
-//          builder.append(new String(buff, 0, len));
-//        }
-//
-//        LOG.debug("Batch request {}", builder.toString());
-//
-//        assertTrue(builder.toString().contains("Content-Id:2"));
-//        assertTrue(builder.toString().contains("GET " + servicesODataServiceRootURL));
-//      } catch (IOException e) {
-//        fail();
-//      }
-//    }
-//  }
+  private static String PREFIX = "!!PREFIX!!";
+
+  private static String SUFFIX = "!!SUFFIX!!";
+
+  private static int MAX = 10000;
+
+  @Test
+  public void stringStreaming() {
+    final TestStreamManager streaming = new TestStreamManager();
+
+    new StreamingThread(streaming).start();
+
+    streaming.addObject((PREFIX + "\n").getBytes());
+
+    for (int i = 0; i <= MAX; i++) {
+      streaming.addObject((i + ") send info\n").getBytes());
+    }
+
+    streaming.addObject((SUFFIX).getBytes());
+    streaming.finalizeBody();
+  }
+
+  @Test
+  public void emptyBatchRequest() {
+    // create your request
+    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
+
+    final BatchStreamManager payload = request.execute();
+    final ODataBatchResponse response = payload.getResponse();
+
+    assertEquals(202, response.getStatusCode());
+    assertEquals("Accepted", response.getStatusMessage());
+
+    final Iterator<ODataBatchResponseItem> iter = response.getBody();
+    assertFalse(iter.hasNext());
+  }
+
+  @Test
+  public void changesetWithError() {
+    // create your request
+    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
+
+    final BatchStreamManager payload = request.execute();
+    final ODataChangeset changeset = payload.addChangeset();
+
+    URIBuilder targetURI;
+    ODataEntityCreateRequest<ODataEntity> createReq;
+
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
+    for (int i = 1; i <= 2; i++) {
+      // Create Customer into the changeset
+      createReq = client.getCUDRequestFactory().getEntityCreateRequest(
+              targetURI.build(),
+              getSampleCustomerProfile(100 + i, "Sample customer", false));
+      createReq.setFormat(ODataPubFormat.JSON);
+      changeset.addRequest(createReq);
+    }
+
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("WrongEntitySet");
+    createReq = client.getCUDRequestFactory().getEntityCreateRequest(
+            targetURI.build(),
+            getSampleCustomerProfile(105, "Sample customer", false));
+    createReq.setFormat(ODataPubFormat.JSON);
+    changeset.addRequest(createReq);
+
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
+    for (int i = 3; i <= 4; i++) {
+      // Create Customer into the changeset
+      createReq = client.getCUDRequestFactory().getEntityCreateRequest(
+              targetURI.build(),
+              getSampleCustomerProfile(100 + i, "Sample customer", false));
+      createReq.setFormat(ODataPubFormat.ATOM);
+      changeset.addRequest(createReq);
+    }
+
+    final ODataBatchResponse response = payload.getResponse();
+    assertEquals(202, response.getStatusCode());
+    assertEquals("Accepted", response.getStatusMessage());
+
+    final Iterator<ODataBatchResponseItem> iter = response.getBody();
+    final ODataChangesetResponseItem chgResponseItem = (ODataChangesetResponseItem) iter.next();
+
+    final ODataResponse res = chgResponseItem.next();
+    assertEquals(404, res.getStatusCode());
+    assertEquals("Not Found", res.getStatusMessage());
+    assertEquals(Integer.valueOf(3), Integer.valueOf(
+            res.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next()));
+    assertFalse(chgResponseItem.hasNext());
+  }
+
+  @Test
+  @Ignore
+  @SuppressWarnings("unchecked")
+  public void changesetWithReference() throws EdmPrimitiveTypeException {
+    // create your request
+    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
+    final BatchStreamManager streamManager = request.execute();
+
+    final ODataChangeset changeset = streamManager.addChangeset();
+    ODataEntity customer = getSampleCustomerProfile(20, "sample customer", false);
+
+    URIBuilder uriBuilder = client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("Customer");
+
+    // add create request
+    final ODataEntityCreateRequest<ODataEntity> createReq =
+            client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), customer);
+
+    changeset.addRequest(createReq);
+
+    // retrieve request reference
+    int createRequestRef = changeset.getLastContentId();
+
+    // add update request: link CustomerInfo(17) to the new customer
+    final ODataEntity customerChanges = client.getObjectFactory().newEntity(customer.getTypeName());
+    customerChanges.addLink(client.getObjectFactory().newEntityNavigationLink(
+            "Info",
+            client.getURIBuilder(testAuthServiceRootURL).appendEntitySetSegment("CustomerInfo").
+            appendKeySegment(17).build()));
+
+    final ODataEntityUpdateRequest updateReq = client.getCUDRequestFactory().getEntityUpdateRequest(
+            URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges);
+
+    changeset.addRequest(updateReq);
+
+    final ODataBatchResponse response = streamManager.getResponse();
+    assertEquals(202, response.getStatusCode());
+    assertEquals("Accepted", response.getStatusMessage());
+
+    // verify response payload ...
+    final Iterator<ODataBatchResponseItem> iter = response.getBody();
+
+    final ODataBatchResponseItem item = iter.next();
+    assertTrue(item instanceof ODataChangesetResponseItem);
+
+    final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
+
+    ODataResponse res = chgitem.next();
+    assertEquals(201, res.getStatusCode());
+    assertTrue(res instanceof ODataEntityCreateResponse);
+
+    customer = ((ODataEntityCreateResponse<ODataEntity>) res).getBody();
+
+    ODataEntityRequest<ODataEntity> req = client.getRetrieveRequestFactory().getEntityRequest(
+            URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString() + "/Info"));
+
+    assertEquals(Integer.valueOf(17),
+            req.execute().getBody().getProperty("CustomerInfoId").getPrimitiveValue().toCastValue(Integer.class));
+
+    res = chgitem.next();
+    assertEquals(204, res.getStatusCode());
+    assertTrue(res instanceof ODataEntityUpdateResponse);
+
+    // clean ...
+    assertEquals(204, client.getCUDRequestFactory().getDeleteRequest(
+            URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString())).execute().
+            getStatusCode());
+
+    try {
+      client.getRetrieveRequestFactory().getEntityRequest(
+              URIUtils.getURI(testStaticServiceRootURL, customer.getEditLink().toASCIIString())).
+              execute().getBody();
+      fail();
+    } catch (Exception e) {
+      // ignore
+    }
+  }
+
+  @Test
+  @SuppressWarnings("unchecked")
+  public void batchRequest() throws EdmPrimitiveTypeException {
+    // create your request
+    final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
+
+    final BatchStreamManager streamManager = request.execute();
+
+    // -------------------------------------------
+    // Add retrieve item
+    // -------------------------------------------
+    ODataRetrieve retrieve = streamManager.addRetrieve();
+
+    // prepare URI
+    URIBuilder targetURI = client.getURIBuilder(testStaticServiceRootURL);
+    targetURI.appendEntitySetSegment("Customer").appendKeySegment(-10).
+            expand("Logins").select("CustomerId,Logins/Username");
+
+    // create new request
+    ODataEntityRequest<ODataEntity> queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
+    queryReq.setFormat(ODataPubFormat.ATOM);
+
+    retrieve.setRequest(queryReq);
+    // -------------------------------------------
+
+    // -------------------------------------------
+    // Add changeset item
+    // -------------------------------------------
+    final ODataChangeset changeset = streamManager.addChangeset();
+
+    // Update Product into the changeset
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("Product").appendKeySegment(-10);
+    final URI editLink = targetURI.build();
+
+    final ODataEntity merge = client.getObjectFactory().newEntity(TEST_PRODUCT_TYPE);
+    merge.setEditLink(editLink);
+
+    merge.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+            "Description",
+            client.getObjectFactory().newPrimitiveValueBuilder().buildString("new description from batch")));
+
+    final ODataEntityUpdateRequest changeReq =
+            client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.MERGE, merge);
+    changeReq.setFormat(ODataPubFormat.JSON_FULL_METADATA);
+    changeReq.setIfMatch(getETag(editLink));
+
+    changeset.addRequest(changeReq);
+
+    // Create Customer into the changeset
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customer");
+    final ODataEntity original = getSampleCustomerProfile(1000, "Sample customer", false);
+    final ODataEntityCreateRequest<ODataEntity> createReq =
+            client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original);
+    createReq.setFormat(ODataPubFormat.ATOM);
+    changeset.addRequest(createReq);
+    // -------------------------------------------
+
+    // -------------------------------------------
+    // Add retrieve item
+    // -------------------------------------------
+    retrieve = streamManager.addRetrieve();
+
+    // prepare URI
+    targetURI = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("Product").appendKeySegment(-10);
+
+    // create new request
+    queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
+
+    retrieve.setRequest(queryReq);
+    // -------------------------------------------
+
+    final ODataBatchResponse response = streamManager.getResponse();
+    assertEquals(202, response.getStatusCode());
+    assertEquals("Accepted", response.getStatusMessage());
+    final Iterator<ODataBatchResponseItem> iter = response.getBody();
+    
+    // retrive the first item (ODataRetrieve)
+    ODataBatchResponseItem item = iter.next();
+    assertTrue(item instanceof ODataRetrieveResponseItem);
+
+    ODataRetrieveResponseItem retitem = (ODataRetrieveResponseItem) item;
+    ODataResponse res = retitem.next();
+    assertTrue(res instanceof ODataEntityResponseImpl);
+    assertEquals(200, res.getStatusCode());
+    assertEquals("OK", res.getStatusMessage());
+
+    ODataEntityRequestImpl<ODataEntity>.ODataEntityResponseImpl entres =
+            (ODataEntityRequestImpl<ODataEntity>.ODataEntityResponseImpl) res;
+
+    ODataEntity entity = entres.getBody();
+    assertEquals(new Integer(-10), entity.getProperty("CustomerId").getPrimitiveValue().toCastValue(Integer.class));
+
+    // retrieve the second item (ODataChangeset)
+    item = iter.next();
+    assertTrue(item instanceof ODataChangesetResponseItem);
+
+    final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
+    res = chgitem.next();
+    assertTrue(res instanceof ODataEntityUpdateResponse);
+    assertEquals(204, res.getStatusCode());
+    assertEquals("No Content", res.getStatusMessage());
+
+    res = chgitem.next();
+    assertTrue(res instanceof ODataEntityCreateResponse);
+    assertEquals(201, res.getStatusCode());
+    assertEquals("Created", res.getStatusMessage());
+
+    final ODataEntityCreateResponse<ODataEntity> createres = (ODataEntityCreateResponse<ODataEntity>) res;
+    entity = createres.getBody();
+    assertEquals(new Integer(1000), entity.getProperty("CustomerId").getPrimitiveValue().toCastValue(Integer.class));
+
+    // retrive the third item (ODataRetrieve)
+    item = iter.next();
+    assertTrue(item instanceof ODataRetrieveResponseItem);
+
+    retitem = (ODataRetrieveResponseItem) item;
+    res = retitem.next();
+    assertTrue(res instanceof ODataEntityResponseImpl);
+    assertEquals(200, res.getStatusCode());
+    assertEquals("OK", res.getStatusMessage());
+
+    entres = (ODataEntityRequestImpl<ODataEntity>.ODataEntityResponseImpl) res;
+    entity = entres.getBody();
+    assertEquals("new description from batch",
+            entity.getProperty("Description").getPrimitiveValue().toCastValue(String.class));
+
+    assertFalse(iter.hasNext());
+  }
+
+  private static class TestStreamManager extends AbstractODataStreamManager<ODataBatchResponse> {
+
+    public TestStreamManager() {
+      super(new Wrapper<Future<HttpResponse>>());
+    }
+
+    public ODataStreamManager<ODataBatchResponse> addObject(byte[] src) {
+      stream(src);
+      return this;
+    }
+
+    @Override
+    protected ODataBatchResponse getResponse(long timeout, TimeUnit unit) {
+      throw new UnsupportedOperationException("Not supported yet.");
+    }
+  };
+
+  /**
+   * To be used for debug purposes.
+   */
+  private static class StreamingThread extends Thread {
+
+    private final TestStreamManager streaming;
+
+    public StreamingThread(final TestStreamManager streaming) {
+      this.streaming = streaming;
+    }
+
+    @Override
+    public void run() {
+      try {
+        final StringBuilder builder = new StringBuilder();
+
+        byte[] buff = new byte[1024];
+
+        int len;
+
+        while ((len = streaming.getBody().read(buff)) >= 0) {
+          builder.append(new String(buff, 0, len));
+        }
+
+        assertTrue(builder.toString().startsWith(PREFIX));
+        assertTrue(builder.toString().contains((MAX / 2) + ") send info"));
+        assertTrue(builder.toString().contains((MAX / 3) + ") send info"));
+        assertTrue(builder.toString().contains((MAX / 20) + ") send info"));
+        assertTrue(builder.toString().contains((MAX / 30) + ") send info"));
+        assertTrue(builder.toString().contains(MAX + ") send info"));
+        assertTrue(builder.toString().endsWith(SUFFIX));
+
+      } catch (IOException e) {
+        fail();
+      }
+    }
+  }
+
+  private static class BatchStreamingThread extends Thread {
+
+    private final BatchStreamManager streaming;
+
+    public BatchStreamingThread(final BatchStreamManager streaming) {
+      this.streaming = streaming;
+    }
+
+    @Override
+    public void run() {
+      try {
+        final StringBuilder builder = new StringBuilder();
+
+        byte[] buff = new byte[1024];
+
+        int len;
+
+        while ((len = streaming.getBody().read(buff)) >= 0) {
+          builder.append(new String(buff, 0, len));
+        }
+
+        LOG.debug("Batch request {}", builder.toString());
+
+        assertTrue(builder.toString().contains("Content-Id:2"));
+        assertTrue(builder.toString().contains("GET " + testStaticServiceRootURL));
+      } catch (IOException e) {
+        fail();
+      }
+    }
+  }
 }