You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2015/04/04 19:05:28 UTC
[22/50] [abbrv] olingo-odata4 git commit: OLINGO-573: New processing
framework on server side with single interface with TripPin example
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
new file mode 100644
index 0000000..f3f4027
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLVisitor.java
@@ -0,0 +1,127 @@
+/*
+ * 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 org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.api.uri.UriInfoAll;
+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.UriInfoResource;
+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.UriResourceIt;
+import org.apache.olingo.server.api.uri.UriResourceLambdaAll;
+import org.apache.olingo.server.api.uri.UriResourceLambdaAny;
+import org.apache.olingo.server.api.uri.UriResourceLambdaVariable;
+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.UriResourceRoot;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
+import org.apache.olingo.server.api.uri.UriResourceValue;
+import org.apache.olingo.server.api.uri.queryoption.CountOption;
+import org.apache.olingo.server.api.uri.queryoption.ExpandOption;
+import org.apache.olingo.server.api.uri.queryoption.FilterOption;
+import org.apache.olingo.server.api.uri.queryoption.FormatOption;
+import org.apache.olingo.server.api.uri.queryoption.IdOption;
+import org.apache.olingo.server.api.uri.queryoption.OrderByOption;
+import org.apache.olingo.server.api.uri.queryoption.SearchOption;
+import org.apache.olingo.server.api.uri.queryoption.SelectOption;
+import org.apache.olingo.server.api.uri.queryoption.SkipOption;
+import org.apache.olingo.server.api.uri.queryoption.SkipTokenOption;
+import org.apache.olingo.server.api.uri.queryoption.TopOption;
+
+public interface RequestURLVisitor {
+
+ void visit(UriInfo info);
+
+ void visit(UriInfoService info);
+
+ void visit(UriInfoAll info);
+
+ void visit(UriInfoBatch info);
+
+ void visit(UriInfoCrossjoin info);
+
+ void visit(UriInfoEntityId info);
+
+ void visit(UriInfoMetadata info);
+
+ void visit(UriInfoResource info);
+
+ // Walk UriInfoResource
+ void visit(ExpandOption option);
+
+ void visit(FilterOption info);
+
+ void visit(FormatOption info);
+
+ void visit(IdOption info, EdmEntityType type);
+
+ void visit(CountOption info);
+
+ void visit(OrderByOption option);
+
+ void visit(SearchOption option);
+
+ void visit(SelectOption option);
+
+ void visit(SkipOption option);
+
+ void visit(SkipTokenOption option);
+
+ void visit(TopOption option);
+
+ void visit(UriResourceCount option);
+
+ void visit(UriResourceRef info);
+
+ void visit(UriResourceRoot info);
+
+ void visit(UriResourceValue info);
+
+ void visit(UriResourceAction info);
+
+ void visit(UriResourceEntitySet info);
+
+ void visit(UriResourceFunction info);
+
+ void visit(UriResourceIt info);
+
+ void visit(UriResourceLambdaAll info);
+
+ void visit(UriResourceLambdaAny info);
+
+ void visit(UriResourceLambdaVariable info);
+
+ void visit(UriResourceNavigation info);
+
+ void visit(UriResourceSingleton info);
+
+ void visit(UriResourceComplexProperty info);
+
+ void visit(UriResourcePrimitiveProperty info);
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReturnRepresentation.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReturnRepresentation.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReturnRepresentation.java
new file mode 100644
index 0000000..e9a213e
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ReturnRepresentation.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public enum ReturnRepresentation {
+ REPRESENTATION, MINIMAL
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/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..7e607d9
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java
@@ -0,0 +1,303 @@
+/*
+ * 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.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 extends 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;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/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/8668b097/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/8668b097/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/8668b097/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/8668b097/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/8668b097/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);
+ }
+}