You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by jh...@apache.org on 2013/08/23 16:29:05 UTC

[4/4] git commit: Batch Support, Validation in Deserialization

Batch Support, Validation in Deserialization

Change-Id: Ic263de081d06b71d395804b75a36c6e4b525d386


Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/commit/45c27419
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/tree/45c27419
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/diff/45c27419

Branch: refs/heads/master
Commit: 45c27419cfcf1024d8e57100e7408276a932598c
Parents: a133661
Author: Jens Huesken <jh...@apache.org>
Authored: Fri Aug 23 16:27:58 2013 +0200
Committer: Jens Huesken <jh...@apache.org>
Committed: Fri Aug 23 16:27:58 2013 +0200

----------------------------------------------------------------------
 .../olingo/odata2/api/batch/BatchException.java |   8 +-
 .../olingo/odata2/api/batch/BatchHandler.java   |   6 +-
 .../olingo/odata2/api/batch/BatchPart.java      |  43 --
 .../odata2/api/batch/BatchRequestPart.java      |  42 ++
 .../odata2/api/client/batch/BatchChangeSet.java |  61 +++
 .../api/client/batch/BatchChangeSetPart.java    | 110 +++++
 .../odata2/api/client/batch/BatchPart.java      |  27 ++
 .../odata2/api/client/batch/BatchQueryPart.java |  97 +++++
 .../api/client/batch/BatchSingleResponse.java   |  65 +++
 .../olingo/odata2/api/ep/EntityProvider.java    |  74 +++-
 .../api/ep/EntityProviderReadProperties.java    |  29 +-
 .../olingo/odata2/api/rt/RuntimeDelegate.java   |  21 +
 .../odata2/core/batch/BatchChangeSetImpl.java   |  49 +++
 .../core/batch/BatchChangeSetPartImpl.java      | 116 +++++
 .../odata2/core/batch/BatchConstants.java       |  36 --
 .../odata2/core/batch/BatchHandlerImpl.java     |  20 +-
 .../olingo/odata2/core/batch/BatchHelper.java   |  51 +++
 .../olingo/odata2/core/batch/BatchPartImpl.java |  58 ---
 .../odata2/core/batch/BatchQueryPartImpl.java   | 102 +++++
 .../odata2/core/batch/BatchRequestParser.java   | 294 +++++++------
 .../odata2/core/batch/BatchRequestPartImpl.java |  58 +++
 .../odata2/core/batch/BatchRequestWriter.java   | 120 ++++++
 .../odata2/core/batch/BatchResponseParser.java  | 354 +++++++++++++++
 .../odata2/core/batch/BatchResponseWriter.java  |  80 ++--
 .../core/batch/BatchSingleResponseImpl.java     |  90 ++++
 .../olingo/odata2/core/commons/Encoder.java     |   1 -
 .../olingo/odata2/core/edm/EdmBinary.java       |   2 +-
 .../olingo/odata2/core/edm/EdmDateTime.java     |   2 +-
 .../apache/olingo/odata2/core/edm/EdmTime.java  |  15 +-
 .../odata2/core/edm/parser/EdmParser.java       |  11 +-
 .../odata2/core/ep/JsonEntityProvider.java      |   9 +-
 .../odata2/core/ep/ProviderFacadeImpl.java      |  22 +-
 .../core/ep/aggregator/EntityPropertyInfo.java  |  20 +-
 .../core/ep/consumer/JsonEntityConsumer.java    |   4 +-
 .../core/ep/consumer/JsonEntryConsumer.java     |   4 +-
 .../core/ep/consumer/JsonPropertyConsumer.java  |   3 +
 .../core/ep/consumer/XmlEntityConsumer.java     |   6 +-
 .../core/ep/consumer/XmlEntryConsumer.java      |  35 +-
 .../core/ep/consumer/XmlPropertyConsumer.java   |  65 ++-
 .../processor/ODataSingleProcessorService.java  |   3 +-
 .../odata2/core/rt/RuntimeDelegateImpl.java     |  25 ++
 odata-core/src/main/resources/i18n.properties   |   4 +-
 .../core/batch/BatchRequestParserTest.java      | 427 ++++++++++---------
 .../core/batch/BatchRequestWriterTest.java      | 236 ++++++++++
 .../core/batch/BatchResponseParserTest.java     | 280 ++++++++++++
 .../core/batch/BatchResponseWriterTest.java     |   4 +-
 .../odata2/core/edm/parser/EdmParserTest.java   |  11 +-
 .../core/ep/AbstractXmlProducerTestHelper.java  |   3 +
 .../core/ep/consumer/JsonEntryConsumerTest.java |  46 +-
 .../ep/consumer/JsonPropertyConsumerTest.java   | 152 ++++---
 .../core/ep/consumer/XmlEntityConsumerTest.java | 123 +++---
 .../core/ep/consumer/XmlLinkConsumerTest.java   |   7 +
 .../ep/consumer/XmlPropertyConsumerTest.java    | 181 +++++---
 .../ODataSingleProcessorServiceTest.java        |   2 +-
 .../uri/expression/TestAbapCompatibility.java   |   3 +-
 .../uri/expression/TestParserExceptions.java    |  10 +-
 .../src/test/resources/batchResponse.batch      |  39 ++
 .../olingo/odata2/fit/basic/BasicBatchTest.java |   9 +-
 .../odata2/fit/client/ClientBatchTest.java      | 235 ++++++++++
 .../apache/olingo/odata2/fit/ref/BatchTest.java |   2 +-
 .../odata2/fit/ref/EntryJsonChangeTest.java     |  29 +-
 .../fit/ref/EntryJsonCreateInlineTest.java      |   2 +
 .../odata2/fit/ref/EntryXmlChangeTest.java      |  53 ++-
 .../odata2/ref/edm/ScenarioEdmProvider.java     |   2 +-
 .../odata2/ref/processor/ListsProcessor.java    |  29 +-
 .../odata2/testutil/helper/StringHelper.java    |  11 +-
 .../olingo/odata2/testutil/mock/EdmMock.java    |   1 +
 .../olingo/odata2/testutil/mock/TecEdmInfo.java |   5 +-
 .../mock/TechnicalScenarioEdmProvider.java      |  47 +-
 69 files changed, 3295 insertions(+), 896 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
