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/02/11 17:45:50 UTC
[4/4] olingo-odata4 git commit: A new service dispatcher mechanism
for handling server side requests
A new service dispatcher mechanism for handling server side requests
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/b25fc8c2
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/b25fc8c2
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/b25fc8c2
Branch: refs/heads/olingo-server-extension
Commit: b25fc8c2148fa6433dc50b9aba96cfb32457a82f
Parents: 200981d
Author: Ramesh Reddy <ra...@jboss.org>
Authored: Tue Feb 3 15:20:39 2015 -0600
Committer: Ramesh Reddy <ra...@jboss.org>
Committed: Wed Feb 11 10:44:35 2015 -0600
----------------------------------------------------------------------
lib/pom.xml | 1 +
.../api/deserializer/ODataDeserializer.java | 29 +-
lib/server-core-ext/pom.xml | 117 ++++
.../olingo/server/core/ActionRequest.java | 114 ++++
.../apache/olingo/server/core/BatchRequest.java | 192 ++++++
.../olingo/server/core/CountResponse.java | 54 ++
.../apache/olingo/server/core/DataRequest.java | 560 +++++++++++++++
.../olingo/server/core/EntityResponse.java | 156 +++++
.../olingo/server/core/EntitySetResponse.java | 95 +++
.../apache/olingo/server/core/ErrorHandler.java | 126 ++++
.../olingo/server/core/FunctionRequest.java | 117 ++++
.../olingo/server/core/LiteralParser.java | 177 +++++
.../apache/olingo/server/core/MediaRequest.java | 77 +++
.../olingo/server/core/MetadataParser.java | 674 +++++++++++++++++++
.../olingo/server/core/MetadataRequest.java | 57 ++
.../olingo/server/core/MetadataResponse.java | 56 ++
.../olingo/server/core/NoContentResponse.java | 97 +++
.../olingo/server/core/OData4HttpHandler.java | 111 +++
.../apache/olingo/server/core/OData4Impl.java | 45 ++
.../olingo/server/core/ODataServiceHandler.java | 38 ++
.../olingo/server/core/OperationRequest.java | 117 ++++
.../server/core/PrimitiveValueResponse.java | 99 +++
.../olingo/server/core/PropertyResponse.java | 133 ++++
.../server/core/RequestURLHierarchyVisitor.java | 332 +++++++++
.../olingo/server/core/RequestURLVisitor.java | 126 ++++
.../server/core/SchemaBasedEdmProvider.java | 301 +++++++++
.../olingo/server/core/ServiceDispatcher.java | 179 +++++
.../server/core/ServiceDocumentRequest.java | 52 ++
.../server/core/ServiceDocumentResponse.java | 58 ++
.../olingo/server/core/ServiceHandler.java | 105 +++
.../olingo/server/core/ServiceRequest.java | 254 +++++++
.../olingo/server/core/ServiceResponse.java | 89 +++
.../server/core/ServiceResponseVisior.java | 71 ++
.../olingo/server/core/StreamResponse.java | 52 ++
.../org/apache/olingo/server/core/Target.java | 48 ++
.../olingo/server/core/MetadataParserTest.java | 190 ++++++
.../server/core/ServiceDispatcherTest.java | 316 +++++++++
.../olingo/server/core/TripPinHandler.java | 426 ++++++++++++
.../olingo/server/core/TripPinServiceTest.java | 298 ++++++++
.../olingo/server/core/TripPinServlet.java | 112 +++
.../src/test/resources/OlingoOrangeTM.png | Bin 0 -> 93316 bytes
.../src/test/resources/airlines.json | 64 ++
.../src/test/resources/airports.json | 394 +++++++++++
.../src/test/resources/people.json | 323 +++++++++
.../src/test/resources/photos.json | 64 ++
.../src/test/resources/trippin.xml | 355 ++++++++++
.../json/ODataJsonDeserializer.java | 78 ++-
.../serializer/json/ODataJsonSerializer.java | 11 +-
.../server/core/uri/UriResourceActionImpl.java | 14 +-
.../server/core/uri/validator/UriValidator.java | 16 +-
.../json/ODataJsonDeserializerBasicTest.java | 20 +-
.../json/ODataJsonDeserializerEntityTest.java | 17 +-
52 files changed, 7571 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/pom.xml
----------------------------------------------------------------------
diff --git a/lib/pom.xml b/lib/pom.xml
index 6843ff3..d1e8864 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -42,6 +42,7 @@
<module>client-core</module>
<module>server-api</module>
<module>server-core</module>
+ <module>server-core-ext</module>
<module>server-tecsvc</module>
<module>server-test</module>
</modules>
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java
index 46567ae..9cd55a3 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/deserializer/ODataDeserializer.java
@@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -19,10 +19,13 @@
package org.apache.olingo.server.api.deserializer;
import java.io.InputStream;
+import java.util.Map;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmProperty;
/**
* Deserializer on OData server side.
@@ -31,7 +34,7 @@ public interface ODataDeserializer {
/**
* Deserializes an entity stream into an {@link Entity} object.
- * Validates: property types, no double properties, correct json types
+ * Validates: property types, no double properties, correct json types
* @param stream
* @param edmEntityType
* @return deserialized {@link Entity} object
@@ -47,7 +50,23 @@ public interface ODataDeserializer {
* @throws DeserializerException
*/
EntitySet entityCollection(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException;
-
-
+
+ /**
+ * Deserializes the Property or collections of properties (primitive & complex)
+ * @param stream
+ * @param edmProperty
+ * @return deserialized {@link Property}
+ * @throws DeserializerException
+ */
+ Property property(InputStream stream, EdmProperty edmProperty) throws DeserializerException;
+
+ /**
+ * Reads a simple key properties from document
+ * @param stream
+ * @param keys
+ * @return
+ * @throws DeserializerException
+ */
+ Map<String, String> read(InputStream stream, String... keys) throws DeserializerException;
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/pom.xml
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/pom.xml b/lib/server-core-ext/pom.xml
new file mode 100644
index 0000000..a5730c0
--- /dev/null
+++ b/lib/server-core-ext/pom.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>odata-server-core-ext</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+
+ <parent>
+ <groupId>org.apache.olingo</groupId>
+ <artifactId>odata-lib</artifactId>
+ <version>4.0.0-beta-03-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <properties>
+ <jetty-version>9.2.7.v20150116</jetty-version>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.olingo</groupId>
+ <artifactId>odata-server-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.olingo</groupId>
+ <artifactId>odata-server-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.olingo</groupId>
+ <artifactId>odata-commons-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.antlr</groupId>
+ <artifactId>antlr4-runtime</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.xml.stream</groupId>
+ <artifactId>stax-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-server</artifactId>
+ <scope>test</scope>
+ <version>${jetty-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-servlet</artifactId>
+ <version>${jetty-version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-http</artifactId>
+ <version>${jetty-version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-client</artifactId>
+ <scope>test</scope>
+ <version>${jetty-version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ActionRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ActionRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ActionRequest.java
new file mode 100644
index 0000000..c29a45d
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ActionRequest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.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;
+
+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(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), response));
+ } else {
+ handler.invoke(this, getETag(),
+ EntityResponse.getInstance(this, getContextURL(odata), response));
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ // 11.5.4.1 Invoking an Action - only allows POST
+ return (isPOST());
+ }
+
+ public UriResourceAction getUriResourceAction() {
+ return uriResourceAction;
+ }
+
+ protected 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/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/BatchRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/BatchRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/BatchRequest.java
new file mode 100644
index 0000000..828d186
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/BatchRequest.java
@@ -0,0 +1,192 @@
+/*
+ * 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.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.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);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/CountResponse.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/CountResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/CountResponse.java
new file mode 100644
index 0000000..bd85f0c
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/CountResponse.java
@@ -0,0 +1,54 @@
+/*
+ * 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.http.HttpContentType;
+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.serializer.FixedFormatSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+
+public class CountResponse extends ServiceResponse {
+ private final FixedFormatSerializer serializer;
+
+ public static CountResponse getInstance(ServiceRequest request, ODataResponse response) {
+ FixedFormatSerializer serializer = request.getOdata().createFixedFormatSerializer();
+ return new CountResponse(serializer, response);
+ }
+
+ private CountResponse(FixedFormatSerializer serializer, ODataResponse response) {
+ super(response);
+ this.serializer = serializer;
+ }
+
+ public void writeCount(int count) throws SerializerException {
+ assert (!isClosed());
+
+ this.response.setContent(this.serializer.count(count));
+ writeOK(HttpContentType.TEXT_PLAIN);
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/DataRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/DataRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/DataRequest.java
new file mode 100644
index 0000000..a96bd1c
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/DataRequest.java
@@ -0,0 +1,560 @@
+/*
+ * 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.net.URISyntaxException;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.ContextURL.Suffix;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+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.core.data.PropertyImpl;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmStream;
+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.deserializer.DeserializerException;
+import org.apache.olingo.server.api.deserializer.DeserializerException.MessageKeys;
+import org.apache.olingo.server.api.deserializer.ODataDeserializer;
+import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
+import org.apache.olingo.server.api.serializer.RepresentationType;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.uri.UriHelper;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceComplexProperty;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty;
+import org.apache.olingo.server.api.uri.UriResourceProperty;
+import org.apache.olingo.server.api.uri.UriResourceSingleton;
+
+public class DataRequest extends ServiceRequest {
+ protected UriResourceEntitySet uriResourceEntitySet;
+ private boolean countRequest;
+ private UriResourceProperty uriResourceProperty;
+ private boolean valueRequest;
+ private UriResourceNavigation uriResourceNavigation;
+ private boolean references;
+
+ private RequestType type;
+ private UriResourceSingleton uriResourceSingleton;
+
+ /**
+ * This sub-categorizes the request so that code can be simplified
+ */
+ interface RequestType {
+ public boolean allowedMethod();
+
+ public ContentType getResponseContentType() throws ContentNegotiatorException;
+
+ public ContextURL getContextURL(OData odata) throws SerializerException;
+
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException;
+ }
+
+ public DataRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ public UriResourceEntitySet getUriResourceEntitySet() {
+ return uriResourceEntitySet;
+ }
+
+ protected void setUriResourceEntitySet(UriResourceEntitySet uriResourceEntitySet) {
+ this.uriResourceEntitySet = uriResourceEntitySet;
+ this.type = new EntityRequest();
+ }
+
+ public boolean isSingleton() {
+ return this.uriResourceSingleton != null;
+ }
+
+ public boolean isCollection() {
+ return this.uriResourceEntitySet != null && this.uriResourceEntitySet.isCollection();
+ }
+
+ public EdmEntitySet getEntitySet() {
+ return this.uriResourceEntitySet.getEntitySet();
+ }
+
+ public boolean isCountRequest() {
+ return countRequest;
+ }
+
+ protected void setCountRequest(boolean countRequest) {
+ this.countRequest = countRequest;
+ this.type = new CountRequest();
+ }
+
+ public boolean isPropertyRequest() {
+ return this.uriResourceProperty != null;
+ }
+
+ public boolean isPropertyComplex() {
+ return (this.uriResourceProperty instanceof UriResourceComplexProperty);
+ }
+
+ public boolean isPropertyStream() {
+ if (isPropertyComplex()) {
+ return false;
+ }
+ EdmProperty property = ((UriResourcePrimitiveProperty)this.uriResourceProperty).getProperty();
+ return (property.getType() instanceof EdmStream);
+ }
+
+ public UriResourceProperty getUriResourceProperty() {
+ return uriResourceProperty;
+ }
+
+ protected void setUriResourceProperty(UriResourceProperty uriResourceProperty) {
+ this.uriResourceProperty = uriResourceProperty;
+ this.type = new PropertyRequest();
+ }
+
+ public UriResourceNavigation getUriResourceNavigation() {
+ return uriResourceNavigation;
+ }
+
+ protected void setUriResourceNavigation(UriResourceNavigation uriResourceNavigation) {
+ this.uriResourceNavigation = uriResourceNavigation;
+ }
+
+ public UriResourceSingleton getUriResourceSingleton() {
+ return this.uriResourceSingleton;
+ }
+
+ protected void setUriResourceSingleton(UriResourceSingleton info) {
+ this.uriResourceSingleton = info;
+ this.type = new SingletonRequest();
+ }
+
+ protected List<UriParameter> getKeyPredicates() {
+ if (this.uriResourceEntitySet != null) {
+ return this.uriResourceEntitySet.getKeyPredicates();
+ }
+ return null;
+ }
+
+ public boolean isReferenceRequest() {
+ return this.references;
+ }
+
+ protected void setReferenceRequest(boolean ref) {
+ this.references = ref;
+ this.type = new ReferenceRequest();
+ }
+
+ public boolean isValueRequest() {
+ return valueRequest;
+ }
+
+ private boolean hasMediaStream() {
+ return this.uriResourceEntitySet != null && this.uriResourceEntitySet.getEntityType().hasStream();
+ }
+
+ private InputStream getMediaStream() {
+ return this.request.getBody();
+ }
+
+ protected void setValueRequest(boolean valueRequest) {
+ this.valueRequest = valueRequest;
+ this.type = new ValueRequest();
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ return this.type.allowedMethod();
+ }
+
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ return type.getContextURL(odata);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ if (!this.type.allowedMethod()) {
+ methodNotAllowed();
+ }
+
+ this.type.execute(handler, response);
+ }
+
+ @Override
+ public <T> T getSerializerOptions(Class<T> serilizerOptions, ContextURL contextUrl)
+ throws ContentNegotiatorException {
+ if (serilizerOptions.isAssignableFrom(PrimitiveSerializerOptions.class)) {
+ return (T) PrimitiveSerializerOptions.with().contextURL(contextUrl)
+ .facetsFrom(getUriResourceProperty().getProperty()).build();
+ }
+ return super.getSerializerOptions(serilizerOptions, contextUrl);
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return type.getResponseContentType();
+ }
+
+ class EntityRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ return true;
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_ENTITY
+ : RepresentationType.ENTITY);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ EntityResponse entityResponse = EntityResponse.getInstance(DataRequest.this,
+ getContextURL(odata), response);
+
+ if (isGET()) {
+ if (isCollection()) {
+ handler.read(DataRequest.this,
+ EntitySetResponse.getInstance(DataRequest.this, getContextURL(odata), response));
+ } else {
+ handler.read(DataRequest.this,entityResponse);
+ }
+ } else if (isPUT() || isPATCH()) {
+ // RFC 2616: he result of a request having both an If-Match header field and either
+ // an If-None-Match or an If-Modified-Since header fields is undefined
+ // by this specification.
+ boolean ifMatch = getHeader(HttpHeader.IF_MATCH) != null;
+ boolean ifNoneMatch = getHeader(HttpHeader.IF_NONE_MATCH).equals("*");
+ if(ifMatch) {
+ handler.updateEntity(DataRequest.this, getEntityFromClient(), isPATCH(), getETag(),
+ entityResponse);
+ } else if (ifNoneMatch) {
+ // 11.4.4
+ handler.createEntity(DataRequest.this, getEntityFromClient(),
+ entityResponse.setReturnRepresentation(getReturnRepresentation()));
+ } else {
+ handler.updateEntity(DataRequest.this, getEntityFromClient(), isPATCH(), getETag(),
+ entityResponse);
+ }
+ } else if (isPOST()) {
+ if (hasMediaStream()) {
+ handler.createMediaEntity(DataRequest.this, getEntityFromClient(), getMediaStream(),
+ entityResponse.setReturnRepresentation(getReturnRepresentation()));
+ } else {
+ handler.createEntity(DataRequest.this, getEntityFromClient(),
+ entityResponse.setReturnRepresentation(getReturnRepresentation()));
+ }
+ } else if (isDELETE()) {
+ handler.deleteEntity(DataRequest.this, getETag(), entityResponse);
+ }
+ }
+
+ private Entity getEntityFromClient() throws DeserializerException {
+ ODataDeserializer deserializer = odata.createDeserializer(ODataFormat
+ .fromContentType(getRequestContentType()));
+ return deserializer.entity(getODataRequest().getBody(), getEntitySet().getEntityType());
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ // EntitySet based return
+ final UriHelper helper = odata.createUriHelper();
+ ContextURL.Builder builder = buildEntitySetContextURL(helper, getEntitySet(), getUriInfo(),
+ isCollection(), false);
+ return builder.build();
+ }
+ }
+
+ class CountRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET();
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentType.TEXT_PLAIN;
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ handler.read(DataRequest.this, CountResponse.getInstance(DataRequest.this, response));
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ return null;
+ }
+ }
+
+ class ReferenceRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ if (isPATCH()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_REFERENCE
+ : RepresentationType.REFERENCE);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ if (isGET()) {
+ if (isCollection()) {
+ handler.read(DataRequest.this,
+ EntitySetResponse.getInstance(DataRequest.this, getContextURL(odata), response)
+ .sendReferences(true));
+ } else {
+ handler.read(DataRequest.this,EntityResponse.getInstance(DataRequest.this,
+ getContextURL(odata), response).sendReferences(true));
+ }
+ } else if (isDELETE()) {
+ handler.deleteReference(DataRequest.this, getETag(), new NoContentResponse(response));
+ } else if (isPUT()) {
+ handler.updateReference(DataRequest.this, getETag(), getPayload(), new NoContentResponse(response));
+ } else if (isPOST()) {
+ // this needs to be against collection of references
+ handler.addReference(DataRequest.this, getETag(), getPayload(), new NoContentResponse(response));
+ }
+ }
+
+ // the payload will be like
+ // {
+ // "@odata.id": "serviceRoot/People('vincentcalabrese')"
+ // }
+ // The below code reads as proeprty and converts to an URI
+ private URI getPayload() throws DeserializerException {
+
+ ODataDeserializer deserializer = odata.createDeserializer(ODataFormat
+ .fromContentType(getRequestContentType()));
+ Map<String, String> values = deserializer.read(getODataRequest().getBody(), "@odata.id");
+ try {
+ return new URI(values.get("@odata.id"));
+ } catch (URISyntaxException e) {
+ throw new DeserializerException("failed to read @odata.id", e, MessageKeys.UNKOWN_CONTENT);
+ }
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ ContextURL.Builder builder = ContextURL.with().suffix(Suffix.REFERENCE);
+ if (isCollection()) {
+ builder.asCollection();
+ }
+ return builder.build();
+ }
+ }
+
+ class PropertyRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ // create of properties is not allowed,
+ // only read, update, delete. Note that delete is
+ // same as update with null
+ if (isPOST()) {
+ return false;
+ }
+
+ // 11.4.9.4, collection properties are not supported with merge
+ if (isPATCH() && (isCollection() || isPropertyStream())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ if (isPropertyComplex()) {
+ return ContentNegotiator.doContentNegotiation(getUriInfo().getFormatOption(),
+ getODataRequest(), getCustomContentTypeSupport(),
+ isCollection() ? RepresentationType.COLLECTION_COMPLEX : RepresentationType.COMPLEX);
+ } else if (isPropertyStream()) {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+ getCustomContentTypeSupport(), RepresentationType.BINARY);
+ }
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_PRIMITIVE
+ : RepresentationType.PRIMITIVE);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ EdmProperty edmProperty = getUriResourceProperty().getProperty();
+
+ PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
+ edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
+
+ if (isGET()) {
+ handler.read(DataRequest.this, propertyResponse);
+ } else if (isPATCH()) {
+ handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), true,
+ getETag(), propertyResponse);
+ } else if (isPUT()) {
+ if (isPropertyStream()) {
+ handler.updateStreamProperty(DataRequest.this, request.getBody(), getETag(),
+ new StreamResponse(response));
+ } else {
+ handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), false,
+ getETag(), propertyResponse);
+ }
+ } else if (isDELETE()) {
+ Property property = new PropertyImpl();
+ property.setName(edmProperty.getName());
+ property.setType(edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString());
+ handler.updateProperty(DataRequest.this, property, false, getETag(), propertyResponse);
+ }
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ final UriHelper helper = odata.createUriHelper();
+ EdmProperty edmProperty = getUriResourceProperty().getProperty();
+
+ ContextURL.Builder builder = ContextURL.with().entitySet(getEntitySet());
+ builder = ContextURL.with().entitySet(getEntitySet());
+ builder.keyPath(helper.buildContextURLKeyPredicate(getUriResourceEntitySet()
+ .getKeyPredicates()));
+ builder.navOrPropertyPath(edmProperty.getName());
+ if (isPropertyComplex()) {
+ EdmComplexType type = ((UriResourceComplexProperty) uriResourceProperty).getComplexType();
+ String select = helper.buildContextURLSelectList(type, getUriInfo().getExpandOption(),
+ getUriInfo().getSelectOption());
+ builder.selectList(select);
+ }
+ return builder.build();
+ }
+ }
+
+ class ValueRequest extends PropertyRequest {
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET() || isDELETE() || isPUT();
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ RepresentationType valueRepresentationType = uriResourceProperty.getType() == EdmPrimitiveTypeFactory
+ .getInstance(EdmPrimitiveTypeKind.Binary) ? RepresentationType.BINARY
+ : RepresentationType.VALUE;
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), request,
+ getCustomContentTypeSupport(), valueRepresentationType);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ EdmProperty edmProperty = getUriResourceProperty().getProperty();
+ if (isGET()) {
+ handler.read(DataRequest.this, PrimitiveValueResponse.getInstance(DataRequest.this,
+ response, isCollection(), getUriResourceProperty().getProperty()));
+ } else if (isDELETE()) {
+ Property property = new PropertyImpl();
+ property.setName(edmProperty.getName());
+ property.setType(edmProperty.getType().getFullQualifiedName().getFullQualifiedNameAsString());
+
+ PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
+ edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
+ handler.updateProperty(DataRequest.this, property, false, getETag(), propertyResponse);
+ } else if (isPUT()) {
+ PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
+ edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
+ handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), false,
+ getETag(), propertyResponse);
+ }
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ return null;
+ }
+ }
+
+ class SingletonRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET();
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), getODataRequest(),
+ getCustomContentTypeSupport(), RepresentationType.ENTITY);
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ final UriHelper helper = odata.createUriHelper();
+ ContextURL.Builder builder = buildEntitySetContextURL(helper,
+ uriResourceSingleton.getSingleton(), getUriInfo(), isCollection(), true);
+ return builder.build();
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ handler.read(DataRequest.this,
+ EntityResponse.getInstance(DataRequest.this, getContextURL(odata), response));
+ }
+ }
+
+ private org.apache.olingo.commons.api.data.Property getPropertyValueFromClient(
+ EdmProperty edmProperty) throws DeserializerException {
+ // TODO:this is not right, we should be deserializing the property
+ // (primitive, complex, collection of)
+ // for now it is responsibility of the user
+ ODataDeserializer deserializer = odata.createDeserializer(ODataFormat
+ .fromContentType(getRequestContentType()));
+ return deserializer.property(getODataRequest().getBody(), edmProperty);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntityResponse.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntityResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntityResponse.java
new file mode 100644
index 0000000..b52090b
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntityResponse.java
@@ -0,0 +1,156 @@
+/*
+ * 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.data.ContextURL;
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+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.ODataApplicationException;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+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.core.ServiceRequest.ReturnRepresentation;
+
+public class EntityResponse extends ServiceResponse {
+ private boolean referencesOnly;
+ private ReturnRepresentation returnRepresentation;
+ private boolean hasMediaStream;
+ private final ODataSerializer serializer;
+ private final EntitySerializerOptions options;
+ private final ContentType responseContentType;
+
+ private EntityResponse(ODataResponse response, ODataSerializer serializer,
+ EntitySerializerOptions options, ContentType responseContentType) {
+ super(response);
+ this.serializer = serializer;
+ this.options = options;
+ this.responseContentType = responseContentType;
+ }
+
+ public static EntityResponse getInstance(ServiceRequest request, ContextURL contextURL,
+ ODataResponse response) throws ContentNegotiatorException, SerializerException {
+ EntitySerializerOptions options = request.getSerializerOptions(EntitySerializerOptions.class,
+ contextURL);
+ return new EntityResponse(response, request.getSerializer(), options,
+ request.getResponseContentType());
+ }
+
+ // write single entity
+ public void writeReadEntity(EdmEntityType entityType, Entity entity) throws SerializerException {
+
+ assert (!isClosed());
+
+ if (entity == null) {
+ writeNotFound(true);
+ return;
+ }
+
+ if (this.referencesOnly) {
+ // TODO: need to write serializer routine to write only references,
+ // i.e URL with ID
+ writeServerError(true);
+ return;
+ }
+
+ // write the entity to response
+ this.response.setContent(this.serializer.entity(entityType, entity, this.options));
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ public void writeCreatedEntity(EdmEntityType entityType, Entity entity, String locationHeader)
+ throws SerializerException {
+ // upsert/insert must created a entity, otherwise should have throw an
+ // exception
+ assert (entity != null);
+
+ if (this.hasMediaStream) {
+ // TODO: need to write serializer routine to write media context
+ writeServerError(true);
+ return;
+ }
+
+ // 8.2.8.7
+ if (this.returnRepresentation == ReturnRepresentation.MINIMAL) {
+ writeNoContent(false);
+ writeHeader(HttpHeader.LOCATION, locationHeader);
+ writeHeader("Preference-Applied", "return=minimal"); //$NON-NLS-1$ //$NON-NLS-2$
+ // 8.3.3
+ writeHeader("OData-EntityId", entity.getId().toASCIIString()); //$NON-NLS-1$
+ close();
+ return;
+ }
+
+ // return the content of the created entity
+ this.response.setContent(this.serializer.entity(entityType, entity, this.options));
+ writeCreated(false);
+ writeHeader(HttpHeader.LOCATION, locationHeader);
+ writeHeader("Preference-Applied", "return=representation"); //$NON-NLS-1$ //$NON-NLS-2$
+ writeHeader(HttpHeader.CONTENT_TYPE, this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ public void writeUpdatedEntity() {
+ // spec says just success response; so either 200 or 204. 200 typically has
+ // payload
+ writeNoContent(true);
+ }
+
+ public void writeDeletedEntityOrReference() {
+ writeNoContent(true);
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+
+ public void writeCreated(boolean closeResponse) {
+ this.response.setStatusCode(HttpStatusCode.CREATED.getStatusCode());
+ if (closeResponse) {
+ close();
+ }
+ }
+
+ protected EntityResponse sendReferences(boolean ref) {
+ this.referencesOnly = ref;
+ return this;
+ }
+
+ protected EntityResponse setReturnRepresentation(ReturnRepresentation returnRepresentation) {
+ this.returnRepresentation = returnRepresentation;
+ return this;
+ }
+
+ protected EntityResponse setReferencesOnly(boolean referencesOnly) {
+ this.referencesOnly = referencesOnly;
+ return this;
+ }
+
+ protected EntityResponse setHasMediaStream(boolean stream) {
+ this.hasMediaStream = stream;
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntitySetResponse.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntitySetResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntitySetResponse.java
new file mode 100644
index 0000000..29e5b10
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/EntitySetResponse.java
@@ -0,0 +1,95 @@
+/*
+ * 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.data.ContextURL;
+import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.format.ContentType;
+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.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+
+public class EntitySetResponse extends ServiceResponse {
+ private boolean referencesOnly;
+ private final ODataSerializer serializer;
+ private final EntityCollectionSerializerOptions options;
+ private final ContentType responseContentType;
+
+ private EntitySetResponse(ODataResponse response, ODataSerializer serializer,
+ EntityCollectionSerializerOptions options,
+ ContentType responseContentType) {
+ super(response);
+ this.serializer = serializer;
+ this.options = options;
+ this.responseContentType = responseContentType;
+ }
+
+ public static EntitySetResponse getInstance(ServiceRequest request, ContextURL contextURL,
+ ODataResponse response) throws ContentNegotiatorException, SerializerException {
+ EntityCollectionSerializerOptions options = request.getSerializerOptions(
+ EntityCollectionSerializerOptions.class, contextURL);
+ return new EntitySetResponse(response, request.getSerializer(), options,
+ request.getResponseContentType());
+ }
+
+ // write collection of entities
+ // TODO: server paging needs to be implemented.
+ public void writeReadEntitySet(EdmEntityType entityType, EntitySet entitySet)
+ throws SerializerException {
+
+ assert (!isClosed());
+
+ if (entitySet == null) {
+ writeNotFound(true);
+ return;
+ }
+
+ if (this.referencesOnly) {
+ // TODO: need to write serializer routine to write only references,
+ // i.e URL with ID
+ writeServerError(true);
+ return;
+ }
+
+ // write the whole collection to response
+ this.response.setContent(this.serializer.entityCollection(entityType, entitySet, this.options));
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+
+ protected EntitySetResponse sendReferences(boolean ref) {
+ this.referencesOnly = ref;
+ return this;
+ }
+
+ protected EntitySetResponse setReferencesOnly(boolean referencesOnly) {
+ this.referencesOnly = referencesOnly;
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java
new file mode 100644
index 0000000..ed662f4
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java
@@ -0,0 +1,126 @@
+/*
+ * 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.ByteArrayInputStream;
+
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+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.HttpStatusCode;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataRequest;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataServerError;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.batch.exception.BatchDeserializerException;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.RepresentationType;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.api.uri.UriInfo;
+import org.apache.olingo.server.core.uri.parser.Parser;
+import org.apache.olingo.server.core.uri.parser.UriParserException;
+import org.apache.olingo.server.core.uri.parser.UriParserSemanticException;
+import org.apache.olingo.server.core.uri.parser.UriParserSyntaxException;
+import org.apache.olingo.server.core.uri.validator.UriValidationException;
+
+public class ErrorHandler {
+ private final OData odata;
+ private final ServiceMetadata metadata;
+ private final CustomContentTypeSupport customContent;
+
+ public ErrorHandler(OData odata, ServiceMetadata metadata, CustomContentTypeSupport customContent) {
+ this.odata = odata;
+ this.metadata = metadata;
+ this.customContent = customContent;
+ }
+
+ public void handleException(Exception e, ODataRequest request, ODataResponse response) {
+ if (e instanceof UriValidationException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((UriValidationException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof UriParserSemanticException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((UriParserSemanticException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof UriParserSyntaxException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((UriParserSyntaxException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof UriParserException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((UriParserException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof ContentNegotiatorException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((ContentNegotiatorException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof SerializerException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((SerializerException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof BatchDeserializerException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((BatchDeserializerException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof DeserializerException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((DeserializerException)e, null);
+ handleServerError(request, response, serverError);
+ } else if(e instanceof ODataHandlerException) {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject((ODataHandlerException)e, null);
+ handleServerError(request, response, serverError);
+ } else {
+ ODataServerError serverError = ODataExceptionHelper.createServerErrorObject(e);
+ handleServerError(request, response, serverError);
+ }
+ }
+
+ void handleServerError(final ODataRequest request, final ODataResponse response,
+ final ODataServerError serverError) {
+ ContentType requestedContentType;
+ try {
+ UriInfo uriInfo = new Parser().parseUri(request.getRawODataPath(), request.getRawQueryPath(),
+ null, this.metadata.getEdm());
+ requestedContentType = ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(),
+ request, this.customContent, RepresentationType.ERROR);
+ } catch (final ContentNegotiatorException e) {
+ requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
+ } catch (UriParserException e) {
+ requestedContentType = ODataFormat.JSON.getContentType(ODataServiceVersion.V40);
+ }
+ processError(response, serverError, requestedContentType);
+ }
+
+ void processError(ODataResponse response, ODataServerError serverError,
+ ContentType requestedContentType) {
+ try {
+ ODataSerializer serializer = this.odata.createSerializer(ODataFormat
+ .fromContentType(requestedContentType));
+ response.setContent(serializer.error(serverError));
+ response.setStatusCode(serverError.getStatusCode());
+ response.setHeader(HttpHeader.CONTENT_TYPE, requestedContentType.toContentTypeString());
+ } catch (Exception e) {
+ // This should never happen but to be sure we have this catch here
+ // to prevent sending a stacktrace to a client.
+ String responseContent = "{\"error\":{\"code\":null,\"message\":\"An unexpected exception occurred during "
+ + "error processing with message: " + e.getMessage() + "\"}}"; //$NON-NLS-1$ //$NON-NLS-2$
+ response.setContent(new ByteArrayInputStream(responseContent.getBytes()));
+ response.setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
+ response.setHeader(HttpHeader.CONTENT_TYPE,
+ ContentType.APPLICATION_JSON.toContentTypeString());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/FunctionRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/FunctionRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/FunctionRequest.java
new file mode 100644
index 0000000..be7b2bd
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/FunctionRequest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.List;
+
+import org.apache.olingo.commons.api.edm.EdmFunction;
+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.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceFunction;
+
+public class FunctionRequest extends OperationRequest {
+ private UriResourceFunction uriResourceFunction;
+
+ public FunctionRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ if (!allowedMethod()) {
+ methodNotAllowed();
+ }
+
+ // Functions always have return per 11.5.3
+ if (isReturnTypePrimitive()) {
+ // functions can not return a typed property in the context of entity, so
+ // it must be treated
+ // as value based response
+ handler.invoke(this, getODataRequest().getMethod(),
+ PrimitiveValueResponse.getInstance(this, response, isCollection(), getReturnType()));
+ } else if (isReturnTypeComplex()) {
+ handler.invoke(this, getODataRequest().getMethod(), PropertyResponse.getInstance(this, response,
+ getReturnType().getType(), getContextURL(this.odata), isCollection()));
+ } else {
+ // returnType.getType().getKind() == EdmTypeKind.ENTITY
+ if (isCollection()) {
+ handler.invoke(this, getODataRequest().getMethod(),
+ EntitySetResponse.getInstance(this, getContextURL(odata), response));
+ } else {
+ handler.invoke(this, getODataRequest().getMethod(),
+ EntityResponse.getInstance(this, getContextURL(odata), response));
+ }
+ }
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ // look for discussion about composable functions in odata-discussion
+ // group with thread "Clarification on "Function" invocations"
+ if (getFunction().isComposable()) {
+ return (isGET() || isPATCH() || isDELETE() || isPOST() || isPUT());
+ }
+ return isGET();
+ }
+
+ public UriResourceFunction getUriResourceFunction() {
+ return uriResourceFunction;
+ }
+
+ protected void setUriResourceFunction(UriResourceFunction uriResourceFunction) {
+ this.uriResourceFunction = uriResourceFunction;
+ }
+
+ @Override
+ public boolean isBound() {
+ return this.uriResourceFunction.getFunctionImport() != null;
+ }
+
+ public EdmFunction getFunction() {
+ return this.uriResourceFunction.getFunction();
+ }
+
+ public List<UriParameter> getParameters() {
+ return this.uriResourceFunction.getParameters();
+ }
+
+ @Override
+ public boolean isCollection() {
+ return getFunction().getReturnType().isCollection();
+ }
+
+ @Override
+ public EdmReturnType getReturnType() {
+ return getFunction().getReturnType();
+ }
+
+ @Override
+ public boolean hasReturnType() {
+ // Part3 {12.1} says must have return type
+ return true;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/LiteralParser.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/LiteralParser.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/LiteralParser.java
new file mode 100644
index 0000000..4c5f596
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/LiteralParser.java
@@ -0,0 +1,177 @@
+/*
+ * 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.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.codec.DecoderException;
+
+@SuppressWarnings("nls")
+/**
+ * TODO: this needs to be folded into ODataDeSeriliazer, to deserialize a primitive type
+ */
+public class LiteralParser {
+
+ private static final Pattern INT_PATTERN = Pattern.compile("[\\+|-]?\\d+");
+ private static final Pattern DOUBLE_PATTERN = Pattern.compile("[\\+|-]?\\d+\\.\\d+((e|E) [\\+|-]? \\d+)?");
+ private static final Pattern BOOLEAN_PATTERN = Pattern.compile("true|false|TRUE|FALSE");
+ private static final Pattern BINARY_PATTERN = Pattern.compile("^binary\\s'([a-fA-f0-9]*)'$");
+ private static final Pattern DATE_PATTERN = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})$");
+ private static final Pattern TIME_PATTERN = Pattern.compile("^(\\d{2}):(\\d{2}):(\\d{2})$");
+
+ // dateTimeOffsetValue = year "-" month "-" day "T" hour ":" minute [ ":"
+ // second [ "." fractionalSeconds ] ] ( "Z" / sign hour ":" minute )
+ private static final Pattern DATETIME_PATTERN = Pattern.compile("" + "^"
+ + "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2})" + // group 1 (datetime)
+ "(:\\d{2})?" + // group 2 (seconds)
+ "(\\.\\d{1,7})?" + // group 3 (nanoseconds)
+ "((Z)|([\\+|-]?\\d{2}:\\d{2}))?" + // group 4 (tz, ignored - handles
+ // bad services)
+ "$");
+
+ private static final Pattern DATETIMEOFFSET_PATTERN = Pattern.compile(""
+ + "^" + "(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2})" + // group 1
+ // (datetime)
+ "(\\.\\d{1,7})?" + // group 2 (nanoSeconds)
+ "(((\\+|-)\\d{2}:\\d{2})|(Z))" + // group 3 (offset) / group 6 (utc)
+ "$");
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+ private static final SimpleDateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
+ private static final SimpleDateFormat DATETIME_WITH_SECONDS_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ private static final SimpleDateFormat DATETIME_WITH_MILLIS_FORMAT = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSS");
+ private static final SimpleDateFormat DATETIMEOFFSET_XML = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZ");
+ private static final SimpleDateFormat DATETIMEOFFSET_WITH_MILLIS_FORMAT = new SimpleDateFormat(
+ "yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
+ private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
+
+ public static Object parseLiteral(String text) {
+ if (text.equals("null")) {
+ return null;
+ }
+
+ text = text.trim();
+
+ if (INT_PATTERN.matcher(text).matches()) {
+ try {
+ return Integer.parseInt(text);
+ } catch (NumberFormatException e) {
+ return Long.parseLong(text);
+ }
+ } else if (DOUBLE_PATTERN.matcher(text).matches()) {
+ return Double.parseDouble(text);
+ } else if (BOOLEAN_PATTERN.matcher(text).matches()) {
+ return Boolean.parseBoolean(text);
+ } else if (BINARY_PATTERN.matcher(text).matches()) {
+ try {
+ return org.apache.commons.codec.binary.Hex.decodeHex(text.toCharArray());
+ } catch (DecoderException e) {
+ // fall through and returned as text
+ }
+ } else if (DATE_PATTERN.matcher(text).matches()) {
+ try {
+ return new Date(DATE_FORMAT.parse(text).getTime());
+ } catch (ParseException e) {
+ // fall through and returned as text
+ }
+ } else if (DATETIME_PATTERN.matcher(text).matches()) {
+ try {
+ return parseDateTime(text);
+ } catch (ParseException e) {
+ }
+ } else if (TIME_PATTERN.matcher(text).matches()) {
+ try {
+ return parseTime(text);
+ } catch (ParseException e) {
+ }
+ }
+
+ return unquote(text);
+ }
+
+ private static String unquote(String str) {
+ if (str.startsWith("'") && str.endsWith("'")) {
+ return str.substring(1, str.length() - 1).replace("''", "'");
+ }
+ return str;
+ }
+
+ private static Timestamp parseDateTime(String value) throws ParseException {
+ Matcher matcher = DATETIME_PATTERN.matcher(value);
+ if (!matcher.matches()) {
+ return null;
+ }
+ String dateTime = matcher.group(1);
+ String seconds = matcher.group(2);
+ String nanoSeconds = matcher.group(3);
+ String offset = matcher.group(6);
+ String utc = matcher.group(5);
+
+ if (utc != null && utc.equals("Z")) {
+ offset = "+00:00";
+ }
+
+ if (seconds == null) {
+ return new Timestamp(DATETIME_FORMAT.parse(dateTime).getTime());
+ }
+
+ if (nanoSeconds == null) {
+ return new Timestamp(DATETIME_WITH_SECONDS_FORMAT.parse(
+ dateTime + seconds).getTime());
+ }
+
+ if (nanoSeconds.length() <= 4) {
+ if (offset == null) {
+ return new Timestamp(DATETIME_WITH_MILLIS_FORMAT.parse(dateTime + seconds + nanoSeconds).getTime());
+ }
+ return new Timestamp(DATETIMEOFFSET_WITH_MILLIS_FORMAT.parse(dateTime + seconds + nanoSeconds + offset)
+ .getTime());
+ }
+ if (offset == null) {
+ return new Timestamp(adjustMillis(DATETIME_WITH_MILLIS_FORMAT
+ .parse(dateTime + seconds + nanoSeconds.substring(0, 4))
+ .getTime(), nanoSeconds));
+ }
+ return new Timestamp(adjustMillis(
+ DATETIME_WITH_MILLIS_FORMAT.parse(dateTime + seconds + nanoSeconds.substring(0, 4)
+ + offset).getTime(), nanoSeconds));
+ }
+
+ private static long adjustMillis(long dateTime, final String nanoSeconds) {
+ return Math.round(Double.parseDouble("0." + nanoSeconds.substring(4))) == 0 ? dateTime : dateTime + 1;
+ }
+
+ private static Time parseTime(String value) throws ParseException {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(TIME_FORMAT.parse(value));
+ StringBuilder sb = new StringBuilder();
+ sb.append(calendar.get(Calendar.HOUR_OF_DAY)).append(":") //$NON-NLS-1$
+ .append(calendar.get(Calendar.MINUTE)).append(":") //$NON-NLS-1$
+ .append(calendar.get(Calendar.SECOND));
+ return java.sql.Time.valueOf(sb.toString());
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/b25fc8c2/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MediaRequest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MediaRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MediaRequest.java
new file mode 100644
index 0000000..4d11260
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MediaRequest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+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.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+
+public class MediaRequest extends ServiceRequest {
+ private UriResourceEntitySet uriResourceEntitySet;
+
+ public MediaRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ if (!allowedMethod()) {
+ methodNotAllowed();
+ }
+ // POST will not be here, because the media is created as part of media
+ // entity creation
+ if (isGET()) {
+ handler.readMediaStream(this, new StreamResponse(response));
+ } else if (isPUT()) {
+ handler.updateMediaStream(this, getETag(), new StreamResponse(response));
+ } else if (isDELETE()) {
+ handler.deleteMediaStream(this, getETag(), new NoContentResponse(response));
+ }
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ // the request must specify the content type requested.
+ return getRequestContentType();
+ }
+
+ public EdmEntitySet getEntitySet() {
+ return this.uriResourceEntitySet.getEntitySet();
+ }
+
+ public EdmEntityType getEntityType() {
+ return this.uriResourceEntitySet.getEntitySet().getEntityType();
+ }
+
+ protected void setUriResourceEntitySet(UriResourceEntitySet uriResourceEntitySet) {
+ this.uriResourceEntitySet = uriResourceEntitySet;
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET() || isPUT() || isDELETE();
+ }
+}