You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2014/10/14 20:16:17 UTC

[04/13] [OLINGO-129] Merge with current master

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmEntityContainerImplProv.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmEntityContainerImplProv.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmEntityContainerImplProv.java
index e4026e6..1472388 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmEntityContainerImplProv.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmEntityContainerImplProv.java
@@ -260,7 +260,7 @@ public class EdmEntityContainerImplProv implements EdmEntityContainer, EdmAnnota
       return entityContainerHierachy;
     }
 
-    List<EntityContainer> entityContainerHierachy = new ArrayList<EntityContainer>();
+    entityContainerHierachy = new ArrayList<EntityContainer>();
     Map<String, EntityContainer> name2Container = getEntityContainerMap();
     String currentName = getName();
     while (currentName != null) {

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmServiceMetadataImplProv.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmServiceMetadataImplProv.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmServiceMetadataImplProv.java
index 16262fc..e7853eb 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmServiceMetadataImplProv.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/edm/provider/EdmServiceMetadataImplProv.java
@@ -23,6 +23,7 @@ import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import javax.xml.stream.XMLOutputFactory;
@@ -104,29 +105,23 @@ public class EdmServiceMetadataImplProv implements EdmServiceMetadata {
     if (dataServiceVersion == null) {
       dataServiceVersion = ODataServiceVersion.V10;
 
-      if (schemas != null) {
-        for (Schema schema : schemas) {
-          List<EntityType> entityTypes = schema.getEntityTypes();
-          if (entityTypes != null) {
-            for (EntityType entityType : entityTypes) {
-              List<Property> properties = entityType.getProperties();
-              if (properties != null) {
-                for (Property property : properties) {
-                  if (property.getCustomizableFeedMappings() != null) {
-                    if (property.getCustomizableFeedMappings().getFcKeepInContent() != null) {
-                      if (!property.getCustomizableFeedMappings().getFcKeepInContent()) {
-                        dataServiceVersion = ODataServiceVersion.V20;
-                        return dataServiceVersion;
-                      }
-                    }
-                  }
+      for (Schema schema : listOrEmptyList(schemas)) {
+        List<EntityType> entityTypes = listOrEmptyList(schema.getEntityTypes());
+        for (EntityType entityType : entityTypes) {
+          List<Property> properties = listOrEmptyList(entityType.getProperties());
+          for (Property property : properties) {
+            if (property.getCustomizableFeedMappings() != null) {
+              if (property.getCustomizableFeedMappings().getFcKeepInContent() != null) {
+                if (!property.getCustomizableFeedMappings().getFcKeepInContent()) {
+                  dataServiceVersion = ODataServiceVersion.V20;
+                  return dataServiceVersion;
                 }
-                if (entityType.getCustomizableFeedMappings() != null) {
-                  if (entityType.getCustomizableFeedMappings().getFcKeepInContent() != null) {
-                    if (entityType.getCustomizableFeedMappings().getFcKeepInContent()) {
-                      dataServiceVersion = ODataServiceVersion.V20;
-                      return dataServiceVersion;
-                    }
+              }
+              if (entityType.getCustomizableFeedMappings() != null) {
+                if (entityType.getCustomizableFeedMappings().getFcKeepInContent() != null) {
+                  if (entityType.getCustomizableFeedMappings().getFcKeepInContent()) {
+                    dataServiceVersion = ODataServiceVersion.V20;
+                    return dataServiceVersion;
                   }
                 }
               }
@@ -148,8 +143,8 @@ public class EdmServiceMetadataImplProv implements EdmServiceMetadata {
       }
 
       for (Schema schema : schemas) {
-        for (EntityContainer entityContainer : schema.getEntityContainers()) {
-          for (EntitySet entitySet : entityContainer.getEntitySets()) {
+        for (EntityContainer entityContainer : listOrEmptyList(schema.getEntityContainers())) {
+          for (EntitySet entitySet : listOrEmptyList(entityContainer.getEntitySets())) {
             EdmEntitySetInfo entitySetInfo = new EdmEntitySetInfoImplProv(entitySet, entityContainer);
             entitySetInfos.add(entitySetInfo);
           }
@@ -160,4 +155,18 @@ public class EdmServiceMetadataImplProv implements EdmServiceMetadata {
 
     return entitySetInfos;
   }
+
+  /**
+   * Return original list if parameter is not NULL or an empty list.
+   *
+   * @param list list which is checked and probably returned
+   * @param <T> type of list
+   * @return original list if parameter is not NULL or an empty list
+   */
+  private <T> List<T> listOrEmptyList(List<T> list) {
+    if(list == null) {
+      return Collections.emptyList();
+    }
+    return list;
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImpl.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImpl.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImpl.java
index c76109b..4937e30 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImpl.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/ep/ProviderFacadeImpl.java
@@ -45,10 +45,9 @@ import org.apache.olingo.odata2.api.exception.ODataNotAcceptableException;
 import org.apache.olingo.odata2.api.processor.ODataErrorContext;
 import org.apache.olingo.odata2.api.processor.ODataResponse;
 import org.apache.olingo.odata2.api.servicedocument.ServiceDocument;
-import org.apache.olingo.odata2.core.batch.BatchRequestParser;
 import org.apache.olingo.odata2.core.batch.BatchRequestWriter;
-import org.apache.olingo.odata2.core.batch.BatchResponseParser;
 import org.apache.olingo.odata2.core.batch.BatchResponseWriter;
+import org.apache.olingo.odata2.core.batch.v2.BatchParser;
 import org.apache.olingo.odata2.core.commons.ContentType;
 import org.apache.olingo.odata2.core.edm.provider.EdmImplProv;
 import org.apache.olingo.odata2.core.edm.provider.EdmxProvider;
@@ -235,7 +234,7 @@ public class ProviderFacadeImpl implements EntityProviderInterface {
   @Override
   public List<BatchRequestPart> parseBatchRequest(final String contentType, final InputStream content,
       final EntityProviderBatchProperties properties) throws BatchException {
-    List<BatchRequestPart> batchParts = new BatchRequestParser(contentType, properties).parse(content);
+    List<BatchRequestPart> batchParts = new BatchParser(contentType, properties, true).parseBatchRequest(content);
     return batchParts;
   }
 
@@ -254,7 +253,7 @@ public class ProviderFacadeImpl implements EntityProviderInterface {
   @Override
   public List<BatchSingleResponse> parseBatchResponse(final String contentType, final InputStream content)
       throws BatchException {
-    List<BatchSingleResponse> responses = new BatchResponseParser(contentType).parse(content);
+    List<BatchSingleResponse> responses = new BatchParser(contentType, true).parseBatchResponse(content);
     return responses;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
index 7ab9ee1..5727b1d 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/rest/ODataSubLocator.java
@@ -34,6 +34,8 @@ import org.apache.olingo.odata2.api.ODataServiceFactory;
 import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
 import org.apache.olingo.odata2.api.exception.MessageReference;
 import org.apache.olingo.odata2.api.exception.ODataException;
+import org.apache.olingo.odata2.api.exception.ODataInternalServerErrorException;
+import org.apache.olingo.odata2.api.exception.ODataMessageException;
 import org.apache.olingo.odata2.api.exception.ODataNotImplementedException;
 import org.apache.olingo.odata2.api.processor.ODataContext;
 import org.apache.olingo.odata2.api.processor.ODataRequest;
@@ -110,6 +112,10 @@ public final class ODataSubLocator {
   private Response returnNotImplementedResponse(final MessageReference messageReference) {
     // RFC 2616, 5.1.1: "An origin server SHOULD return the status code [...]
     // 501 (Not Implemented) if the method is unrecognized [...] by the origin server."
+    return returnException(new ODataNotImplementedException(messageReference));
+  }
+
+  private Response returnException(final ODataMessageException messageException) {
     ODataContextImpl context = new ODataContextImpl(request, serviceFactory);
     context.setRequest(request);
     context.setAcceptableLanguages(request.getAcceptableLanguages());
@@ -119,9 +125,13 @@ public final class ODataSubLocator {
     ODataExceptionWrapper exceptionWrapper =
         new ODataExceptionWrapper(context, request.getQueryParameters(), request.getAcceptHeaders());
     ODataResponse response =
-        exceptionWrapper.wrapInExceptionResponse(new ODataNotImplementedException(messageReference));
+        exceptionWrapper.wrapInExceptionResponse(messageException);
     return RestUtil.convertResponse(response);
   }
+  
+  private Response returnNoServiceResponse(MessageReference messageReference) {
+    return returnException(new ODataInternalServerErrorException(messageReference));
+  }
 
   @OPTIONS
   public Response handleOptions() throws ODataException {
@@ -146,7 +156,9 @@ public final class ODataSubLocator {
     context.setParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT, httpRequest);
 
     ODataService service = serviceFactory.createService(context);
-
+    if(service == null){
+      return returnNoServiceResponse(ODataInternalServerErrorException.NOSERVICE);
+    }
     service.getProcessor().setContext(context);
     context.setService(service);
 
@@ -158,6 +170,8 @@ public final class ODataSubLocator {
     return response;
   }
 
+
+
   public static ODataSubLocator create(final SubLocatorParameter param) throws ODataException {
     ODataSubLocator subLocator = new ODataSubLocator();
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
index 1d4dfe3..2312170 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/servlet/ODataServlet.java
@@ -35,6 +35,7 @@ import org.apache.olingo.odata2.api.exception.MessageReference;
 import org.apache.olingo.odata2.api.exception.ODataBadRequestException;
 import org.apache.olingo.odata2.api.exception.ODataException;
 import org.apache.olingo.odata2.api.exception.ODataHttpException;
+import org.apache.olingo.odata2.api.exception.ODataInternalServerErrorException;
 import org.apache.olingo.odata2.api.exception.ODataMethodNotAllowedException;
 import org.apache.olingo.odata2.api.exception.ODataNotAcceptableException;
 import org.apache.olingo.odata2.api.exception.ODataNotImplementedException;
@@ -174,6 +175,9 @@ public class ODataServlet extends HttpServlet {
       context.setParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT, req);
 
       ODataService service = serviceFactory.createService(context);
+      if(service == null){
+        createServiceUnavailableResponse(req, ODataInternalServerErrorException.NOSERVICE, resp);
+      }
       context.setService(service);
       service.getProcessor().setContext(context);
 
@@ -271,7 +275,14 @@ public class ODataServlet extends HttpServlet {
     ODataResponse response =
         exceptionWrapper.wrapInExceptionResponse(new ODataNotAcceptableException(messageReference));
     createResponse(resp, response);
+  }
 
+  private void createServiceUnavailableResponse(HttpServletRequest req, MessageReference messageReference,
+      HttpServletResponse resp) throws IOException {
+    ODataExceptionWrapper exceptionWrapper = new ODataExceptionWrapper(req);
+    ODataResponse response =
+        exceptionWrapper.wrapInExceptionResponse(new ODataInternalServerErrorException(messageReference));
+    createResponse(resp, response);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/resources/i18n.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/resources/i18n.properties b/odata2-lib/odata-core/src/main/resources/i18n.properties
index 90b7045..2adda81 100644
--- a/odata2-lib/odata-core/src/main/resources/i18n.properties
+++ b/odata2-lib/odata-core/src/main/resources/i18n.properties
@@ -118,36 +118,38 @@ org.apache.olingo.odata2.api.ep.EntityProviderException.INVALID_DELETED_ENTRY_ME
 ##################################
 # BatchParserexceptions
 ##################################
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_BOUNDARY=The boundary of the ChangeSet should be different from that used by the Batch: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_LANGUAGE_HEADER=Invalid Accept-Language: '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_HEADER=Invalid Accept header: '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_BOUNDARY_DELIMITER=The boundary delimiter must begin with two hyphen characters: line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_OPERATION_METHOD=Invalid method: a Query Operation cannot contain insert, update or delete requests at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_METHOD= Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_PARAMETER=Invalid query parameters.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_URI=Invalid URI: line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.UNSUPPORTED_ABSOLUTE_PATH = An absolute-path in request line is not supported: line '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_BOUNDARY=Invalid boundary at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.NO_MATCH_WITH_BOUNDARY_STRING=The boundary string does not match the boundary from the Content-Type header field '%1$s': line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_CONTENT_TYPE=No Content-Type field for MIME-header is present.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TYPE=Content-Type should be '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_PARAMETER_IN_CONTENT_TYPE=The Content-Type field for multipart entities requires boundary parameter.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfer-Encoding should be binary.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_HEADER=Invalid Accept header: '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_LANGUAGE_HEADER=Invalid Accept-Language: '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_HEADER=Invalid header: '%1$s' at line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_BLANK_LINE=Expected empty line but was '%1$s': line '%2$s'  .
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfer-Encoding should be binary: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TYPE=Content-Type should be '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_BOUNDARY=The boundary of the ChangeSet should be different from that used by the Batch: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_METHOD= Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid request line '%1$s' at line '%2$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_PATHINFO=PathInfo should not be null.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_URI=Invalid URI: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_OPERATION_METHOD=Invalid method: a Query Operation cannot contain insert, update or delete requests at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_PARAMETER=Invalid query parameters.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_BLANK_LINE=Expected empty line but was '%1$s': line '%2$s'  .
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_CONTENT_TYPE=No Content-Type field for MIME-header is present.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_MANDATORY_HEADER=Missing mandatory header '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.MISSING_METHOD=Missing method in request line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid request line '%1$s' at line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid status line '%1$s' at line '%2$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_PARAMETER_IN_CONTENT_TYPE=The Content-Type field for multipart entities requires boundary parameter.
+org.apache.olingo.odata2.api.batch.BatchException.NO_MATCH_WITH_BOUNDARY_STRING=The boundary string does not match the boundary from the Content-Type header field '%1$s': line '%2$s'.
 org.apache.olingo.odata2.api.batch.BatchException.TRUNCATED_BODY=Body is truncated: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.UNSUPPORTED_ABSOLUTE_PATH = An absolute-path in request line is not supported: line '%1$s'.
 
 ##################################
 # HttpExceptions
 ##################################
 org.apache.olingo.odata2.api.exception.ODataHttpException.COMMON=Common exception
 
+org.apache.olingo.odata2.api.exception.ODataInternalServerErrorException.NOSERVICE=Service unavailable.
+
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.COMMON=Bad Request.
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.NOTSUPPORTED=The request is not supported by the processor.
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.INVALID_HEADER=The request contains an invalid header parameter '%1$s' with value '%2$s'.

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/main/resources/i18n_en.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/resources/i18n_en.properties b/odata2-lib/odata-core/src/main/resources/i18n_en.properties
index 564dfef..7de9229 100644
--- a/odata2-lib/odata-core/src/main/resources/i18n_en.properties
+++ b/odata2-lib/odata-core/src/main/resources/i18n_en.properties
@@ -97,6 +97,8 @@ org.apache.olingo.odata2.api.ep.EntityProviderException.ILLEGAL_ARGUMENT=Illegal
 ##################################
 org.apache.olingo.odata2.api.exception.ODataHttpException.COMMON=Common exception
 
+org.apache.olingo.odata2.api.exception.ODataInternalServerErrorException.NOSERVICE=Service unavailable.
+
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.COMMON=Bad Request.
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.NOTSUPPORTED=The request is not supported by the processor.
 org.apache.olingo.odata2.api.exception.ODataBadRequestException.URLTOOSHORT=The URL is too short.

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/AcceptParserTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/AcceptParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/AcceptParserTest.java
index a3c0512..b7d7fac 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/AcceptParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/AcceptParserTest.java
@@ -31,8 +31,10 @@ public class AcceptParserTest {
 
   @Test
   public void testAcceptHeader() throws BatchException {
-    List<String> acceptHeaders =
-        AcceptParser.parseAcceptHeaders("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+    
     assertNotNull(acceptHeaders);
     assertEquals(4, acceptHeaders.size());
     assertEquals("text/html", acceptHeaders.get(0));
@@ -43,48 +45,58 @@ public class AcceptParserTest {
 
   @Test
   public void testAcceptHeaderWithParameter() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("application/json;odata=verbose;q=1.0, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/json;odata=verbose;q=1.0, */*;q=0.1");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+    
     assertNotNull(acceptHeaders);
     assertEquals(2, acceptHeaders.size());
     assertEquals("application/json;odata=verbose", acceptHeaders.get(0));
-    ;
     assertEquals("*/*", acceptHeaders.get(1));
   }
 
   @Test
   public void testAcceptHeaderWithParameterAndLws() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("application/json;  odata=verbose;q=1.0, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/json;  odata=verbose;q=1.0, */*;q=0.1");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+
     assertNotNull(acceptHeaders);
     assertEquals(2, acceptHeaders.size());
     assertEquals("application/json;  odata=verbose", acceptHeaders.get(0));
-    ;
     assertEquals("*/*", acceptHeaders.get(1));
   }
 
   @Test
   public void testAcceptHeaderWithTabulator() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("application/json;\todata=verbose;q=1.0, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/json;\todata=verbose;q=1.0, */*;q=0.1");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+    
     assertNotNull(acceptHeaders);
     assertEquals(2, acceptHeaders.size());
     assertEquals("application/json;" + TAB + "odata=verbose", acceptHeaders.get(0));
-    ;
     assertEquals("*/*", acceptHeaders.get(1));
   }
 
   @Test
   public void testAcceptHeaderWithTwoParameters() throws BatchException {
-    List<String> acceptHeaders =
-        AcceptParser.parseAcceptHeaders("application/xml;another=test ; param=alskdf, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/xml;another=test ; param=alskdf, */*;q=0.1");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+
     assertNotNull(acceptHeaders);
     assertEquals(2, acceptHeaders.size());
     assertEquals("application/xml;another=test ; param=alskdf", acceptHeaders.get(0));
-    ;
     assertEquals("*/*", acceptHeaders.get(1));
   }
 
   @Test
   public void testAcceptHeader2() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("text/html;level=1, application/*, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("text/html;level=1, application/*, */*;q=0.1");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+
     assertNotNull(acceptHeaders);
     assertEquals(3, acceptHeaders.size());
     assertEquals("text/html;level=1", acceptHeaders.get(0));
@@ -94,7 +106,10 @@ public class AcceptParserTest {
 
   @Test
   public void testMoreSpecificMediaType() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("application/*, application/xml");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/*, application/xml");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+    
     assertNotNull(acceptHeaders);
     assertEquals(2, acceptHeaders.size());
     assertEquals("application/xml", acceptHeaders.get(0));
@@ -103,28 +118,40 @@ public class AcceptParserTest {
 
   @Test
   public void testQualityParameter() throws BatchException {
-    List<String> acceptHeaders = AcceptParser.parseAcceptHeaders("application/*, */*; q=0.012");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("application/*, */*; q=0.012");
+    List<String> acceptHeaders = parser.parseAcceptHeaders();
+    
     assertNotNull(acceptHeaders);
   }
 
   @Test(expected = BatchException.class)
   public void testInvalidAcceptHeader() throws BatchException {
-    AcceptParser.parseAcceptHeaders("appi cation/*, */*;q=0.1");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("appi cation/*, */*;q=0.1");
+    parser.parseAcceptHeaders();
   }
 
   @Test(expected = BatchException.class)
   public void testInvalidQualityParameter() throws BatchException {
-    AcceptParser.parseAcceptHeaders("appication/*, */*;q=0,9");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("appication/*, */*;q=0,9");
+    parser.parseAcceptHeaders();
   }
 
   @Test(expected = BatchException.class)
   public void testInvalidQualityParameter2() throws BatchException {
-    AcceptParser.parseAcceptHeaders("appication/*, */*;q=1.0001");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptHeaderValue("appication/*, */*;q=1.0001");
+    parser.parseAcceptHeaders();
   }
 
   @Test
   public void testAcceptLanguages() throws BatchException {
-    List<String> acceptLanguageHeaders = AcceptParser.parseAcceptableLanguages("en-US,en;q=0.7,en-UK;q=0.9");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptLanguageHeaderValue("en-US,en;q=0.7,en-UK;q=0.9");
+    List<String> acceptLanguageHeaders = parser.parseAcceptableLanguages();
+
     assertNotNull(acceptLanguageHeaders);
     assertEquals(3, acceptLanguageHeaders.size());
     assertEquals("en-US", acceptLanguageHeaders.get(0));
@@ -134,20 +161,28 @@ public class AcceptParserTest {
 
   @Test
   public void testAllAcceptLanguages() throws BatchException {
-    List<String> acceptLanguageHeaders = AcceptParser.parseAcceptableLanguages("*");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptLanguageHeaderValue("*");
+    List<String> acceptLanguageHeaders = parser.parseAcceptableLanguages();
+    
     assertNotNull(acceptLanguageHeaders);
     assertEquals(1, acceptLanguageHeaders.size());
   }
 
   @Test
   public void testLongAcceptLanguageValue() throws BatchException {
-    List<String> acceptLanguageHeaders = AcceptParser.parseAcceptableLanguages("english");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptLanguageHeaderValue("english");
+    List<String> acceptLanguageHeaders = parser.parseAcceptableLanguages();
+    
     assertNotNull(acceptLanguageHeaders);
     assertEquals("english", acceptLanguageHeaders.get(0));
   }
 
   @Test(expected = BatchException.class)
   public void testInvalidAcceptLanguageValue() throws BatchException {
-    AcceptParser.parseAcceptableLanguages("en_US");
+    AcceptParser parser = new AcceptParser();
+    parser.addAcceptLanguageHeaderValue("en_US");
+    parser.parseAcceptableLanguages();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
new file mode 100644
index 0000000..89eb14a
--- /dev/null
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
@@ -0,0 +1,230 @@
+/*******************************************************************************
+ * 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 static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.olingo.odata2.api.batch.BatchException;
+import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.core.batch.v2.BatchParserCommon;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.odata2.core.batch.v2.Header;
+import org.junit.Test;
+
+public class BatchParserCommonTest {
+  
+  private static final String CRLF = "\r\n";
+  
+  @Test
+  public void testMultipleHeader() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Content-Id: 1" + CRLF,
+        "Content-Id: 2" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> contentIdHeaders = header.getHeaders(BatchHelper.HTTP_CONTENT_ID);
+    assertNotNull(contentIdHeaders);
+    assertEquals(2, contentIdHeaders.size());
+    assertEquals("1", contentIdHeaders.get(0));
+    assertEquals("2", contentIdHeaders.get(1));
+  }
+  
+  @Test
+  public void testMultipleHeaderSameValue() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Content-Id: 1" + CRLF,
+        "Content-Id: 1" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> contentIdHeaders = header.getHeaders(BatchHelper.HTTP_CONTENT_ID);
+    assertNotNull(contentIdHeaders);
+    assertEquals(1, contentIdHeaders.size());
+    assertEquals("1", contentIdHeaders.get(0));
+  }
+  
+  @Test
+  public void testHeaderSperatedByComma() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Content-Id: 1" + CRLF,
+        "Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> upgradeHeader = header.getHeaders("upgrade");
+    assertNotNull(upgradeHeader);
+    assertEquals(4, upgradeHeader.size());
+    assertEquals("HTTP/2.0", upgradeHeader.get(0));
+    assertEquals("SHTTP/1.3", upgradeHeader.get(1));
+    assertEquals("IRC/6.9", upgradeHeader.get(2));
+    assertEquals("RTA/x11", upgradeHeader.get(3));
+  }
+  
+  @Test
+  public void testMultipleAcceptHeader() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF,
+        "Accept: text/plain;q=0.3" + CRLF,
+        "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> acceptHeader = header.getHeaders(HttpHeaders.ACCEPT);
+    assertNotNull(acceptHeader);
+    assertEquals(4, acceptHeader.size());
+  }
+  
+  @Test
+  public void testMultipleAcceptHeaderSameValue() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF,
+        "Accept: application/atomsvc+xml;q=0.8" + CRLF,
+        "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> acceptHeader = header.getHeaders(HttpHeaders.ACCEPT);
+    assertNotNull(acceptHeader);
+    assertEquals(3, acceptHeader.size());
+  }
+  
+  @Test
+  public void testMultipleAccepLanguagetHeader() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+        "Accept-Language: de-DE;q=0.3" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> acceptLanguageHeader = header.getHeaders(HttpHeaders.ACCEPT_LANGUAGE);
+    assertNotNull(acceptLanguageHeader);
+    assertEquals(4, acceptLanguageHeader.size());
+  }
+  
+  @Test
+  public void testMultipleAccepLanguagetHeaderSameValue() throws BatchException {
+    String[] messageRaw = new String[] {
+        "Accept-Language:en-US,en;q=0.7,en-UK;q=0.9" + CRLF,
+        "Accept-Language:en-US,en;q=0.7" + CRLF,
+        "content-type: Application/http" + CRLF,
+        "content-transfer-encoding: Binary" + CRLF
+      };
+    List<Line> message = toLineList(messageRaw);
+    
+    final Header header = BatchParserCommon.consumeHeaders(message);
+    assertNotNull(header);
+    
+    final List<String> acceptLanguageHeader = header.getHeaders(HttpHeaders.ACCEPT_LANGUAGE);
+    assertNotNull(acceptLanguageHeader);
+    assertEquals(3, acceptLanguageHeader.size());
+  }
+  
+  @Test
+  public void testRemoveEndingCRLF() {
+    String line = "Test\r\n";
+    assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveLastEndingCRLF() {
+    String line = "Test\r\n\r\n";
+    assertEquals("Test\r\n", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveEndingCRLFWithWS() {
+    String line = "Test\r\n            ";
+    assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveEndingCRLFNothingToRemove() {
+    String line = "Hallo\r\nBla";
+    assertEquals("Hallo\r\nBla", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveEndingCRLFAll() {
+    String line = "\r\n";
+    assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveEndingCRLFSpace() {
+    String line = "\r\n                      ";
+    assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveLastEndingCRLFWithWS() {
+    String line = "Test            \r\n";
+    assertEquals("Test            ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+
+  @Test
+  public void testRemoveLastEndingCRLFWithWSLong() {
+    String line = "Test            \r\nTest2    \r\n";
+    assertEquals("Test            \r\nTest2    ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+  
+  private List<Line> toLineList(String[] messageRaw) {
+    final List<Line> lineList = new ArrayList<Line>();
+    int counter = 1;
+    
+    for(final String currentLine : messageRaw) {
+      lineList.add(new Line(currentLine, counter++));
+    }
+    
+    return lineList;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
index f9b19b9..447049e 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
@@ -6,9 +6,9 @@
  * 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
@@ -38,14 +38,11 @@ import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
 import org.apache.olingo.odata2.api.processor.ODataRequest;
 import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
 import org.apache.olingo.odata2.core.PathInfoImpl;
+import org.apache.olingo.odata2.core.batch.v2.BatchParser;
 import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 
-/**
- *  
- */
 public class BatchRequestParserTest {
 
   private static final String CRLF = "\r\n";
@@ -67,7 +64,6 @@ public class BatchRequestParserTest {
     PathInfoImpl pathInfo = new PathInfoImpl();
     pathInfo.setServiceRoot(new URI(SERVICE_ROOT));
     batchProperties = EntityProviderBatchProperties.init().pathInfo(pathInfo).build();
-
   }
 
   @Test
@@ -78,8 +74,8 @@ public class BatchRequestParserTest {
       throw new IOException("Requested file '" + fileName + "' was not found.");
     }
 
-    BatchRequestParser parser = new BatchRequestParser(contentType, batchProperties);
-    List<BatchRequestPart> batchRequestParts = parser.parse(in);
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
     assertNotNull(batchRequestParts);
     assertEquals(false, batchRequestParts.isEmpty());
     for (BatchRequestPart object : batchRequestParts) {
@@ -122,7 +118,7 @@ public class BatchRequestParserTest {
       }
     }
   }
-
+  
   @Test
   public void testImageInContent() throws IOException, BatchException, URISyntaxException {
     String fileName = "/batchWithContent.batch";
@@ -169,7 +165,7 @@ public class BatchRequestParserTest {
         for (ODataRequest request : requests) {
           assertEquals(ODataHttpMethod.POST, request.getMethod());
           assertEquals("100000", request.getRequestHeaderValue(HttpHeaders.CONTENT_LENGTH.toLowerCase()));
-          assertEquals("1", request.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID.toLowerCase()));
+          assertEquals("1", request.getRequestHeaderValue(BatchHelper.MIME_HEADER_CONTENT_ID));
           assertEquals("application/octet-stream", request.getContentType());
           InputStream body = request.getBody();
           assertEquals(content, StringHelper.inputStreamToString(body));
@@ -194,6 +190,7 @@ public class BatchRequestParserTest {
         + CRLF
         + "--changeset_f980-1cb6-94dd" + CRLF
         + MIME_HEADERS
+        + "Content-ID: changeRequest1" + CRLF
         + CRLF
         + "POST Employees('2') HTTP/1.1" + CRLF
         + "Content-Length: 100" + CRLF
@@ -225,8 +222,8 @@ public class BatchRequestParserTest {
         + GET_REQUEST
         + "--batch_1.2+34:2j)0?--";
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(contentType, batchProperties);
-    List<BatchRequestPart> batchRequestParts = parser.parse(in);
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
     assertNotNull(batchRequestParts);
     assertEquals(false, batchRequestParts.isEmpty());
   }
@@ -239,8 +236,8 @@ public class BatchRequestParserTest {
         + GET_REQUEST
         + "--batch_1740-bb84-2f7f--";
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(invalidContentType, batchProperties);
-    parser.parse(in);
+    BatchParser parser = new BatchParser(invalidContentType, batchProperties, true);
+    parser.parseBatchRequest(in);
   }
 
   @Test(expected = BatchException.class)
@@ -250,19 +247,19 @@ public class BatchRequestParserTest {
         + GET_REQUEST
         + "--batch_1740-bb84-2f7f--";
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(invalidContentType, batchProperties);
-    parser.parse(in);
+    BatchParser parser = new BatchParser(invalidContentType, batchProperties, true);
+    parser.parseBatchRequest(in);
   }
 
   @Test(expected = BatchException.class)
   public void testBoundaryParameterWithoutQuota() throws BatchException {
-    String invalidContentType = "multipart;boundary=batch_1740-bb:84-2f7f";
+    String invalidContentType = "multipart/mixed;boundary=batch_1740-bb:84-2f7f";
     String batch = "--batch_1740-bb:84-2f7f" + CRLF
         + GET_REQUEST
         + "--batch_1740-bb:84-2f7f--";
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(invalidContentType, batchProperties);
-    parser.parse(in);
+    BatchParser parser = new BatchParser(invalidContentType, batchProperties, true);
+    parser.parseBatchRequest(in);
   }
 
   @Test(expected = BatchException.class)
@@ -273,6 +270,51 @@ public class BatchRequestParserTest {
     parseInvalidBatchBody(batch);
   }
 
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion() throws BatchException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding:binary" + CRLF
+        + CRLF
+        + "GET Employees?$format=json" + CRLF
+        + "Host: localhost:8080" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion2() throws BatchException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding:binary" + CRLF
+        + CRLF
+        + "GET Employees?$format=json " + CRLF
+        + "Host: localhost:8080" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
+  @Test(expected=BatchException.class)
+  public void testMissingHttpVersion3() throws BatchException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding:binary" + CRLF
+        + CRLF
+        + "GET Employees?$format=json SMTP:3.1" + CRLF
+        + "Host: localhost:8080" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    
+    parseInvalidBatchBody(batch);
+  }
+  
   @Test(expected = BatchException.class)
   public void testBoundaryWithoutHyphen() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
@@ -290,6 +332,7 @@ public class BatchRequestParserTest {
         // + no boundary string
         + GET_REQUEST
         + "--batch_8194-cf13-1f56--";
+
     parseInvalidBatchBody(batch);
   }
 
@@ -346,20 +389,21 @@ public class BatchRequestParserTest {
         + "--batch_8194-cf13-1f56--";
     parseInvalidBatchBody(batch);
   }
-
+  
   @Test(expected = BatchException.class)
-  @Ignore("What should here throw an exception")
-  public void testMimeHeaderContentId() throws BatchException {
+  public void testGetRequestMissingCRLF() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
         + MIME_HEADERS
         + "Content-ID: 1" + CRLF
         + CRLF
         + "GET Employees('1')/EmployeeName HTTP/1.1" + CRLF
-        + CRLF
+        //+ CRLF  // Belongs to the GET request
+        + CRLF    //Belongs to the 
         + "--batch_8194-cf13-1f56--";
+    
     parseInvalidBatchBody(batch);
   }
-
+  
   @Test(expected = BatchException.class)
   public void testInvalidMethodForBatch() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
@@ -372,6 +416,22 @@ public class BatchRequestParserTest {
   }
 
   @Test(expected = BatchException.class)
+  public void testNoBoundaryFound() throws BatchException {
+    String batch = "batch_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "POST Employees('1')/EmployeeName HTTP/1.1" + CRLF
+        + CRLF;
+    parseInvalidBatchBody(batch);
+  }
+
+  @Test(expected = BatchException.class)
+  public void testBadRequest() throws BatchException {
+    String batch = "This is a bad request. There is no syntax and also no semantic";
+    parseInvalidBatchBody(batch);
+  }
+
+  @Test(expected = BatchException.class)
   public void testNoMethod() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
         + MIME_HEADERS
@@ -398,7 +458,7 @@ public class BatchRequestParserTest {
     parseInvalidBatchBody(batch);
   }
 
-  @Test(expected = BatchException.class)
+  @Test(expected=BatchException.class)
   public void testInvalidChangeSetBoundary() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
         + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
@@ -410,6 +470,69 @@ public class BatchRequestParserTest {
         + "Content-Type: application/json;odata=verbose" + CRLF
         + "MaxDataServiceVersion: 2.0" + CRLF
         + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    parseInvalidBatchBody(batch);
+  }
+  
+  @Test(expected=BatchException.class)
+  public void testNestedChangeset() throws BatchException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd2" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd2" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "POST Employees('2') HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "MaxDataServiceVersion: 2.0" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    parse(batch);
+  }
+  
+  @Test(expected = BatchException.class)
+  public void testMissingContentTransferEncoding() throws BatchException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + "Content-Type: application/http" + CRLF
+        // + "Content-Transfer-Encoding: binary" + CRLF
+        + CRLF
+        + "POST Employees('2') HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "MaxDataServiceVersion: 2.0" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + "--batch_8194-cf13-1f56--";
+    parseInvalidBatchBody(batch);
+  }
+
+  @Test(expected = BatchException.class)
+  public void testMissingContentType() throws BatchException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        // + "Content-Type: application/http" + CRLF
+        + "Content-Transfer-Encoding: binary" + CRLF
+        + CRLF
+        + "POST Employees('2') HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "MaxDataServiceVersion: 2.0" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
         + "--batch_8194-cf13-1f56--";
     parseInvalidBatchBody(batch);
   }
@@ -552,6 +675,197 @@ public class BatchRequestParserTest {
     }
   }
 
+  @SuppressWarnings("unused")
+  @Test
+  public void testNegativeContentLengthChangeSet() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: -2" + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+  }
+
+  @SuppressWarnings("unused")
+  @Test(expected = BatchException.class)
+  public void testNegativeContentLengthRequest() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: -2" + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+  }
+
+  @Test
+  public void testContentLengthGreatherThanBodyLength() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: 100000" + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+    assertNotNull(batchRequestParts);
+    for (BatchRequestPart multipart : batchRequestParts) {
+      if (multipart.isChangeSet()) {
+        assertEquals(1, multipart.getRequests().size());
+        ODataRequest request = multipart.getRequests().get(0);
+        assertEquals("{\"EmployeeName\":\"Peter Fall\"}", inputStreamToString(request.getBody()));
+      }
+    }
+  }
+
+  @Test
+  public void testContentLengthSmallerThanBodyLength() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: 10" + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+    assertNotNull(batchRequestParts);
+    for (BatchRequestPart multipart : batchRequestParts) {
+      if (multipart.isChangeSet()) {
+        assertEquals(1, multipart.getRequests().size());
+        ODataRequest request = multipart.getRequests().get(0);
+        assertEquals("{\"Employee", inputStreamToString(request.getBody()));
+      }
+    }
+  }
+
+  @Test(expected = BatchException.class)
+  public void testNonNumericContentLength() throws BatchException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: 10abc" + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    parser.parseBatchRequest(in);
+  }
+
+  @Test
+  public void testNonStrictParser() throws BatchException, IOException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_8194-cf13-1f56" + CRLF
+        + "--changeset_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: myRequest" + CRLF
+        + "PUT Employees('2')/EmployeeName HTTP/1.1" + CRLF
+        + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "MaxDataServiceVersion: 2.0" + CRLF
+        + "{\"EmployeeName\":\"Frederic Fall MODIFIED\"}" + CRLF
+        + "--changeset_8194-cf13-1f56--" + CRLF
+        + "--batch_8194-cf13-1f56--";
+
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, false);
+    List<BatchRequestPart> requests = parser.parseBatchRequest(in);
+    assertNotNull(requests);
+    assertEquals(1, requests.size());
+
+    BatchRequestPart part = requests.get(0);
+    assertTrue(part.isChangeSet());
+    assertNotNull(part.getRequests());
+    assertEquals(1, part.getRequests().size());
+
+    ODataRequest changeRequest = part.getRequests().get(0);
+    assertEquals("{\"EmployeeName\":\"Frederic Fall MODIFIED\"}", inputStreamToString(changeRequest.getBody()));
+    assertEquals("application/json;odata=verbose", changeRequest.getContentType());
+    assertEquals(ODataHttpMethod.PUT, changeRequest.getMethod());
+  }
+
+  @Test(expected = BatchException.class)
+  public void testNonStrictParserMoreCRLF() throws BatchException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed;boundary=changeset_8194-cf13-1f56" + CRLF
+        + "--changeset_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + CRLF // Only one CRLF allowed
+        + "PUT Employees('2')/EmployeeName HTTP/1.1" + CRLF
+        + "Accept: application/atomsvc+xml;q=0.8, application/json;odata=verbose;q=0.5, */*;q=0.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "MaxDataServiceVersion: 2.0" + CRLF
+        + "{\"EmployeeName\":\"Frederic Fall MODIFIED\"}" + CRLF
+        + "--changeset_8194-cf13-1f56--" + CRLF
+        + "--batch_8194-cf13-1f56--";
+
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, false);
+    parser.parseBatchRequest(in);
+  }
+
   @Test
   public void testContentId() throws BatchException {
     String batch = "--batch_8194-cf13-1f56" + CRLF
@@ -586,8 +900,8 @@ public class BatchRequestParserTest {
         + CRLF
         + "--batch_8194-cf13-1f56--";
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(contentType, batchProperties);
-    List<BatchRequestPart> batchRequestParts = parser.parse(in);
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
     assertNotNull(batchRequestParts);
     for (BatchRequestPart multipart : batchRequestParts) {
       if (!multipart.isChangeSet()) {
@@ -612,10 +926,241 @@ public class BatchRequestParserTest {
     }
   }
 
+  @Test
+  public void testNoContentId() throws BatchException {
+    String batch = "--batch_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "GET Employees HTTP/1.1" + CRLF
+        + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+        + CRLF + CRLF
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "POST Employees HTTP/1.1" + CRLF
+        + "Content-type: application/octet-stream" + CRLF
+        + CRLF
+        + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in); // No exception should be thrown
+    assertNotNull(batchRequestParts);
+  }
+
+  @Test
+  public void testPreamble() throws BatchException, IOException {
+    String batch = ""
+        + "This is a preamble and must be ignored" + CRLF
+        + CRLF
+        + CRLF
+        + "----1242" + CRLF
+        + "--batch_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "GET Employees HTTP/1.1" + CRLF
+        + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+        + "Content-Id: BBB" + CRLF
+        + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "This is a preamble and must be ignored" + CRLF
+        + CRLF
+        + CRLF
+        + "----1242" + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-Id: " + CONTENT_ID_REFERENCE + CRLF
+        + CRLF
+        + "POST Employees HTTP/1.1" + CRLF
+        + "Content-type: application/octet-stream" + CRLF
+        + CRLF
+        + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+
+    assertNotNull(batchRequestParts);
+    assertEquals(2, batchRequestParts.size());
+
+    BatchRequestPart getRequestPart = batchRequestParts.get(0);
+    assertEquals(1, getRequestPart.getRequests().size());
+    ODataRequest getRequest = getRequestPart.getRequests().get(0);
+    assertEquals(ODataHttpMethod.GET, getRequest.getMethod());
+
+    BatchRequestPart changeSetPart = batchRequestParts.get(1);
+    assertEquals(2, changeSetPart.getRequests().size());
+    assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
+        + CRLF,
+        inputStreamToString(changeSetPart.getRequests().get(0).getBody()));
+    assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
+        inputStreamToString(changeSetPart.getRequests().get(1).getBody()));
+  }
+
+  @SuppressWarnings("unused")
+  @Test
+  public void testContentTypeCaseInsensitive() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: muLTiParT/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: -2" + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+  }
+
+  @Test
+  public void testContentTypeBoundaryCaseInsensitive() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; bOunDaRy=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + "Content-Length: -2" + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56--";
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+
+    assertNotNull(batchRequestParts);
+    assertEquals(1, batchRequestParts.size());
+    assertTrue(batchRequestParts.get(0).isChangeSet());
+    assertEquals(1, batchRequestParts.get(0).getRequests().size());
+  }
+
+  @Test
+  public void testEpilog() throws BatchException, IOException {
+    String batch = ""
+        + "--batch_8194-cf13-1f56" + CRLF
+        + MIME_HEADERS
+        + CRLF
+        + "GET Employees HTTP/1.1" + CRLF
+        + "accept: */*,application/atom+xml,application/atomsvc+xml,application/xml" + CRLF
+        + "Content-Id: BBB" + CRLF
+        + CRLF
+        + CRLF
+        + "--batch_8194-cf13-1f56" + CRLF
+        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-Id: " + CONTENT_ID_REFERENCE + CRLF
+        + CRLF
+        + "POST Employees HTTP/1.1" + CRLF
+        + "Content-type: application/octet-stream" + CRLF
+        + CRLF
+        + "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA" + CRLF
+        + CRLF
+        + "--changeset_f980-1cb6-94dd" + CRLF
+        + MIME_HEADERS
+        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
+        + "Content-Type: application/json;odata=verbose" + CRLF
+        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
+        + CRLF
+        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
+        + "--changeset_f980-1cb6-94dd--" + CRLF
+        + CRLF
+        + "This is an epilog and must be ignored" + CRLF
+        + CRLF
+        + CRLF
+        + "----1242"
+        + CRLF
+        + "--batch_8194-cf13-1f56--"
+        + CRLF
+        + "This is an epilog and must be ignored" + CRLF
+        + CRLF
+        + CRLF
+        + "----1242";
+
+    InputStream in = new ByteArrayInputStream(batch.getBytes());
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
+
+    assertNotNull(batchRequestParts);
+    assertEquals(2, batchRequestParts.size());
+
+    BatchRequestPart getRequestPart = batchRequestParts.get(0);
+    assertEquals(1, getRequestPart.getRequests().size());
+    ODataRequest getRequest = getRequestPart.getRequests().get(0);
+    assertEquals(ODataHttpMethod.GET, getRequest.getMethod());
+
+    BatchRequestPart changeSetPart = batchRequestParts.get(1);
+    assertEquals(2, changeSetPart.getRequests().size());
+    assertEquals("/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEAAAEaAAUAAAABAAAAYgEbAAUAAAA"
+        + CRLF,
+        inputStreamToString(changeSetPart.getRequests().get(0).getBody()));
+    assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
+        inputStreamToString(changeSetPart.getRequests().get(1).getBody()));
+  }
+
+  @Test
+  public void testLargeBatch() throws BatchException, IOException {
+    String fileName = "/batchLarge.batch";
+    InputStream in = ClassLoader.class.getResourceAsStream(fileName);
+    if (in == null) {
+      throw new IOException("Requested file '" + fileName + "' was not found.");
+    }
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    parser.parseBatchRequest(in);
+  }
+
   private List<BatchRequestPart> parse(final String batch) throws BatchException {
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(contentType, batchProperties);
-    List<BatchRequestPart> batchRequestParts = parser.parse(in);
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
     assertNotNull(batchRequestParts);
     assertEquals(false, batchRequestParts.isEmpty());
     return batchRequestParts;
@@ -623,7 +1168,18 @@ public class BatchRequestParserTest {
 
   private void parseInvalidBatchBody(final String batch) throws BatchException {
     InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchRequestParser parser = new BatchRequestParser(contentType, batchProperties);
-    parser.parse(in);
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    parser.parseBatchRequest(in);
+  }
+
+  private String inputStreamToString(final InputStream in) throws IOException {
+    int input;
+    final StringBuilder builder = new StringBuilder();
+
+    while ((input = in.read()) != -1) {
+      builder.append((char) input);
+    }
+
+    return builder.toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
index ca6b655..f526920 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
@@ -39,6 +39,7 @@ import org.apache.olingo.odata2.api.client.batch.BatchPart;
 import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
 import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
 import org.apache.olingo.odata2.core.PathInfoImpl;
+import org.apache.olingo.odata2.core.batch.v2.BatchParser;
 import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.Test;
 
@@ -88,8 +89,8 @@ public class BatchRequestTest {
     checkHeaders(headers, requestBody);
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
-    BatchRequestParser parser = new BatchRequestParser(contentType, parseProperties);
-    List<BatchRequestPart> parseResult = parser.parse(batchRequestStream.asStream());
+    BatchParser parser = new BatchParser(contentType, parseProperties, true);
+    List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream());
     assertEquals(1, parseResult.size());
   }
 
@@ -100,7 +101,7 @@ public class BatchRequestTest {
     headers.put("content-type", "application/json");
     BatchChangeSetPart request = BatchChangeSetPart.method(PUT)
         .uri("Employees('2')")
-        .body("{\"Возраст\":40}")
+        .body("{\"Возра�т\":40}")
         .headers(headers)
         .contentId("111")
         .build();
@@ -120,12 +121,12 @@ public class BatchRequestTest {
     assertTrue(requestBody.contains("--batch_"));
     assertTrue(requestBody.contains("--changeset_"));
     assertTrue(requestBody.contains("PUT Employees('2') HTTP/1.1"));
-    assertTrue(requestBody.contains("{\"Возраст\":40}"));
+    assertTrue(requestBody.contains("{\"Возра�т\":40}"));
     assertEquals(16, batchRequestStream.linesCount());
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
-    BatchRequestParser parser = new BatchRequestParser(contentType, parseProperties);
-    List<BatchRequestPart> parseResult = parser.parse(batchRequestStream.asStream());
+    BatchParser parser = new BatchParser(contentType, parseProperties, true);
+    List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream());
     assertEquals(1, parseResult.size());
   }
 
@@ -152,7 +153,6 @@ public class BatchRequestTest {
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
     assertNotNull(batchRequest);
-
     StringHelper.Stream batchRequestStream = StringHelper.toStream(batchRequest);
     String requestBody = batchRequestStream.asString();
     checkMimeHeaders(requestBody);
@@ -162,14 +162,14 @@ public class BatchRequestTest {
     assertTrue(requestBody.contains("GET Employees HTTP/1.1"));
     assertTrue(requestBody.contains("POST Employees HTTP/1.1"));
     assertTrue(requestBody.contains(body));
-    assertEquals(23, batchRequestStream.linesCount());
+    assertEquals(25, batchRequestStream.linesCount());
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
-    BatchRequestParser parser = new BatchRequestParser(contentType, parseProperties);
-    List<BatchRequestPart> parseResult = parser.parse(batchRequestStream.asStream());
+    BatchParser parser = new BatchParser(contentType, parseProperties, true);
+    List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream());
     assertEquals(2, parseResult.size());
   }
-
+  
   @Test
   public void testChangeSetWithContentIdReferencing() throws BatchException, IOException {
     List<BatchPart> batch = new ArrayList<BatchPart>();
@@ -212,8 +212,8 @@ public class BatchRequestTest {
     assertTrue(requestBody.contains(body));
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
-    BatchRequestParser parser = new BatchRequestParser(contentType, parseProperties);
-    List<BatchRequestPart> parseResult = parser.parse(batchRequestStream.asStream());
+    BatchParser parser = new BatchParser(contentType, parseProperties, true);
+    List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream());
     assertEquals(1, parseResult.size());
   }
 
@@ -227,6 +227,7 @@ public class BatchRequestTest {
     String body = "/9j/4AAQSkZJRgABAQEBLAEsAAD/4RM0RXhpZgAATU0AKgAAAAgABwESAAMAAAABAAEA";
     BatchChangeSetPart changeRequest = BatchChangeSetPart.method(POST)
         .uri("Employees")
+        .contentId("111request")
         .body(body)
         .headers(changeSetHeaders)
         .build();
@@ -239,6 +240,7 @@ public class BatchRequestTest {
     changeSetHeaders2.put("content-Id", "222");
     BatchChangeSetPart changeRequest2 = BatchChangeSetPart.method(PUT)
         .uri("Employees('2')/ManagerId")
+        .contentId("222request")
         .body("{\"ManagerId\":1}")
         .headers(changeSetHeaders2)
         .build();
@@ -260,8 +262,8 @@ public class BatchRequestTest {
     assertTrue(requestBody.contains(body));
 
     String contentType = "multipart/mixed; boundary=" + BOUNDARY;
-    BatchRequestParser parser = new BatchRequestParser(contentType, parseProperties);
-    List<BatchRequestPart> parseResult = parser.parse(batchRequestStream.asStream());
+    BatchParser parser = new BatchParser(contentType, parseProperties, true);
+    List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream());
     assertEquals(2, parseResult.size());
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/a6e2fbe5/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
index 051e1da..e8f7cd4 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestWriterTest.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -34,6 +35,8 @@ import org.apache.olingo.odata2.api.client.batch.BatchChangeSet;
 import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
 import org.apache.olingo.odata2.api.client.batch.BatchPart;
 import org.apache.olingo.odata2.api.client.batch.BatchQueryPart;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.testutil.helper.StringHelper;
 import org.junit.Test;
 
@@ -43,6 +46,7 @@ public class BatchRequestWriterTest {
   private static final String GET = "GET";
   private static final String PUT = "PUT";
   private static final String BOUNDARY = "batch_123";
+  private static final Object CRLF = "\r\n";
 
   private void checkMimeHeaders(final String requestBody) {
     assertTrue(requestBody.contains("Content-Type: application/http"));
@@ -67,7 +71,7 @@ public class BatchRequestWriterTest {
     assertTrue(requestBody.contains("--batch_"));
     assertTrue(requestBody.contains("GET Employees HTTP/1.1"));
     checkHeaders(headers, requestBody);
-    assertEquals(8, StringHelper.countLines(requestBody));
+    assertEquals(9, StringHelper.countLines(requestBody));
   }
 
   @Test
@@ -122,7 +126,7 @@ public class BatchRequestWriterTest {
     BatchRequestWriter writer = new BatchRequestWriter();
     InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
 
-    String requestBody = StringHelper.inputStreamToString(batchRequest);
+    String requestBody = StringHelper.inputStreamToString(batchRequest, true);
     assertNotNull(batchRequest);
     checkMimeHeaders(requestBody);
 
@@ -134,6 +138,50 @@ public class BatchRequestWriterTest {
   }
 
   @Test
+  public void testGetRequest() throws IOException {
+    List<BatchPart> batch = new ArrayList<BatchPart>();
+
+    Map<String, String> headers = new HashMap<String, String>();
+    headers.put("Accept", "application/json");
+    BatchPart request = BatchQueryPart.method(GET).uri("Employees").headers(headers).contentId("123").build();
+
+    batch.add(request);
+    batch.add(request);
+
+    BatchRequestWriter writer = new BatchRequestWriter();
+    InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY);
+    
+    BufferedReaderIncludingLineEndings reader =
+        new BufferedReaderIncludingLineEndings(new InputStreamReader(batchRequest));
+    List<Line> lines = reader.toList();
+    reader.close();
+    
+    int line = 0;
+    assertEquals("--" + BOUNDARY + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Id: 123" + CRLF, lines.get(line++).toString());
+    assertEquals(CRLF, lines.get(line++).toString());
+    assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(line++).toString());
+    assertEquals("Accept: application/json" + CRLF, lines.get(line++).toString());
+    assertEquals(CRLF, lines.get(line++).toString());   // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
+    
+    assertEquals(CRLF, lines.get(line++).toString());   // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
+    assertEquals("--" + BOUNDARY + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Type: application/http" + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Transfer-Encoding: binary" + CRLF, lines.get(line++).toString());
+    assertEquals("Content-Id: 123" + CRLF, lines.get(line++).toString());
+    assertEquals(CRLF, lines.get(line++).toString());
+    assertEquals("GET Employees HTTP/1.1" + CRLF, lines.get(line++).toString());
+    assertEquals("Accept: application/json" + CRLF, lines.get(line++).toString());
+    assertEquals(CRLF, lines.get(line++).toString());   // Belongs to the GET request [OData Protocol - 2.2.7.2.1]
+    
+    assertEquals(CRLF, lines.get(line++).toString());   // Belongs conceptually to the boundary [RFC 2046 - 5.1.1]
+    assertEquals("--" + BOUNDARY + "--", lines.get(line++).toString());
+    assertEquals(19, lines.size());
+  }
+
+  @Test
   public void testChangeSetWithContentIdReferencing() throws BatchException, IOException {
     List<BatchPart> batch = new ArrayList<BatchPart>();