index bbed22b..f5582d6 100644
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
@@ -49,7 +49,7 @@ public class BatchException extends ODataMessageException {
   /** INVALID_QUERY_PARAMETER requires no content value */
   public static final MessageReference INVALID_QUERY_PARAMETER = createMessageReference(BatchException.class, "INVALID_QUERY_PARAMETER");
 
-  /** INVALID_URI requires no content value */
+  /** INVALID_URI requires 1 content value('line number') */
   public static final MessageReference INVALID_URI = createMessageReference(BatchException.class, "INVALID_URI");
 
   /** INVALID_BOUNDARY requires 1 content value('line number') */
@@ -91,9 +91,15 @@ public class BatchException extends ODataMessageException {
   /** INVALID_REQUEST_LINE requires 2 content value ('request line', 'line number') */
   public static final MessageReference INVALID_REQUEST_LINE = createMessageReference(BatchException.class, "INVALID_REQUEST_LINE");
 
+  /** INVALID_STATUS_LINE requires 2 content value ('status line', 'line number') */
+  public static final MessageReference INVALID_STATUS_LINE = createMessageReference(BatchException.class, "INVALID_STATUS_LINE");
+
   /** TRUNCETED_BODY requires 1 content value ('line number') */
   public static final MessageReference TRUNCATED_BODY = createMessageReference(BatchException.class, "TRUNCATED_BODY");
 
+  /** UNSUPPORTED_ABSOLUTE_PATH requires 1 content value ('line number') */
+  public static final MessageReference UNSUPPORTED_ABSOLUTE_PATH = createMessageReference(BatchException.class, "UNSUPPORTED_ABSOLUTE_PATH");
+
   public BatchException(final MessageReference messageReference) {
     super(messageReference);
   }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchHandler.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchHandler.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchHandler.java
index 23584f3..1748e3e 100644
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchHandler.java
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchHandler.java
@@ -27,12 +27,12 @@ import org.apache.olingo.odata2.api.processor.ODataResponse;
  */
 public interface BatchHandler {
   /**
-   * <p>Handles the {@link BatchPart} in a way that it results in a corresponding {@link BatchResponsePart}.</p>
-   * @param batchPart the incoming batchPart
+   * <p>Handles the {@link BatchRequestPart} in a way that it results in a corresponding {@link BatchResponsePart}.</p>
+   * @param batchRequestPart the incoming MIME part
    * @return the corresponding result
    * @throws ODataException
    */
-  public BatchResponsePart handleBatchPart(BatchPart batchPart) throws ODataException;
+  public BatchResponsePart handleBatchPart(BatchRequestPart batchRequestPart) throws ODataException;
 
   /**
    * <p>Delegates a handling of the request {@link ODataRequest} to the request handler and provides ODataResponse {@link ODataResponse}.</p>

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchPart.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchPart.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchPart.java
deleted file mode 100644
index b7d8539..0000000
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchPart.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*******************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- * 
- *    http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- ******************************************************************************/
-package org.apache.olingo.odata2.api.batch;
-
-import java.util.List;
-
-import org.apache.olingo.odata2.api.processor.ODataRequest;
-
-/**
- * A BatchPart
- * <p> BatchPart represents a distinct MIME part of a Batch Request body. It can be ChangeSet or Query Operation
- *  
- */
-public interface BatchPart {
-
-  /**
-   * Get the info if a BatchPart is a ChangeSet
-   * @return true or false
-   */
-  public boolean isChangeSet();
-
-  /**
-   * Get requests. If a BatchPart is a Query Operation, the list contains one request.
-   * @return a list of {@link ODataRequest}
-   */
-  public List<ODataRequest> getRequests();
-}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchRequestPart.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchRequestPart.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchRequestPart.java
new file mode 100644
index 0000000..a12f774
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchRequestPart.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.batch;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.processor.ODataRequest;
+
+/**
+ * A BatchPart
+ * <p> BatchPart represents a distinct MIME part of a Batch Request body. It can be ChangeSet or Query Operation
+ */
+public interface BatchRequestPart {
+
+  /**
+   * Get the info if a BatchPart is a ChangeSet
+   * @return true or false
+   */
+  public boolean isChangeSet();
+
+  /**
+   * Get requests. If a BatchPart is a Query Operation, the list contains one request.
+   * @return a list of {@link ODataRequest}
+   */
+  public List<ODataRequest> getRequests();
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSet.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSet.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSet.java
new file mode 100644
index 0000000..13f7df0
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSet.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.client.batch;
+
+import java.util.List;
+
+import org.apache.olingo.odata2.api.rt.RuntimeDelegate;
+
+/**
+ * A BatchChangeSet
+ * <p> BatchChangeSet represents a Change Set, that consists of change requests
+ */
+public abstract class BatchChangeSet implements BatchPart {
+
+  /**
+   * Add a new change request to the ChangeSet
+   * @param BatchChangeSetPart {@link BatchChangeSetPart}
+   */
+  public abstract void add(BatchChangeSetPart request);
+
+  /**
+   * Get change requests 
+   * @return a list of {@link BatchChangeSetPart}
+   */
+  public abstract List<BatchChangeSetPart> getChangeSetParts();
+
+  /**
+   * Get new builder instance 
+   * @return {@link BatchChangeSetBuilder}
+   */
+  public static BatchChangeSetBuilder newBuilder() {
+    return BatchChangeSetBuilder.newInstance();
+  }
+
+  public static abstract class BatchChangeSetBuilder {
+
+    protected BatchChangeSetBuilder() {}
+
+    private static BatchChangeSetBuilder newInstance() {
+      return RuntimeDelegate.createBatchChangeSetBuilder();
+    }
+
+    public abstract BatchChangeSet build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java
new file mode 100644
index 0000000..ba59edc
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java
@@ -0,0 +1,110 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.client.batch;
+
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.rt.RuntimeDelegate;
+
+/**
+ * A BatchChangeSetPart
+ * <p> BatchChangeSetPart represents a change request within a Change Set
+ */
+public abstract class BatchChangeSetPart {
+
+  public abstract Map<String, String> getHeaders();
+
+  public abstract String getBody();
+
+  public abstract String getUri();
+
+  public abstract String getMethod();
+
+  public abstract String getContentId();
+
+  /**
+   * @param headers
+   * @return a new builder object
+   */
+  public static BatchChangeSetPartBuilder headers(final Map<String, String> headers) {
+    return newBuilder().headers(headers);
+  }
+
+  /**
+   * @param body a change request body
+   * @return a new builder object
+   */
+  public static BatchChangeSetPartBuilder body(final String body) {
+    return newBuilder().body(body);
+  }
+
+  /**
+   * @param uri should not be null
+   * @return a new builder object
+   */
+  public static BatchChangeSetPartBuilder uri(final String uri) {
+    return newBuilder().uri(uri);
+  }
+
+  /**
+   * @param method MUST be the PUT, POST, MERGE, DELETE or PATCH method
+   * @return a new builder object
+   */
+  public static BatchChangeSetPartBuilder method(final String method) {
+    return newBuilder().method(method);
+  }
+
+  /**
+   * @param contentId can be used to identify the different request within a the batch
+   * @return a new builder object
+   */
+  public static BatchChangeSetPartBuilder contentId(final String contentId) {
+    return newBuilder().contentId(contentId);
+  }
+
+  /**
+   * @return returns a new builder object
+   */
+  public static BatchChangeSetPartBuilder newBuilder() {
+    return BatchChangeSetPartBuilder.newInstance();
+  }
+
+  public static abstract class BatchChangeSetPartBuilder {
+
+    protected BatchChangeSetPartBuilder() {}
+
+    private static BatchChangeSetPartBuilder newInstance() {
+      return RuntimeDelegate.createBatchChangeSetPartBuilder();
+    }
+
+    public abstract BatchChangeSetPart build();
+
+    public abstract BatchChangeSetPartBuilder headers(Map<String, String> headers);
+
+    public abstract BatchChangeSetPartBuilder body(String body);
+
+    public abstract BatchChangeSetPartBuilder uri(String uri);
+
+    public abstract BatchChangeSetPartBuilder method(String method);
+
+    public abstract BatchChangeSetPartBuilder contentId(String contentId);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchPart.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchPart.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchPart.java
new file mode 100644
index 0000000..6c6c7f0
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchPart.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.client.batch;
+
+/**
+ * A BatchPartRequest
+ * <p> BatchPartRequest represents a distinct part of a Batch Request body. It can be a ChangeSet or Query Operation.
+ */
+public interface BatchPart {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchQueryPart.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchQueryPart.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchQueryPart.java
new file mode 100644
index 0000000..42bd28d
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchQueryPart.java
@@ -0,0 +1,97 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.client.batch;
+
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.rt.RuntimeDelegate;
+
+/**
+ * A BatchQueryPart
+ * <p>BatchQueryPart represents a single retrieve request
+ */
+public abstract class BatchQueryPart implements BatchPart {
+
+  public abstract Map<String, String> getHeaders();
+
+  public abstract String getUri();
+
+  public abstract String getMethod();
+
+  public abstract String getContentId();
+
+  /**
+   * @param headers
+   * @return a new builder object
+   */
+  public static BatchQueryPartBuilder headers(final Map<String, String> headers) {
+    return newBuilder().headers(headers);
+  }
+
+  /**
+   * @param uri should not be null
+   * @return a new builder object
+   */
+  public static BatchQueryPartBuilder uri(final String uri) {
+    return newBuilder().uri(uri);
+  }
+
+  /**
+   * @param method MUST be the HTTP GET method
+   * @return a new builder object
+   */
+  public static BatchQueryPartBuilder method(final String method) {
+    return newBuilder().method(method);
+  }
+
+  /**
+   * @param contentId can be used to identify the different request within a the batch
+   * @return a new builder object
+   */
+  public static BatchQueryPartBuilder contentId(final String contentId) {
+    return newBuilder().contentId(contentId);
+  }
+
+  /**
+   * @return returns a new builder object
+   */
+  public static BatchQueryPartBuilder newBuilder() {
+    return BatchQueryPartBuilder.newInstance();
+  }
+
+  public static abstract class BatchQueryPartBuilder {
+
+    protected BatchQueryPartBuilder() {}
+
+    private static BatchQueryPartBuilder newInstance() {
+      return RuntimeDelegate.createBatchQueryPartBuilder();
+    }
+
+    public abstract BatchQueryPart build();
+
+    public abstract BatchQueryPartBuilder headers(Map<String, String> headers);
+
+    public abstract BatchQueryPartBuilder uri(String uri);
+
+    public abstract BatchQueryPartBuilder method(String method);
+
+    public abstract BatchQueryPartBuilder contentId(String contentId);
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchSingleResponse.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchSingleResponse.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchSingleResponse.java
new file mode 100644
index 0000000..4d99c7d
--- /dev/null
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchSingleResponse.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * 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.olingo.odata2.api.client.batch;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+* A BatchSingleResponse
+* <p> BatchSingleResponse represents a single response of a Batch Response body. It can be a response to a change request of ChangeSet or a response to a retrieve request
+*/
+public interface BatchSingleResponse {
+  /**
+   * @return a result code of the attempt to understand and satisfy the request
+   */
+  public String getStatusCode();
+
+  /**
+   * @return a short textual description of the status code
+   */
+  public String getStatusInfo();
+
+  /**
+   * @return a value of the Content-Id header
+   */
+  public String getContentId();
+
+  /**
+   * @return a body part of a response message
+   */
+  public String getBody();
+
+  /**
+   * @return all available headers
+   */
+  public Map<String, String> getHeaders();
+
+  /**
+   * @param name HTTP response header name
+   * @return a header value or null if not set
+   */
+  public String getHeader(final String name);
+
+  /**
+   * @return a set of all available header names
+   */
+  public Set<String> getHeaderNames();
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProvider.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProvider.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProvider.java
index 97cc26a..cb876aa 100644
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProvider.java
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProvider.java
@@ -23,8 +23,10 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
-import org.apache.olingo.odata2.api.batch.BatchPart;
+import org.apache.olingo.odata2.api.batch.BatchRequestPart;
 import org.apache.olingo.odata2.api.batch.BatchResponsePart;
+import org.apache.olingo.odata2.api.client.batch.BatchPart;
+import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
 import org.apache.olingo.odata2.api.edm.Edm;
 import org.apache.olingo.odata2.api.edm.EdmEntitySet;
 import org.apache.olingo.odata2.api.edm.EdmFunctionImport;
@@ -220,13 +222,18 @@ public final class EntityProvider {
     ODataFeed readFeed(String contentType, EdmEntitySet entitySet, InputStream content, EntityProviderReadProperties properties) throws EntityProviderException;
 
     /**
-     * Read (de-serialize) data from <code>content</code> (as {@link InputStream}) in specified format (given as <code>contentType</code>)
-     * based on <code>entity data model</code> (given as {@link EdmEntitySet}) and provide this data as {@link ODataEntry}.
-     * 
-     * @param contentType format of content in the given input stream.
-     * @param entitySet entity data model for entity set to be read
-     * @param content data in form of an {@link InputStream} which contains the data in specified format
-     * @param properties additional properties necessary for reading content from {@link InputStream} into {@link Map}.
+     * Reads (de-serializes) data from <code>content</code> (as {@link InputStream})
+     * in specified format (given as <code>contentType</code>) based on
+     * <code>entity data model</code> (given as {@link EdmEntitySet})
+     * and provides this data as {@link ODataEntry}.
+     * Does not return complete entry data but only data present in the
+     * de-serialized content.
+     * @param contentType format of content in the given input stream
+     * @param entitySet   entity data model for entity set to be read
+     * @param content     data in form of an {@link InputStream} which
+     *                    contains the data in specified format
+     * @param properties  additional properties necessary for reading
+     *                    content from {@link InputStream} into {@link Map}.
      * @return entry as {@link ODataEntry}
      * @throws EntityProviderException if reading of data (de-serialization) fails
      */
@@ -331,7 +338,7 @@ public final class EntityProvider {
      * @return list of {@link BatchPart}
      * @throws BatchException  if parsing fails
      */
-    List<BatchPart> parseBatchRequest(String contentType, InputStream content, EntityProviderBatchProperties properties) throws BatchException;
+    List<BatchRequestPart> parseBatchRequest(String contentType, InputStream content, EntityProviderBatchProperties properties) throws BatchException;
 
     /**
      * Write responses of Batch Response Parts in Batch Response as {@link ODataResponse}.
@@ -342,6 +349,26 @@ public final class EntityProvider {
      * @throws BatchException 
      */
     ODataResponse writeBatchResponse(List<BatchResponsePart> batchResponseParts) throws BatchException;
+
+    /**
+     * Create Batch Request body as InputStream.
+     * 
+     * @param batchParts a list of BatchPartRequests {@link BatchPart}
+     * @param boundary 
+     * @return Batch Request as InputStream
+     */
+    InputStream writeBatchRequest(List<BatchPart> batchParts, String boundary);
+
+    /** 
+     * Parse Batch Response body (as {@link InputStream}) and provide a list of single responses as {@link BatchSingleResponse}
+     *
+     * @param content response body
+     * @param contentType format of content in the given input stream (incl. boundary parameter)
+     * @return list of {@link BatchSingleResponse}
+     * @throws BatchException 
+     */
+    List<BatchSingleResponse> parseBatchResponse(String contentType, InputStream content) throws BatchException;
+
   }
 
   /**
@@ -671,15 +698,15 @@ public final class EntityProvider {
   }
 
   /**
-   * Parse Batch Request body <code>inputStream</code> (as {@link InputStream}) and provide a list of Batch Parts as {@link BatchPart}
+   * Parse Batch Request body <code>inputStream</code> (as {@link InputStream}) and provide a list of Batch Request parts as {@link BatchRequestPart}
    * 
    * @param contentType format of content in the given input stream
    * @param content request body
    * @param properties additional properties necessary for parsing. Must not be null.
-   * @return list of {@link BatchPart}
+   * @return list of {@link BatchRequestPart}
    * @throws BatchException if parsing fails
    */
-  public static List<BatchPart> parseBatchRequest(final String contentType, final InputStream content, final EntityProviderBatchProperties properties) throws BatchException {
+  public static List<BatchRequestPart> parseBatchRequest(final String contentType, final InputStream content, final EntityProviderBatchProperties properties) throws BatchException {
     return createEntityProvider().parseBatchRequest(contentType, content, properties);
   }
 
@@ -695,4 +722,27 @@ public final class EntityProvider {
     return createEntityProvider().writeBatchResponse(batchResponseParts);
   }
 
+  /**
+   * Create Batch Request body as InputStream.
+   * 
+   * @param batchParts a list of BatchPartRequests {@link BatchPart}
+   * @param boundary 
+   * @return Batch Request as InputStream
+   */
+  public static InputStream writeBatchRequest(final List<BatchPart> batchParts, final String boundary) {
+    return createEntityProvider().writeBatchRequest(batchParts, boundary);
+  }
+
+  /** 
+   * Parse Batch Response body (as {@link InputStream}) and provide a list of single responses as {@link BatchSingleResponse}
+   *
+   * @param content response body
+   * @param contentType format of content in the given input stream (inclusive boundary parameter)
+   * @return list of {@link BatchSingleResponse}
+   * @throws BatchException 
+   */
+  public static List<BatchSingleResponse> parseBatchResponse(final InputStream content, final String contentType) throws BatchException {
+    return createEntityProvider().parseBatchResponse(contentType, content);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProviderReadProperties.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProviderReadProperties.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProviderReadProperties.java
index f64aa93..97e4002 100644
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProviderReadProperties.java
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/ep/EntityProviderReadProperties.java
@@ -25,28 +25,28 @@ import java.util.Map;
 import org.apache.olingo.odata2.api.ep.callback.OnReadInlineContent;
 
 /**
- * The {@link EntityProviderReadProperties} contains all necessary settings to read an entity with the {@link EntityProvider}.
- * 
- * The main settings are
+ * <p>The {@link EntityProviderReadProperties} contains all necessary settings
+ * to read an entity with the {@link EntityProvider}.</p>
+ * <p>The main settings are
  * <ul>
  * <li>the <code>mergeSemantic</code></li>
  * <li>the <code>callback for inlined navigation properties</code></li>
  * <li>and the <code>type mappings</code></li>
  * </ul>
- * 
+ * </p>
  *  
  */
 public class EntityProviderReadProperties {
   /** Callback which is necessary if entity contains inlined navigation properties. */
   private OnReadInlineContent callback;
   /**
-   * if merge is <code>true</code> the input content is in context of an <b>merge</b> (e.g. MERGE, PATCH) read request, 
-   * otherwise if <code>false</code> it is an <b>none merge</b> (e.g. CREATE) read request
+   * if merge is <code>true</code> the input content is in context of a <b>merge</b> (e.g. MERGE, PATCH) read request, 
+   * otherwise if <code>false</code> it is a <b>non-merge</b> (e.g. CREATE) read request
    */
   private boolean merge;
   /**
-   * typeMappings contains mappings from <code>edm property name</code> to <code>java class</code> which should be used 
-   * for a type mapping during read of content. If according <code>edm property</code> can not be read
+   * typeMappings contains mappings from <code>edm property name</code> to <code>java class</code> which should be used
+   * for a type mapping during reading of content. If according <code>edm property</code> can not be read
    * into given <code>java class</code> an {@link EntityProviderException} is thrown.
    * Supported mappings are documented in {@link org.apache.olingo.odata2.api.edm.EdmSimpleType}.
    */
@@ -78,6 +78,14 @@ public class EntityProviderReadProperties {
     return callback;
   }
 
+  /**
+   * <p>Gets the merge semantics.</p>
+   * <p>Merge semantics is set if the input content has to be treated in the context
+   * of a request to merge incoming data with existing data (e.g., indicated by the
+   * HTTP verbs <code>MERGE</code> or <code>PATCH</code> in a server application).
+   * Otherwise the request, even if not all data are supplied, is supposed to
+   * overwrite the existing entity completely.</p>
+   */
   public boolean getMergeSemantic() {
     return merge;
   }
@@ -97,6 +105,11 @@ public class EntityProviderReadProperties {
       addTypeMappings(propertiesFrom.typeMappings);
     }
 
+    /**
+     * Sets the merge semantics.
+     * @param mergeSemantic whether merge semantics is requested
+     * @see EntityProviderReadProperties#getMergeSemantic()
+     */
     public EntityProviderReadPropertiesBuilder mergeSemantic(final boolean mergeSemantic) {
       properties.merge = mergeSemantic;
       return this;

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-api/src/main/java/org/apache/olingo/odata2/api/rt/RuntimeDelegate.java
----------------------------------------------------------------------
diff --git a/odata-api/src/main/java/org/apache/olingo/odata2/api/rt/RuntimeDelegate.java b/odata-api/src/main/java/org/apache/olingo/odata2/api/rt/RuntimeDelegate.java
index 6590bc5..88c87cd 100644
--- a/odata-api/src/main/java/org/apache/olingo/odata2/api/rt/RuntimeDelegate.java
+++ b/odata-api/src/main/java/org/apache/olingo/odata2/api/rt/RuntimeDelegate.java
@@ -22,6 +22,9 @@ import java.io.InputStream;
 
 import org.apache.olingo.odata2.api.ODataService;
 import org.apache.olingo.odata2.api.batch.BatchResponsePart.BatchResponsePartBuilder;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSet.BatchChangeSetBuilder;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart.BatchChangeSetPartBuilder;
+import org.apache.olingo.odata2.api.client.batch.BatchQueryPart.BatchQueryPartBuilder;
 import org.apache.olingo.odata2.api.edm.Edm;
 import org.apache.olingo.odata2.api.edm.EdmSimpleType;
 import org.apache.olingo.odata2.api.edm.EdmSimpleTypeFacade;
@@ -96,6 +99,12 @@ public abstract class RuntimeDelegate {
 
     protected abstract ODataRequestBuilder createODataRequestBuilder();
 
+    protected abstract BatchChangeSetBuilder createBatchChangeSetBuilder();
+
+    protected abstract BatchQueryPartBuilder createBatchQueryRequestBuilder();
+
+    protected abstract BatchChangeSetPartBuilder createBatchChangeSetRequest();
+
   }
 
   /**
@@ -185,4 +194,16 @@ public abstract class RuntimeDelegate {
   public static ODataRequestBuilder createODataRequestBuilder() {
     return RuntimeDelegate.getInstance().createODataRequestBuilder();
   }
+
+  public static BatchChangeSetBuilder createBatchChangeSetBuilder() {
+    return RuntimeDelegate.getInstance().createBatchChangeSetBuilder();
+  }
+
+  public static BatchQueryPartBuilder createBatchQueryPartBuilder() {
+    return RuntimeDelegate.getInstance().createBatchQueryRequestBuilder();
+  }
+
+  public static BatchChangeSetPartBuilder createBatchChangeSetPartBuilder() {
+    return RuntimeDelegate.getInstance().createBatchChangeSetRequest();
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetImpl.java
new file mode 100644
index 0000000..2db859c
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetImpl.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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.olingo.odata2.core.batch;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSet;
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+
+public class BatchChangeSetImpl extends BatchChangeSet {
+  private List<BatchChangeSetPart> requests = new ArrayList<BatchChangeSetPart>();
+
+  @Override
+  public void add(final BatchChangeSetPart request) {
+    requests.add(request);
+  }
+
+  @Override
+  public List<BatchChangeSetPart> getChangeSetParts() {
+    return Collections.unmodifiableList(requests);
+  }
+
+  public class BatchChangeSetBuilderImpl extends BatchChangeSetBuilder {
+
+    @Override
+    public BatchChangeSet build() {
+      return BatchChangeSetImpl.this;
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java
new file mode 100644
index 0000000..9546017
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * 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.olingo.odata2.core.batch;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+
+public class BatchChangeSetPartImpl extends BatchChangeSetPart {
+  private String method;
+  private Map<String, String> headers = new HashMap<String, String>();
+  private String body;
+  private String uri;
+  public String contentId;
+  private static final String CHANGE_METHODS = "(PUT|POST|DELETE|MERGE|PATCH)";
+
+  @Override
+  public Map<String, String> getHeaders() {
+    return Collections.unmodifiableMap(headers);
+  }
+
+  @Override
+  public String getBody() {
+    return body;
+  }
+
+  @Override
+  public String getMethod() {
+    return method;
+  }
+
+  @Override
+  public String getUri() {
+    return uri;
+  }
+
+  @Override
+  public String getContentId() {
+    return contentId;
+  }
+
+  public class BatchChangeSetRequestBuilderImpl extends BatchChangeSetPartBuilder {
+    private String method;
+    private Map<String, String> headers = new HashMap<String, String>();
+    private String body;
+    private String uri;
+    private String contentId;
+
+    @Override
+    public BatchChangeSetPart build() {
+      if (method == null || uri == null) {
+        throw new IllegalArgumentException();
+      }
+      BatchChangeSetPartImpl.this.method = method;
+      BatchChangeSetPartImpl.this.headers = headers;
+      BatchChangeSetPartImpl.this.body = body;
+      BatchChangeSetPartImpl.this.uri = uri;
+      BatchChangeSetPartImpl.this.contentId = contentId;
+      return BatchChangeSetPartImpl.this;
+    }
+
+    @Override
+    public BatchChangeSetPartBuilder headers(final Map<String, String> headers) {
+      this.headers = headers;
+      return this;
+    }
+
+    @Override
+    public BatchChangeSetPartBuilder body(final String body) {
+      this.body = body;
+      return this;
+    }
+
+    @Override
+    public BatchChangeSetPartBuilder uri(final String uri) {
+      this.uri = uri;
+      return this;
+    }
+
+    @Override
+    public BatchChangeSetPartBuilder method(final String method) {
+      if (method != null && method.matches(CHANGE_METHODS)) {
+        this.method = method;
+      } else {
+        throw new IllegalArgumentException();
+      }
+      return this;
+    }
+
+    @Override
+    public BatchChangeSetPartBuilder contentId(final String contentId) {
+      this.contentId = contentId;
+      return this;
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchConstants.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchConstants.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchConstants.java
deleted file mode 100644
index 052365d..0000000
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchConstants.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*******************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- *        or more contributor license agreements.  See the NOTICE file
- *        distributed with this work for additional information
- *        regarding copyright ownership.  The ASF licenses this file
- *        to you under the Apache License, Version 2.0 (the
- *        "License"); you may not use this file except in compliance
- *        with the License.  You may obtain a copy of the License at
- * 
- *          http://www.apache.org/licenses/LICENSE-2.0
- * 
- *        Unless required by applicable law or agreed to in writing,
- *        software distributed under the License is distributed on an
- *        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *        KIND, either express or implied.  See the License for the
- *        specific language governing permissions and limitations
- *        under the License.
- ******************************************************************************/
-package org.apache.olingo.odata2.core.batch;
-
-public class BatchConstants {
-
-  public static final String HTTP_APPLICATION_HTTP = "application/http";
-
-  public static final String MULTIPART_MIXED = "multipart/mixed";
-
-  public static final String BINARY_ENCODING = "binary";
-
-  public static final String HTTP_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
-
-  public static final String HTTP_CONTENT_ID = "Content-Id";
-
-  public static final String MIME_HEADER_CONTENT_ID = "MimeHeader-ContentId";
-
-  public static final String REQUEST_HEADER_CONTENT_ID = "RequestHeader-ContentId";
-}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHandlerImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHandlerImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHandlerImpl.java
index 22dfd70..8869a33 100644
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHandlerImpl.java
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHandlerImpl.java
@@ -29,7 +29,7 @@ import java.util.Map;
 import org.apache.olingo.odata2.api.ODataService;
 import org.apache.olingo.odata2.api.ODataServiceFactory;
 import org.apache.olingo.odata2.api.batch.BatchHandler;
-import org.apache.olingo.odata2.api.batch.BatchPart;
+import org.apache.olingo.odata2.api.batch.BatchRequestPart;
 import org.apache.olingo.odata2.api.batch.BatchResponsePart;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
@@ -55,7 +55,7 @@ public class BatchHandlerImpl implements BatchHandler {
   }
 
   @Override
-  public BatchResponsePart handleBatchPart(final BatchPart batchPart) throws ODataException {
+  public BatchResponsePart handleBatchPart(final BatchRequestPart batchPart) throws ODataException {
     if (batchPart.isChangeSet()) {
       List<ODataRequest> changeSetRequests = batchPart.getRequests();
       contentIdMap = new HashMap<String, String>();
@@ -66,8 +66,8 @@ public class BatchHandlerImpl implements BatchHandler {
       }
       ODataRequest request = batchPart.getRequests().get(0);
       ODataRequestHandler handler = createHandler(request);
-      String mimeHeaderContentId = request.getRequestHeaderValue(BatchConstants.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
-      String requestHeaderContentId = request.getRequestHeaderValue(BatchConstants.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
+      String mimeHeaderContentId = request.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
+      String requestHeaderContentId = request.getRequestHeaderValue(BatchHelper.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
       ODataResponse response = setContentIdHeader(handler.handle(request), mimeHeaderContentId, requestHeaderContentId);
       List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
       responses.add(response);
@@ -78,8 +78,8 @@ public class BatchHandlerImpl implements BatchHandler {
   @Override
   public ODataResponse handleRequest(final ODataRequest suppliedRequest) throws ODataException {
     ODataRequest request;
-    String mimeHeaderContentId = suppliedRequest.getRequestHeaderValue(BatchConstants.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
-    String requestHeaderContentId = suppliedRequest.getRequestHeaderValue(BatchConstants.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
+    String mimeHeaderContentId = suppliedRequest.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
+    String requestHeaderContentId = suppliedRequest.getRequestHeaderValue(BatchHelper.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH));
 
     List<PathSegment> odataSegments = suppliedRequest.getPathInfo().getODataSegments();
     if (!odataSegments.isEmpty() && odataSegments.get(0).getPath().matches("\\$.*")) {
@@ -142,12 +142,12 @@ public class BatchHandlerImpl implements BatchHandler {
   private ODataResponse setContentIdHeader(final ODataResponse response, final String mimeHeaderContentId, final String requestHeaderContentId) {
     ODataResponse modifiedResponse;
     if (requestHeaderContentId != null && mimeHeaderContentId != null) {
-      modifiedResponse = ODataResponse.fromResponse(response).header(BatchConstants.REQUEST_HEADER_CONTENT_ID, requestHeaderContentId)
-          .header(BatchConstants.MIME_HEADER_CONTENT_ID, mimeHeaderContentId).build();
+      modifiedResponse = ODataResponse.fromResponse(response).header(BatchHelper.REQUEST_HEADER_CONTENT_ID, requestHeaderContentId)
+          .header(BatchHelper.MIME_HEADER_CONTENT_ID, mimeHeaderContentId).build();
     } else if (requestHeaderContentId != null) {
-      modifiedResponse = ODataResponse.fromResponse(response).header(BatchConstants.REQUEST_HEADER_CONTENT_ID, requestHeaderContentId).build();
+      modifiedResponse = ODataResponse.fromResponse(response).header(BatchHelper.REQUEST_HEADER_CONTENT_ID, requestHeaderContentId).build();
     } else if (mimeHeaderContentId != null) {
-      modifiedResponse = ODataResponse.fromResponse(response).header(BatchConstants.MIME_HEADER_CONTENT_ID, mimeHeaderContentId).build();
+      modifiedResponse = ODataResponse.fromResponse(response).header(BatchHelper.MIME_HEADER_CONTENT_ID, mimeHeaderContentId).build();
     } else {
       return response;
     }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java
new file mode 100644
index 0000000..38d02ae
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * 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.olingo.odata2.core.batch;
+
+import java.io.UnsupportedEncodingException;
+import java.util.UUID;
+
+import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
+
+public class BatchHelper {
+
+  public static final String BINARY_ENCODING = "binary";
+
+  public static final String DEFAULT_ENCODING = "utf-8";
+
+  public static final String HTTP_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
+
+  public static final String HTTP_CONTENT_ID = "Content-Id";
+
+  public static final String MIME_HEADER_CONTENT_ID = "MimeHeader-ContentId";
+
+  public static final String REQUEST_HEADER_CONTENT_ID = "RequestHeader-ContentId";
+
+  protected static String generateBoundary(final String value) {
+    return value + "_" + UUID.randomUUID().toString();
+  }
+
+  protected static byte[] getBytes(final String body) {
+    try {
+      return body.getBytes(DEFAULT_ENCODING);
+    } catch (UnsupportedEncodingException e) {
+      throw new ODataRuntimeException(e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchPartImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchPartImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchPartImpl.java
deleted file mode 100644
index 665b01b..0000000
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchPartImpl.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * Licensed to the Apache Software Foundation (ASF) under one
- *        or more contributor license agreements.  See the NOTICE file
- *        distributed with this work for additional information
- *        regarding copyright ownership.  The ASF licenses this file
- *        to you under the Apache License, Version 2.0 (the
- *        "License"); you may not use this file except in compliance
- *        with the License.  You may obtain a copy of the License at
- * 
- *          http://www.apache.org/licenses/LICENSE-2.0
- * 
- *        Unless required by applicable law or agreed to in writing,
- *        software distributed under the License is distributed on an
- *        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *        KIND, either express or implied.  See the License for the
- *        specific language governing permissions and limitations
- *        under the License.
- ******************************************************************************/
-package org.apache.olingo.odata2.core.batch;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.apache.olingo.odata2.api.batch.BatchPart;
-import org.apache.olingo.odata2.api.processor.ODataRequest;
-
-public class BatchPartImpl implements BatchPart {
-
-  private List<ODataRequest> requests = new ArrayList<ODataRequest>();
-  private boolean isChangeSet;
-
-  public BatchPartImpl() {}
-
-  public BatchPartImpl(final boolean isChangeSet, final List<ODataRequest> requests) {
-    this.isChangeSet = isChangeSet;
-    this.requests = requests;
-  }
-
-  @Override
-  public boolean isChangeSet() {
-    return isChangeSet;
-  }
-
-  public void setChangeSet(final boolean isChangeSet) {
-    this.isChangeSet = isChangeSet;
-  }
-
-  @Override
-  public List<ODataRequest> getRequests() {
-    return Collections.unmodifiableList(requests);
-  }
-
-  public void setRequests(final List<ODataRequest> requests) {
-    this.requests = requests;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchQueryPartImpl.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchQueryPartImpl.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchQueryPartImpl.java
new file mode 100644
index 0000000..e22a3b3
--- /dev/null
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchQueryPartImpl.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.olingo.odata2.core.batch;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
+
+public class BatchQueryPartImpl extends BatchQueryPart {
+  private String method;
+  private Map<String, String> headers = new HashMap<String, String>();
+  private String uri;
+  private String contentId;
+  private static final String GET = "GET";
+
+  @Override
+  public Map<String, String> getHeaders() {
+    return Collections.unmodifiableMap(headers);
+  }
+
+  @Override
+  public String getMethod() {
+    return method;
+  }
+
+  @Override
+  public String getUri() {
+    return uri;
+  }
+
+  @Override
+  public String getContentId() {
+    return contentId;
+  }
+
+  public class BatchQueryRequestBuilderImpl extends BatchQueryPartBuilder {
+    private String method;
+    private Map<String, String> headers = new HashMap<String, String>();
+    private String uri;
+    private String contentId;
+
+    @Override
+    public BatchQueryPart build() {
+      if (method == null || uri == null) {
+        throw new IllegalArgumentException();
+      }
+      BatchQueryPartImpl.this.method = method;
+      BatchQueryPartImpl.this.headers = headers;
+      BatchQueryPartImpl.this.uri = uri;
+      BatchQueryPartImpl.this.contentId = contentId;
+      return BatchQueryPartImpl.this;
+    }
+
+    @Override
+    public BatchQueryPartBuilder headers(final Map<String, String> headers) {
+      this.headers = headers;
+      return this;
+    }
+
+    @Override
+    public BatchQueryPartBuilder uri(final String uri) {
+      this.uri = uri;
+      return this;
+    }
+
+    @Override
+    public BatchQueryPartBuilder method(final String method) {
+      if (method != null && method.matches(GET)) {
+        this.method = method;
+      } else {
+        throw new IllegalArgumentException();
+      }
+      return this;
+    }
+
+    @Override
+    public BatchQueryPartBuilder contentId(final String contentId) {
+      this.contentId = contentId;
+      return this;
+    }
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata2/blob/45c27419/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestParser.java
----------------------------------------------------------------------
diff --git a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestParser.java b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestParser.java
index 4c0ae32..e37d572 100644
--- a/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestParser.java
+++ b/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestParser.java
@@ -21,10 +21,10 @@ package org.apache.olingo.odata2.core.batch;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -37,11 +37,13 @@ import java.util.regex.MatchResult;
 import java.util.regex.Pattern;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
-import org.apache.olingo.odata2.api.batch.BatchPart;
+import org.apache.olingo.odata2.api.batch.BatchRequestPart;
+import org.apache.olingo.odata2.api.commons.HttpContentType;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
 import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
 import org.apache.olingo.odata2.api.processor.ODataRequest;
+import org.apache.olingo.odata2.api.processor.ODataRequest.ODataRequestBuilder;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.api.uri.PathSegment;
 import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
@@ -64,7 +66,7 @@ public class BatchRequestParser {
   private static final Pattern REG_EX_ANY_BOUNDARY_STRING = Pattern.compile("--" + ANY_CHARACTERS + REG_EX_ZERO_OR_MORE_WHITESPACES);
   private static final Pattern REG_EX_REQUEST_LINE = Pattern.compile("(GET|POST|PUT|DELETE|MERGE|PATCH)\\s(.*)\\s?" + REG_EX_VERSION + REG_EX_ZERO_OR_MORE_WHITESPACES);
   private static final Pattern REG_EX_BOUNDARY_PARAMETER = Pattern.compile(REG_EX_OPTIONAL_WHITESPACE + "boundary=(\".*\"|.*)" + REG_EX_ZERO_OR_MORE_WHITESPACES);
-  private static final Pattern REG_EX_CONTENT_TYPE = Pattern.compile(REG_EX_OPTIONAL_WHITESPACE + BatchConstants.MULTIPART_MIXED);
+  private static final Pattern REG_EX_CONTENT_TYPE = Pattern.compile(REG_EX_OPTIONAL_WHITESPACE + HttpContentType.MULTIPART_MIXED);
   private static final Pattern REG_EX_QUERY_PARAMETER = Pattern.compile("((?:\\$|)[^=]+)=([^=]+)");
 
   private static final String REG_EX_BOUNDARY = "([a-zA-Z0-9_\\-\\.'\\+]{1,70})|\"([a-zA-Z0-9_\\-\\.'\\+\\s\\(\\),/:=\\?]{1,69}[a-zA-Z0-9_\\-\\.'\\+\\(\\),/:=\\?])\""; // See RFC 2046
@@ -74,19 +76,21 @@ public class BatchRequestParser {
   private String boundary;
   private String currentMimeHeaderContentId;
   private int currentLineNumber = 0;
-  private static Set<String> HTTP_CHANGESET_METHODS;
-  private static Set<String> HTTP_BATCH_METHODS;
+  private final static Set<String> HTTP_CHANGESET_METHODS;
+  private final static Set<String> HTTP_BATCH_METHODS;
 
   static {
-    HTTP_CHANGESET_METHODS = new HashSet<String>();
-    HTTP_CHANGESET_METHODS.add("POST");
-    HTTP_CHANGESET_METHODS.add("PUT");
-    HTTP_CHANGESET_METHODS.add("DELETE");
-    HTTP_CHANGESET_METHODS.add("MERGE");
-    HTTP_CHANGESET_METHODS.add("PATCH");
-
-    HTTP_BATCH_METHODS = new HashSet<String>();
-    HTTP_BATCH_METHODS.add("GET");
+    HashSet<String> httpChangesetMethods = new HashSet<String>();
+    httpChangesetMethods.add("POST");
+    httpChangesetMethods.add("PUT");
+    httpChangesetMethods.add("DELETE");
+    httpChangesetMethods.add("MERGE");
+    httpChangesetMethods.add("PATCH");
+    HTTP_CHANGESET_METHODS = Collections.unmodifiableSet(httpChangesetMethods);
+
+    HashSet<String> httpBatchMethods = new HashSet<String>();
+    httpBatchMethods.add("GET");
+    HTTP_BATCH_METHODS = Collections.unmodifiableSet(httpBatchMethods);
   }
 
   public BatchRequestParser(final String contentType, final EntityProviderBatchProperties properties) {
@@ -94,10 +98,10 @@ public class BatchRequestParser {
     batchRequestPathInfo = properties.getPathInfo();
   }
 
-  public List<BatchPart> parse(final InputStream in) throws BatchException {
-    Scanner scanner = new Scanner(in, "UTF-8").useDelimiter(LF);
+  public List<BatchRequestPart> parse(final InputStream in) throws BatchException {
+    Scanner scanner = new Scanner(in, BatchHelper.DEFAULT_ENCODING).useDelimiter(LF);
     baseUri = getBaseUri();
-    List<BatchPart> requestList;
+    List<BatchRequestPart> requestList;
     try {
       requestList = parseBatchRequest(scanner);
     } finally {// NOPMD (suppress DoNotThrowExceptionInFinally)
@@ -111,12 +115,12 @@ public class BatchRequestParser {
     return requestList;
   }
 
-  private List<BatchPart> parseBatchRequest(final Scanner scanner) throws BatchException {
-    List<BatchPart> requests = new LinkedList<BatchPart>();
+  private List<BatchRequestPart> parseBatchRequest(final Scanner scanner) throws BatchException {
+    List<BatchRequestPart> requests = new LinkedList<BatchRequestPart>();
     if (contentTypeMime != null) {
       boundary = getBoundary(contentTypeMime);
       parsePreamble(scanner);
-      String closeDelimiter = "--" + boundary + "--" + REG_EX_ZERO_OR_MORE_WHITESPACES;
+      final String closeDelimiter = "--" + boundary + "--" + REG_EX_ZERO_OR_MORE_WHITESPACES;
       while (scanner.hasNext() && !scanner.hasNext(closeDelimiter)) {
         requests.add(parseMultipart(scanner, boundary, false));
         parseNewLine(scanner);
@@ -131,7 +135,6 @@ public class BatchRequestParser {
       throw new BatchException(BatchException.MISSING_CONTENT_TYPE);
     }
     return requests;
-
   }
 
   //The method parses additional information prior to the first boundary delimiter line
@@ -142,37 +145,37 @@ public class BatchRequestParser {
     }
   }
 
-  private BatchPart parseMultipart(final Scanner scanner, final String boundary, final boolean isChangeSet) throws BatchException {
+  private BatchRequestPart parseMultipart(final Scanner scanner, final String boundary, final boolean isChangeSet) throws BatchException {
     Map<String, String> mimeHeaders = new HashMap<String, String>();
-    BatchPart multipart = null;
+    BatchRequestPart multipart = null;
     List<ODataRequest> requests = new ArrayList<ODataRequest>();
     if (scanner.hasNext("--" + boundary + REG_EX_ZERO_OR_MORE_WHITESPACES)) {
       scanner.next();
       currentLineNumber++;
       mimeHeaders = parseHeaders(scanner);
-      currentMimeHeaderContentId = mimeHeaders.get(BatchConstants.HTTP_CONTENT_ID.toLowerCase(Locale.ENGLISH));
+      currentMimeHeaderContentId = mimeHeaders.get(BatchHelper.HTTP_CONTENT_ID.toLowerCase(Locale.ENGLISH));
 
       String contentType = mimeHeaders.get(HttpHeaders.CONTENT_TYPE.toLowerCase(Locale.ENGLISH));
       if (contentType == null) {
         throw new BatchException(BatchException.MISSING_CONTENT_TYPE);
       }
       if (isChangeSet) {
-        if (BatchConstants.HTTP_APPLICATION_HTTP.equalsIgnoreCase(contentType)) {
-          validateEncoding(mimeHeaders.get(BatchConstants.HTTP_CONTENT_TRANSFER_ENCODING.toLowerCase(Locale.ENGLISH)));
+        if (HttpContentType.APPLICATION_HTTP.equalsIgnoreCase(contentType)) {
+          validateEncoding(mimeHeaders.get(BatchHelper.HTTP_CONTENT_TRANSFER_ENCODING.toLowerCase(Locale.ENGLISH)));
           parseNewLine(scanner);// mandatory
 
           requests.add(parseRequest(scanner, isChangeSet));
-          multipart = new BatchPartImpl(false, requests);
+          multipart = new BatchRequestPartImpl(false, requests);
         } else {
-          throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(BatchConstants.HTTP_APPLICATION_HTTP));
+          throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(HttpContentType.APPLICATION_HTTP));
         }
       } else {
-        if (BatchConstants.HTTP_APPLICATION_HTTP.equalsIgnoreCase(contentType)) {
-          validateEncoding(mimeHeaders.get(BatchConstants.HTTP_CONTENT_TRANSFER_ENCODING.toLowerCase(Locale.ENGLISH)));
+        if (HttpContentType.APPLICATION_HTTP.equalsIgnoreCase(contentType)) {
+          validateEncoding(mimeHeaders.get(BatchHelper.HTTP_CONTENT_TRANSFER_ENCODING.toLowerCase(Locale.ENGLISH)));
           parseNewLine(scanner);// mandatory
           requests.add(parseRequest(scanner, isChangeSet));
-          multipart = new BatchPartImpl(false, requests);
-        } else if (contentType.matches(REG_EX_OPTIONAL_WHITESPACE + BatchConstants.MULTIPART_MIXED + ANY_CHARACTERS)) {
+          multipart = new BatchRequestPartImpl(false, requests);
+        } else if (contentType.matches(REG_EX_OPTIONAL_WHITESPACE + HttpContentType.MULTIPART_MIXED + ANY_CHARACTERS)) {
           String changeSetBoundary = getBoundary(contentType);
           if (boundary.equals(changeSetBoundary)) {
             throw new BatchException(BatchException.INVALID_CHANGESET_BOUNDARY.addContent(currentLineNumber));
@@ -181,16 +184,14 @@ public class BatchRequestParser {
           parseNewLine(scanner);// mandatory
           Pattern changeSetCloseDelimiter = Pattern.compile("--" + changeSetBoundary + "--" + REG_EX_ZERO_OR_MORE_WHITESPACES);
           while (!scanner.hasNext(changeSetCloseDelimiter)) {
-            BatchPart part = parseMultipart(scanner, changeSetBoundary, true);
-            if (part.getRequests().size() == 1) {
-              changeSetRequests.add(part.getRequests().get(0));
-            }
+            BatchRequestPart part = parseMultipart(scanner, changeSetBoundary, true);
+            changeSetRequests.addAll(part.getRequests());
           }
           scanner.next(changeSetCloseDelimiter);
           currentLineNumber++;
-          multipart = new BatchPartImpl(true, changeSetRequests);
+          multipart = new BatchRequestPartImpl(true, changeSetRequests);
         } else {
-          throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(BatchConstants.MULTIPART_MIXED + " or " + BatchConstants.HTTP_APPLICATION_HTTP));
+          throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(HttpContentType.MULTIPART_MIXED + " or " + HttpContentType.APPLICATION_HTTP));
         }
       }
     } else if (scanner.hasNext(boundary + REG_EX_ZERO_OR_MORE_WHITESPACES)) {
@@ -208,60 +209,62 @@ public class BatchRequestParser {
   }
 
   private ODataRequest parseRequest(final Scanner scanner, final boolean isChangeSet) throws BatchException {
-    try {
-      ODataRequest request;
-      if (scanner.hasNext(REG_EX_REQUEST_LINE)) {
-        scanner.next(REG_EX_REQUEST_LINE);
-        currentLineNumber++;
-        String method = null;
-        String uri = null;
-        MatchResult result = scanner.match();
-        if (result.groupCount() == 2) {
-          method = result.group(1);
-          uri = result.group(2).trim();
-        } else {
-          currentLineNumber++;
-          throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(scanner.next()).addContent(currentLineNumber));
-        }
-        PathInfo pathInfo = parseRequestUri(uri);
-        Map<String, String> queryParameters = parseQueryParameters(uri);
-        if (isChangeSet) {
-          if (!HTTP_CHANGESET_METHODS.contains(method)) {
-            throw new BatchException(BatchException.INVALID_CHANGESET_METHOD.addContent(currentLineNumber));
-          }
-        } else if (!HTTP_BATCH_METHODS.contains(method)) {
-          throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD.addContent(currentLineNumber));
-        }
-        ODataHttpMethod httpMethod = ODataHttpMethod.valueOf(method);
-        Map<String, List<String>> headers = parseRequestHeaders(scanner);
-        if (currentMimeHeaderContentId != null) {
-          List<String> headerList = new ArrayList<String>();
-          headerList.add(currentMimeHeaderContentId);
-          headers.put(BatchConstants.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH), headerList);
-        }
-
-        String contentType = getContentTypeHeader(headers);
-        List<String> acceptHeaders = getAcceptHeader(headers);
-        List<Locale> acceptLanguages = getAcceptLanguageHeader(headers);
-        parseNewLine(scanner);
-        InputStream body;
-        body = new ByteArrayInputStream("".getBytes("UTF-8"));
-        if (isChangeSet) {
-          body = parseBody(scanner);
-        }
-        if (contentType != null) {
-          request = ODataRequest.method(httpMethod).queryParameters(queryParameters).requestHeaders(headers).pathInfo(pathInfo).acceptableLanguages(acceptLanguages).contentType(contentType).body(body).acceptHeaders(acceptHeaders).build();
-        } else {
-          request = ODataRequest.method(httpMethod).queryParameters(queryParameters).requestHeaders(headers).pathInfo(pathInfo).acceptableLanguages(acceptLanguages).body(body).acceptHeaders(acceptHeaders).build();
-        }
+    if (scanner.hasNext(REG_EX_REQUEST_LINE)) {
+      scanner.next(REG_EX_REQUEST_LINE);
+      currentLineNumber++;
+      final String method;
+      final String uri;
+      MatchResult result = scanner.match();
+      if (result.groupCount() == 2) {
+        method = result.group(1);
+        uri = result.group(2).trim();
       } else {
         currentLineNumber++;
         throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(scanner.next()).addContent(currentLineNumber));
       }
-      return request;
-    } catch (UnsupportedEncodingException e) {
-      throw new ODataRuntimeException(e);
+      PathInfo pathInfo = parseRequestUri(uri);
+      Map<String, String> queryParameters = parseQueryParameters(uri);
+      if (isChangeSet) {
+        if (!HTTP_CHANGESET_METHODS.contains(method)) {
+          throw new BatchException(BatchException.INVALID_CHANGESET_METHOD.addContent(currentLineNumber));
+        }
+      } else if (!HTTP_BATCH_METHODS.contains(method)) {
+        throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD.addContent(currentLineNumber));
+      }
+      ODataHttpMethod httpMethod = ODataHttpMethod.valueOf(method);
+      Map<String, List<String>> headers = parseRequestHeaders(scanner);
+      if (currentMimeHeaderContentId != null) {
+        List<String> headerList = new ArrayList<String>();
+        headerList.add(currentMimeHeaderContentId);
+        headers.put(BatchHelper.MIME_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH), headerList);
+      }
+
+      String contentType = getContentTypeHeader(headers);
+      List<String> acceptHeaders = getAcceptHeader(headers);
+      List<Locale> acceptLanguages = getAcceptLanguageHeader(headers);
+      parseNewLine(scanner);
+      InputStream body = new ByteArrayInputStream(new byte[0]);
+      if (isChangeSet) {
+        body = parseBody(scanner);
+      }
+
+      ODataRequestBuilder requestBuilder = ODataRequest.method(httpMethod)
+          .queryParameters(queryParameters)
+          .requestHeaders(headers)
+          .pathInfo(pathInfo)
+          .acceptableLanguages(acceptLanguages)
+          .body(body)
+          .acceptHeaders(acceptHeaders);
+
+      if (contentType != null) {
+        requestBuilder = requestBuilder.contentType(contentType);
+      }
+      return requestBuilder.build();
+    } else {
+      currentLineNumber++;
+      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(scanner.next()).addContent(currentLineNumber));
     }
+
   }
 
   private Map<String, List<String>> parseRequestHeaders(final Scanner scanner) throws BatchException {
@@ -280,7 +283,7 @@ public class BatchRequestParser {
           } else if (HttpHeaders.ACCEPT_LANGUAGE.equalsIgnoreCase(headerName)) {
             List<String> acceptLanguageHeaders = parseAcceptableLanguages(headerValue);
             headers.put(headerName, acceptLanguageHeaders);
-          } else if (!BatchConstants.HTTP_CONTENT_ID.equalsIgnoreCase(headerName)) {
+          } else if (!BatchHelper.HTTP_CONTENT_ID.equalsIgnoreCase(headerName)) {
             if (headers.containsKey(headerName)) {
               headers.get(headerName).add(headerValue);
             } else {
@@ -291,7 +294,7 @@ public class BatchRequestParser {
           } else {
             List<String> headerList = new ArrayList<String>();
             headerList.add(headerValue);
-            headers.put(BatchConstants.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH), headerList);
+            headers.put(BatchHelper.REQUEST_HEADER_CONTENT_ID.toLowerCase(Locale.ENGLISH), headerList);
           }
         }
       } else {
@@ -306,40 +309,65 @@ public class BatchRequestParser {
     PathInfoImpl pathInfo = new PathInfoImpl();
     pathInfo.setServiceRoot(batchRequestPathInfo.getServiceRoot());
     pathInfo.setPrecedingPathSegment(batchRequestPathInfo.getPrecedingSegments());
-    Scanner uriScanner = new Scanner(uri);
-    Pattern regexRequestUri = Pattern.compile("(?:" + baseUri + ")?/?([^?]+)(\\?.*)?");
-    if (uriScanner.hasNext(regexRequestUri)) {
-      uriScanner.next(regexRequestUri);
-      MatchResult result = uriScanner.match();
-      if (result.groupCount() == 2) {
-        String odataPathSegmentsAsString = result.group(1);
-        String queryParametersAsString = result.group(2) != null ? result.group(2) : "";
-        pathInfo.setODataPathSegment(parseODataPathSegments(odataPathSegmentsAsString));
-        try {
-          if (!odataPathSegmentsAsString.startsWith("$")) {
-            String requestUri = baseUri + "/" + odataPathSegmentsAsString + queryParametersAsString;
-            pathInfo.setRequestUri(new URI(requestUri));
+    final String odataPathSegmentsAsString;
+    final String queryParametersAsString;
+    try {
+      Scanner uriScanner = new Scanner(uri).useDelimiter(LF);
+      URI uriObject = new URI(uri);
+      if (uriObject.isAbsolute()) {
+        Pattern regexRequestUri = Pattern.compile(baseUri + "/([^/][^?]*)(\\?.*)?");
+        if (uriScanner.hasNext(regexRequestUri)) {
+          uriScanner.next(regexRequestUri);
+          MatchResult result = uriScanner.match();
+          if (result.groupCount() == 2) {
+            odataPathSegmentsAsString = result.group(1);
+            queryParametersAsString = result.group(2) != null ? result.group(2) : "";
+          } else {
+            uriScanner.close();
+            throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber));
           }
-        } catch (URISyntaxException e) {
+        } else {
           uriScanner.close();
-          throw new BatchException(BatchException.INVALID_URI, e);
+          throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber));
         }
       } else {
-        uriScanner.close();
-        throw new BatchException(BatchException.INVALID_URI);
+        Pattern regexRequestUri = Pattern.compile("([^/][^?]*)(\\?.*)?");
+        if (uriScanner.hasNext(regexRequestUri)) {
+          uriScanner.next(regexRequestUri);
+          MatchResult result = uriScanner.match();
+          if (result.groupCount() == 2) {
+            odataPathSegmentsAsString = result.group(1);
+            queryParametersAsString = result.group(2) != null ? result.group(2) : "";
+          } else {
+            uriScanner.close();
+            throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber));
+          }
+        } else if (uriScanner.hasNext("/(.*)")) {
+          uriScanner.close();
+          throw new BatchException(BatchException.UNSUPPORTED_ABSOLUTE_PATH.addContent(currentLineNumber));
+        } else {
+          uriScanner.close();
+          throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber));
+        }
+
       }
-    } else {
       uriScanner.close();
-      throw new BatchException(BatchException.INVALID_URI);
+      pathInfo.setODataPathSegment(parseODataPathSegments(odataPathSegmentsAsString));
+      if (!odataPathSegmentsAsString.startsWith("$")) {
+        String requestUri = baseUri + "/" + odataPathSegmentsAsString + queryParametersAsString;
+        pathInfo.setRequestUri(new URI(requestUri));
+      }
+      return pathInfo;
+    } catch (URISyntaxException e) {
+      throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber), e);
     }
-    uriScanner.close();
-    return pathInfo;
+
   }
 
   private Map<String, String> parseQueryParameters(final String uri) throws BatchException {
-    Scanner uriScanner = new Scanner(uri);
+    Scanner uriScanner = new Scanner(uri).useDelimiter("\n");
     Map<String, String> queryParametersMap = new HashMap<String, String>();
-    Pattern regex = Pattern.compile("(?:" + baseUri + ")?/?" + "[^?]+" + "\\?(.*)");
+    Pattern regex = Pattern.compile("(?:" + baseUri + "/)?" + "[^?]+" + "\\?(.*)");
     if (uriScanner.hasNext(regex)) {
       uriScanner.next(regex);
       MatchResult uriResult = uriScanner.match();
@@ -362,7 +390,7 @@ public class BatchRequestParser {
 
       } else {
         uriScanner.close();
-        throw new BatchException(BatchException.INVALID_URI);
+        throw new BatchException(BatchException.INVALID_URI.addContent(currentLineNumber));
       }
     }
     uriScanner.close();
@@ -388,30 +416,28 @@ public class BatchRequestParser {
   }
 
   private InputStream parseBody(final Scanner scanner) {
-    try {
-      String body = null;
-      InputStream requestBody;
-      while (scanner.hasNext() && !scanner.hasNext(REG_EX_ANY_BOUNDARY_STRING)) {
-        if (!scanner.hasNext(REG_EX_ZERO_OR_MORE_WHITESPACES)) {
-          if (body == null) {
-            body = scanner.next();
-          } else {
-            body = body + LF + scanner.next();
-          }
+    StringBuilder body = null;
+    final InputStream requestBody;
+
+    while (scanner.hasNext() && !scanner.hasNext(REG_EX_ANY_BOUNDARY_STRING)) {
+      if (!scanner.hasNext(REG_EX_ZERO_OR_MORE_WHITESPACES)) {
+        if (body == null) {
+          body = new StringBuilder(scanner.next());
         } else {
-          scanner.next();
+          body.append(LF).append(scanner.next());
         }
-        currentLineNumber++;
-      }
-      if (body != null) {
-        requestBody = new ByteArrayInputStream(body.getBytes("UTF-8"));
       } else {
-        requestBody = new ByteArrayInputStream("".getBytes("UTF-8"));
+        scanner.next();
       }
-      return requestBody;
-    } catch (UnsupportedEncodingException e) {
-      throw new ODataRuntimeException(e);
+      currentLineNumber++;
+    }
+
+    if (body != null) {
+      requestBody = new ByteArrayInputStream(BatchHelper.getBytes(body.toString()));
+    } else {
+      requestBody = new ByteArrayInputStream(new byte[0]);
     }
+    return requestBody;
   }
 
   private String getBoundary(final String contentType) throws BatchException {
@@ -420,7 +446,7 @@ public class BatchRequestParser {
       contentTypeScanner.next(REG_EX_CONTENT_TYPE);
     } else {
       contentTypeScanner.close();
-      throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(BatchConstants.MULTIPART_MIXED));
+      throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(HttpContentType.MULTIPART_MIXED));
     }
     if (contentTypeScanner.hasNext(REG_EX_BOUNDARY_PARAMETER)) {
       contentTypeScanner.next(REG_EX_BOUNDARY_PARAMETER);
@@ -438,7 +464,7 @@ public class BatchRequestParser {
   }
 
   private void validateEncoding(final String encoding) throws BatchException {
-    if (!BatchConstants.BINARY_ENCODING.equalsIgnoreCase(encoding)) {
+    if (!BatchHelper.BINARY_ENCODING.equalsIgnoreCase(encoding)) {
       throw new BatchException(BatchException.INVALID_CONTENT_TRANSFER_ENCODING);
     }
   }