You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ra...@apache.org on 2015/03/31 18:35:55 UTC

[09/11] olingo-odata4 git commit: OLINGO-573: New processing framework on server side with single interface with TripPin as example

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java
new file mode 100644
index 0000000..8641a69
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java
@@ -0,0 +1,315 @@
+/*
+ * 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.server.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.olingo.commons.api.ODataException;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.provider.Action;
+import org.apache.olingo.commons.api.edm.provider.ActionImport;
+import org.apache.olingo.commons.api.edm.provider.AliasInfo;
+import org.apache.olingo.commons.api.edm.provider.Annotatable;
+import org.apache.olingo.commons.api.edm.provider.Annotations;
+import org.apache.olingo.commons.api.edm.provider.ComplexType;
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.commons.api.edm.provider.EntityContainer;
+import org.apache.olingo.commons.api.edm.provider.EntityContainerInfo;
+import org.apache.olingo.commons.api.edm.provider.EntitySet;
+import org.apache.olingo.commons.api.edm.provider.EntityType;
+import org.apache.olingo.commons.api.edm.provider.EnumType;
+import org.apache.olingo.commons.api.edm.provider.Function;
+import org.apache.olingo.commons.api.edm.provider.FunctionImport;
+import org.apache.olingo.commons.api.edm.provider.Schema;
+import org.apache.olingo.commons.api.edm.provider.Singleton;
+import org.apache.olingo.commons.api.edm.provider.Term;
+import org.apache.olingo.commons.api.edm.provider.TypeDefinition;
+
+public class SchemaBasedEdmProvider implements EdmProvider {
+  private final List<Schema> edmSchemas = new ArrayList<Schema>();
+
+  protected void addSchema(Schema schema) {
+    this.edmSchemas.add(schema);
+  }
+
+  private Schema getSchema(String ns) {
+    for (Schema s : this.edmSchemas) {
+      if (s.getNamespace().equals(ns)) {
+        return s;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public EnumType getEnumType(FullQualifiedName fqn) throws ODataException {
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      List<EnumType> types = schema.getEnumTypes();
+      if (types != null) {
+        for (EnumType type : types) {
+          if (type.getName().equals(fqn.getName())) {
+            return type;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public TypeDefinition getTypeDefinition(FullQualifiedName fqn) throws ODataException {
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      List<TypeDefinition> types = schema.getTypeDefinitions();
+      if (types != null) {
+        for (TypeDefinition type : types) {
+          if (type.getName().equals(fqn.getName())) {
+            return type;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<Function> getFunctions(FullQualifiedName fqn) throws ODataException {
+    ArrayList<Function> foundFuncs = new ArrayList<Function>();
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      List<Function> functions = schema.getFunctions();
+      if (functions != null) {
+        for (Function func : functions) {
+          if (func.getName().equals(fqn.getName())) {
+            foundFuncs.add(func);
+          }
+        }
+      }
+    }
+    return foundFuncs;
+  }
+
+  @Override
+  public Term getTerm(FullQualifiedName fqn) throws ODataException {
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      List<Term> terms = schema.getTerms();
+      if (terms != null) {
+        for (Term term : terms) {
+          if (term.getName().equals(fqn.getName())) {
+            return term;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public EntitySet getEntitySet(FullQualifiedName fqn, String entitySetName) throws ODataException {
+    Schema schema = getSchema(fqn.getFullQualifiedNameAsString());
+    if (schema != null) {
+      EntityContainer ec = schema.getEntityContainer();
+      if (ec != null && ec.getEntitySets() != null) {
+        for (EntitySet es : ec.getEntitySets()) {
+          if (es.getName().equals(entitySetName)) {
+            return es;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public Singleton getSingleton(FullQualifiedName fqn, String singletonName) throws ODataException {
+    Schema schema = getSchema(fqn.getFullQualifiedNameAsString());
+    if (schema != null) {
+      EntityContainer ec = schema.getEntityContainer();
+      if (ec != null && ec.getSingletons() != null) {
+        for (Singleton es : ec.getSingletons()) {
+          if (es.getName().equals(singletonName)) {
+            return es;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public ActionImport getActionImport(FullQualifiedName fqn, String actionImportName)
+      throws ODataException {
+    Schema schema = getSchema(fqn.getFullQualifiedNameAsString());
+    if (schema != null) {
+      EntityContainer ec = schema.getEntityContainer();
+      if (ec != null && ec.getActionImports() != null) {
+        for (ActionImport es : ec.getActionImports()) {
+          if (es.getName().equals(actionImportName)) {
+            return es;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public FunctionImport getFunctionImport(FullQualifiedName fqn, String functionImportName)
+      throws ODataException {
+    Schema schema = getSchema(fqn.getFullQualifiedNameAsString());
+    if (schema != null) {
+      EntityContainer ec = schema.getEntityContainer();
+      if (ec != null && ec.getFunctionImports() != null) {
+        for (FunctionImport es : ec.getFunctionImports()) {
+          if (es.getName().equals(functionImportName)) {
+            return es;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public EntityContainerInfo getEntityContainerInfo(FullQualifiedName fqn) throws ODataException {
+    Schema schema = null;
+
+    if (fqn == null) {
+      for (Schema s : this.edmSchemas) {
+        if (s.getEntityContainer() != null) {
+          schema = s;
+          break;
+        }
+      }
+    } else {
+      schema = getSchema(fqn.getFullQualifiedNameAsString());
+    }
+
+    if (schema != null) {
+      EntityContainer ec = schema.getEntityContainer();
+      if (ec != null) {
+        EntityContainerInfo info = new EntityContainerInfo();
+        info.setContainerName(new FullQualifiedName(schema.getNamespace()));
+        if (schema.getEntityContainer().getExtendsContainer() != null) {
+          info.setExtendsContainer(new FullQualifiedName(schema.getEntityContainer().getExtendsContainer()));
+        }
+        return info;
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<AliasInfo> getAliasInfos() throws ODataException {
+    Schema schema = null;
+    for (Schema s : this.edmSchemas) {
+      if (s.getEntityContainer() != null) {
+        schema = s;
+        break;
+      }
+    }
+
+    if (schema == null) {
+      schema = this.edmSchemas.get(0);
+    }
+
+    AliasInfo ai = new AliasInfo();
+    ai.setAlias(schema.getAlias());
+    ai.setNamespace(schema.getNamespace());
+    return Arrays.asList(ai);
+  }
+
+  @Override
+  public EntityContainer getEntityContainer() throws ODataException {
+    // note that there can be many schemas, but only one needs to contain the
+    // entity container in a given metadata document.
+    for (Schema s : this.edmSchemas) {
+      if (s.getEntityContainer() != null) {
+        return s.getEntityContainer();
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<Schema> getSchemas() throws ODataException {
+    return new ArrayList<Schema>(this.edmSchemas);
+  }
+
+  @Override
+  public EntityType getEntityType(final FullQualifiedName fqn) throws ODataException {
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      if (schema.getEntityTypes() != null) {
+        for (EntityType type : schema.getEntityTypes()) {
+          if (type.getName().equals(fqn.getName())) {
+            return type;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public ComplexType getComplexType(final FullQualifiedName fqn) throws ODataException {
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      if (schema.getComplexTypes() != null) {
+        for (ComplexType type : schema.getComplexTypes()) {
+          if (type.getName().equals(fqn.getName())) {
+            return type;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public List<Action> getActions(final FullQualifiedName fqn) throws ODataException {
+    ArrayList<Action> actions = new ArrayList<Action>();
+    Schema schema = getSchema(fqn.getNamespace());
+    if (schema != null) {
+      List<Action> types = schema.getActions();
+      if (types != null) {
+        for (Action type : types) {
+          if (type.getName().equals(fqn.getName())) {
+            actions.add(type);
+          }
+        }
+      }
+    }
+    return actions;
+  }
+
+  @Override
+  public Annotations getAnnotationsGroup(FullQualifiedName targetName) throws ODataException {
+    return null;
+  }
+
+  @Override
+  public Annotatable getAnnoatatable(FullQualifiedName annotatedName) throws ODataException {
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java
new file mode 100644
index 0000000..839d877
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceDispatcher.java
@@ -0,0 +1,227 @@
+/*
+ * 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.server.core;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.uri.UriInfoBatch;
+import org.apache.olingo.server.api.uri.UriInfoCrossjoin;
+import org.apache.olingo.server.api.uri.UriInfoEntityId;
+import org.apache.olingo.server.api.uri.UriInfoMetadata;
+import org.apache.olingo.server.api.uri.UriInfoService;
+import org.apache.olingo.server.api.uri.UriResourceAction;
+import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
+import org.apache.olingo.server.api.uri.UriResourceCount;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.UriResourceFunction;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
+import org.apache.olingo.server.api.uri.UriResourceRef;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
+import org.apache.olingo.server.api.uri.UriResourceValue;
+import org.apache.olingo.server.core.requests.ActionRequest;
+import org.apache.olingo.server.core.requests.BatchRequest;
+import org.apache.olingo.server.core.requests.DataRequest;
+import org.apache.olingo.server.core.requests.FunctionRequest;
+import org.apache.olingo.server.core.requests.MediaRequest;
+import org.apache.olingo.server.core.requests.MetadataRequest;
+import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
+import org.apache.olingo.server.core.uri.parser.Parser;
+import org.apache.olingo.server.core.uri.validator.UriValidator;
+
+public class ServiceDispatcher extends RequestURLHierarchyVisitor {
+  private final OData odata;
+  protected ServiceMetadata metadata;
+  protected ServiceHandler handler;
+  protected CustomContentTypeSupport customContentSupport;
+  private String idOption;
+  protected ServiceRequest request;
+
+  public ServiceDispatcher(OData odata, ServiceMetadata metadata, ServiceHandler handler,
+      CustomContentTypeSupport customContentSupport) {
+    this.odata = odata;
+    this.metadata = metadata;
+    this.handler = handler;
+    this.customContentSupport = customContentSupport;
+  }
+
+  public void execute(ODataRequest odRequest, ODataResponse odResponse)
+      throws ODataTranslatedException, ODataApplicationException {
+
+    UriInfo uriInfo = new Parser().parseUri(odRequest.getRawODataPath(), odRequest.getRawQueryPath(), null,
+        this.metadata.getEdm());
+
+    new UriValidator().validate(uriInfo, odRequest.getMethod());
+
+    visit(uriInfo);
+
+    // this should cover for any unsupported calls until they are implemented
+    if (this.request == null) {
+      this.request = new ServiceRequest(this.odata, this.metadata) {
+        @Override
+        public ContentType getResponseContentType() throws ContentNegotiatorException {
+          return ContentType.APPLICATION_JSON;
+        }
+
+        @Override
+        public void execute(ServiceHandler handler, ODataResponse response)
+            throws ODataTranslatedException, ODataApplicationException {
+          handler.anyUnsupported(getODataRequest(), response);
+        }
+      };
+    }
+
+    // To handle $entity?$id=http://localhost/EntitySet(key) as
+    // http://localhost/EntitySet(key)
+    if (this.idOption != null) {
+      try {
+        this.request = this.request.parseLink(new URI(this.idOption));
+      } catch (URISyntaxException e) {
+        throw new ODataHandlerException("Invalid $id value",
+            ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED, this.idOption);
+      }
+    }
+
+    this.request.setODataRequest(odRequest);
+    this.request.setUriInfo(uriInfo);
+    this.request.setCustomContentTypeSupport(this.customContentSupport);
+    this.request.execute(this.handler, odResponse);
+  }
+
+  @Override
+  public void visit(UriInfoMetadata info) {
+    this.request = new MetadataRequest(this.odata, this.metadata);
+  }
+
+  @Override
+  public void visit(UriInfoService info) {
+    this.request = new ServiceDocumentRequest(this.odata, this.metadata);
+  }
+
+  @Override
+  public void visit(UriResourceEntitySet info) {
+    DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
+    dataRequest.setUriResourceEntitySet(info);
+    this.request = dataRequest;
+  }
+
+  @Override
+  public void visit(UriResourceCount option) {
+    DataRequest dataRequest = (DataRequest) this.request;
+    dataRequest.setCountRequest(option != null);
+  }
+
+  @Override
+  public void visit(UriResourceComplexProperty info) {
+    DataRequest dataRequest = (DataRequest) this.request;
+    dataRequest.setUriResourceProperty(info);
+  }
+
+  @Override
+  public void visit(UriResourcePrimitiveProperty info) {
+    DataRequest dataRequest = (DataRequest) this.request;
+    dataRequest.setUriResourceProperty(info);
+  }
+
+  @Override
+  public void visit(UriResourceValue info) {
+    DataRequest dataRequest = (DataRequest) this.request;
+    if (dataRequest.isPropertyRequest()) {
+      dataRequest.setValueRequest(info != null);
+    } else {
+      MediaRequest mediaRequest = new MediaRequest(this.odata, this.metadata);
+      mediaRequest.setUriResourceEntitySet(dataRequest.getUriResourceEntitySet());
+      this.request = mediaRequest;
+    }
+  }
+
+  @Override
+  public void visit(UriResourceAction info) {
+    ActionRequest actionRequest = new ActionRequest(this.odata, this.metadata);
+    actionRequest.setUriResourceAction(info);
+    this.request = actionRequest;
+  }
+
+  @Override
+  public void visit(UriResourceFunction info) {
+    FunctionRequest functionRequest = new FunctionRequest(this.odata, this.metadata);
+    functionRequest.setUriResourceFunction(info);
+    this.request = functionRequest;
+  }
+
+  @Override
+  public void visit(UriResourceNavigation info) {
+    DataRequest dataRequest = (DataRequest) this.request;
+    dataRequest.addUriResourceNavigation(info);
+  }
+
+  @Override
+  public void visit(UriResourceRef info) {
+    // this is same as data, but return is just entity references.
+    DataRequest dataRequest = (DataRequest) this.request;
+    dataRequest.setReferenceRequest(info != null);
+  }
+
+  @Override
+  public void visit(UriInfoBatch info) {
+    this.request = new BatchRequest(this.odata, this.metadata);
+  }
+
+  @Override
+  public void visit(UriResourceSingleton info) {
+    DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
+    dataRequest.setUriResourceSingleton(info);
+    this.request = dataRequest;
+  }
+
+  @Override
+  public void visit(UriInfoEntityId info) {
+    DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
+    this.request = dataRequest;
+
+    // this can relative or absolute form
+    String id = info.getIdOption().getValue();
+    try {
+      URL url = new URL(id);
+      this.idOption = url.getPath();
+    } catch (MalformedURLException e) {
+      this.idOption = id;
+    }
+    super.visit(info);
+  }
+
+  @Override
+  public void visit(UriInfoCrossjoin info) {
+    DataRequest dataRequest = new DataRequest(this.odata, this.metadata);
+    dataRequest.setCrossJoin(info);
+    this.request = dataRequest;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceHandler.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceHandler.java
new file mode 100644
index 0000000..8a9c610
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceHandler.java
@@ -0,0 +1,263 @@
+/*
+ * 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.server.core;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.core.requests.ActionRequest;
+import org.apache.olingo.server.core.requests.DataRequest;
+import org.apache.olingo.server.core.requests.FunctionRequest;
+import org.apache.olingo.server.core.requests.MediaRequest;
+import org.apache.olingo.server.core.requests.MetadataRequest;
+import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
+import org.apache.olingo.server.core.responses.EntityResponse;
+import org.apache.olingo.server.core.responses.MetadataResponse;
+import org.apache.olingo.server.core.responses.NoContentResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+import org.apache.olingo.server.core.responses.ServiceDocumentResponse;
+import org.apache.olingo.server.core.responses.ServiceResponse;
+import org.apache.olingo.server.core.responses.StreamResponse;
+
+public interface ServiceHandler extends Processor {
+
+  /**
+   * Read CSDL document of the Service
+   * @param request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void readMetadata(MetadataRequest request, MetadataResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Read ServiceDocument of the service
+   * @param request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Read operation for EntitySets, Entities, Properties, Media etc. Based on the type of request
+   * the response object is different. Even the navigation based queries are handled by this method.
+   * @param request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  <T extends ServiceResponse> void read(DataRequest request, T response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Create new entity in the service based on the entity object provided
+   * @param request
+   * @param entity
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void createEntity(DataRequest request, Entity entity, EntityResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Update the entity object.
+   * @param request
+   * @param entity
+   * @param merge - true if merge operation, false it needs to be replaced
+   * @param entityETag - previous entity tag if provided by the user. "*" means allow.
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void updateEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
+      EntityResponse response) throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Delete the Entity
+   * @param request
+   * @param entityETag - entity tag to match, if provided by the user. "*" means allow
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void deleteEntity(DataRequest request, String entityETag, EntityResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Update a non-media/stream property.if the value of property NULL, it should be treated as
+   * DeleteProperty 11.4.9.2
+   * @param request
+   * @param property - Updated property.
+   * @param merge - if the property is complex, true here means merge, false is replace
+   * @param entityETag - entity tag to match before update operation, "*" allows all.
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void updateProperty(DataRequest request, Property property, boolean merge, String entityETag,
+      PropertyResponse response) throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Update Stream property, if StreamContent is null, it should treated as delete request
+   * @param request
+   * @param entityETag - entity tag to match before update operation, "*" allows all.
+   * @param streamContent - updated stream content
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void upsertStreamProperty(DataRequest request, String entityETag, InputStream streamContent,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Invocation of a Function. The response object will be based on metadata defined for service
+   * @param request
+   * @param method
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  <T extends ServiceResponse> void invoke(FunctionRequest request, HttpMethod method, T response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Invocation of a Function. The response object will be based on metadata defined for service
+   * @param request
+   * @param eTag
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  <T extends ServiceResponse> void invoke(ActionRequest request, String eTag, T response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Read media stream content of a Entity
+   * @param request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void readMediaStream(MediaRequest request, StreamResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Update of Media Stream Content of a Entity. If the mediaContent is null it should be treated
+   * as delete request.
+   * @param request
+   * @param entityETag - entity etag to match before update operation, "*" allows all.
+   * @param mediaContent - if null, must be treated as delete request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void upsertMediaStream(MediaRequest request, String entityETag, InputStream mediaContent,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Any Unsupported one will be directed here.
+   * @param request
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void anyUnsupported(ODataRequest request, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Add references (relationships) to Entity.
+   * @param request
+   * @param entityETag - entity etag to match before add operation, "*" allows all.
+   * @param idReferences - references to add
+   * @param response - return always should be 204
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void addReference(DataRequest request, String entityETag, List<URI> idReferences, NoContentResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Update references (relationships) in an Entity
+   * @param request
+   * @param entityETag
+   * @param referenceId
+   * @param response - always should be 204
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void updateReference(DataRequest request, String entityETag, URI referenceId, NoContentResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  /**
+   * Delete references (relationships) in an Entity
+   * @param request
+   * @param deleteId
+   * @param entityETag
+   * @param response - always should be 204
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void deleteReference(DataRequest request, URI deleteId, String entityETag, NoContentResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+
+  /**
+   * During a batch operation, this method starts the transaction (if any) before any operation is handled
+   * by the service. No nested transactions.
+   * @return must return a unique transaction id that references a atomic operation.
+   */
+  String startTransaction();
+
+  /**
+   * When a batch operation is complete and all the intermediate service requests are successful, then
+   * commit is called with transaction id returned in the startTransaction method.
+   * @param txnId
+   */
+  void commit(String txnId);
+  /**
+   * When a batch operation is in-complete due to an error in the middle of changeset, then rollback is
+   * called with transaction id, that returned from startTransaction method.
+   * @param txnId
+   */
+  void rollback(String txnId);
+
+  /**
+   * This is not complete, more URL parsing changes required. Cross join between two entities.
+   * @param dataRequest
+   * @param entitySetNames
+   * @param response
+   * @throws ODataTranslatedException
+   * @throws ODataApplicationException
+   */
+  void crossJoin(DataRequest dataRequest, List<String> entitySetNames, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
new file mode 100644
index 0000000..e9a8cfe
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ServiceRequest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.server.core;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.core.requests.DataRequest;
+import org.apache.olingo.server.core.uri.parser.Parser;
+import org.apache.olingo.server.core.uri.parser.UriParserException;
+
+public abstract class ServiceRequest {
+  protected OData odata;
+  protected UriInfo uriInfo;
+  protected ServiceMetadata serviceMetadata;
+  protected CustomContentTypeSupport customContentType;
+  protected ODataRequest request;
+
+  public ServiceRequest(OData odata, ServiceMetadata serviceMetadata) {
+    this.odata = odata;
+    this.serviceMetadata = serviceMetadata;
+  }
+
+  public OData getOdata() {
+    return odata;
+  }
+
+  public ServiceMetadata getServiceMetaData() {
+    return this.serviceMetadata;
+  }
+
+  public UriInfo getUriInfo() {
+    return uriInfo;
+  }
+
+  protected void setUriInfo(UriInfo uriInfo) {
+    this.uriInfo = uriInfo;
+  }
+
+  public boolean allowedMethod() {
+    return isGET();
+  }
+
+  public CustomContentTypeSupport getCustomContentTypeSupport() {
+    return this.customContentType;
+  }
+
+  public void setCustomContentTypeSupport(CustomContentTypeSupport support) {
+    this.customContentType = support;
+  }
+
+  public ODataRequest getODataRequest() {
+    return this.request;
+  }
+
+  protected void setODataRequest(ODataRequest request) {
+    this.request = request;
+  }
+
+  public ContentType getRequestContentType() {
+    if (this.request.getHeader(HttpHeader.CONTENT_TYPE) != null) {
+      return ContentType.parse(this.request.getHeader(HttpHeader.CONTENT_TYPE));
+    }
+    return ContentType.APPLICATION_OCTET_STREAM;
+  }
+
+  public abstract void execute(ServiceHandler handler, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException;
+
+  public abstract ContentType getResponseContentType() throws ContentNegotiatorException;
+
+  public void methodNotAllowed() throws ODataHandlerException {
+    throw new ODataHandlerException("HTTP method " + this.request.getMethod() + " is not allowed.",
+        ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, this.request.getMethod()
+            .toString());
+  }
+
+  public void notImplemented() throws ODataHandlerException {
+    throw new ODataHandlerException("not implemented", //$NON-NLS-1$
+        ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+  }
+
+  protected boolean isGET() {
+    return this.request.getMethod() == HttpMethod.GET;
+  }
+
+  protected boolean isPUT() {
+    return this.request.getMethod() == HttpMethod.PUT;
+  }
+
+  protected boolean isDELETE() {
+    return this.request.getMethod() == HttpMethod.DELETE;
+  }
+
+  protected boolean isPATCH() {
+    return this.request.getMethod() == HttpMethod.PATCH;
+  }
+
+  protected boolean isPOST() {
+    return this.request.getMethod() == HttpMethod.POST;
+  }
+
+  public <T> T getSerializerOptions(Class<T> serilizerOptions, ContextURL contextUrl,
+      boolean references) throws ContentNegotiatorException {
+    final ODataFormat format = ODataFormat.fromContentType(getResponseContentType());
+
+    if (serilizerOptions.isAssignableFrom(EntitySerializerOptions.class)) {
+      return (T) EntitySerializerOptions.with()
+          .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : contextUrl)
+          .expand(uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption())
+          .setWriteOnlyReferences(references).build();
+    } else if (serilizerOptions.isAssignableFrom(EntityCollectionSerializerOptions.class)) {
+      return (T) EntityCollectionSerializerOptions.with()
+          .contextURL(format == ODataFormat.JSON_NO_METADATA ? null : contextUrl)
+          .count(uriInfo.getCountOption()).expand(uriInfo.getExpandOption())
+          .select(uriInfo.getSelectOption()).setWriteOnlyReferences(references).build();
+    } else if (serilizerOptions.isAssignableFrom(ComplexSerializerOptions.class)) {
+      return (T) ComplexSerializerOptions.with().contextURL(contextUrl)
+          .expand(this.uriInfo.getExpandOption()).select(this.uriInfo.getSelectOption()).build();
+    }
+    return null;
+  }
+
+  public ReturnRepresentation getReturnRepresentation() {
+    String prefer = this.request.getHeader(HttpHeader.PREFER);
+    if (prefer == null) {
+      return ReturnRepresentation.REPRESENTATION;
+    }
+    if (prefer.contains("return=minimal")) { //$NON-NLS-1$
+      return ReturnRepresentation.MINIMAL;
+    }
+    return ReturnRepresentation.REPRESENTATION;
+  }
+
+  public String getHeader(String key) {
+    return this.request.getHeader(key);
+  }
+
+  public String getETag() {
+    String etag = getHeader(HttpHeader.IF_MATCH);
+    if (etag == null) {
+      etag = getHeader(HttpHeader.IF_NONE_MATCH);
+    }
+    return ((etag == null) ? "*" : etag); //$NON-NLS-1$
+  }
+
+  public ODataSerializer getSerializer() throws ContentNegotiatorException,
+      SerializerException {
+    ODataFormat format = ODataFormat.fromContentType(getResponseContentType());
+    return this.odata.createSerializer(format);
+  }
+
+  public Map<String, String> getPreferences(){
+    HashMap<String, String> map = new HashMap<String, String>();
+    List<String> headers = request.getHeaders(HttpHeader.PREFER);
+    if (headers != null) {
+      for (String header:headers) {
+        int idx = header.indexOf('=');
+        if (idx != -1) {
+          String key = header.substring(0, idx);
+          String value = header.substring(idx+1);
+          if (value.startsWith("\"")) {
+            value = value.substring(1);
+          }
+          if (value.endsWith("\"")) {
+            value = value.substring(0, value.length()-1);
+          }
+          map.put(key, value);
+        } else {
+          map.put(header, "true");
+        }
+      }
+    }
+    return map;
+  }
+
+  public String getPreference(String key) {
+    return getPreferences().get(key);
+  }
+
+  public String getQueryParameter(String param) {
+    String queryPath = getODataRequest().getRawQueryPath();
+    if (queryPath != null) {
+      StringTokenizer st = new StringTokenizer(queryPath, ",");
+      while (st.hasMoreTokens()) {
+        String token = st.nextToken();
+        int index = token.indexOf('=');
+        if (index != -1) {
+          String key = token.substring(0, index);
+          String value = token.substring(index+1);
+          if (key.equals(param)) {
+            return value;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  public DataRequest parseLink(URI uri) throws UriParserException {
+    String rawPath = uri.getPath();
+    int e = rawPath.indexOf("/", 1);
+    if (-1 == e) {
+      rawPath = uri.getPath();
+    } else {
+      rawPath = rawPath.substring(e);
+    }
+
+    UriInfo uriInfo = new Parser().parseUri(rawPath, uri.getQuery(), null,
+        this.serviceMetadata.getEdm());
+    ServiceDispatcher dispatcher = new ServiceDispatcher(odata, serviceMetadata, null, customContentType);
+    dispatcher.visit(uriInfo);
+    return (DataRequest)dispatcher.request;
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/legacy/ProcessorServiceHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/legacy/ProcessorServiceHandler.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/legacy/ProcessorServiceHandler.java
new file mode 100644
index 0000000..fa8f445
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/legacy/ProcessorServiceHandler.java
@@ -0,0 +1,433 @@
+/*
+ * 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.server.core.legacy;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.processor.ComplexCollectionProcessor;
+import org.apache.olingo.server.api.processor.ComplexProcessor;
+import org.apache.olingo.server.api.processor.CountComplexCollectionProcessor;
+import org.apache.olingo.server.api.processor.CountEntityCollectionProcessor;
+import org.apache.olingo.server.api.processor.CountPrimitiveCollectionProcessor;
+import org.apache.olingo.server.api.processor.EntityCollectionProcessor;
+import org.apache.olingo.server.api.processor.EntityProcessor;
+import org.apache.olingo.server.api.processor.MediaEntityProcessor;
+import org.apache.olingo.server.api.processor.MetadataProcessor;
+import org.apache.olingo.server.api.processor.PrimitiveCollectionProcessor;
+import org.apache.olingo.server.api.processor.PrimitiveProcessor;
+import org.apache.olingo.server.api.processor.PrimitiveValueProcessor;
+import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.processor.ReferenceProcessor;
+import org.apache.olingo.server.api.processor.ServiceDocumentProcessor;
+import org.apache.olingo.server.core.ODataHandlerException;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.requests.ActionRequest;
+import org.apache.olingo.server.core.requests.DataRequest;
+import org.apache.olingo.server.core.requests.FunctionRequest;
+import org.apache.olingo.server.core.requests.MediaRequest;
+import org.apache.olingo.server.core.requests.MetadataRequest;
+import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
+import org.apache.olingo.server.core.responses.CountResponse;
+import org.apache.olingo.server.core.responses.EntityResponse;
+import org.apache.olingo.server.core.responses.EntitySetResponse;
+import org.apache.olingo.server.core.responses.MetadataResponse;
+import org.apache.olingo.server.core.responses.NoContentResponse;
+import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+import org.apache.olingo.server.core.responses.ServiceDocumentResponse;
+import org.apache.olingo.server.core.responses.ServiceResponse;
+import org.apache.olingo.server.core.responses.ServiceResponseVisior;
+import org.apache.olingo.server.core.responses.StreamResponse;
+
+public class ProcessorServiceHandler implements ServiceHandler {
+  private final List<Processor> processors = new LinkedList<Processor>();
+  private OData odata;
+  private ServiceMetadata serviceMetadata;
+
+  @Override
+  public void init(OData odata, ServiceMetadata serviceMetadata) {
+    this.odata = odata;
+    this.serviceMetadata = serviceMetadata;
+  }
+
+  public void register(Processor processor) {
+    this.processors.add(processor);
+    processor.init(odata, serviceMetadata);
+  }
+
+  private <T extends Processor> T selectProcessor(final Class<T> cls) throws ODataHandlerException {
+    for (final Processor processor : processors) {
+      if (cls.isAssignableFrom(processor.getClass())) {
+        processor.init(odata, serviceMetadata);
+        return cls.cast(processor);
+      }
+    }
+    throw new ODataHandlerException("Processor: " + cls.getSimpleName() + " not registered.",
+        ODataHandlerException.MessageKeys.PROCESSOR_NOT_IMPLEMENTED, cls.getSimpleName());
+  }
+
+  @Override
+  public void readMetadata(MetadataRequest request, MetadataResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(MetadataProcessor.class).readMetadata(request.getODataRequest(),
+        response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+  }
+
+  @Override
+  public void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(ServiceDocumentProcessor.class).readServiceDocument(request.getODataRequest(),
+        response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+  }
+
+  @Override
+  public <T extends ServiceResponse> void read(final DataRequest request, final T response)
+      throws ODataTranslatedException, ODataApplicationException {
+    response.accepts(new ServiceResponseVisior() {
+      @Override
+      public void visit(CountResponse response) throws ODataTranslatedException, ODataApplicationException {
+        if (request.getUriResourceProperty() != null) {
+          EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+          if (edmProperty.isPrimitive()) {
+            selectProcessor(CountPrimitiveCollectionProcessor.class).countPrimitiveCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo());
+          } else {
+            selectProcessor(CountComplexCollectionProcessor.class).countComplexCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo());
+          }
+        } else {
+          selectProcessor(CountEntityCollectionProcessor.class).countEntityCollection(
+              request.getODataRequest(), response.getODataResponse(), request.getUriInfo());
+        }
+      }
+
+      @Override
+      public void visit(EntityResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityProcessor.class).readEntity(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+
+      @Override
+      public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(PrimitiveValueProcessor.class).readPrimitiveValue(
+            request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+            request.getResponseContentType());
+      }
+
+      @Override
+      public void visit(PropertyResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+        if (edmProperty.isPrimitive()) {
+          if(edmProperty.isCollection()) {
+            selectProcessor(PrimitiveCollectionProcessor.class).readPrimitiveCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(PrimitiveProcessor.class).readPrimitive(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+          }
+        } else {
+          if(edmProperty.isCollection()) {
+            selectProcessor(ComplexCollectionProcessor.class).readComplexCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(ComplexProcessor.class).readComplex(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          }
+        }
+      }
+
+      @Override
+      public void visit(StreamResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        response.writeServerError(true);
+      }
+
+      @Override
+      public void visit(EntitySetResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityCollectionProcessor.class).readEntityCollection(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+    });
+  }
+
+  @Override
+  public void createEntity(DataRequest request, Entity entity, EntityResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    if (request.getEntitySet().getEntityType().hasStream()) {
+      selectProcessor(MediaEntityProcessor.class).createMediaEntity(
+          request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+          request.getRequestContentType(),request.getResponseContentType());
+    } else {
+      selectProcessor(EntityProcessor.class).createEntity(request.getODataRequest(),
+          response.getODataResponse(), request.getUriInfo(), request.getRequestContentType(),
+          request.getResponseContentType());
+    }
+  }
+
+  @Override
+  public void updateEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
+      EntityResponse response) throws ODataTranslatedException, ODataApplicationException {
+    if (request.getEntitySet().getEntityType().hasStream()) {
+      selectProcessor(MediaEntityProcessor.class).updateMediaEntity(
+          request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+          request.getRequestContentType(),request.getResponseContentType());
+    } else {
+    selectProcessor(EntityProcessor.class).updateEntity(request.getODataRequest(),
+        response.getODataResponse(), request.getUriInfo(), request.getRequestContentType(),
+        request.getResponseContentType());
+    }
+  }
+
+  @Override
+  public void deleteEntity(DataRequest request, String entityETag, EntityResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(EntityProcessor.class).deleteEntity(request.getODataRequest(),
+        response.getODataResponse(), request.getUriInfo());
+  }
+
+  @Override
+  public void updateProperty(DataRequest request, Property property, boolean merge,
+      String entityETag, PropertyResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    if (property.isPrimitive()) {
+      if (property.isCollection()) {
+        selectProcessor(PrimitiveCollectionProcessor.class).updatePrimitiveCollection(
+            request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+            request.getRequestContentType(), request.getResponseContentType());
+      } else {
+        selectProcessor(PrimitiveProcessor.class).updatePrimitive(
+            request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+            request.getRequestContentType(), request.getResponseContentType());
+      }
+    } else {
+      if (property.isCollection()) {
+        selectProcessor(ComplexCollectionProcessor.class).updateComplexCollection(
+            request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+            request.getRequestContentType(), request.getResponseContentType());
+      } else {
+        selectProcessor(ComplexProcessor.class).updateComplex(
+            request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+            request.getRequestContentType(), request.getResponseContentType());
+      }
+    }
+  }
+
+  @Override
+  public void upsertStreamProperty(DataRequest request, String entityETag,
+      InputStream streamContent, NoContentResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    throw new ODataHandlerException("not implemented",
+        ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+  }
+
+  @Override
+  public <T extends ServiceResponse> void invoke(final FunctionRequest request, HttpMethod method,
+      final T response) throws ODataTranslatedException, ODataApplicationException {
+    if (method != HttpMethod.GET) {
+      throw new ODataHandlerException("HTTP method " + method + " is not allowed.",
+          ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString());
+    }
+
+    response.accepts(new ServiceResponseVisior() {
+      @Override
+      public void visit(EntityResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityProcessor.class).readEntity(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+
+      @Override
+      public void visit(PropertyResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        if (request.isReturnTypePrimitive()) {
+          if(request.isCollection()) {
+            selectProcessor(PrimitiveCollectionProcessor.class).readPrimitiveCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(PrimitiveProcessor.class).readPrimitive(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+          }
+        } else {
+          if(request.isCollection()) {
+            selectProcessor(ComplexCollectionProcessor.class).readComplexCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(ComplexProcessor.class).readComplex(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+          }
+        }
+      }
+      @Override
+      public void visit(EntitySetResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityCollectionProcessor.class).readEntityCollection(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+    });
+  }
+
+  @Override
+  public <T extends ServiceResponse> void invoke(final ActionRequest request, String eTag, final T response)
+      throws ODataTranslatedException, ODataApplicationException {
+    final HttpMethod method = request.getODataRequest().getMethod();
+    if (method != HttpMethod.POST) {
+      throw new ODataHandlerException("HTTP method " + method + " is not allowed.",
+          ODataHandlerException.MessageKeys.HTTP_METHOD_NOT_ALLOWED, method.toString());
+    }
+    response.accepts(new ServiceResponseVisior() {
+      @Override
+      public void visit(EntityResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityProcessor.class).readEntity(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+
+      @Override
+      public void visit(PropertyResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        if (request.isReturnTypePrimitive()) {
+          if(request.isCollection()) {
+            selectProcessor(PrimitiveCollectionProcessor.class).readPrimitiveCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(PrimitiveProcessor.class).readPrimitive(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+          }
+        } else {
+          if(request.isCollection()) {
+            selectProcessor(ComplexCollectionProcessor.class).readComplexCollection(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+
+          } else {
+            selectProcessor(ComplexProcessor.class).readComplex(
+                request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+                request.getResponseContentType());
+          }
+        }
+      }
+      @Override
+      public void visit(EntitySetResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        selectProcessor(EntityCollectionProcessor.class).readEntityCollection(request.getODataRequest(),
+            response.getODataResponse(), request.getUriInfo(), request.getResponseContentType());
+      }
+    });
+  }
+
+
+  @Override
+  public void readMediaStream(MediaRequest request, StreamResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(MediaEntityProcessor.class).readMediaEntity(
+        request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+        request.getResponseContentType());
+  }
+
+  @Override
+  public void upsertMediaStream(MediaRequest request, String entityETag, InputStream mediaContent,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(MediaEntityProcessor.class).updateMediaEntity(
+        request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+        request.getRequestContentType(), request.getResponseContentType());
+  }
+
+  @Override
+  public void anyUnsupported(ODataRequest request, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    throw new ODataHandlerException("not implemented",
+        ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+  }
+
+  @Override
+  public void addReference(DataRequest request, String entityETag, List<URI> idReferences,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+      selectProcessor(ReferenceProcessor.class).createReference(
+          request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+          request.getResponseContentType());
+  }
+
+  @Override
+  public void updateReference(DataRequest request, String entityETag, URI referenceId,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(ReferenceProcessor.class).updateReference(
+        request.getODataRequest(), response.getODataResponse(), request.getUriInfo(),
+        request.getResponseContentType());
+  }
+
+  @Override
+  public void deleteReference(DataRequest request, URI deleteId, String entityETag,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+    selectProcessor(ReferenceProcessor.class).deleteReference(
+        request.getODataRequest(), response.getODataResponse(), request.getUriInfo());
+  }
+
+  @Override
+  public String startTransaction() {
+    return null;
+  }
+
+  @Override
+  public void commit(String txnId) {
+  }
+
+  @Override
+  public void rollback(String txnId) {
+  }
+
+  @Override
+  public void crossJoin(DataRequest dataRequest, List<String> entitySetNames, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    throw new ODataHandlerException("not implemented",
+        ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ActionRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ActionRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ActionRequest.java
new file mode 100644
index 0000000..133ee3e
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ActionRequest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.server.core.requests;
+
+import org.apache.olingo.commons.api.edm.EdmAction;
+import org.apache.olingo.commons.api.edm.EdmReturnType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.uri.UriResourceAction;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.responses.EntityResponse;
+import org.apache.olingo.server.core.responses.EntitySetResponse;
+import org.apache.olingo.server.core.responses.NoContentResponse;
+import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+
+public class ActionRequest extends OperationRequest {
+  private UriResourceAction uriResourceAction;
+
+  public ActionRequest(OData odata, ServiceMetadata serviceMetadata) {
+    super(odata, serviceMetadata);
+  }
+
+  @Override
+  public void execute(ServiceHandler handler, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+
+    if (!allowedMethod()) {
+      methodNotAllowed();
+    }
+    // Actions MAY return data but MUST NOT be further composed with additional
+    // path segments.
+    // On success, the response is 201 Created for actions that create entities,
+    // 200 OK for actions
+    // that return results or 204 No Content for action without a return type.
+    // The client can request
+    // whether any results from the action be returned using the Prefer header.
+
+    if (!hasReturnType()) {
+      handler.invoke(this, getETag(), new NoContentResponse(getServiceMetaData(), response));
+    } else {
+      if (isReturnTypePrimitive()) {
+        handler.invoke(this, getETag(),
+            PrimitiveValueResponse.getInstance(this, response, isCollection(), getReturnType()));
+      } else if (isReturnTypeComplex()) {
+        handler.invoke(this, getETag(), PropertyResponse.getInstance(this, response,
+            getReturnType().getType(), getContextURL(this.odata), isCollection()));
+      } else {
+        // EdmTypeKind.ENTITY
+        if (isCollection()) {
+          handler.invoke(this, getETag(),
+              EntitySetResponse.getInstance(this, getContextURL(odata), false, response));
+        } else {
+          handler.invoke(this, getETag(),
+              EntityResponse.getInstance(this, getContextURL(odata), false, response));
+        }
+      }
+    }
+  }
+
+  @Override
+  public boolean allowedMethod() {
+    // 11.5.4.1 Invoking an Action - only allows POST
+    return (isPOST());
+  }
+
+  public UriResourceAction getUriResourceAction() {
+    return uriResourceAction;
+  }
+
+  public void setUriResourceAction(UriResourceAction uriResourceAction) {
+    this.uriResourceAction = uriResourceAction;
+  }
+
+  @Override
+  public boolean isBound() {
+    return this.uriResourceAction.getActionImport() != null;
+  }
+
+  public EdmAction getAction() {
+    return this.uriResourceAction.getAction();
+  }
+
+  @Override
+  public boolean isCollection() {
+    assert (hasReturnType());
+    return getAction().getReturnType().isCollection();
+  }
+
+  @Override
+  public EdmReturnType getReturnType() {
+    assert (hasReturnType());
+    return getAction().getReturnType();
+  }
+
+  @Override
+  public boolean hasReturnType() {
+    return getAction().getReturnType() != null;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/BatchRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/BatchRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/BatchRequest.java
new file mode 100644
index 0000000..25af023
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/BatchRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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.server.core.requests;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.UUID;
+
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.batch.exception.BatchDeserializerException;
+import org.apache.olingo.server.api.deserializer.batch.BatchOptions;
+import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart;
+import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ErrorHandler;
+import org.apache.olingo.server.core.ServiceDispatcher;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.ServiceRequest;
+import org.apache.olingo.server.core.batchhandler.referenceRewriting.BatchReferenceRewriter;
+import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon;
+
+public class BatchRequest extends ServiceRequest {
+  private static final String PREFERENCE_CONTINUE_ON_ERROR = "odata.continue-on-error";
+  private final BatchReferenceRewriter rewriter;
+
+  public BatchRequest(OData odata, ServiceMetadata serviceMetadata) {
+    super(odata, serviceMetadata);
+    this.rewriter = new BatchReferenceRewriter();
+  }
+
+  @Override
+  public void execute(ServiceHandler handler, ODataResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+
+    if (!allowedMethod()) {
+      methodNotAllowed();
+    }
+
+    validateContentType();
+    boolean continueOnError = isContinueOnError();
+    final String boundary = extractBoundary(getRequestContentType());
+
+    final BatchOptions options = BatchOptions.with().rawBaseUri(request.getRawBaseUri())
+        .rawServiceResolutionUri(this.request.getRawServiceResolutionUri()).build();
+
+    final List<BatchRequestPart> parts = this.odata.createFixedFormatDeserializer()
+        .parseBatchRequest(request.getBody(), boundary, options);
+
+    ODataResponsePart partResponse = null;
+    final List<ODataResponsePart> responseParts = new ArrayList<ODataResponsePart>();
+
+    for (BatchRequestPart part : parts) {
+      if (part.isChangeSet()) {
+        String txnId = handler.startTransaction();
+        partResponse = processChangeSet(part, handler);
+        if (partResponse.getResponses().get(0).getStatusCode() > 400) {
+          handler.rollback(txnId);
+        }
+        handler.commit(txnId);
+      } else {
+        // single request, a static request
+        ODataRequest partRequest = part.getRequests().get(0);
+        partResponse = process(partRequest, handler);
+      }
+      responseParts.add(partResponse);
+
+      // on error, should we continue?
+      final int statusCode = partResponse.getResponses().get(0).getStatusCode();
+      if ((statusCode >= 400 && statusCode <= 600) && !continueOnError) {
+        break;
+      }
+    }
+
+    // send response
+    final String responseBoundary = "batch_" + UUID.randomUUID().toString();
+    ;
+    final InputStream responseContent = odata.createFixedFormatSerializer().batchResponse(
+        responseParts, responseBoundary);
+    response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.MULTIPART_MIXED + ";boundary="
+        + responseBoundary);
+    response.setContent(responseContent);
+    response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
+  }
+
+  ODataResponsePart process(ODataRequest partRequest, ServiceHandler serviceHandler) {
+    ODataResponse partResponse = executeSingleRequest(partRequest, serviceHandler);
+    addContentID(partRequest, partResponse);
+    return new ODataResponsePart(partResponse, false);
+  }
+
+  ODataResponsePart processChangeSet(BatchRequestPart partRequest, ServiceHandler serviceHandler)
+      throws BatchDeserializerException {
+    List<ODataResponse> changeSetResponses = new ArrayList<ODataResponse>();
+    // change set need to be a in a atomic operation
+    for (ODataRequest changeSetPartRequest : partRequest.getRequests()) {
+
+      this.rewriter.replaceReference(changeSetPartRequest);
+
+      ODataResponse partResponse = executeSingleRequest(changeSetPartRequest, serviceHandler);
+
+      this.rewriter.addMapping(changeSetPartRequest, partResponse);
+      addContentID(changeSetPartRequest, partResponse);
+
+      if (partResponse.getStatusCode() < 400) {
+        changeSetResponses.add(partResponse);
+      } else {
+        // 11.7.4 Responding to a Batch Request
+        return new ODataResponsePart(partResponse, false);
+      }
+    }
+    return new ODataResponsePart(changeSetResponses, true);
+  }
+
+  ODataResponse executeSingleRequest(ODataRequest singleRequest, ServiceHandler handler) {
+    ServiceDispatcher dispatcher = new ServiceDispatcher(this.odata, this.serviceMetadata, handler,
+        this.customContentType);
+    ODataResponse res = new ODataResponse();
+    try {
+      dispatcher.execute(singleRequest, res);
+    } catch (Exception e) {
+      ErrorHandler ehandler = new ErrorHandler(this.odata, this.serviceMetadata,
+          getCustomContentTypeSupport());
+      ehandler.handleException(e, singleRequest, res);
+    }
+    return res;
+  }
+
+  private void addContentID(ODataRequest batchPartRequest, ODataResponse batchPartResponse) {
+    final String contentId = batchPartRequest.getHeader(BatchParserCommon.HTTP_CONTENT_ID);
+    if (contentId != null) {
+      batchPartResponse.setHeader(BatchParserCommon.HTTP_CONTENT_ID, contentId);
+    }
+  }
+
+  @Override
+  public boolean allowedMethod() {
+    return isPOST();
+  }
+
+  private void validateContentType() throws ODataApplicationException {
+    final String contentType = getRequestContentType().toContentTypeString();
+
+    if (contentType == null
+        || !BatchParserCommon.PATTERN_MULTIPART_BOUNDARY.matcher(contentType).matches()) {
+      throw new ODataApplicationException("Invalid content type",
+          HttpStatusCode.PRECONDITION_FAILED.getStatusCode(), Locale.getDefault());
+    }
+  }
+
+  @Override
+  public ContentType getResponseContentType() throws ContentNegotiatorException {
+    return null;
+  }
+
+  private boolean isContinueOnError() {
+    final List<String> preferValues = this.request.getHeaders(HttpHeader.PREFER);
+
+    if (preferValues != null) {
+      for (final String preference : preferValues) {
+        if (PREFERENCE_CONTINUE_ON_ERROR.equals(preference)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  private String extractBoundary(ContentType contentType) throws BatchDeserializerException {
+    return BatchParserCommon.getBoundary(contentType.toContentTypeString(), 0);
+  }
+}