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>();
 

Reply via email to