diff --git a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/DecoderTest.java b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/DecoderTest.java
index 019ef5a4f..09f232697 100644
--- a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/DecoderTest.java
+++ b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/DecoderTest.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
@@ -18,13 +18,13 @@
******************************************************************************/
package org.apache.olingo.commons.core;
-import org.junit.Test;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
/**
- *
+ *
*/
public class DecoderTest {
diff --git a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/EncoderTest.java b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/EncoderTest.java
index 74eb1af77..7db30cea4 100644
--- a/lib/commons-core/src/test/java/org/apache/olingo/commons/core/EncoderTest.java
+++ b/lib/commons-core/src/test/java/org/apache/olingo/commons/core/EncoderTest.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
@@ -18,16 +18,16 @@
******************************************************************************/
package org.apache.olingo.commons.core;
-import org.junit.Test;
+import static org.junit.Assert.assertEquals;
import java.net.URI;
import java.net.URISyntaxException;
-import static org.junit.Assert.assertEquals;
+import org.junit.Test;
/**
* Tests for percent-encoding.
- *
+ *
*/
public class EncoderTest {
diff --git a/lib/pom.xml b/lib/pom.xml
index 6843ff376..d1e88649b 100644
--- a/lib/pom.xml
+++ b/lib/pom.xml
@@ -42,6 +42,7 @@
client-core
server-api
server-core
+ server-core-ext
server-tecsvc
server-test
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 b361844a7..9aabf332e 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,13 +19,16 @@
package org.apache.olingo.server.api.deserializer;
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.EntitySet;
import org.apache.olingo.commons.api.data.Parameter;
+import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.EdmAction;
import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmProperty;
/**
* Deserializer on OData server side.
@@ -34,7 +37,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
@@ -50,7 +53,7 @@ public interface ODataDeserializer {
* @throws DeserializerException
*/
EntitySet entityCollection(InputStream stream, EdmEntityType edmEntityType) throws DeserializerException;
-
+
/**
* Deserializes an action-parameters stream into an {@link Entity} object.
* Validates: parameter types, no double parameters, correct json types.
@@ -60,4 +63,22 @@ public interface ODataDeserializer {
* @throws DeserializerException
*/
List actionParameters(InputStream stream, EdmAction edmAction) 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;
+
+ /**
+ * Read entity references from the provided document
+ * @param stream
+ * @param keys
+ * @return
+ * @throws DeserializerException
+ */
+ List entityReferences(InputStream stream) throws DeserializerException;
}
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
index 14c588d25..e5dd6b0bc 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntityCollectionSerializerOptions.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
@@ -30,6 +30,7 @@ public class EntityCollectionSerializerOptions {
private CountOption count;
private ExpandOption expand;
private SelectOption select;
+ private boolean onlyReferences;
/** Gets the {@link ContextURL}. */
public ContextURL getContextURL() {
@@ -51,6 +52,11 @@ public class EntityCollectionSerializerOptions {
return select;
}
+ /** only writes the references of the entities*/
+ public boolean onlyReferences() {
+ return onlyReferences;
+ }
+
/** Initializes the options builder. */
public static Builder with() {
return new Builder();
@@ -59,7 +65,7 @@ public class EntityCollectionSerializerOptions {
/** Builder of OData serializer options. */
public static final class Builder {
- private EntityCollectionSerializerOptions options;
+ private final EntityCollectionSerializerOptions options;
private Builder() {
options = new EntityCollectionSerializerOptions();
@@ -89,6 +95,12 @@ public class EntityCollectionSerializerOptions {
return this;
}
+ /** Sets to serialize only references */
+ public Builder setWriteOnlyReferences(final boolean ref) {
+ options.onlyReferences = ref;
+ return this;
+ }
+
/** Builds the OData serializer options. */
public EntityCollectionSerializerOptions build() {
return options;
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntitySerializerOptions.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntitySerializerOptions.java
index fcbd150fd..0abb31cf2 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntitySerializerOptions.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/EntitySerializerOptions.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
@@ -27,6 +27,7 @@ public class EntitySerializerOptions {
private ContextURL contextURL;
private ExpandOption expand;
private SelectOption select;
+ private boolean onlyReferences;
/** Gets the {@link ContextURL}. */
public ContextURL getContextURL() {
@@ -43,6 +44,11 @@ public class EntitySerializerOptions {
return select;
}
+ /** only writes the references of the entities*/
+ public boolean onlyReferences() {
+ return onlyReferences;
+ }
+
private EntitySerializerOptions() {}
/** Initializes the options builder. */
@@ -53,7 +59,7 @@ public class EntitySerializerOptions {
/** Builder of OData serializer options. */
public static final class Builder {
- private EntitySerializerOptions options;
+ private final EntitySerializerOptions options;
private Builder() {
options = new EntitySerializerOptions();
@@ -77,6 +83,12 @@ public class EntitySerializerOptions {
return this;
}
+ /** Sets to serialize only references */
+ public Builder setWriteOnlyReferences(final boolean ref) {
+ options.onlyReferences = ref;
+ return this;
+ }
+
/** Builds the OData serializer options. */
public EntitySerializerOptions build() {
return options;
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
index 72f8ee862..55377cea6 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/ODataSerializer.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
@@ -39,7 +39,7 @@ public interface ODataSerializer {
/**
* Writes the service document into an InputStream.
* @param edm the Entity Data Model
- * @param serviceRoot the service-root URI of this OData service
+ * @param serviceRoot the service-root URI of this OData service
*/
InputStream serviceDocument(Edm edm, String serviceRoot) throws SerializerException;
@@ -58,21 +58,23 @@ public interface ODataSerializer {
/**
* Writes entity-collection data into an InputStream.
+ * @param metadata Metadata for the service
* @param entityType the {@link EdmEntityType}
* @param entitySet the data of the entity set
* @param options options for the serializer
*/
- InputStream entityCollection(EdmEntityType entityType, EntitySet entitySet,
- EntityCollectionSerializerOptions options) throws SerializerException;
+ InputStream entityCollection(ServiceMetadata metadata, EdmEntityType entityType,
+ EntitySet entitySet, EntityCollectionSerializerOptions options) throws SerializerException;
/**
* Writes entity data into an InputStream.
+ * @param metadata Metadata for the service
* @param entityType the {@link EdmEntityType}
* @param entity the data of the entity
* @param options options for the serializer
*/
- InputStream entity(EdmEntityType entityType, Entity entity, EntitySerializerOptions options)
- throws SerializerException;
+ InputStream entity(ServiceMetadata metadata, EdmEntityType entityType, Entity entity,
+ EntitySerializerOptions options) throws SerializerException;
/**
* Writes primitive-type instance data into an InputStream.
@@ -85,12 +87,13 @@ public interface ODataSerializer {
/**
* Writes complex-type instance data into an InputStream.
+ * @param metadata Metadata for the service
* @param type complex type
* @param property property value
* @param options options for the serializer
*/
- InputStream complex(EdmComplexType type, Property property, ComplexSerializerOptions options)
- throws SerializerException;
+ InputStream complex(ServiceMetadata metadata, EdmComplexType type, Property property,
+ ComplexSerializerOptions options) throws SerializerException;
/**
* Writes data of a collection of primitive-type instances into an InputStream.
@@ -103,10 +106,11 @@ public interface ODataSerializer {
/**
* Writes data of a collection of complex-type instances into an InputStream.
+ * @param metadata Metadata for the service
* @param type complex type
* @param property property value
* @param options options for the serializer
*/
- InputStream complexCollection(EdmComplexType type, Property property, ComplexSerializerOptions options)
- throws SerializerException;
+ InputStream complexCollection(ServiceMetadata metadata, EdmComplexType type, Property property,
+ ComplexSerializerOptions options) throws SerializerException;
}
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
index 1583241b6..a7d067fa6 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/serializer/SerializerException.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
@@ -37,7 +37,9 @@ public class SerializerException extends ODataTranslatedException {
/** parameter: property name */ INCONSISTENT_PROPERTY_TYPE,
/** parameter: property name */ MISSING_PROPERTY,
/** parameters: property name, property value */ WRONG_PROPERTY_VALUE,
- /** parameters: primitive-type name, value */ WRONG_PRIMITIVE_VALUE;
+ /** parameters: primitive-type name, value */ WRONG_PRIMITIVE_VALUE,
+ UNKNOWN_TYPE,
+ WRONG_BASE_TYPE;
@Override
public String getKey() {
diff --git a/lib/server-core-ext/pom.xml b/lib/server-core-ext/pom.xml
new file mode 100644
index 000000000..a5730c01b
--- /dev/null
+++ b/lib/server-core-ext/pom.xml
@@ -0,0 +1,117 @@
+
+
+
+ 4.0.0
+
+ odata-server-core-ext
+ jar
+ ${project.artifactId}
+
+
+ org.apache.olingo
+ odata-lib
+ 4.0.0-beta-03-SNAPSHOT
+ ..
+
+
+ 9.2.7.v20150116
+
+
+
+ org.apache.olingo
+ odata-server-api
+ ${project.version}
+
+
+ org.apache.olingo
+ odata-server-core
+ ${project.version}
+
+
+ org.apache.olingo
+ odata-commons-core
+ ${project.version}
+
+
+ org.antlr
+ antlr4-runtime
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+ javax.xml.stream
+ stax-api
+
+
+ junit
+ junit
+
+
+ org.mockito
+ mockito-all
+
+
+ org.slf4j
+ slf4j-simple
+
+
+ commons-io
+ commons-io
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ test
+ ${jetty-version}
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ ${jetty-version}
+ test
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+
+ org.eclipse.jetty
+ jetty-http
+ ${jetty-version}
+ test
+
+
+ org.eclipse.jetty
+ jetty-client
+ test
+ ${jetty-version}
+
+
+
+
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 000000000..199c62d2a
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/ErrorHandler.java
@@ -0,0 +1,125 @@
+/*
+ * 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.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();
+ } catch (UriParserException e) {
+ requestedContentType = ODataFormat.JSON.getContentType();
+ }
+ 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());
+ }
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java
new file mode 100644
index 000000000..e34a28a6f
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/MetadataParser.java
@@ -0,0 +1,679 @@
+/*
+ * 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.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+
+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.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.EntitySet;
+import org.apache.olingo.commons.api.edm.provider.EntityType;
+import org.apache.olingo.commons.api.edm.provider.EnumMember;
+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.NavigationProperty;
+import org.apache.olingo.commons.api.edm.provider.NavigationPropertyBinding;
+import org.apache.olingo.commons.api.edm.provider.OnDelete;
+import org.apache.olingo.commons.api.edm.provider.OnDeleteAction;
+import org.apache.olingo.commons.api.edm.provider.Operation;
+import org.apache.olingo.commons.api.edm.provider.Parameter;
+import org.apache.olingo.commons.api.edm.provider.Property;
+import org.apache.olingo.commons.api.edm.provider.PropertyRef;
+import org.apache.olingo.commons.api.edm.provider.ReferentialConstraint;
+import org.apache.olingo.commons.api.edm.provider.ReturnType;
+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;
+
+/**
+ * This class can convert a CSDL document into EDMProvider object
+ */
+public class MetadataParser {
+
+ public EdmProvider buildEdmProvider(Reader csdl) throws XMLStreamException {
+ XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
+ XMLEventReader reader = xmlInputFactory.createXMLEventReader(csdl);
+
+ SchemaBasedEdmProvider provider = new SchemaBasedEdmProvider();
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider provider,
+ String name) throws XMLStreamException {
+ String version = attr(element, "Version");
+ if (version.equals("4.0")) {
+ readDataServicesAndReference(reader, element, provider);
+ }
+ }
+ }.read(reader, null, provider, "Edmx");
+
+ return provider;
+ }
+
+ private void readDataServicesAndReference(XMLEventReader reader, StartElement element,
+ SchemaBasedEdmProvider provider) throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider provider,
+ String name) throws XMLStreamException {
+ if (name.equals("DataServices")) {
+ readSchema(reader, element, provider);
+ } else if (name.equals("Reference")) {
+ readReference(reader, element, provider, "Reference");
+ }
+ }
+ }.read(reader, element, provider, "DataServices", "Reference");
+ }
+
+ private void readReference(XMLEventReader reader, StartElement element,
+ SchemaBasedEdmProvider provider, String name) throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, SchemaBasedEdmProvider t, String name)
+ throws XMLStreamException {
+ // TODO:
+ }
+ }.read(reader, element, provider, name);
+ }
+
+ private void readSchema(XMLEventReader reader, StartElement element,
+ SchemaBasedEdmProvider provider) throws XMLStreamException {
+
+ Schema schema = new Schema();
+ schema.setComplexTypes(new ArrayList());
+ schema.setActions(new ArrayList());
+ schema.setEntityTypes(new ArrayList());
+ schema.setEnumTypes(new ArrayList());
+ schema.setFunctions(new ArrayList());
+ schema.setTerms(new ArrayList());
+ schema.setTypeDefinitions(new ArrayList());
+
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, Schema schema, String name)
+ throws XMLStreamException {
+ schema.setNamespace(attr(element, "Namespace"));
+ schema.setAlias(attr(element, "Alias"));
+ readSchemaContents(reader, schema);
+ }
+ }.read(reader, element, schema, "Schema");
+ provider.addSchema(schema);
+ }
+
+ private void readSchemaContents(XMLEventReader reader, Schema schema) throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, Schema schema, String name)
+ throws XMLStreamException {
+ if (name.equals("Action")) {
+ readAction(reader, element, schema);
+ } else if (name.equals("Annotations")) {
+ // TODO:
+ } else if (name.equals("Annotation")) {
+ // TODO:
+ } else if (name.equals("ComplexType")) {
+ readComplexType(reader, element, schema);
+ } else if (name.equals("EntityContainer")) {
+ readEntityContainer(reader, element, schema);
+ } else if (name.equals("EntityType")) {
+ readEntityType(reader, element, schema);
+ } else if (name.equals("EnumType")) {
+ readEnumType(reader, element, schema);
+ } else if (name.equals("Function")) {
+ readFunction(reader, element, schema);
+ } else if (name.equals("Term")) {
+ schema.getTerms().add(readTerm(element));
+ } else if (name.equals("TypeDefinition")) {
+ schema.getTypeDefinitions().add(readTypeDefinition(element));
+ }
+ }
+ }.read(reader, null, schema, "Action", "Annotations", "Annotation", "ComplexType",
+ "EntityContainer", "EntityType", "EnumType", "Function", "Term", "TypeDefinition");
+ }
+
+ private void readAction(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+
+ Action action = new Action();
+ action.setParameters(new ArrayList());
+ action.setName(attr(element, "Name"));
+ action.setBound(Boolean.parseBoolean(attr(element, "IsBound")));
+ String entitySetPath = attr(element, "EntitySetPath");
+ if (entitySetPath != null) {
+ // TODO: need to parse into binding and path.
+ action.setEntitySetPath(entitySetPath);
+ }
+ readOperationParameters(reader, action);
+ schema.getActions().add(action);
+ }
+
+ private FullQualifiedName readType(StartElement element) {
+ String type = attr(element, "Type");
+ if (type.startsWith("Collection(") && type.endsWith(")")) {
+ return new FullQualifiedName(type.substring(11, type.length() - 1));
+ }
+ return new FullQualifiedName(type);
+ }
+
+ private boolean isCollectionType(StartElement element) {
+ String type = attr(element, "Type");
+ if (type.startsWith("Collection(") && type.endsWith(")")) {
+ return true;
+ }
+ return false;
+ }
+
+ private void readReturnType(StartElement element, Operation operation) {
+ ReturnType returnType = new ReturnType();
+ returnType.setType(readType(element));
+ returnType.setCollection(isCollectionType(element));
+ returnType.setNullable(Boolean.parseBoolean(attr(element, "Nullable")));
+
+ String maxLength = attr(element, "MaxLength");
+ if (maxLength != null) {
+ returnType.setMaxLength(Integer.parseInt(maxLength));
+ }
+ String precision = attr(element, "Precision");
+ if (precision != null) {
+ returnType.setPrecision(Integer.parseInt(precision));
+ }
+ String scale = attr(element, "Scale");
+ if (scale != null) {
+ returnType.setScale(Integer.parseInt(scale));
+ }
+ String srid = attr(element, "SRID");
+ if (srid != null) {
+ // TODO: no olingo support yet.
+ }
+ operation.setReturnType(returnType);
+ }
+
+ private void readParameter(StartElement element, Operation operation) {
+ Parameter parameter = new Parameter();
+ parameter.setName(attr(element, "Name"));
+ parameter.setType(readType(element));
+ parameter.setCollection(isCollectionType(element));
+ parameter.setNullable(Boolean.parseBoolean(attr(element, "Nullable")));
+
+ String maxLength = attr(element, "MaxLength");
+ if (maxLength != null) {
+ parameter.setMaxLength(Integer.parseInt(maxLength));
+ }
+ String precision = attr(element, "Precision");
+ if (precision != null) {
+ parameter.setPrecision(Integer.parseInt(precision));
+ }
+ String scale = attr(element, "Scale");
+ if (scale != null) {
+ parameter.setScale(Integer.parseInt(scale));
+ }
+ String srid = attr(element, "SRID");
+ if (srid != null) {
+ // TODO: no olingo support yet.
+ }
+ operation.getParameters().add(parameter);
+ }
+
+ private TypeDefinition readTypeDefinition(StartElement element) {
+ TypeDefinition td = new TypeDefinition();
+ td.setName(attr(element, "Name"));
+ td.setUnderlyingType(new FullQualifiedName(attr(element, "UnderlyingType")));
+ td.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
+
+ String maxLength = attr(element, "MaxLength");
+ if (maxLength != null) {
+ td.setMaxLength(Integer.parseInt(maxLength));
+ }
+ String precision = attr(element, "Precision");
+ if (precision != null) {
+ td.setPrecision(Integer.parseInt(precision));
+ }
+ String scale = attr(element, "Scale");
+ if (scale != null) {
+ td.setScale(Integer.parseInt(scale));
+ }
+ String srid = attr(element, "SRID");
+ if (srid != null) {
+ // TODO: no olingo support yet.
+ }
+ return td;
+ }
+
+ private Term readTerm(StartElement element) {
+ Term term = new Term();
+ term.setName(attr(element, "Name"));
+ term.setType(attr(element, "Type"));
+ if (attr(element, "BaseTerm") != null) {
+ term.setBaseTerm(attr(element, "BaseTerm"));
+ }
+ if (attr(element, "DefaultValue") != null) {
+ term.setDefaultValue(attr(element, "DefaultValue"));
+ }
+ if (attr(element, "AppliesTo") != null) {
+ term.setAppliesTo(Arrays.asList(attr(element, "AppliesTo")));
+ }
+ term.setNullable(Boolean.parseBoolean(attr(element, "Nullable")));
+ String maxLength = attr(element, "MaxLength");
+ if (maxLength != null) {
+ term.setMaxLength(Integer.parseInt(maxLength));
+ }
+ String precision = attr(element, "Precision");
+ if (precision != null) {
+ term.setPrecision(Integer.parseInt(precision));
+ }
+ String scale = attr(element, "Scale");
+ if (scale != null) {
+ term.setScale(Integer.parseInt(scale));
+ }
+ String srid = attr(element, "SRID");
+ if (srid != null) {
+ // TODO: no olingo support yet.
+ }
+ return term;
+ }
+
+ private void readFunction(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+ Function function = new Function();
+ function.setParameters(new ArrayList());
+ function.setName(attr(element, "Name"));
+ function.setBound(Boolean.parseBoolean(attr(element, "IsBound")));
+ function.setComposable(Boolean.parseBoolean(attr(element, "IsComposable")));
+ String entitySetPath = attr(element, "EntitySetPath");
+ if (entitySetPath != null) {
+ // TODO: need to parse into binding and path.
+ function.setEntitySetPath(entitySetPath);
+ }
+ readOperationParameters(reader, function);
+ schema.getFunctions().add(function);
+ }
+
+ private void readOperationParameters(XMLEventReader reader, final Operation operation)
+ throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, Operation operation, String name)
+ throws XMLStreamException {
+ if (name.equals("Parameter")) {
+ readParameter(element, operation);
+ } else if (name.equals("ReturnType")) {
+ readReturnType(element, operation);
+ }
+ }
+ }.read(reader, null, operation, "Parameter", "ReturnType");
+ }
+
+ private void readEnumType(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+ EnumType type = new EnumType();
+ type.setMembers(new ArrayList());
+ type.setName(attr(element, "Name"));
+ if (attr(element, "UnderlyingType") != null) {
+ type.setUnderlyingType(new FullQualifiedName(attr(element, "UnderlyingType")));
+ }
+ type.setFlags(Boolean.parseBoolean(attr(element, "IsFlags")));
+
+ readEnumMembers(reader, element, type);
+ schema.getEnumTypes().add(type);
+ }
+
+ private void readEnumMembers(XMLEventReader reader, StartElement element, EnumType type)
+ throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, EnumType type, String name)
+ throws XMLStreamException {
+ EnumMember member = new EnumMember();
+ member.setName(attr(element, "Name"));
+ member.setValue(attr(element, "Value"));
+ type.getMembers().add(member);
+ }
+ }.read(reader, element, type, "Member");
+ }
+
+ private void readEntityType(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+ EntityType entityType = new EntityType();
+ entityType.setProperties(new ArrayList());
+ entityType.setNavigationProperties(new ArrayList());
+ entityType.setKey(new ArrayList());
+ entityType.setName(attr(element, "Name"));
+ if (attr(element, "BaseType") != null) {
+ entityType.setBaseType(new FullQualifiedName(attr(element, "BaseType")));
+ }
+ entityType.setAbstract(Boolean.parseBoolean(attr(element, "Abstract")));
+ entityType.setOpenType(Boolean.parseBoolean(attr(element, "OpenType")));
+ entityType.setHasStream(Boolean.parseBoolean(attr(element, "HasStream")));
+ readEntityProperties(reader, entityType);
+ schema.getEntityTypes().add(entityType);
+ }
+
+ private void readEntityProperties(XMLEventReader reader, EntityType entityType)
+ throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, EntityType entityType, String name)
+ throws XMLStreamException {
+ if (name.equals("Property")) {
+ entityType.getProperties().add(readProperty(element));
+ } else if (name.equals("NavigationProperty")) {
+ entityType.getNavigationProperties().add(readNavigationProperty(reader, element));
+ } else if (name.equals("Key")) {
+ readKey(reader, element, entityType);
+ }
+ }
+ }.read(reader, null, entityType, "Property", "NavigationProperty", "Key");
+ }
+
+ private void readKey(XMLEventReader reader, StartElement element, EntityType entityType)
+ throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, EntityType entityType, String name)
+ throws XMLStreamException {
+ PropertyRef ref = new PropertyRef();
+ ref.setName(attr(element, "Name"));
+ ref.setAlias(attr(element, "Alias"));
+ entityType.getKey().add(ref);
+ }
+ }.read(reader, element, entityType, "PropertyRef");
+ }
+
+ private NavigationProperty readNavigationProperty(XMLEventReader reader, StartElement element)
+ throws XMLStreamException {
+ NavigationProperty property = new NavigationProperty();
+ property.setReferentialConstraints(new ArrayList());
+
+ property.setName(attr(element, "Name"));
+ property.setType(readType(element));
+ property.setCollection(isCollectionType(element));
+ property.setNullable(Boolean.parseBoolean(attr(element, "Nullable")));
+ property.setPartner(attr(element, "Partner"));
+ property.setContainsTarget(Boolean.parseBoolean(attr(element, "ContainsTarget")));
+
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, NavigationProperty property,
+ String name) throws XMLStreamException {
+ if (name.equals("ReferentialConstraint")) {
+ ReferentialConstraint constraint = new ReferentialConstraint();
+ constraint.setProperty(attr(element, "Property"));
+ constraint.setReferencedProperty(attr(element, "ReferencedProperty"));
+ property.getReferentialConstraints().add(constraint);
+ } else if (name.equals("OnDelete")) {
+ property.setOnDelete(new OnDelete().setAction(OnDeleteAction.valueOf(attr(element, "Action"))));
+ }
+ }
+ }.read(reader, element, property, "ReferentialConstraint", "OnDelete");
+ return property;
+ }
+
+ private String attr(StartElement element, String name) {
+ Attribute attr = element.getAttributeByName(new QName(name));
+ if (attr != null) {
+ return attr.getValue();
+ }
+ return null;
+ }
+
+ private Property readProperty(StartElement element) {
+ Property property = new Property();
+ property.setName(attr(element, "Name"));
+ property.setType(readType(element));
+ property.setCollection(isCollectionType(element));
+ property.setNullable(Boolean.parseBoolean(attr(element, "Nullable") == null ? "true" : attr(
+ element, "Nullable")));
+ property.setUnicode(Boolean.parseBoolean(attr(element, "Unicode")));
+
+ String maxLength = attr(element, "MaxLength");
+ if (maxLength != null) {
+ property.setMaxLength(Integer.parseInt(maxLength));
+ }
+ String precision = attr(element, "Precision");
+ if (precision != null) {
+ property.setPrecision(Integer.parseInt(precision));
+ }
+ String scale = attr(element, "Scale");
+ if (scale != null) {
+ property.setScale(Integer.parseInt(scale));
+ }
+ String srid = attr(element, "SRID");
+ if (srid != null) {
+ // TODO: no olingo support yet.
+ }
+ String defaultValue = attr(element, "DefaultValue");
+ if (defaultValue != null) {
+ property.setDefaultValue(defaultValue);
+ }
+ return property;
+ }
+
+ private void readEntityContainer(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+ final EntityContainer container = new EntityContainer();
+ container.setName(attr(element, "Name"));
+ if (attr(element, "Extends") != null) {
+ container.setExtendsContainer(attr(element, "Extends"));
+ }
+ container.setActionImports(new ArrayList());
+ container.setFunctionImports(new ArrayList());
+ container.setEntitySets(new ArrayList());
+ container.setSingletons(new ArrayList());
+
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, Schema schema, String name)
+ throws XMLStreamException {
+ if (name.equals("EntitySet")) {
+ readEntitySet(reader, element, container);
+ } else if (name.equals("Singleton")) {
+ readSingleton(reader, element, container);
+ } else if (name.equals("ActionImport")) {
+ readActionImport(element, container);
+ } else if (name.equals("FunctionImport")) {
+ readFunctionImport(element, container);
+ }
+ }
+
+ private void readFunctionImport(StartElement element, EntityContainer container) {
+ FunctionImport functionImport = new FunctionImport();
+ functionImport.setName(attr(element, "Name"));
+ functionImport.setFunction(new FullQualifiedName(attr(element, "Function")));
+ functionImport.setIncludeInServiceDocument(Boolean.parseBoolean(attr(element,
+ "IncludeInServiceDocument")));
+
+ String entitySet = attr(element, "EntitySet");
+ if (entitySet != null) {
+ functionImport.setEntitySet(entitySet);
+ }
+ container.getFunctionImports().add(functionImport);
+ }
+
+ private void readActionImport(StartElement element, EntityContainer container) {
+ ActionImport actionImport = new ActionImport();
+ actionImport.setName(attr(element, "Name"));
+ actionImport.setAction(new FullQualifiedName(attr(element, "Action")));
+
+ String entitySet = attr(element, "EntitySet");
+ if (entitySet != null) {
+ actionImport.setEntitySet(entitySet);
+ }
+ container.getActionImports().add(actionImport);
+ }
+
+ private void readSingleton(XMLEventReader reader, StartElement element,
+ EntityContainer container) throws XMLStreamException {
+ Singleton singleton = new Singleton();
+ singleton.setNavigationPropertyBindings(new ArrayList());
+ singleton.setName(attr(element, "Name"));
+ singleton.setType(new FullQualifiedName(attr(element, "Type")));
+ singleton.setNavigationPropertyBindings(new ArrayList());
+ readNavigationPropertyBindings(reader, element, singleton.getNavigationPropertyBindings());
+ container.getSingletons().add(singleton);
+ }
+
+ private void readEntitySet(XMLEventReader reader, StartElement element,
+ EntityContainer container) throws XMLStreamException {
+ EntitySet entitySet = new EntitySet();
+ entitySet.setName(attr(element, "Name"));
+ entitySet.setType(new FullQualifiedName(attr(element, "EntityType")));
+ entitySet.setIncludeInServiceDocument(Boolean.parseBoolean(attr(element,
+ "IncludeInServiceDocument")));
+ entitySet.setNavigationPropertyBindings(new ArrayList());
+ readNavigationPropertyBindings(reader, element, entitySet.getNavigationPropertyBindings());
+ container.getEntitySets().add(entitySet);
+ }
+
+ private void readNavigationPropertyBindings(XMLEventReader reader, StartElement element,
+ List bindings) throws XMLStreamException {
+ new ElementReader>() {
+ @Override
+ void build(XMLEventReader reader, StartElement element,
+ List bindings, String name) throws XMLStreamException {
+ NavigationPropertyBinding binding = new NavigationPropertyBinding();
+ binding.setPath(attr(element, "Path"));
+ binding.setTarget(attr(element, "Target"));
+ bindings.add(binding);
+ }
+
+ }.read(reader, element, bindings, "NavigationPropertyBinding");
+ ;
+ }
+ }.read(reader, element, schema, "EntitySet", "Singleton", "ActionImport", "FunctionImport");
+ schema.setEntityContainer(container);
+ }
+
+ private void readComplexType(XMLEventReader reader, StartElement element, Schema schema)
+ throws XMLStreamException {
+ ComplexType complexType = new ComplexType();
+ complexType.setProperties(new ArrayList());
+ complexType.setNavigationProperties(new ArrayList());
+ complexType.setName(attr(element, "Name"));
+ if (attr(element, "BaseType") != null) {
+ complexType.setBaseType(new FullQualifiedName(attr(element, "BaseType")));
+ }
+ complexType.setAbstract(Boolean.parseBoolean(attr(element, "Abstract")));
+ complexType.setOpenType(Boolean.parseBoolean(attr(element, "OpenType")));
+ readProperties(reader, complexType);
+
+ schema.getComplexTypes().add(complexType);
+ }
+
+ private void readProperties(XMLEventReader reader, ComplexType complexType)
+ throws XMLStreamException {
+ new ElementReader() {
+ @Override
+ void build(XMLEventReader reader, StartElement element, ComplexType complexType, String name)
+ throws XMLStreamException {
+ if (name.equals("Property")) {
+ complexType.getProperties().add(readProperty(element));
+ } else if (name.equals("NavigationProperty")) {
+ complexType.getNavigationProperties().add(readNavigationProperty(reader, element));
+ }
+ }
+ }.read(reader, null, complexType, "Property", "NavigationProperty");
+ }
+
+ abstract class ElementReader {
+ void read(XMLEventReader reader, StartElement element, T t, String... names)
+ throws XMLStreamException {
+ while (reader.hasNext()) {
+ XMLEvent event = reader.peek();
+
+ event = skipAnnotations(reader, event);
+
+ if (!event.isStartElement() && !event.isEndElement()) {
+ reader.nextEvent();
+ continue;
+ }
+
+ boolean hit = false;
+
+ for (int i = 0; i < names.length; i++) {
+ if (event.isStartElement()) {
+ element = event.asStartElement();
+ if (element.getName().getLocalPart().equals(names[i])) {
+ reader.nextEvent(); // advance cursor
+ // System.out.println("reading = "+names[i]);
+ build(reader, element, t, names[i]);
+ hit = true;
+ }
+ }
+ if (event.isEndElement()) {
+ EndElement e = event.asEndElement();
+ if (e.getName().getLocalPart().equals(names[i])) {
+ reader.nextEvent(); // advance cursor
+ // System.out.println("done reading = "+names[i]);
+ hit = true;
+ }
+ }
+ }
+ if (!hit) {
+ break;
+ }
+ }
+ }
+
+ private XMLEvent skipAnnotations(XMLEventReader reader, XMLEvent event)
+ throws XMLStreamException {
+ boolean skip = false;
+
+ while (reader.hasNext()) {
+ if (event.isStartElement()) {
+ StartElement element = event.asStartElement();
+ if (element.getName().getLocalPart().equals("Annotation")) {
+ skip = true;
+ }
+ }
+ if (event.isEndElement()) {
+ EndElement element = event.asEndElement();
+ if (element.getName().getLocalPart().equals("Annotation")) {
+ return reader.peek();
+ }
+ }
+ if (skip) {
+ event = reader.nextEvent();
+ } else {
+ return event;
+ }
+ }
+ return event;
+ }
+
+ abstract void build(XMLEventReader reader, StartElement element, T t, String name)
+ throws XMLStreamException;
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4HttpHandler.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4HttpHandler.java
new file mode 100644
index 000000000..ddb8e6b78
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4HttpHandler.java
@@ -0,0 +1,123 @@
+/*
+ * 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.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.api.http.HttpHeader;
+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.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.processor.Processor;
+import org.apache.olingo.server.api.serializer.CustomContentTypeSupport;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.legacy.ProcessorServiceHandler;
+
+public class OData4HttpHandler extends ODataHttpHandlerImpl {
+ private ServiceHandler handler;
+ private final ServiceMetadata serviceMetadata;
+ private final OData odata;
+ private CustomContentTypeSupport customContentTypeSupport;
+
+
+ public OData4HttpHandler(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ this.odata = odata;
+ this.serviceMetadata = serviceMetadata;
+ // this is support old interfaces
+ this.handler = new ProcessorServiceHandler();
+ this.handler.init(odata, serviceMetadata);
+ }
+
+ @Override
+ public void process(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {
+ ODataRequest request = null;
+ ODataResponse response = new ODataResponse();
+
+ try {
+ request = createODataRequest(httpRequest, 0);
+ validateODataVersion(request, response);
+
+ ServiceDispatcher dispatcher = new ServiceDispatcher(this.odata, this.serviceMetadata,
+ handler, this.customContentTypeSupport);
+ dispatcher.execute(request, response);
+
+ } catch (Exception e) {
+ ErrorHandler handler = new ErrorHandler(this.odata, this.serviceMetadata,
+ this.customContentTypeSupport);
+ handler.handleException(e, request, response);
+ }
+ convertToHttp(httpResponse, response);
+ }
+
+
+ ODataRequest createODataRequest(final HttpServletRequest httpRequest, final int split)
+ throws ODataTranslatedException {
+ try {
+ ODataRequest odRequest = new ODataRequest();
+
+ odRequest.setBody(httpRequest.getInputStream());
+ extractHeaders(odRequest, httpRequest);
+ extractMethod(odRequest, httpRequest);
+ extractUri(odRequest, httpRequest, split);
+
+ return odRequest;
+ } catch (final IOException e) {
+ throw new SerializerException(
+ "An I/O exception occurred.", e, SerializerException.MessageKeys.IO_EXCEPTION); //$NON-NLS-1$
+ }
+ }
+
+ void validateODataVersion(final ODataRequest request, final ODataResponse response)
+ throws ODataHandlerException {
+ final String maxVersion = request.getHeader(HttpHeader.ODATA_MAX_VERSION);
+ response.setHeader(HttpHeader.ODATA_VERSION, ODataServiceVersion.V40.toString());
+
+ if (maxVersion != null) {
+ if (ODataServiceVersion.isBiggerThan(ODataServiceVersion.V40.toString(), maxVersion)) {
+ throw new ODataHandlerException("ODataVersion not supported: " + maxVersion, //$NON-NLS-1$
+ ODataHandlerException.MessageKeys.ODATA_VERSION_NOT_SUPPORTED, maxVersion);
+ }
+ }
+ }
+
+ @Override
+ public void register(final Processor processor) {
+
+ if (processor instanceof ServiceHandler) {
+ this.handler = (ServiceHandler) processor;
+ this.handler.init(this.odata, this.serviceMetadata);
+ }
+
+ if (this.handler instanceof ProcessorServiceHandler) {
+ ((ProcessorServiceHandler)this.handler).register(processor);
+ }
+ }
+
+ @Override
+ public void register(final CustomContentTypeSupport customContentTypeSupport) {
+ this.customContentTypeSupport = customContentTypeSupport;
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java
new file mode 100644
index 000000000..bde9c966c
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/OData4Impl.java
@@ -0,0 +1,45 @@
+/*
+ * 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.ODataRuntimeException;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataHttpHandler;
+import org.apache.olingo.server.api.ServiceMetadata;
+
+public class OData4Impl extends ODataImpl {
+
+ public static OData newInstance() {
+ try {
+ final Class> clazz = Class.forName(OData4Impl.class.getName());
+ final Object object = clazz.newInstance();
+ return (OData) object;
+ } catch (final Exception e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ private OData4Impl() {
+ }
+
+ @Override
+ public ODataHttpHandler createHandler(final ServiceMetadata edm) {
+ return new OData4HttpHandler(this, edm);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
new file mode 100644
index 000000000..ee0063819
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/RequestURLHierarchyVisitor.java
@@ -0,0 +1,333 @@
+/*
+ * 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.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.UriInfoKind;
+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.UriResource;
+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 class RequestURLHierarchyVisitor implements RequestURLVisitor {
+
+ private UriInfo uriInfo;
+
+ public UriInfo getUriInfo() {
+ return this.uriInfo;
+ }
+
+ @Override
+ public void visit(UriInfo info) {
+ this.uriInfo = info;
+
+ UriInfoKind kind = info.getKind();
+ switch (kind) {
+ case all:
+ visit(info.asUriInfoAll());
+ break;
+ case batch:
+ visit(info.asUriInfoBatch());
+ break;
+ case crossjoin:
+ visit(info.asUriInfoCrossjoin());
+ break;
+ case entityId:
+ visit(info.asUriInfoEntityId());
+ break;
+ case metadata:
+ visit(info.asUriInfoMetadata());
+ break;
+ case resource:
+ visit(info.asUriInfoResource());
+ break;
+ case service:
+ visit(info.asUriInfoService());
+ break;
+ }
+ }
+
+ @Override
+ public void visit(UriInfoService info) {
+ }
+
+ @Override
+ public void visit(UriInfoAll info) {
+ }
+
+ @Override
+ public void visit(UriInfoBatch info) {
+ }
+
+ @Override
+ public void visit(UriInfoCrossjoin info) {
+ }
+
+ @Override
+ public void visit(UriInfoEntityId info) {
+ visit(info.getSelectOption());
+
+ if (info.getExpandOption() != null) {
+ visit(info.getExpandOption());
+ }
+ if (info.getFormatOption() != null) {
+ visit(info.getFormatOption());
+ }
+ if (info.getIdOption() != null) {
+ visit(info.getIdOption(), info.getEntityTypeCast());
+ }
+ }
+
+ @Override
+ public void visit(UriInfoMetadata info) {
+ }
+
+ @Override
+ public void visit(UriInfoResource info) {
+ List parts = info.getUriResourceParts();
+ for (UriResource resource : parts) {
+ switch (resource.getKind()) {
+ case action:
+ visit((UriResourceAction) resource);
+ break;
+ case complexProperty:
+ visit((UriResourceComplexProperty) resource);
+ break;
+ case count:
+ visit((UriResourceCount) resource);
+ break;
+ case entitySet:
+ visit((UriResourceEntitySet) resource);
+ break;
+ case function:
+ visit((UriResourceFunction) resource);
+ break;
+ case it:
+ visit((UriResourceIt) resource);
+ break;
+ case lambdaAll:
+ visit((UriResourceLambdaAll) resource);
+ break;
+ case lambdaAny:
+ visit((UriResourceLambdaAny) resource);
+ break;
+ case lambdaVariable:
+ visit((UriResourceLambdaVariable) resource);
+ break;
+ case navigationProperty:
+ visit((UriResourceNavigation) resource);
+ break;
+ case ref:
+ visit((UriResourceRef) resource);
+ break;
+ case root:
+ visit((UriResourceRoot) resource);
+ break;
+ case primitiveProperty:
+ visit((UriResourcePrimitiveProperty) resource);
+ break;
+ case singleton:
+ visit((UriResourceSingleton) resource);
+ break;
+ case value:
+ visit((UriResourceValue) resource);
+ break;
+ }
+ }
+
+ // http://docs.oasis-open.org/odata/odata/v4.0/os/part1-protocol/odata-v4.0-os-part1-protocol.html#_Toc372793682
+ if (info.getSearchOption() != null) {
+ visit(info.getSearchOption());
+ }
+
+ if (info.getFilterOption() != null) {
+ visit(info.getFilterOption());
+ }
+
+ if (info.getCountOption() != null) {
+ visit(info.getCountOption());
+ }
+
+ visit(info.getOrderByOption());
+
+ if (info.getSkipOption() != null) {
+ visit(info.getSkipOption());
+ }
+
+ if (info.getTopOption() != null) {
+ visit(info.getTopOption());
+ }
+
+ if (info.getExpandOption() != null) {
+ visit(info.getExpandOption());
+ }
+
+ visit(info.getSelectOption());
+
+ if (info.getFormatOption() != null) {
+ visit(info.getFormatOption());
+ }
+
+ if (info.getIdOption() != null) {
+ visit(info.getIdOption(), null);
+ }
+
+ if (info.getSkipTokenOption() != null) {
+ visit(info.getSkipTokenOption());
+ }
+
+ }
+
+ @Override
+ public void visit(ExpandOption option) {
+ }
+
+ @Override
+ public void visit(FilterOption info) {
+ }
+
+ @Override
+ public void visit(FormatOption info) {
+ }
+
+ @Override
+ public void visit(IdOption info, EdmEntityType type) {
+ }
+
+ @Override
+ public void visit(CountOption info) {
+ }
+
+ @Override
+ public void visit(OrderByOption option) {
+ }
+
+ @Override
+ public void visit(SearchOption option) {
+ }
+
+ @Override
+ public void visit(SelectOption option) {
+ }
+
+ @Override
+ public void visit(SkipOption option) {
+ }
+
+ @Override
+ public void visit(SkipTokenOption option) {
+ }
+
+ @Override
+ public void visit(TopOption option) {
+ }
+
+ @Override
+ public void visit(UriResourceCount option) {
+ }
+
+ @Override
+ public void visit(UriResourceRef info) {
+ }
+
+ @Override
+ public void visit(UriResourceRoot info) {
+ }
+
+ @Override
+ public void visit(UriResourceValue info) {
+ }
+
+ @Override
+ public void visit(UriResourceAction info) {
+ }
+
+ @Override
+ public void visit(UriResourceEntitySet info) {
+ }
+
+ @Override
+ public void visit(UriResourceFunction info) {
+ }
+
+ @Override
+ public void visit(UriResourceIt info) {
+ }
+
+ @Override
+ public void visit(UriResourceLambdaAll info) {
+ }
+
+ @Override
+ public void visit(UriResourceLambdaAny info) {
+ }
+
+ @Override
+ public void visit(UriResourceLambdaVariable info) {
+ }
+
+ @Override
+ public void visit(UriResourceNavigation info) {
+ }
+
+ @Override
+ public void visit(UriResourceSingleton info) {
+ }
+
+ @Override
+ public void visit(UriResourceComplexProperty info) {
+ }
+
+ @Override
+ public void visit(UriResourcePrimitiveProperty info) {
+ }
+}
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 000000000..f3f4027f4
--- /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);
+}
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 000000000..e9a213ec3
--- /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
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 000000000..8641a6941
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/SchemaBasedEdmProvider.java
@@ -0,0 +1,315 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.olingo.commons.api.ODataException;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.provider.Action;
+import org.apache.olingo.commons.api.edm.provider.ActionImport;
+import org.apache.olingo.commons.api.edm.provider.AliasInfo;
+import org.apache.olingo.commons.api.edm.provider.Annotatable;
+import org.apache.olingo.commons.api.edm.provider.Annotations;
+import org.apache.olingo.commons.api.edm.provider.ComplexType;
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.commons.api.edm.provider.EntityContainer;
+import org.apache.olingo.commons.api.edm.provider.EntityContainerInfo;
+import org.apache.olingo.commons.api.edm.provider.EntitySet;
+import org.apache.olingo.commons.api.edm.provider.EntityType;
+import org.apache.olingo.commons.api.edm.provider.EnumType;
+import org.apache.olingo.commons.api.edm.provider.Function;
+import org.apache.olingo.commons.api.edm.provider.FunctionImport;
+import org.apache.olingo.commons.api.edm.provider.Schema;
+import org.apache.olingo.commons.api.edm.provider.Singleton;
+import org.apache.olingo.commons.api.edm.provider.Term;
+import org.apache.olingo.commons.api.edm.provider.TypeDefinition;
+
+public class SchemaBasedEdmProvider implements EdmProvider {
+ private final List edmSchemas = new ArrayList();
+
+ 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 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 types = schema.getTypeDefinitions();
+ if (types != null) {
+ for (TypeDefinition type : types) {
+ if (type.getName().equals(fqn.getName())) {
+ return type;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List getFunctions(FullQualifiedName fqn) throws ODataException {
+ ArrayList foundFuncs = new ArrayList();
+ Schema schema = getSchema(fqn.getNamespace());
+ if (schema != null) {
+ List 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 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 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 getSchemas() throws ODataException {
+ return new ArrayList(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 getActions(final FullQualifiedName fqn) throws ODataException {
+ ArrayList actions = new ArrayList();
+ Schema schema = getSchema(fqn.getNamespace());
+ if (schema != null) {
+ List types = schema.getActions();
+ if (types != null) {
+ for (Action type : types) {
+ if (type.getName().equals(fqn.getName())) {
+ actions.add(type);
+ }
+ }
+ }
+ }
+ return actions;
+ }
+
+ @Override
+ public Annotations getAnnotationsGroup(FullQualifiedName targetName) throws ODataException {
+ return null;
+ }
+
+ @Override
+ public Annotatable getAnnoatatable(FullQualifiedName annotatedName) throws ODataException {
+ return null;
+ }
+}
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 000000000..839d87780
--- /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;
+ }
+}
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 000000000..8a9c6108b
--- /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
+ */
+ 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
+ */
+ 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
+ */
+ 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 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 entitySetNames, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException;
+}
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 000000000..e9a8cfe4f
--- /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 getSerializerOptions(Class 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 getPreferences(){
+ HashMap map = new HashMap();
+ List 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;
+ }
+}
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 000000000..fa8f44557
--- /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 processors = new LinkedList();
+ 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 selectProcessor(final Class 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 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 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 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 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 entitySetNames, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ throw new ODataHandlerException("not implemented",
+ ODataHandlerException.MessageKeys.FUNCTIONALITY_NOT_IMPLEMENTED);
+ }
+}
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 000000000..133ee3e0c
--- /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
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 000000000..25af023e0
--- /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 parts = this.odata.createFixedFormatDeserializer()
+ .parseBatchRequest(request.getBody(), boundary, options);
+
+ ODataResponsePart partResponse = null;
+ final List responseParts = new ArrayList();
+
+ 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 changeSetResponses = new ArrayList();
+ // 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 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);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/DataRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/DataRequest.java
new file mode 100644
index 000000000..32bf26a57
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/DataRequest.java
@@ -0,0 +1,769 @@
+/*
+ * 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedList;
+import java.util.List;
+
+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.EdmBindingTarget;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
+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.UriInfo;
+import org.apache.olingo.server.api.uri.UriInfoCrossjoin;
+import org.apache.olingo.server.api.uri.UriInfoResource;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResource;
+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;
+import org.apache.olingo.server.core.ContentNegotiator;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.ServiceRequest;
+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.NoContentResponse;
+import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+import org.apache.olingo.server.core.responses.StreamResponse;
+
+public class DataRequest extends ServiceRequest {
+ protected UriResourceEntitySet uriResourceEntitySet;
+ private boolean countRequest;
+ private UriResourceProperty uriResourceProperty;
+ private boolean valueRequest;
+ private final LinkedList uriNavigations = new LinkedList();
+ 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;
+ }
+
+ public void setUriResourceEntitySet(UriResourceEntitySet uriResourceEntitySet) {
+ this.uriResourceEntitySet = uriResourceEntitySet;
+ this.type = new EntityRequest();
+ }
+
+ public void setCrossJoin(UriInfoCrossjoin info) {
+ this.type = new CrossJoinRequest(info.getEntitySetNames());
+ }
+
+ public boolean isSingleton() {
+ return this.uriResourceSingleton != null;
+ }
+
+ public boolean isCollection() {
+ if (!this.uriNavigations.isEmpty()) {
+ return this.uriNavigations.getLast().isCollection();
+ }
+ return this.uriResourceEntitySet != null && this.uriResourceEntitySet.isCollection();
+ }
+
+ public EdmEntitySet getEntitySet() {
+ return this.uriResourceEntitySet.getEntitySet();
+ }
+
+ public boolean isCountRequest() {
+ return countRequest;
+ }
+
+ public 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;
+ }
+
+ public void setUriResourceProperty(UriResourceProperty uriResourceProperty) {
+ this.uriResourceProperty = uriResourceProperty;
+ this.type = new PropertyRequest();
+ }
+
+ public LinkedList getNavigations() {
+ return this.uriNavigations;
+ }
+
+ public void addUriResourceNavigation(UriResourceNavigation uriResourceNavigation) {
+ this.uriNavigations.add(uriResourceNavigation);
+ }
+
+ public UriResourceSingleton getUriResourceSingleton() {
+ return this.uriResourceSingleton;
+ }
+
+ public void setUriResourceSingleton(UriResourceSingleton info) {
+ this.uriResourceSingleton = info;
+ this.type = new SingletonRequest();
+ }
+
+ public List getKeyPredicates() {
+ if (this.uriResourceEntitySet != null) {
+ return this.uriResourceEntitySet.getKeyPredicates();
+ }
+ return null;
+ }
+
+ public boolean isReferenceRequest() {
+ return this.references;
+ }
+
+ public 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();
+ }
+
+ public 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 getSerializerOptions(Class serilizerOptions, ContextURL contextUrl, boolean references)
+ throws ContentNegotiatorException {
+ if (serilizerOptions.isAssignableFrom(PrimitiveSerializerOptions.class)) {
+ return (T) PrimitiveSerializerOptions.with().contextURL(contextUrl)
+ .facetsFrom(getUriResourceProperty().getProperty()).build();
+ }
+ return super.getSerializerOptions(serilizerOptions, contextUrl, references);
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return type.getResponseContentType();
+ }
+
+ class EntityRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ // the create/update/delete to navigation property is done through references
+ // see # 11.4.6
+ if (!getNavigations().isEmpty() && !isGET()) {
+ return false;
+ }
+ 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), false, response);
+
+ if (isGET()) {
+ if (isCollection()) {
+ handler.read(DataRequest.this,
+ EntitySetResponse.getInstance(DataRequest.this, getContextURL(odata), false, 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
+ entityResponse = EntityResponse.getInstance(DataRequest.this,
+ getContextURL(odata), false, response, getReturnRepresentation());
+ handler.createEntity(DataRequest.this, getEntityFromClient(), entityResponse);
+ } else {
+ handler.updateEntity(DataRequest.this, getEntityFromClient(), isPATCH(), getETag(),
+ entityResponse);
+ }
+ } else if (isPOST()) {
+ entityResponse = EntityResponse.getInstance(DataRequest.this,
+ getContextURL(odata), false, response, getReturnRepresentation());
+ handler.createEntity(DataRequest.this, getEntityFromClient(),entityResponse);
+ } 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(),
+ getKeyPredicates(), getUriInfo(), getNavigations(), 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;
+ }
+ }
+
+ /**
+ * Is NavigationProperty Reference.
+ */
+ class ReferenceRequest implements RequestType {
+
+ @Override
+ public boolean allowedMethod() {
+ // references are only allowed on the navigation properties
+ if (getNavigations().isEmpty()) {
+ return false;
+ }
+
+ // 11.4.6.1 - post allowed on only collection valued navigation
+ if (isPOST() && !getNavigations().getLast().isCollection()) {
+ return false;
+ }
+
+ // 11.4.6.3 - PUT allowed on single valued navigation
+ if (isPUT() && getNavigations().getLast().isCollection()) {
+ return false;
+ }
+
+ // No defined behavior in spec
+ 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), true, response));
+ } else {
+ handler.read(DataRequest.this,
+ EntityResponse.getInstance(DataRequest.this, getContextURL(odata), true, response));
+ }
+ } else if (isDELETE()) {
+ // if this against the collection, user need to look at $id param for entity ref #11.4.6.2
+ String id = getQueryParameter("$id");
+ if (id == null) {
+ handler.deleteReference(DataRequest.this, null, getETag(), new NoContentResponse(
+ getServiceMetaData(), response));
+ } else {
+ try {
+ handler.deleteReference(DataRequest.this, new URI(id), getETag(), new NoContentResponse(
+ getServiceMetaData(), response));
+ } catch (URISyntaxException e) {
+ throw new DeserializerException("failed to read $id", e, MessageKeys.UNKOWN_CONTENT);
+ }
+ }
+ } else if (isPUT()) {
+ // note this is always against single reference
+ handler.updateReference(DataRequest.this, getETag(), getPayload().get(0), new NoContentResponse(
+ getServiceMetaData(), response));
+ } else if (isPOST()) {
+ // this needs to be against collection of references
+ handler.addReference(DataRequest.this, getETag(), getPayload(), new NoContentResponse(
+ getServiceMetaData(), response));
+ }
+ }
+
+ // http://docs.oasis-open.org/odata/odata-json-format/v4.0/errata02/os
+ // /odata-json-format-v4.0-errata02-os-complete.html#_Toc403940643
+ // The below code reads as property and converts to an URI
+ private List getPayload() throws DeserializerException {
+ ODataDeserializer deserializer = odata.createDeserializer(ODataFormat
+ .fromContentType(getRequestContentType()));
+ return deserializer.entityReferences(getODataRequest().getBody());
+ }
+
+ @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();
+
+ if (isGET()) {
+ if (isPropertyStream()) {
+ handler.read(DataRequest.this, new StreamResponse(getServiceMetaData(), response));
+ } else {
+ handler.read(DataRequest.this, buildResponse(response, edmProperty));
+ }
+ } else if (isPATCH()) {
+ handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), true,
+ getETag(), buildResponse(response, edmProperty));
+ } else if (isPUT()) {
+ if (isPropertyStream()) {
+ handler.upsertStreamProperty(DataRequest.this, getETag(), request.getBody(),
+ new NoContentResponse(getServiceMetaData(), response));
+ } else {
+ handler.updateProperty(DataRequest.this, getPropertyValueFromClient(edmProperty), false,
+ getETag(), buildResponse(response, edmProperty));
+ }
+ } else if (isDELETE()) {
+ if (isPropertyStream()) {
+ handler.upsertStreamProperty(DataRequest.this, getETag(), request.getBody(),
+ new NoContentResponse(getServiceMetaData(), response));
+ } else {
+ Property property = new PropertyImpl();
+ property.setName(edmProperty.getName());
+ property.setType(edmProperty.getType().getFullQualifiedName()
+ .getFullQualifiedNameAsString());
+ handler.updateProperty(DataRequest.this, property, false, getETag(),
+ buildResponse(response, edmProperty));
+ }
+ }
+ }
+
+ private PropertyResponse buildResponse(ODataResponse response, EdmProperty edmProperty)
+ throws ContentNegotiatorException, SerializerException {
+ PropertyResponse propertyResponse = PropertyResponse.getInstance(DataRequest.this, response,
+ edmProperty.getType(), getContextURL(odata), edmProperty.isCollection());
+ return 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()));
+ String navPath = buildNavPath(helper, getEntitySet().getEntityType(), getNavigations(), true);
+ if (navPath != null && !navPath.isEmpty()) {
+ builder.navOrPropertyPath(navPath+"/"+edmProperty.getName());
+ } else {
+ 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() {
+ //part2-url-conventions # 4.2
+ if (isPropertyStream() && isGET()) {
+ return false;
+ }
+
+ 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(), null, getUriInfo(), getNavigations(), 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), false, response));
+ }
+ }
+
+ class CrossJoinRequest implements RequestType {
+ private final List entitySetNames;
+
+ public CrossJoinRequest(List entitySetNames) {
+ this.entitySetNames = entitySetNames;
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET();
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(getUriInfo().getFormatOption(),
+ getODataRequest(), getCustomContentTypeSupport(), RepresentationType.COLLECTION_COMPLEX);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ handler.crossJoin(DataRequest.this, this.entitySetNames, response);
+ }
+
+ @Override
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ ContextURL.Builder builder = ContextURL.with().asCollection();
+ return builder.build();
+ }
+ }
+
+ 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);
+ }
+
+ static ContextURL.Builder buildEntitySetContextURL(UriHelper helper,
+ EdmBindingTarget edmEntitySet, List keyPredicates, UriInfo uriInfo,
+ LinkedList navigations, boolean collectionReturn, boolean singleton)
+ throws SerializerException {
+
+ ContextURL.Builder builder = ContextURL.with().entitySetOrSingletonOrType(edmEntitySet.getName());
+ String select = helper.buildContextURLSelectList(edmEntitySet.getEntityType(),
+ uriInfo.getExpandOption(), uriInfo.getSelectOption());
+ if (!singleton) {
+ builder.suffix(collectionReturn ? null : Suffix.ENTITY);
+ }
+
+ builder.selectList(select);
+
+ final UriInfoResource resource = uriInfo.asUriInfoResource();
+ final List resourceParts = resource.getUriResourceParts();
+ final List path = getPropertyPath(resourceParts);
+ String propertyPath = buildPropertyPath(path);
+ final String navPath = buildNavPath(helper, edmEntitySet.getEntityType(), navigations, collectionReturn);
+ if (navPath != null && !navPath.isEmpty()) {
+ if (keyPredicates != null) {
+ builder.keyPath(helper.buildContextURLKeyPredicate(keyPredicates));
+ }
+ if (propertyPath != null) {
+ propertyPath = navPath+"/"+propertyPath;
+ } else {
+ propertyPath = navPath;
+ }
+ }
+ builder.navOrPropertyPath(propertyPath);
+ return builder;
+ }
+
+ private static List getPropertyPath(final List path) {
+ List result = new LinkedList();
+ int index = 1;
+ while (index < path.size() && path.get(index) instanceof UriResourceProperty) {
+ result.add(((UriResourceProperty) path.get(index)).getProperty().getName());
+ index++;
+ }
+ return result;
+ }
+
+ private static String buildPropertyPath(final List path) {
+ StringBuilder result = new StringBuilder();
+ for (final String segment : path) {
+ result.append(result.length() == 0 ? "" : '/').append(segment); //$NON-NLS-1$
+ }
+ return result.length() == 0?null:result.toString();
+ }
+
+ static String buildNavPath(UriHelper helper, EdmEntityType rootType,
+ LinkedList navigations, boolean includeLastPredicates)
+ throws SerializerException {
+ if (navigations.isEmpty()) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ boolean containsTarget = false;
+ EdmEntityType type = rootType;
+ for (UriResourceNavigation nav:navigations) {
+ String name = nav.getProperty().getName();
+ EdmNavigationProperty property = type.getNavigationProperty(name);
+ if (property.containsTarget()) {
+ containsTarget = true;
+ }
+ type = nav.getProperty().getType();
+ }
+
+ if (containsTarget) {
+ for (int i = 0; i < navigations.size(); i++) {
+ UriResourceNavigation nav = navigations.get(i);
+ if (i > 0) {
+ sb.append("/");
+ }
+ sb.append(nav.getProperty().getName());
+
+ boolean skipKeys = false;
+ if (navigations.size() == i+1 && !includeLastPredicates ) {
+ skipKeys = true;
+ }
+
+ if (!skipKeys && !nav.getKeyPredicates().isEmpty()) {
+ sb.append("(");
+ sb.append(helper.buildContextURLKeyPredicate(nav.getKeyPredicates()));
+ sb.append(")");
+ }
+
+ if (nav.getTypeFilterOnCollection() != null) {
+ sb.append("/")
+ .append(nav.getTypeFilterOnCollection().getFullQualifiedName().getFullQualifiedNameAsString());
+ } else if (nav.getTypeFilterOnEntry() != null) {
+ sb.append("/")
+ .append(nav.getTypeFilterOnEntry().getFullQualifiedName().getFullQualifiedNameAsString());
+ }
+ }
+ }
+ return sb.toString();
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/FunctionRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/FunctionRequest.java
new file mode 100644
index 000000000..a9f9341d6
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/FunctionRequest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.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;
+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.PrimitiveValueResponse;
+import org.apache.olingo.server.core.responses.PropertyResponse;
+
+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), false, response));
+ } else {
+ handler.invoke(this, getODataRequest().getMethod(),
+ EntityResponse.getInstance(this, getContextURL(odata), false, 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;
+ }
+
+ public 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 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
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MediaRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MediaRequest.java
new file mode 100644
index 000000000..a4a333a1d
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MediaRequest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.List;
+
+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.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceEntitySet;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.ServiceRequest;
+import org.apache.olingo.server.core.responses.NoContentResponse;
+import org.apache.olingo.server.core.responses.StreamResponse;
+
+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(getServiceMetaData(), response));
+ } else if (isPUT()) {
+ handler.upsertMediaStream(this, getETag(), getMediaStream(), new NoContentResponse(
+ getServiceMetaData(), response));
+ } else if (isDELETE()) {
+ handler.upsertMediaStream(this, getETag(), null, new NoContentResponse(getServiceMetaData(),
+ 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();
+ }
+
+ public void setUriResourceEntitySet(UriResourceEntitySet uriResourceEntitySet) {
+ this.uriResourceEntitySet = uriResourceEntitySet;
+ }
+
+ public List getKeyPredicates() {
+ if (this.uriResourceEntitySet != null) {
+ return this.uriResourceEntitySet.getKeyPredicates();
+ }
+ return null;
+ }
+
+ private InputStream getMediaStream() {
+ return this.request.getBody();
+ }
+
+ @Override
+ public boolean allowedMethod() {
+ return isGET() || isPUT() || isDELETE();
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MetadataRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MetadataRequest.java
new file mode 100644
index 000000000..e2c5c5476
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/MetadataRequest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.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.serializer.RepresentationType;
+import org.apache.olingo.server.api.uri.UriInfoMetadata;
+import org.apache.olingo.server.core.ContentNegotiator;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.ServiceRequest;
+import org.apache.olingo.server.core.responses.MetadataResponse;
+
+public class MetadataRequest extends ServiceRequest {
+
+ public MetadataRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(this.uriInfo.getFormatOption(), this.request,
+ this.customContentType, RepresentationType.METADATA);
+ }
+
+ public UriInfoMetadata getUriInfoMetadata() {
+ return uriInfo.asUriInfoMetadata();
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ if (!allowedMethod()) {
+ methodNotAllowed();
+ }
+
+ handler.readMetadata(this, MetadataResponse.getInstance(this, response));
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/OperationRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/OperationRequest.java
new file mode 100644
index 000000000..1f1b1946b
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/OperationRequest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.data.ContextURL;
+import org.apache.olingo.commons.api.edm.EdmReturnType;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
+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.core.ContentNegotiator;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public abstract class OperationRequest extends ServiceRequest {
+
+ public OperationRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ if (!hasReturnType()) {
+ // this default content type
+ return ContentType.APPLICATION_OCTET_STREAM;
+ }
+
+ if (isReturnTypePrimitive()) {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), this.request,
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_PRIMITIVE
+ : RepresentationType.PRIMITIVE);
+ } else if (isReturnTypeComplex()) {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), this.request,
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_COMPLEX
+ : RepresentationType.COMPLEX);
+ } else {
+ return ContentNegotiator.doContentNegotiation(uriInfo.getFormatOption(), this.request,
+ getCustomContentTypeSupport(), isCollection() ? RepresentationType.COLLECTION_ENTITY
+ : RepresentationType.ENTITY);
+ }
+ }
+
+ public abstract boolean isBound();
+
+ public abstract boolean isCollection();
+
+ public abstract EdmReturnType getReturnType();
+
+ public abstract boolean hasReturnType();
+
+ public ContextURL getContextURL(OData odata) throws SerializerException {
+ if (!hasReturnType()) {
+ return null;
+ }
+
+ final UriHelper helper = odata.createUriHelper();
+
+ if (isReturnTypePrimitive() || isReturnTypeComplex()) {
+ // Part 1 {10.14, 10.14} since the function return properties does not
+ // represent a Entity property
+ ContextURL.Builder builder = ContextURL.with().type(getReturnType().getType());
+ if (isCollection()) {
+ builder.asCollection();
+ }
+ return builder.build();
+ }
+
+ /*
+ // EdmTypeKind.ENTITY;
+ if (isBound()) {
+ // Bound means, we know the EnitySet of the return type. Part 1 {10.2,
+ // 10.3}
+ EdmEntitySet entitySet = this.uriResourceFunction.getFunctionImport().getReturnedEntitySet();
+ ContextURL.Builder builder = DataRequest.buildEntitySetContextURL(helper, entitySet,
+ this.uriInfo, isCollection(), false);
+ return builder.build();
+ }
+ */
+
+ // EdmTypeKind.ENTITY; Not Bound
+ // Here we do not know the EntitySet, then follow directions from
+ // Part-1{10.2. 10.3} to use
+ // {context-url}#{type-name}
+ ContextURL.Builder builder = ContextURL.with().type(getReturnType().getType());
+ if (isCollection()) {
+ builder.asCollection();
+ }
+ return builder.build();
+ }
+
+ public boolean isReturnTypePrimitive() {
+ return getReturnType().getType().getKind() == EdmTypeKind.PRIMITIVE;
+ }
+
+ public boolean isReturnTypeComplex() {
+ return getReturnType().getType().getKind() == EdmTypeKind.COMPLEX;
+ }
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ServiceDocumentRequest.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ServiceDocumentRequest.java
new file mode 100644
index 000000000..f99aaf56e
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/requests/ServiceDocumentRequest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.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.serializer.RepresentationType;
+import org.apache.olingo.server.core.ContentNegotiator;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceHandler;
+import org.apache.olingo.server.core.ServiceRequest;
+import org.apache.olingo.server.core.responses.ServiceDocumentResponse;
+
+public class ServiceDocumentRequest extends ServiceRequest {
+
+ public ServiceDocumentRequest(OData odata, ServiceMetadata serviceMetadata) {
+ super(odata, serviceMetadata);
+ }
+
+ @Override
+ public ContentType getResponseContentType() throws ContentNegotiatorException {
+ return ContentNegotiator.doContentNegotiation(getUriInfo().getFormatOption(),
+ getODataRequest(), getCustomContentTypeSupport(), RepresentationType.SERVICE);
+ }
+
+ @Override
+ public void execute(ServiceHandler handler, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ if (!allowedMethod()) {
+ methodNotAllowed();
+ }
+ handler.readServiceDocument(this,
+ ServiceDocumentResponse.getInstace(this, response, getResponseContentType()));
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/CountResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/CountResponse.java
new file mode 100644
index 000000000..f7cde332b
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/CountResponse.java
@@ -0,0 +1,60 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+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(request.getServiceMetaData(), serializer, response,
+ request.getPreferences());
+ }
+
+ private CountResponse(ServiceMetadata metadata, FixedFormatSerializer serializer,
+ ODataResponse response, Map preferences) {
+ super(metadata, response, preferences);
+ 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);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntityResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntityResponse.java
new file mode 100644
index 000000000..fd29bbd03
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntityResponse.java
@@ -0,0 +1,140 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+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.ContentNegotiatorException;
+import org.apache.olingo.server.core.ReturnRepresentation;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class EntityResponse extends ServiceResponse {
+ private final ReturnRepresentation returnRepresentation;
+ private final ODataSerializer serializer;
+ private final EntitySerializerOptions options;
+ private final ContentType responseContentType;
+
+ private EntityResponse(ServiceMetadata metadata, ODataResponse response,
+ ODataSerializer serializer, EntitySerializerOptions options, ContentType responseContentType,
+ Map preferences, ReturnRepresentation returnRepresentation) {
+ super(metadata, response, preferences);
+ this.serializer = serializer;
+ this.options = options;
+ this.responseContentType = responseContentType;
+ this.returnRepresentation = returnRepresentation;
+ }
+
+ public static EntityResponse getInstance(ServiceRequest request, ContextURL contextURL,
+ boolean references, ODataResponse response, ReturnRepresentation returnRepresentation)
+ throws ContentNegotiatorException, SerializerException {
+ EntitySerializerOptions options = request.getSerializerOptions(EntitySerializerOptions.class,
+ contextURL, references);
+ return new EntityResponse(request.getServiceMetaData(), response, request.getSerializer(),
+ options, request.getResponseContentType(), request.getPreferences(), returnRepresentation);
+ }
+
+ public static EntityResponse getInstance(ServiceRequest request, ContextURL contextURL,
+ boolean references, ODataResponse response)
+ throws ContentNegotiatorException, SerializerException {
+ EntitySerializerOptions options = request.getSerializerOptions(EntitySerializerOptions.class,
+ contextURL, references);
+ return new EntityResponse(request.getServiceMetaData(), response, request.getSerializer(),
+ options, request.getResponseContentType(), request.getPreferences(), null);
+ }
+
+ // write single entity
+ public void writeReadEntity(EdmEntityType entityType, Entity entity) throws SerializerException {
+
+ assert (!isClosed());
+
+ if (entity == null) {
+ writeNotFound(true);
+ return;
+ }
+
+ // write the entity to response
+ this.response.setContent(this.serializer.entity(this.metadata, 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);
+
+ // Note that if media written just like Stream, but on entity URL
+
+ // 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(this.metadata, 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();
+ }
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntitySetResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntitySetResponse.java
new file mode 100644
index 000000000..40276d210
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/EntitySetResponse.java
@@ -0,0 +1,82 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class EntitySetResponse extends ServiceResponse {
+ private final ODataSerializer serializer;
+ private final EntityCollectionSerializerOptions options;
+ private final ContentType responseContentType;
+
+ private EntitySetResponse(ServiceMetadata metadata, ODataResponse response, ODataSerializer serializer,
+ EntityCollectionSerializerOptions options,
+ ContentType responseContentType, Map preferences) {
+ super(metadata, response, preferences);
+ this.serializer = serializer;
+ this.options = options;
+ this.responseContentType = responseContentType;
+ }
+
+ public static EntitySetResponse getInstance(ServiceRequest request, ContextURL contextURL,
+ boolean referencesOnly, ODataResponse response) throws ContentNegotiatorException, SerializerException {
+ EntityCollectionSerializerOptions options = request.getSerializerOptions(
+ EntityCollectionSerializerOptions.class, contextURL, referencesOnly);
+ return new EntitySetResponse(request.getServiceMetaData(),response, request.getSerializer(), options,
+ request.getResponseContentType(), request.getPreferences());
+ }
+
+ // 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;
+ }
+
+ // write the whole collection to response
+ this.response.setContent(this.serializer.entityCollection(metadata, entityType, entitySet, this.options));
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/MetadataResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/MetadataResponse.java
new file mode 100644
index 000000000..055c0b0be
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/MetadataResponse.java
@@ -0,0 +1,62 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class MetadataResponse extends ServiceResponse {
+ private final ODataSerializer serializer;
+ private final ContentType responseContentType;
+
+ public static MetadataResponse getInstance(ServiceRequest request,
+ ODataResponse response) throws ContentNegotiatorException, SerializerException {
+ return new MetadataResponse(request.getServiceMetaData(), response, request.getSerializer(),
+ request.getResponseContentType(), request.getPreferences());
+ }
+
+ private MetadataResponse(ServiceMetadata metadata, ODataResponse response, ODataSerializer serializer,
+ ContentType responseContentType, Map preferences) {
+ super(metadata, response, preferences);
+ this.serializer = serializer;
+ this.responseContentType = responseContentType;
+ }
+
+ public void writeMetadata()throws ODataTranslatedException {
+ assert (!isClosed());
+ this.response.setContent(this.serializer.metadataDocument(this.metadata));
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/NoContentResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/NoContentResponse.java
new file mode 100644
index 000000000..eb163658b
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/NoContentResponse.java
@@ -0,0 +1,100 @@
+/*
+ * 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.responses;
+
+import java.util.Collections;
+
+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.ServiceMetadata;
+
+public class NoContentResponse extends ServiceResponse {
+
+ public NoContentResponse(ServiceMetadata metadata, ODataResponse response) {
+ super(metadata, response, Collections.EMPTY_MAP);
+ }
+
+ // 200
+ public void writeOK() {
+ this.response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+ close();
+ }
+
+ // 201
+ public void writeCreated() {
+ this.response.setStatusCode(HttpStatusCode.CREATED.getStatusCode());
+ close();
+ }
+
+ // 202
+ public void writeAccepted() {
+ this.response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
+ close();
+ }
+
+ // 204
+ public void writeNoContent() {
+ writeNoContent(true);
+ }
+
+ // 304
+ public void writeNotModified() {
+ this.response.setStatusCode(HttpStatusCode.NOT_MODIFIED.getStatusCode());
+ close();
+ }
+
+ // error response codes
+
+ // 404
+ public void writeNotFound() {
+ writeNotFound(true);
+ }
+
+ // 501
+ public void writeNotImplemented() {
+ this.response.setStatusCode(HttpStatusCode.NOT_IMPLEMENTED.getStatusCode());
+ close();
+ }
+
+ // 405
+ public void writeMethodNotAllowed() {
+ this.response.setStatusCode(HttpStatusCode.METHOD_NOT_ALLOWED.getStatusCode());
+ close();
+ }
+
+ // 410
+ public void writeGone() {
+ this.response.setStatusCode(HttpStatusCode.GONE.getStatusCode());
+ close();
+ }
+
+ // 412
+ public void writePreConditionFailed() {
+ this.response.setStatusCode(HttpStatusCode.PRECONDITION_FAILED.getStatusCode());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PrimitiveValueResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PrimitiveValueResponse.java
new file mode 100644
index 000000000..005bfca9f
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PrimitiveValueResponse.java
@@ -0,0 +1,105 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmReturnType;
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.FixedFormatSerializer;
+import org.apache.olingo.server.api.serializer.PrimitiveValueSerializerOptions;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class PrimitiveValueResponse extends ServiceResponse {
+ private final boolean returnCollection;
+ private EdmProperty type;
+ private EdmReturnType returnType;
+ private final FixedFormatSerializer serializer;
+
+ public static PrimitiveValueResponse getInstance(ServiceRequest request, ODataResponse response,
+ boolean collection, EdmProperty type) {
+ FixedFormatSerializer serializer = request.getOdata().createFixedFormatSerializer();
+ return new PrimitiveValueResponse(request.getServiceMetaData(), serializer, response,
+ collection, type, request.getPreferences());
+ }
+
+ public static PrimitiveValueResponse getInstance(ServiceRequest request, ODataResponse response,
+ boolean collection, EdmReturnType type) {
+ FixedFormatSerializer serializer = request.getOdata().createFixedFormatSerializer();
+ return new PrimitiveValueResponse(request.getServiceMetaData(), serializer, response,
+ collection, type, request.getPreferences());
+ }
+
+ private PrimitiveValueResponse(ServiceMetadata metadata, FixedFormatSerializer serializer,
+ ODataResponse response, boolean collection, EdmProperty type, Map preferences) {
+ super(metadata, response, preferences);
+ this.returnCollection = collection;
+ this.type = type;
+ this.serializer = serializer;
+ }
+
+ private PrimitiveValueResponse(ServiceMetadata metadata, FixedFormatSerializer serializer,
+ ODataResponse response, boolean collection, EdmReturnType type,
+ Map preferences) {
+ super(metadata, response, preferences);
+ this.returnCollection = collection;
+ this.returnType = type;
+ this.serializer = serializer;
+ }
+
+ public void write(Object value) throws SerializerException {
+ if (value == null) {
+ writeNoContent(true);
+ return;
+ }
+
+ if (this.type != null) {
+ PrimitiveValueSerializerOptions options = PrimitiveValueSerializerOptions.with()
+ .facetsFrom(this.type).build();
+
+ this.response.setContent(this.serializer.primitiveValue((EdmPrimitiveType) this.type.getType(),
+ value, options));
+ } else {
+ PrimitiveValueSerializerOptions options = PrimitiveValueSerializerOptions.with()
+ .nullable(this.returnType.isNullable()).maxLength(this.returnType.getMaxLength())
+ .precision(this.returnType.getPrecision()).scale(this.returnType.getScale()).build();
+ this.response.setContent(this.serializer.primitiveValue(
+ (EdmPrimitiveType) this.returnType.getType(), value, options));
+ }
+
+ writeOK(HttpContentType.TEXT_PLAIN);
+ }
+
+ public boolean isReturnCollection() {
+ return returnCollection;
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
new file mode 100644
index 000000000..e6b951d39
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/PropertyResponse.java
@@ -0,0 +1,144 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+import org.apache.olingo.commons.api.data.ContextURL;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmComplexType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class PropertyResponse extends ServiceResponse {
+ private PrimitiveSerializerOptions primitiveOptions;
+ private ComplexSerializerOptions complexOptions;
+ private final ContentType responseContentType;
+ private final ODataSerializer serializer;
+ private final boolean collection;
+
+ public static PropertyResponse getInstance(ServiceRequest request, ODataResponse response,
+ EdmType edmType, ContextURL contextURL, boolean collection) throws ContentNegotiatorException,
+ SerializerException {
+ if (edmType.getKind() == EdmTypeKind.PRIMITIVE) {
+ PrimitiveSerializerOptions options = request.getSerializerOptions(
+ PrimitiveSerializerOptions.class, contextURL, false);
+ ContentType type = request.getResponseContentType();
+ return new PropertyResponse(request.getServiceMetaData(), request.getSerializer(), response,
+ options, type, collection, request.getPreferences());
+ }
+ ComplexSerializerOptions options = request.getSerializerOptions(ComplexSerializerOptions.class,
+ contextURL, false);
+ ContentType type = request.getResponseContentType();
+ return new PropertyResponse(request.getServiceMetaData(), request.getSerializer(), response,
+ options, type, collection, request.getPreferences());
+ }
+
+ private PropertyResponse(ServiceMetadata metadata, ODataSerializer serializer,
+ ODataResponse response, PrimitiveSerializerOptions options, ContentType contentType,
+ boolean collection, Map preferences) {
+ super(metadata, response, preferences);
+ this.serializer = serializer;
+ this.primitiveOptions = options;
+ this.responseContentType = contentType;
+ this.collection = collection;
+ }
+
+ private PropertyResponse(ServiceMetadata metadata, ODataSerializer serializer, ODataResponse response,
+ ComplexSerializerOptions options, ContentType contentType, boolean collection,
+ Map preferences) {
+ super(metadata, response, preferences);
+ this.serializer = serializer;
+ this.complexOptions = options;
+ this.responseContentType = contentType;
+ this.collection = collection;
+ }
+
+ public void writeProperty(EdmType edmType, Property property) throws SerializerException {
+ assert (!isClosed());
+
+ if (property == null) {
+ writeNotFound(true);
+ return;
+ }
+
+ if (property.getValue() == null) {
+ writeNoContent(true);
+ return;
+ }
+
+ if (edmType.getKind() == EdmTypeKind.PRIMITIVE) {
+ writePrimitiveProperty((EdmPrimitiveType) edmType, property);
+ } else {
+ writeComplexProperty((EdmComplexType) edmType, property);
+ }
+ }
+
+ private void writeComplexProperty(EdmComplexType type, Property property)
+ throws SerializerException {
+ if (this.collection) {
+ this.response.setContent(this.serializer.complexCollection(this.metadata, type, property,
+ this.complexOptions));
+ } else {
+ this.response.setContent(this.serializer.complex(this.metadata, type, property,
+ this.complexOptions));
+ }
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ private void writePrimitiveProperty(EdmPrimitiveType type, Property property)
+ throws SerializerException {
+ if(this.collection) {
+ this.response.setContent(this.serializer.primitiveCollection(type, property, this.primitiveOptions));
+ } else {
+ this.response.setContent(this.serializer.primitive(type, property, this.primitiveOptions));
+ }
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+
+ public void writePropertyUpdated() {
+ // spec says just success response; so either 200 or 204. 200 typically has
+ // payload
+ writeNoContent(true);
+ }
+
+ public void writePropertyDeleted() {
+ writeNoContent(true);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
new file mode 100644
index 000000000..86c420b66
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceDocumentResponse.java
@@ -0,0 +1,63 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
+import org.apache.olingo.server.api.serializer.SerializerException;
+import org.apache.olingo.server.core.ContentNegotiatorException;
+import org.apache.olingo.server.core.ServiceRequest;
+
+public class ServiceDocumentResponse extends ServiceResponse {
+ private final ODataSerializer serializer;
+ private final ContentType responseContentType;
+
+ public static ServiceDocumentResponse getInstace(ServiceRequest request, ODataResponse respose,
+ ContentType responseContentType) throws ContentNegotiatorException, SerializerException {
+ return new ServiceDocumentResponse(request.getServiceMetaData(), respose,
+ request.getSerializer(), responseContentType, request.getPreferences());
+ }
+
+ private ServiceDocumentResponse(ServiceMetadata metadata, ODataResponse respose,
+ ODataSerializer serializer, ContentType responseContentType, Map preferences) {
+ super(metadata, respose, preferences);
+ this.serializer = serializer;
+ this.responseContentType = responseContentType;
+ }
+
+ public void writeServiceDocument(String serviceRoot)
+ throws ODataTranslatedException {
+ assert (!isClosed());
+ this.response.setContent(this.serializer.serviceDocument(this.metadata.getEdm(), serviceRoot));
+ writeOK(this.responseContentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java
new file mode 100644
index 000000000..a3065515d
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java
@@ -0,0 +1,119 @@
+/*
+ * 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.responses;
+
+import java.util.Map;
+
+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.ServiceMetadata;
+
+public abstract class ServiceResponse {
+ protected ServiceMetadata metadata;
+ protected ODataResponse response;
+ protected Map preferences;
+ private boolean closed;
+ private boolean strictApplyPreferences = true;
+
+ public ServiceResponse(ServiceMetadata metadata, ODataResponse response,
+ Map preferences) {
+ this.metadata = metadata;
+ this.response = response;
+ this.preferences = preferences;
+ }
+
+ public ODataResponse getODataResponse() {
+ return this.response;
+ }
+
+ protected boolean isClosed() {
+ return this.closed;
+ }
+
+ protected void close() {
+ if (!this.closed) {
+ if (this.strictApplyPreferences) {
+ if (!preferences.isEmpty()) {
+ assert(this.response.getHeaders().get("Preference-Applied") != null);
+ }
+ }
+ this.closed = true;
+ }
+ }
+
+ public void writeNoContent(boolean closeResponse) {
+ this.response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+ if (closeResponse) {
+ close();
+ }
+ }
+
+ public void writeNotFound(boolean closeResponse) {
+ response.setStatusCode(HttpStatusCode.NOT_FOUND.getStatusCode());
+ if (closeResponse) {
+ close();
+ }
+ }
+
+ public void writeServerError(boolean closeResponse) {
+ response.setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
+ if (closeResponse) {
+ close();
+ }
+ }
+
+ public void writeBadRequest(boolean closeResponse) {
+ response.setStatusCode(HttpStatusCode.BAD_REQUEST.getStatusCode());
+ if (closeResponse) {
+ close();
+ }
+ }
+
+ public void writeOK(String contentType) {
+ this.response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+ this.response.setHeader(HttpHeader.CONTENT_TYPE, contentType);
+ }
+
+ public void writeHeader(String key, String value) {
+ if ("Preference-Applied".equals(key)) {
+ String previous = this.response.getHeaders().get(key);
+ if (previous != null) {
+ value = previous+";"+value;
+ }
+ this.response.setHeader(key, value);
+ } else {
+ this.response.setHeader(key, value);
+ }
+ }
+
+ /**
+ * When true; the "Preference-Applied" header is strictly checked.
+ * @param flag
+ */
+ public void setStrictlyApplyPreferences(boolean flag) {
+ this.strictApplyPreferences = flag;
+ }
+
+ public abstract void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException;
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java
new file mode 100644
index 000000000..4f11cb8b6
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java
@@ -0,0 +1,71 @@
+/*
+ * 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.responses;
+
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataTranslatedException;
+
+@SuppressWarnings("unused")
+public class ServiceResponseVisior {
+
+ public void visit(CountResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(EntityResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(MetadataResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(NoContentResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(PropertyResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(ServiceDocumentResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(StreamResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ public void visit(EntitySetResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeServerError(true);
+ }
+}
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java
new file mode 100644
index 000000000..ec7db032a
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.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.responses;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+
+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.ServiceMetadata;
+
+public class StreamResponse extends ServiceResponse {
+
+ public StreamResponse(ServiceMetadata metadata, ODataResponse response) {
+ super(metadata, response, Collections.EMPTY_MAP);
+ }
+
+ public void writeStreamResponse(InputStream streamContent, ContentType contentType) {
+ this.response.setContent(streamContent);
+ writeOK(contentType.toContentTypeString());
+ close();
+ }
+
+ public void writeBinaryResponse(byte[] streamContent, ContentType contentType) {
+ this.response.setContent(new ByteArrayInputStream(streamContent));
+ writeOK(contentType.toContentTypeString());
+ close();
+ }
+
+ @Override
+ public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+ ODataApplicationException {
+ visitor.visit(this);
+ }
+}
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java
new file mode 100644
index 000000000..9749a6cc5
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+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.ComplexType;
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+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.NavigationPropertyBinding;
+import org.apache.olingo.commons.api.edm.provider.Parameter;
+import org.apache.olingo.commons.api.edm.provider.Property;
+import org.apache.olingo.commons.api.edm.provider.Singleton;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MetadataParserTest {
+ final String NS = "Microsoft.OData.SampleService.Models.TripPin";
+ final FullQualifiedName NSF = new FullQualifiedName(NS);
+
+ EdmProvider provider = null;
+
+ @Before
+ public void setUp() throws Exception {
+ MetadataParser parser = new MetadataParser();
+ provider = parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
+ }
+
+ @Test
+ public void testAction() throws ODataException {
+ // test action
+ List actions = provider.getActions(new FullQualifiedName(NS, "ResetDataSource"));
+ assertNotNull(actions);
+ assertEquals(1, actions.size());
+ }
+
+ @Test
+ public void testFunction() throws ODataException {
+ // test function
+ List functions = provider
+ .getFunctions(new FullQualifiedName(NS, "GetFavoriteAirline"));
+ assertNotNull(functions);
+ assertEquals(1, functions.size());
+ assertEquals("GetFavoriteAirline", functions.get(0).getName());
+ assertTrue(functions.get(0).isBound());
+ assertTrue(functions.get(0).isComposable());
+ assertEquals(
+ "person/Trips/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Flight/Airline",
+ functions.get(0).getEntitySetPath());
+
+ List parameters = functions.get(0).getParameters();
+ assertNotNull(parameters);
+ assertEquals(1, parameters.size());
+ assertEquals("person", parameters.get(0).getName());
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",parameters.get(0).getType());
+ assertFalse(parameters.get(0).isNullable());
+
+ assertNotNull(functions.get(0).getReturnType());
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Airline",
+ functions.get(0).getReturnType().getType());
+ assertFalse(functions.get(0).getReturnType().isNullable());
+ }
+
+ @Test
+ public void testEnumType() throws ODataException {
+ // test enum type
+ EnumType enumType = provider.getEnumType(new FullQualifiedName(NS, "PersonGender"));
+ assertNotNull(enumType);
+ assertEquals("Male", enumType.getMembers().get(0).getName());
+ assertEquals("Female", enumType.getMembers().get(1).getName());
+ assertEquals("Unknown", enumType.getMembers().get(2).getName());
+ assertEquals("0", enumType.getMembers().get(0).getValue());
+ assertEquals("1", enumType.getMembers().get(1).getValue());
+ assertEquals("2", enumType.getMembers().get(2).getValue());
+ }
+
+ @Test
+ public void testEntityType() throws ODataException {
+ // test Entity Type
+ EntityType et = provider.getEntityType(new FullQualifiedName(NS, "Photo"));
+ assertNotNull(et);
+ assertNotNull(et.getKey());
+ assertEquals("Id", et.getKey().get(0).getName());
+ assertTrue(et.hasStream());
+ assertEquals("Id", et.getProperties().get(0).getName());
+ assertEquals("Edm.Int64", et.getProperties().get(0).getType());
+ assertEquals("Name", et.getProperties().get(1).getName());
+ assertEquals("Edm.String", et.getProperties().get(1).getType());
+ }
+
+ @Test
+ public void testComplexType() throws ODataException {
+ // Test Complex Type
+ ComplexType ct = provider.getComplexType(new FullQualifiedName(NS, "City"));
+ assertNotNull(ct);
+ assertEquals(3, ct.getProperties().size());
+ Property p = ct.getProperties().get(0);
+ assertEquals("CountryRegion", p.getName());
+ assertEquals("Edm.String", p.getType());
+ assertEquals(false, p.isNullable());
+
+ ct = provider.getComplexType(new FullQualifiedName(NS, "Location"));
+ assertNotNull(ct);
+
+ ct = provider.getComplexType(new FullQualifiedName(NS, "EventLocation"));
+ assertNotNull(ct);
+ }
+
+ @Test
+ public void testEntitySet() throws Exception {
+ EntitySet es = provider.getEntitySet(NSF, "People");
+ assertNotNull(es);
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",es.getType());
+
+ List bindings = es.getNavigationPropertyBindings();
+ assertNotNull(bindings);
+ assertEquals(6, bindings.size());
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Flight/From", bindings.get(2)
+ .getPath());
+ assertEquals("Airports", bindings.get(2).getTarget());
+ }
+
+ @Test
+ public void testFunctionImport() throws Exception {
+ FunctionImport fi = provider.getFunctionImport(NSF, "GetNearestAirport");
+ assertNotNull(fi);
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.GetNearestAirport", fi.getFunction());
+ assertEquals("Airports", fi.getEntitySet());
+ assertTrue(fi.isIncludeInServiceDocument());
+ }
+
+ @Test
+ public void testActionImport() throws Exception {
+ ActionImport ai = provider.getActionImport(NSF, "ResetDataSource");
+ assertNotNull(ai);
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.ResetDataSource", ai.getAction());
+ assertNull(ai.getEntitySet());
+ }
+
+ @Test
+ public void testSingleton() throws Exception {
+ Singleton single = this.provider.getSingleton(NSF, "Me");
+ assertNotNull(single);
+
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",single.getType());
+
+ List bindings = single.getNavigationPropertyBindings();
+ assertNotNull(bindings);
+ assertEquals(6, bindings.size());
+ assertEquals("Microsoft.OData.SampleService.Models.TripPin.Flight/From", bindings.get(2).getPath());
+ assertEquals("Airports", bindings.get(2).getTarget());
+
+ }
+}
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java
new file mode 100644
index 000000000..16caa1ae4
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java
@@ -0,0 +1,417 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataHttpHandler;
+import org.apache.olingo.server.api.ServiceMetadata;
+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.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.StreamResponse;
+import org.apache.olingo.server.example.TripPinServiceTest;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+public class ServiceDispatcherTest {
+ private Server server;
+
+ public class SampleODataServlet extends HttpServlet {
+ private final ServiceHandler handler; // must be stateless
+ private final EdmProvider provider; // must be stateless
+
+ public SampleODataServlet(ServiceHandler handler, EdmProvider provider) {
+ this.handler = handler;
+ this.provider = provider;
+ }
+
+ @Override
+ public void service(HttpServletRequest request, HttpServletResponse response)
+ throws IOException {
+ OData odata = OData4Impl.newInstance();
+ ServiceMetadata metadata = odata.createServiceMetadata(this.provider, Collections.EMPTY_LIST);
+
+ ODataHttpHandler handler = odata.createHandler(metadata);
+
+ handler.register(this.handler);
+ handler.process(request, response);
+ }
+ }
+
+ public int beforeTest(ServiceHandler serviceHandler) throws Exception {
+ MetadataParser parser = new MetadataParser();
+ EdmProvider edmProvider = parser.buildEdmProvider(new FileReader(
+ "src/test/resources/trippin.xml"));
+
+ this.server = new Server();
+
+ ServerConnector connector = new ServerConnector(this.server);
+ this.server.setConnectors(new Connector[] { connector });
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/trippin");
+ context
+ .addServlet(new ServletHolder(new SampleODataServlet(serviceHandler, edmProvider)), "/*");
+ this.server.setHandler(context);
+ this.server.start();
+
+ return connector.getLocalPort();
+ }
+
+ public void afterTest() throws Exception {
+ this.server.stop();
+ }
+
+ interface TestResult {
+ void validate() throws Exception;
+ }
+
+ private void helpGETTest(ServiceHandler handler, String path, TestResult validator)
+ throws Exception {
+ int port = beforeTest(handler);
+ HttpClient http = new HttpClient();
+ http.start();
+ http.GET("http://localhost:" + port + "/" + path);
+ validator.validate();
+ afterTest();
+ }
+
+ private void helpTest(ServiceHandler handler, String path, String method, String payload,
+ TestResult validator) throws Exception {
+ int port = beforeTest(handler);
+ HttpClient http = new HttpClient();
+ http.start();
+ String editUrl = "http://localhost:" + port + "/" + path;
+ http.newRequest(editUrl).method(method)
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .content(TripPinServiceTest.content(payload)).send();
+ validator.validate();
+ afterTest();
+ }
+
+ @Test
+ public void testMetadata() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/$metadata", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(MetadataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(MetadataResponse.class);
+ Mockito.verify(handler).readMetadata(arg1.capture(), arg2.capture());
+ }
+ });
+ }
+
+ @Test
+ public void testEntitySet() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(EntityResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ // Need toString on ContextURL class
+ // assertEquals("",
+ // request.getContextURL(request.getOdata()).toString());
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testEntitySetCount() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports/$count", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(CountResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ // Need toString on ContextURL class
+ // assertEquals("",
+ // request.getContextURL(request.getOdata()).toString());
+ assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testEntity() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports('0')", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(EntityResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadProperty() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports('0')/IataCode", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(PropertyResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals(true, request.isPropertyRequest());
+ assertEquals(false, request.isPropertyComplex());
+ assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadComplexProperty() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports('0')/Location", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(PropertyResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals(true, request.isPropertyRequest());
+ assertEquals(true, request.isPropertyComplex());
+ assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadProperty$Value() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports('0')/IataCode/$value", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor
+ .forClass(PrimitiveValueResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals(true, request.isPropertyRequest());
+ assertEquals(false, request.isPropertyComplex());
+ assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+ assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadPropertyRef() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Airports('0')/IataCode/$value", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor
+ .forClass(PrimitiveValueResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals(true, request.isPropertyRequest());
+ assertEquals(false, request.isPropertyComplex());
+ assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+ assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testFunctionImport() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/GetNearestAirport(lat=12.11,lon=34.23)", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(FunctionRequest.class);
+ ArgumentCaptor arg3 = ArgumentCaptor.forClass(PropertyResponse.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(HttpMethod.class);
+ Mockito.verify(handler).invoke(arg1.capture(), arg2.capture(), arg3.capture());
+
+ FunctionRequest request = arg1.getValue();
+ }
+ });
+ }
+
+ @Test
+ public void testActionImport() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpTest(handler, "trippin/ResetDataSource", "POST", "", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(ActionRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(NoContentResponse.class);
+ Mockito.verify(handler).invoke(arg1.capture(), Mockito.anyString(), arg2.capture());
+
+ ActionRequest request = arg1.getValue();
+ }
+ });
+ }
+
+ @Test
+ public void testReadMedia() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/Photos(1)/$value", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(MediaRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(StreamResponse.class);
+ Mockito.verify(handler).readMediaStream(arg1.capture(), arg2.capture());
+
+ MediaRequest request = arg1.getValue();
+ assertEquals("application/octet-stream", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadNavigation() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/People('russelwhyte')/Friends", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(EntitySetResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testReadReference() throws Exception {
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpGETTest(handler, "trippin/People('russelwhyte')/Friends/$ref", new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(EntitySetResponse.class);
+ Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+ .toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testWriteReferenceCollection() throws Exception {
+ String payload = "{\n" + "\"@odata.id\": \"/Photos(11)\"\n" + "}";
+
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpTest(handler, "trippin/People('russelwhyte')/Friends/$ref", "POST", payload,
+ new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor arg3 = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor arg4 = ArgumentCaptor
+ .forClass(NoContentResponse.class);
+ Mockito.verify(handler).addReference(arg1.capture(), arg2.capture(), arg3.capture(),
+ arg4.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals("application/json;odata.metadata=minimal", request
+ .getResponseContentType().toContentTypeString());
+ }
+ });
+ }
+
+ @Test
+ public void testWriteReference() throws Exception {
+ String payload = "{\n" + "\"@odata.id\": \"/Photos(11)\"\n" + "}";
+
+ final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+ helpTest(handler, "trippin/People('russelwhyte')/Friends('someone')/Photo/$ref", "PUT", payload,
+ new TestResult() {
+ @Override
+ public void validate() throws Exception {
+ ArgumentCaptor arg1 = ArgumentCaptor.forClass(DataRequest.class);
+ ArgumentCaptor arg2 = ArgumentCaptor.forClass(String.class);
+ ArgumentCaptor arg3 = ArgumentCaptor.forClass(URI.class);
+ ArgumentCaptor arg4 = ArgumentCaptor
+ .forClass(NoContentResponse.class);
+ Mockito.verify(handler).updateReference(arg1.capture(), arg2.capture(), arg3.capture(),
+ arg4.capture());
+
+ DataRequest request = arg1.getValue();
+ assertEquals("application/json;odata.metadata=minimal", request
+ .getResponseContentType().toContentTypeString());
+ }
+ });
+ }
+}
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java
new file mode 100644
index 000000000..904f4d86a
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java
@@ -0,0 +1,843 @@
+/*
+ * 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.example;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.core.data.EntityImpl;
+import org.apache.olingo.commons.core.data.EntitySetImpl;
+import org.apache.olingo.commons.core.data.LinkImpl;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TripPinDataModel {
+ private final ServiceMetadata metadata;
+ private HashMap entitySetMap;
+ private Map tripLinks;
+ private Map peopleLinks;
+ private Map flightLinks;
+
+ public TripPinDataModel(ServiceMetadata metadata) throws Exception {
+ this.metadata = metadata;
+ loadData();
+ }
+
+ public void loadData() throws Exception {
+ this.entitySetMap = new HashMap();
+ this.tripLinks = new HashMap();
+ this.peopleLinks = new HashMap();
+ this.flightLinks = new HashMap();
+
+ EdmEntityContainer ec = metadata.getEdm().getEntityContainer(null);
+ for (EdmEntitySet edmEntitySet : ec.getEntitySets()) {
+ String entitySetName = edmEntitySet.getName();
+ EntitySet set = loadEnities(entitySetName, edmEntitySet.getEntityType());
+ if (set != null) {
+ this.entitySetMap.put(entitySetName, set);
+ }
+ }
+
+ EdmEntityType type = metadata.getEdm().getEntityType(
+ new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Trip"));
+ this.entitySetMap.put("Trip", loadEnities("Trip", type));
+
+ type = metadata.getEdm().getEntityType(
+ new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Flight"));
+ this.entitySetMap.put("Flight", loadEnities("Flight", type));
+
+ type = metadata.getEdm().getEntityType(
+ new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Event"));
+ this.entitySetMap.put("Event", loadEnities("Event", type));
+
+ ObjectMapper mapper = new ObjectMapper();
+ Map tripLinks = mapper.readValue(new FileInputStream(new File(
+ "src/test/resources/trip-links.json")), Map.class);
+ for (Object link : (ArrayList) tripLinks.get("value")) {
+ Map map = (Map) link;
+ this.tripLinks.put((Integer) map.get("TripId"), map);
+ }
+
+ Map peopleLinks = mapper.readValue(new FileInputStream(new File(
+ "src/test/resources/people-links.json")), Map.class);
+ for (Object link : (ArrayList) peopleLinks.get("value")) {
+ Map map = (Map) link;
+ this.peopleLinks.put((String) map.get("UserName"), map);
+ }
+
+ Map flightLinks = mapper.readValue(new FileInputStream(new File(
+ "src/test/resources/flight-links.json")), Map.class);
+ for (Object link : (ArrayList) flightLinks.get("value")) {
+ Map map = (Map) link;
+ this.flightLinks.put((Integer) map.get("PlanItemId"), map);
+ }
+ }
+
+ private EntitySet loadEnities(String entitySetName, EdmEntityType type) {
+ try {
+ ODataJsonDeserializer deserializer = new ODataJsonDeserializer();
+
+ EntitySet set = deserializer.entityCollection(new FileInputStream(new File(
+ "src/test/resources/" + entitySetName.toLowerCase() + ".json")), type);
+ // TODO: the count needs to be part of deserializer
+ set.setCount(set.getEntities().size());
+ for (Entity entity : set.getEntities()) {
+ ((EntityImpl) entity).setETag(UUID.randomUUID().toString());
+ ((EntityImpl) entity).setId(new URI(TripPinHandler.buildLocation(entity, entitySetName,
+ type)));
+ ((EntityImpl) entity).setType(type.getFullQualifiedName().getFullQualifiedNameAsString());
+ }
+ return set;
+ } catch (FileNotFoundException e) {
+ // keep going
+ e.printStackTrace();
+ } catch (DeserializerException e) {
+ // keep going
+ e.printStackTrace();
+ } catch (URISyntaxException e) {
+ // keep going
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public EntitySet getEntitySet(String name) {
+ return getEntitySet(name, -1, -1);
+ }
+
+ public EntitySet getEntitySet(String name, int skip, int pageSize) {
+ EntitySet set = this.entitySetMap.get(name);
+ if (set == null) {
+ return null;
+ }
+
+ EntitySetImpl modifiedES = new EntitySetImpl();
+ int i = 0;
+ for (Entity e : set.getEntities()) {
+ if (skip >= 0 && i >= skip && modifiedES.getEntities().size() < pageSize) {
+ modifiedES.getEntities().add(e);
+ }
+ i++;
+ }
+ modifiedES.setCount(i);
+ set.setCount(i);
+
+ if (skip == -1 && pageSize == -1) {
+ return set;
+ }
+ return modifiedES;
+ }
+
+ private List getMatch(UriParameter param, List es)
+ throws ODataApplicationException {
+ ArrayList list = new ArrayList();
+ for (Entity entity : es) {
+
+ EdmEntityType entityType = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(entity.getType()));
+
+ EdmProperty property = (EdmProperty) entityType.getProperty(param.getName());
+ EdmType type = property.getType();
+ if (type.getKind() == EdmTypeKind.PRIMITIVE) {
+ Object match = readPrimitiveValue(property, param.getText());
+ Property entityValue = entity.getProperty(param.getName());
+ if (match.equals(entityValue.asPrimitive())) {
+ list.add(entity);
+ }
+ } else {
+ throw new RuntimeException("Can not compare complex objects");
+ }
+ }
+ return list;
+ }
+
+ static Object readPrimitiveValue(EdmProperty edmProperty, String value)
+ throws ODataApplicationException {
+ if (value == null) {
+ return null;
+ }
+ try {
+ if (value.startsWith("'") && value.endsWith("'")) {
+ value = value.substring(1,value.length()-1);
+ }
+ EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) edmProperty.getType();
+ Class> javaClass = getJavaClassForPrimitiveType(edmProperty, edmPrimitiveType);
+ return edmPrimitiveType.valueOfString(value, edmProperty.isNullable(),
+ edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(),
+ edmProperty.isUnicode(), javaClass);
+ } catch (EdmPrimitiveTypeException e) {
+ throw new ODataApplicationException("Invalid value: " + value + " for property: "
+ + edmProperty.getName(), 500, Locale.getDefault());
+ }
+ }
+
+ static Class> getJavaClassForPrimitiveType(EdmProperty edmProperty, EdmPrimitiveType edmPrimitiveType) {
+ Class> javaClass = null;
+ if (edmProperty.getMapping() != null && edmProperty.getMapping().getMappedJavaClass() != null) {
+ javaClass = edmProperty.getMapping().getMappedJavaClass();
+ } else {
+ javaClass = edmPrimitiveType.getDefaultType();
+ }
+
+ edmPrimitiveType.getDefaultType();
+ return javaClass;
+ }
+
+ public Entity getEntity(String name, List keys) throws ODataApplicationException {
+ EntitySet es = getEntitySet(name);
+ return getEntity(es, keys);
+ }
+
+ public Entity getEntity(EntitySet es, List keys) throws ODataApplicationException {
+ List search = es.getEntities();
+ for (UriParameter param : keys) {
+ search = getMatch(param, search);
+ }
+ if (search.isEmpty()) {
+ return null;
+ }
+ return search.get(0);
+ }
+
+ private EntitySet getFriends(String userName) {
+ Map map = this.peopleLinks.get(userName);
+ if (map == null) {
+ return null;
+ }
+ ArrayList friends = (ArrayList) map.get("Friends");
+ EntitySet set = getEntitySet("People");
+
+ EntitySetImpl result = new EntitySetImpl();
+ int i = 0;
+ if (friends != null) {
+ for (String friend : friends) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("UserName").getValue().equals(friend)) {
+ result.getEntities().add(e);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ result.setCount(i);
+ return result;
+ }
+
+ private EntitySet getTrips(String userName) {
+ Map map = this.peopleLinks.get(userName);
+ if (map == null) {
+ return null;
+ }
+
+ ArrayList trips = (ArrayList) map.get("Trips");
+ EntitySet set = getEntitySet("Trip");
+
+ EntitySetImpl result = new EntitySetImpl();
+ int i = 0;
+ if (trips != null) {
+ for (int trip : trips) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("TripId").getValue().equals(trip)) {
+ result.getEntities().add(e);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ result.setCount(i);
+ return result;
+ }
+
+ private Entity getPhoto(String userName) {
+ Map map = this.peopleLinks.get(userName);
+ if (map == null) {
+ return null;
+ }
+
+ Integer photoID = (Integer) map.get("Photo");
+ EntitySet set = getEntitySet("Photos");
+ if (photoID != null) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("Id").getValue().equals(photoID.longValue())) {
+ return e;
+ }
+ }
+ }
+ return null;
+ }
+
+ private EntitySet getPlanItems(int tripId, EntitySetImpl result) {
+ getFlights(tripId, result);
+ getEvents(tripId, result);
+ return result;
+ }
+
+ private EntitySet getEvents(int tripId, EntitySetImpl result) {
+ Map map = this.tripLinks.get(tripId);
+ if (map == null) {
+ return null;
+ }
+
+ ArrayList events = (ArrayList) map.get("Events");
+ EntitySet set = getEntitySet("Event");
+ int i = result.getEntities().size();
+ if (events != null) {
+ for (int event : events) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("PlanItemId").getValue().equals(event)) {
+ result.getEntities().add(e);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ result.setCount(i);
+ return result;
+ }
+
+ private EntitySet getFlights(int tripId, EntitySetImpl result) {
+ Map map = this.tripLinks.get(tripId);
+ if (map == null) {
+ return null;
+ }
+
+ ArrayList flights = (ArrayList) map.get("Flights");
+ EntitySet set = getEntitySet("Flight");
+ int i = result.getEntities().size();
+ if (flights != null) {
+ for (int flight : flights) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("PlanItemId").getValue().equals(flight)) {
+ result.getEntities().add(e);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ result.setCount(i);
+ return result;
+ }
+
+ private EntitySet getTripPhotos(int tripId) {
+ Map map = this.tripLinks.get(tripId);
+ if (map == null) {
+ return null;
+ }
+
+ ArrayList photos = (ArrayList) map.get("Photos");
+
+ EntitySet set = getEntitySet("Photos");
+ EntitySetImpl result = new EntitySetImpl();
+ int i = 0;
+ if (photos != null) {
+ for (int photo : photos) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("Id").getValue().equals(photo)) {
+ result.getEntities().add(e);
+ i++;
+ break;
+ }
+ }
+ }
+ }
+ result.setCount(i);
+ return result;
+ }
+
+ private Entity getFlightFrom(int flighID) {
+ Map map = this.flightLinks.get(flighID);
+ if (map == null) {
+ return null;
+ }
+
+ String from = (String) map.get("From");
+ EntitySet set = getEntitySet("Airports");
+
+ if (from != null) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("IataCode").getValue().equals(from)) {
+ return e;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Entity getFlightTo(int flighID) {
+ Map map = this.flightLinks.get(flighID);
+ if (map == null) {
+ return null;
+ }
+
+ String to = (String) map.get("To");
+ EntitySet set = getEntitySet("Airports");
+
+ if (to != null) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("IataCode").getValue().equals(to)) {
+ return e;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Entity getFlightAirline(int flighID) {
+ Map map = this.flightLinks.get(flighID);
+ if (map == null) {
+ return null;
+ }
+
+ String airline = (String) map.get("Airline");
+ EntitySet set = getEntitySet("Airlines");
+
+ if (airline != null) {
+ for (Entity e : set.getEntities()) {
+ if (e.getProperty("AirlineCode").getValue().equals(airline)) {
+ return e;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void addNavigationLink(String navigation, Entity parentEntity, Entity childEntity) {
+
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(parentEntity.getType()));
+ String key = type.getKeyPredicateNames().get(0);
+ if (type.getName().equals("Person") && navigation.equals("Friends")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+ }
+
+ ArrayList friends = (ArrayList) map.get("Friends");
+ if (friends == null) {
+ friends = new ArrayList();
+ map.put("Friends", friends);
+ }
+ friends.add((String) childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Person") && navigation.equals("Trips")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+ }
+
+ ArrayList trips = (ArrayList) map.get("Trips");
+ if (trips == null) {
+ trips = new ArrayList();
+ map.put("Trips", trips);
+ }
+ trips.add((Integer) childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Person") && navigation.equals("Photo")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+ }
+ map.put("Photo", childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Trip") && navigation.equals("PlanItems")) {
+ Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.tripLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+ }
+ if (childEntity.getType().equals("Flight")) {
+ ArrayList flights = (ArrayList) map.get("Flights");
+ if (flights == null) {
+ flights = new ArrayList();
+ map.put("Flights", flights);
+ }
+ flights.add((Integer) childEntity.getProperty(key).getValue());
+ } else {
+ ArrayList events = (ArrayList) map.get("Events");
+ if (events == null) {
+ events = new ArrayList();
+ map.put("Events", events);
+ }
+ events.add((Integer) childEntity.getProperty(key).getValue());
+ }
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Trip") && navigation.equals("Photo")) {
+ Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.tripLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+ }
+ ArrayList photos = (ArrayList) map.get("Photos");
+ if (photos == null) {
+ photos = new ArrayList();
+ map.put("Photos", photos);
+ }
+ photos.add((Integer) childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Flight") && navigation.equals("From")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+ }
+ map.put("From", childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Flight") && navigation.equals("To")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+ }
+ map.put("To", childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else if (type.getName().equals("Flight") && navigation.equals("Airline")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map == null) {
+ map = new HashMap();
+ this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+ }
+ map.put("Airline", childEntity.getProperty(key).getValue());
+ setLink(parentEntity, navigation, childEntity);
+ } else {
+ throw new RuntimeException("unknown relation");
+ }
+ }
+
+ protected static void setLink(Entity entity, final String navigationPropertyName,
+ final Entity target) {
+ Link link = new LinkImpl();
+ link.setTitle(navigationPropertyName);
+ link.setInlineEntity(target);
+ entity.getNavigationLinks().add(link);
+ }
+
+ public boolean updateNavigationLink(String navigationProperty, Entity parentEntity,
+ Entity updateEntity) {
+ boolean updated = false;
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(parentEntity.getType()));
+ String key = type.getKeyPredicateNames().get(0);
+
+ EdmEntityType updateType = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(updateEntity.getType()));
+ String updateKey = updateType.getKeyPredicateNames().get(0);
+
+ if (type.getName().equals("Person") && navigationProperty.equals("Photo")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.put("Photo", ((Long) updateEntity.getProperty(updateKey).getValue()).intValue());
+ updated = true;
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("From")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.put("From", updateEntity.getProperty(updateKey).getValue());
+ updated = true;
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("To")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.put("To", updateEntity.getProperty(updateKey).getValue());
+ updated = true;
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("Airline")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.put("Airline", updateEntity.getProperty(updateKey).getValue());
+ updated = true;
+ }
+ } else {
+ throw new RuntimeException("unknown relation");
+ }
+ return updated;
+ }
+
+ public Entity createEntity(String entitySetName, Entity entity, String location)
+ throws ODataApplicationException {
+
+ EntitySet set = this.entitySetMap.get(entitySetName);
+ EntityImpl copy = new EntityImpl();
+ copy.setType(entity.getType());
+ for (Property p : entity.getProperties()) {
+ copy.addProperty(p);
+ }
+
+ try {
+ copy.setId(new URI(location));
+ copy.setETag(UUID.randomUUID().toString());
+ } catch (URISyntaxException e) {
+ throw new ODataApplicationException("Failed to create ID for entity", 500,
+ Locale.getDefault());
+ }
+
+ set.getEntities().add(copy);
+ return copy;
+ }
+
+ public boolean deleteEntity(String entitySetName, String eTag, String key, Object keyValue) {
+ EntitySet set = getEntitySet(entitySetName);
+ Iterator it = set.getEntities().iterator();
+ boolean removed = false;
+ while (it.hasNext()) {
+ Entity entity = it.next();
+ if (entity.getProperty(key).getValue().equals(keyValue) && eTag.equals("*")
+ || eTag.equals(entity.getETag())) {
+ it.remove();
+ removed = true;
+ break;
+ }
+ }
+ return removed;
+ }
+
+ public boolean updateProperty(String entitySetName, String eTag, String key, Object keyValue,
+ Property property) {
+ EntitySet set = getEntitySet(entitySetName);
+ Iterator it = set.getEntities().iterator();
+ boolean replaced = false;
+ while (it.hasNext()) {
+ Entity entity = it.next();
+ if (entity.getProperty(key).getValue().equals(keyValue) && eTag.equals("*")
+ || eTag.equals(entity.getETag())) {
+ entity.getProperty(property.getName()).setValue(property.getValueType(),
+ property.getValue());
+ replaced = true;
+ break;
+ }
+ }
+ return replaced;
+ }
+
+ public EntitySet getNavigableEntitySet(Entity parentEntity, UriResourceNavigation navigation) {
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(parentEntity.getType()));
+
+ String key = type.getKeyPredicateNames().get(0);
+ String linkName = navigation.getProperty().getName();
+
+ EntitySet results = null;
+ if (type.getName().equals("Person") && linkName.equals("Friends")) {
+ results = getFriends((String) parentEntity.getProperty(key).getValue());
+ } else if (type.getName().equals("Person") && linkName.equals("Trips")) {
+ results = getTrips((String) parentEntity.getProperty(key).getValue());
+ } else if (type.getName().equals("Trip") && linkName.equals("PlanItems")) {
+ EntitySetImpl planitems = new EntitySetImpl();
+ if (navigation.getTypeFilterOnCollection() == null) {
+ results = getPlanItems((Integer) parentEntity.getProperty(key).getValue(), planitems);
+ } else if (navigation.getTypeFilterOnCollection().getName().equals("Flight")) {
+ results = getFlights((Integer) parentEntity.getProperty(key).getValue(), planitems);
+ } else if (navigation.getTypeFilterOnCollection().getName().equals("Event")) {
+ results = getEvents((Integer) parentEntity.getProperty(key).getValue(), planitems);
+ } else {
+ throw new RuntimeException("unknown relation");
+ }
+ } else if (type.getName().equals("Trip") && linkName.equals("Photos")) {
+ results = getTripPhotos((Integer) parentEntity.getProperty(key).getValue());
+ }
+ return results;
+ }
+
+ public Entity getNavigableEntity(Entity parentEntity, UriResourceNavigation navigation)
+ throws ODataApplicationException {
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(parentEntity.getType()));
+
+ String key = type.getKeyPredicateNames().get(0);
+ String linkName = navigation.getProperty().getName();
+
+ EntitySet results = null;
+ if (navigation.getProperty().isCollection()) {
+ results = getNavigableEntitySet(parentEntity, navigation);
+ return this.getEntity(results, navigation.getKeyPredicates());
+ }
+ if (type.getName().equals("Person") && linkName.equals("Photo")) {
+ return getPhoto((String) parentEntity.getProperty(key).getValue());
+ } else if (type.getName().equals("Flight") && linkName.equals("From")) {
+ return getFlightFrom((Integer) parentEntity.getProperty(key).getValue());
+ } else if (type.getName().equals("Flight") && linkName.equals("To")) {
+ return getFlightTo((Integer) parentEntity.getProperty(key).getValue());
+ } else if (type.getName().equals("Flight") && linkName.equals("Airline")) {
+ return getFlightAirline((Integer) parentEntity.getProperty(key).getValue());
+ } else {
+ throw new RuntimeException("unknown relation");
+ }
+ }
+
+ public boolean removeNavigationLink(String navigationProperty, Entity parentEntity,
+ Entity deleteEntity) {
+ boolean removed = false;
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(parentEntity.getType()));
+ String key = type.getKeyPredicateNames().get(0);
+
+ if (type.getName().equals("Person") && navigationProperty.equals("Friends")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ ArrayList friends = (ArrayList) map.get("Friends");
+ if (friends != null) {
+ friends.remove(deleteEntity.getProperty(key).getValue());
+ removed = true;
+ }
+ }
+ } else if (type.getName().equals("Person") && navigationProperty.equals("Trips")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ ArrayList trips = (ArrayList) map.get("Trips");
+ if (trips != null) {
+ trips.remove(deleteEntity.getProperty(key).getValue());
+ removed = true;
+ }
+ }
+ } else if (type.getName().equals("Person") && navigationProperty.equals("Photo")) {
+ Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.remove("Photo");
+ removed = true;
+ }
+ } else if (type.getName().equals("Trip") && navigationProperty.equals("PlanItems")) {
+ Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ if (deleteEntity.getType().equals("Flight")) {
+ ArrayList flights = (ArrayList) map.get("Flights");
+ if (flights != null) {
+ flights.remove(deleteEntity.getProperty(key).getValue());
+ removed = true;
+ }
+ } else {
+ ArrayList events = (ArrayList) map.get("Events");
+ if (events != null) {
+ events.remove(deleteEntity.getProperty(key).getValue());
+ removed = true;
+ }
+ }
+ }
+ } else if (type.getName().equals("Trip") && navigationProperty.equals("Photo")) {
+ Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ ArrayList photos = (ArrayList) map.get("Photos");
+ if (photos != null) {
+ photos.remove(deleteEntity.getProperty(key).getValue());
+ removed = true;
+ }
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("From")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.remove("From");
+ removed = true;
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("To")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.remove("To");
+ removed = true;
+ }
+ } else if (type.getName().equals("Flight") && navigationProperty.equals("Airline")) {
+ Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+ if (map != null) {
+ map.remove("Airline");
+ removed = true;
+ }
+ } else {
+ throw new RuntimeException("unknown relation");
+ }
+ return removed;
+ }
+
+ // note these are not tied to entities for simplicity sake
+ public boolean updateMedia(Entity entity, InputStream mediaContent)
+ throws ODataApplicationException {
+ checkForMedia(entity);
+ return true;
+ }
+
+ // note these are not tied to entities for simplicity sake
+ public InputStream readMedia(Entity entity) throws ODataApplicationException {
+ checkForMedia(entity);
+ try {
+ return new FileInputStream(new File("src/test/resources/OlingoOrangeTM.png"));
+ } catch (FileNotFoundException e) {
+ throw new ODataApplicationException("image not found", 500, Locale.getDefault());
+ }
+ }
+
+ // note these are not tied to entities for simplicity sake
+ public boolean deleteMedia(Entity entity) throws ODataApplicationException {
+ checkForMedia(entity);
+ return true;
+ }
+
+ private void checkForMedia(Entity entity) throws ODataApplicationException {
+ EdmEntityType type = this.metadata.getEdm().getEntityType(
+ new FullQualifiedName(entity.getType()));
+ if (!type.hasStream()) {
+ throw new ODataApplicationException("No Media proeprty on the entity", 500,
+ Locale.getDefault());
+ }
+ }
+
+ public boolean deleteStream(Entity entity, EdmProperty property) {
+ // should remove stream links
+ return true;
+ }
+
+ public boolean updateStream(Entity entity, EdmProperty property, InputStream streamContent) {
+ // should add stream links
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
new file mode 100644
index 000000000..19a038761
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
@@ -0,0 +1,546 @@
+/*
+ * 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.example;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+import org.apache.olingo.commons.api.data.Entity;
+import org.apache.olingo.commons.api.data.EntitySet;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.edm.EdmAction;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmFunction;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmSingleton;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.commons.core.data.EntitySetImpl;
+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.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+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 TripPinHandler implements ServiceHandler {
+ private OData odata;
+ private ServiceMetadata serviceMetadata;
+ private final TripPinDataModel dataModel;
+
+ public TripPinHandler(TripPinDataModel datamodel) {
+ this.dataModel = datamodel;
+ }
+
+ @Override
+ public void init(OData odata, ServiceMetadata serviceMetadata) {
+ this.odata = odata;
+ this.serviceMetadata = serviceMetadata;
+ }
+
+ @Override
+ public void readMetadata(MetadataRequest request, MetadataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.writeMetadata();
+ }
+
+ @Override
+ public void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.writeServiceDocument(request.getODataRequest().getRawBaseUri());
+ }
+
+ static class EntityDetails {
+ EntitySet entitySet = null;
+ Entity entity = null;
+ EdmEntityType entityType;
+ String navigationProperty;
+ Entity parentEntity = null;
+ }
+
+ private EntityDetails process(final DataRequest request) throws ODataApplicationException {
+ EntitySet entitySet = null;
+ Entity entity = null;
+ EdmEntityType entityType;
+ Entity parentEntity = null;
+
+ if (request.isSingleton()) {
+ EdmSingleton singleton = request.getUriResourceSingleton().getSingleton();
+ entityType = singleton.getEntityType();
+ if (singleton.getName().equals("Me")) {
+ entitySet = this.dataModel.getEntitySet("People");
+ entity = entitySet.getEntities().get(0);
+ }
+ } else {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ entityType = edmEntitySet.getEntityType();
+ List keys = request.getKeyPredicates();
+
+ // TODO: This example so far ignores all system options; but a real
+ // service should not
+ if (keys != null && !keys.isEmpty()) {
+ entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+ } else {
+ int skip = 0;
+ if (request.getUriInfo().getSkipTokenOption() != null) {
+ skip = Integer.parseInt(request.getUriInfo().getSkipTokenOption().getValue());
+ }
+ int pageSize = getPageSize(request);
+ entitySet = this.dataModel.getEntitySet(edmEntitySet.getName(), skip, pageSize);
+ if (entitySet.getEntities().size() == pageSize) {
+ try {
+ entitySet.setNext(new URI(request.getODataRequest().getRawRequestUri() + "?$skiptoken="
+ + (skip + pageSize)));
+ } catch (URISyntaxException e) {
+ throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
+ }
+ }
+ }
+ }
+ EntityDetails details = new EntityDetails();
+
+ if (!request.getNavigations().isEmpty() && entity != null) {
+ UriResourceNavigation lastNavigation = request.getNavigations().getLast();
+ for (UriResourceNavigation nav : request.getNavigations()) {
+ entityType = nav.getProperty().getType();
+ if (nav.isCollection()) {
+ entitySet = this.dataModel.getNavigableEntitySet(entity, nav);
+ } else {
+ parentEntity = entity;
+ entity = this.dataModel.getNavigableEntity(parentEntity, nav);
+ }
+ }
+ details.navigationProperty = lastNavigation.getProperty().getName();
+ }
+
+ details.entity = entity;
+ details.entitySet = entitySet;
+ details.entityType = entityType;
+ details.parentEntity = parentEntity;
+ return details;
+ }
+
+ @Override
+ public void read(final DataRequest request, final T response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ final EntityDetails details = process(request);
+
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(CountResponse response) throws ODataTranslatedException, ODataApplicationException {
+ response.writeCount(details.entitySet.getCount());
+ }
+
+ @Override
+ public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+ Property property = details.entity.getProperty(edmProperty.getName());
+ response.write(property.getValue());
+ }
+
+ @Override
+ public void visit(PropertyResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+ Property property = details.entity.getProperty(edmProperty.getName());
+ response.writeProperty(edmProperty.getType(), property);
+ }
+
+ @Override
+ public void visit(StreamResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ // stream property response
+ response.writeStreamResponse(new ByteArrayInputStream("dummy".getBytes()),
+ ContentType.APPLICATION_OCTET_STREAM);
+ }
+
+ @Override
+ public void visit(EntitySetResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ if (request.getPreference("odata.maxpagesize") != null) {
+ response.writeHeader("Preference-Applied", request.getPreference("odata.maxpagesize"));
+ }
+ if (details.entity == null && !request.getNavigations().isEmpty()) {
+ response.writeReadEntitySet(details.entityType, new EntitySetImpl());
+ } else {
+ response.writeReadEntitySet(details.entityType, details.entitySet);
+ }
+ }
+
+ @Override
+ public void visit(EntityResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ if (details.entity == null && !request.getNavigations().isEmpty()) {
+ response.writeNoContent(true);
+ } else {
+ response.writeReadEntity(details.entityType, details.entity);
+ }
+ }
+ });
+ }
+
+ private int getPageSize(DataRequest request) {
+ String size = request.getPreference("odata.maxpagesize");
+ if (size == null) {
+ return 8;
+ }
+ return Integer.parseInt(size);
+ }
+
+ @Override
+ public void createEntity(DataRequest request, Entity entity, EntityResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+
+ String location = buildLocation(entity, edmEntitySet.getName(), edmEntitySet.getEntityType());
+ Entity created = this.dataModel.createEntity(edmEntitySet.getName(), entity, location);
+
+ try {
+ // create references, they come in "@odata.bind" value
+ List bindings = entity.getNavigationBindings();
+ if (bindings != null & !bindings.isEmpty()) {
+ for (Link link : bindings) {
+ String navigationProperty = link.getTitle();
+ String uri = link.getBindingLink();
+ if (uri != null) {
+ DataRequest bindingRequest = request.parseLink(new URI(uri));
+
+ Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+
+ this.dataModel.addNavigationLink(navigationProperty, created, reference);
+
+ } else {
+ for (String binding : link.getBindingLinks()) {
+ DataRequest bindingRequest = request.parseLink(new URI(binding));
+
+ Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+
+ this.dataModel.addNavigationLink(navigationProperty, created, reference);
+ }
+ }
+ }
+ }
+ } catch (URISyntaxException e) {
+ throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
+ }
+
+ response.writeCreatedEntity(edmEntitySet.getEntityType(), created, location);
+ }
+
+ static String buildLocation(Entity entity, String name, EdmEntityType type) {
+ String location = "/" + name + "(";
+ int i = 0;
+ boolean usename = type.getKeyPredicateNames().size() > 1;
+
+ for (String key : type.getKeyPredicateNames()) {
+ if (i > 0) {
+ location += ",";
+ }
+ i++;
+ if (usename) {
+ location += (key + "=");
+ }
+ if (entity.getProperty(key).getType().equals("Edm.String")) {
+ location = location + "'" + entity.getProperty(key).getValue().toString() + "'";
+ } else {
+ location = location + entity.getProperty(key).getValue().toString();
+ }
+ }
+ location += ")";
+ return location;
+ }
+
+ @Override
+ public void updateEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
+ EntityResponse response) throws ODataTranslatedException, ODataApplicationException {
+ response.writeServerError(true);
+ }
+
+ @Override
+ public void deleteEntity(DataRequest request, String eTag, EntityResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
+ if (entity == null) {
+ response.writeNotFound(true);
+ return;
+ }
+ String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
+ boolean removed = this.dataModel.deleteEntity(edmEntitySet.getName(), eTag, key, entity
+ .getProperty(key).getValue());
+
+ if (removed) {
+ response.writeDeletedEntityOrReference();
+ } else {
+ response.writeNotFound(true);
+ }
+ }
+
+ @Override
+ public void updateProperty(DataRequest request, final Property property, boolean merge,
+ String entityETag, PropertyResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+
+ EdmEntitySet edmEntitySet = request.getEntitySet();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
+ if (entity == null) {
+ response.writeNotFound(true);
+ return;
+ }
+
+ String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
+ boolean replaced = this.dataModel.updateProperty(edmEntitySet.getName(), entityETag, key,
+ entity.getProperty(key).getValue(), property);
+
+ if (replaced) {
+ if (property.getValue() == null) {
+ response.writePropertyDeleted();
+ } else {
+ response.writePropertyUpdated();
+ }
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void invoke(FunctionRequest request, HttpMethod method,
+ T response) throws ODataTranslatedException, ODataApplicationException {
+ EdmFunction function = request.getFunction();
+ if (function.getName().equals("GetNearestAirport")) {
+
+ final EdmEntityType type = serviceMetadata.getEdm().getEntityContainer(null)
+ .getEntitySet("Airports").getEntityType();
+
+ EntitySet es = this.dataModel.getEntitySet("Airports");
+ int i = new Random().nextInt(es.getEntities().size());
+ final Entity entity = es.getEntities().get(i);
+
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(EntityResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeReadEntity(type, entity);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void invoke(ActionRequest request, String eTag, T response)
+ throws ODataTranslatedException, ODataApplicationException {
+ EdmAction action = request.getAction();
+ if (action.getName().equals("ResetDataSource")) {
+ try {
+ this.dataModel.loadData();
+ response.accepts(new ServiceResponseVisior() {
+ @Override
+ public void visit(NoContentResponse response) throws ODataTranslatedException,
+ ODataApplicationException {
+ response.writeNoContent();
+ }
+ });
+ } catch (Exception e) {
+ response.writeServerError(true);
+ }
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void readMediaStream(MediaRequest request, StreamResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ InputStream contents = this.dataModel.readMedia(entity);
+ response.writeStreamResponse(contents, request.getResponseContentType());
+ }
+
+ @Override
+ public void upsertMediaStream(MediaRequest request, String entityETag, InputStream mediaContent,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ if (mediaContent == null) {
+ boolean deleted = this.dataModel.deleteMedia(entity);
+ if (deleted) {
+ response.writeNoContent();
+ } else {
+ response.writeNotFound();
+ }
+ } else {
+ boolean updated = this.dataModel.updateMedia(entity, mediaContent);
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+ }
+
+ @Override
+ public void upsertStreamProperty(DataRequest request, String entityETag, InputStream streamContent,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ final EdmEntitySet edmEntitySet = request.getEntitySet();
+ List keys = request.getKeyPredicates();
+ Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
+
+ EdmProperty property = request.getUriResourceProperty().getProperty();
+
+ if (streamContent == null) {
+ boolean deleted = this.dataModel.deleteStream(entity, property);
+ if (deleted) {
+ response.writeNoContent();
+ } else {
+ response.writeNotFound();
+ }
+ } else {
+ boolean updated = this.dataModel.updateStream(entity, property, streamContent);
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+ }
+
+ @Override
+ public void addReference(DataRequest request, String entityETag, List references,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+
+ final EntityDetails details = process(request);
+
+ for (URI reference : references) {
+ DataRequest bindingRequest = request.parseLink(reference);
+ Entity linkEntity = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
+ bindingRequest.getKeyPredicates());
+ this.dataModel.addNavigationLink(details.navigationProperty, details.entity, linkEntity);
+ }
+ response.writeNoContent();
+ }
+
+ @Override
+ public void updateReference(DataRequest request, String entityETag, URI updateId,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ // this single valued navigation.
+ final EntityDetails details = process(request);
+ DataRequest updateRequest = request.parseLink(updateId);
+ Entity updateEntity = this.dataModel.getEntity(updateRequest.getEntitySet().getName(),
+ updateRequest.getKeyPredicates());
+ boolean updated = false;
+ if (updateEntity != null) {
+ updated = this.dataModel.updateNavigationLink(details.navigationProperty,
+ details.parentEntity, updateEntity);
+ }
+
+ if (updated) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void deleteReference(DataRequest request, URI deleteId, String entityETag,
+ NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+ boolean removed = false;
+ if (deleteId != null) {
+ final EntityDetails details = process(request);
+ DataRequest deleteRequest = request.parseLink(deleteId);
+ Entity deleteEntity = this.dataModel.getEntity(deleteRequest.getEntitySet().getName(),
+ deleteRequest.getKeyPredicates());
+ if (deleteEntity != null) {
+ removed = this.dataModel.removeNavigationLink(details.navigationProperty, details.entity,
+ deleteEntity);
+ }
+ } else {
+ // this single valued navigation.
+ final EntityDetails details = process(request);
+ removed = this.dataModel.removeNavigationLink(details.navigationProperty,
+ details.parentEntity, details.entity);
+ }
+ if (removed) {
+ response.writeNoContent();
+ } else {
+ response.writeServerError(true);
+ }
+ }
+
+ @Override
+ public void anyUnsupported(ODataRequest request, ODataResponse response)
+ throws ODataTranslatedException, ODataApplicationException {
+ response.setStatusCode(500);
+ }
+
+ @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 entitySetNames, ODataResponse response) {
+ response.setStatusCode(200);
+ }
+}
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
new file mode 100644
index 000000000..4b26b8eb5
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
@@ -0,0 +1,756 @@
+/*
+ * 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.example;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.apache.olingo.commons.core.Encoder;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ * Please note that NONE of the system query options are developed in the sample
+ * service like $filter, $orderby etc. So using those options will be ignored
+ * right now. These tests designed to test the framework, all options are responsibilities
+ * of service developer.
+ */
+public class TripPinServiceTest {
+ private static Server server = new Server();
+ private static String baseURL;
+ private static HttpClient http = new HttpClient();
+
+ @BeforeClass
+ public static void beforeTest() throws Exception {
+ ServerConnector connector = new ServerConnector(server);
+ server.setConnectors(new Connector[] { connector });
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/trippin");
+ context.addServlet(new ServletHolder(new TripPinServlet()), "/*");
+ server.setHandler(context);
+ server.start();
+ int port = connector.getLocalPort();
+ http.start();
+ baseURL = "http://localhost:"+port+"/trippin";
+ }
+
+ @AfterClass
+ public static void afterTest() throws Exception {
+ server.stop();
+ }
+
+ @Test
+ public void testEntitySet() throws Exception {
+ ContentResponse response = http.newRequest(baseURL + "/People")
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertEquals(baseURL+"/People?$skiptoken=8", node.get("@odata.nextLink").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("russellwhyte", person.get("UserName").asText());
+ }
+
+ private JsonNode getJSONNode(ContentResponse response) throws IOException,
+ JsonProcessingException {
+ ObjectMapper objectMapper = new ObjectMapper();
+ JsonNode node = objectMapper.readTree(response.getContent());
+ return node;
+ }
+
+ @Test
+ public void testReadEntitySetWithPaging() throws Exception {
+ ContentResponse response = http.newRequest(baseURL + "/People")
+ .header("Prefer", "odata.maxpagesize=10").send();
+
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertEquals(baseURL+"/People?$skiptoken=10", node.get("@odata.nextLink").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("russellwhyte", person.get("UserName").asText());
+
+ assertNotNull(response.getHeaders().get("Preference-Applied"));
+ }
+
+ @Test
+ public void testReadEntityWithKey() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Airlines/$entity", node.get("@odata.context").asText());
+ assertEquals("American Airlines", node.get("Name").asText());
+ }
+
+ @Test
+ public void testReadEntityWithNonExistingKey() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('OO')");
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testRead$Count() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines/$count");
+ assertEquals(200, response.getStatus());
+ assertEquals("15", response.getContentAsString());
+ }
+
+ @Test
+ public void testReadPrimitiveProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Airlines('AA')/Name", node.get("@odata.context").asText());
+ assertEquals("American Airlines", node.get("value").asText());
+ }
+
+ @Test
+ public void testReadNonExistentProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Unknown");
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testReadPrimitiveArrayProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Emails");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("Russell@example.com", ((ArrayNode)node.get("value")).get(0).asText());
+ assertEquals("Russell@contoso.com", ((ArrayNode)node.get("value")).get(1).asText());
+ }
+
+ @Test
+ public void testReadPrimitivePropertyValue() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name/$value");
+ assertEquals(200, response.getStatus());
+ assertEquals("American Airlines", response.getContentAsString());
+ }
+
+ @Test @Ignore
+ // TODO: Support geometry types to make this run
+ public void testReadComplexProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Airports('KSFO')/Location");
+ fail("support geometry type");
+ }
+
+ @Test
+ public void testReadComplexArrayProperty() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/AddressInfo");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/AddressInfo", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("187 Suffolk Ln.", ((ArrayNode)node.get("value")).get(0).get("Address").asText());
+ }
+
+ @Test
+ public void testReadMedia() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Photos(1)/$value");
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testCreateMedia() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Photos(1)/$value";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteMedia() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Photos(1)/$value";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testCreateStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.POST)
+ .send();
+ // method not allowed
+ assertEquals(405, response.getStatus());
+ }
+
+ @Test
+ public void testCreateStream2() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content("bytecontents"), "image/jpeg")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testDeleteStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadStream() throws Exception {
+ // treating update and create as same for now, as there is details about
+ // how entity payload and media payload can be sent at same time in request's body
+ String editUrl = baseURL + "/Airlines('AA')/Picture";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testLambdaAny() throws Exception {
+ // this is just testing to see the labba expresions are going through the
+ // framework, none of the system options are not implemented in example service
+ String query = "Friends/any(d:d/UserName eq 'foo')";
+ ContentResponse response = http.newRequest(baseURL + "/People?$filter="+Encoder.encode(query))
+ .method(HttpMethod.GET)
+ .send();
+ assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testSingleton() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/Me");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Me", node.get("@odata.context").asText());
+ assertEquals("russellwhyte", node.get("UserName").asText());
+ }
+
+ @Test
+ public void testSelectOption() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')?$select=FirstName,LastName");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People(FirstName,LastName)/$entity", node.get("@odata.context").asText());
+ assertEquals("Russell", node.get("FirstName").asText());
+ }
+
+ @Test
+ public void testActionImportWithNoResponse() throws Exception {
+ ContentResponse response = http.POST(baseURL + "/ResetDataSource").send();
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testFunctionImport() throws Exception {
+ //TODO: fails because of lack of geometery support
+ ContentResponse response = http.GET(baseURL + "/GetNearestAirport(lat=23.0,lon=34.0)");
+ }
+
+ @Test
+ public void testBadReferences() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russelwhyte')/$ref");
+ assertEquals(405, response.getStatus());
+ }
+
+ @Test
+ public void testReadReferences() throws Exception {
+ ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#Collection($ref)", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ }
+
+ @Test
+ public void testAddCollectionReferences() throws Exception {
+ //GET
+ ContentResponse response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ assertNull(((ArrayNode)node.get("value")).get(1));
+
+ //ADD
+ String payload = "{\n" +
+ " \"@odata.context\": \""+baseURL+"/$metadata#Collection($ref)\",\n" +
+ " \"value\": [\n" +
+ " { \"@odata.id\": \"People('russellwhyte')\" },\n" +
+ " { \"@odata.id\": \"People('scottketchum')\" } \n" +
+ " ]\n" +
+ "}";
+ response = http.POST(baseURL + "/People('kristakemp')/Friends/$ref")
+ .content(content(payload), "application/json")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ //GET
+ response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+
+ assertTrue(node.get("value").isArray());
+ assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+ assertEquals("/People('russellwhyte')", ((ArrayNode)node.get("value")).get(1).get("@odata.id").asText());
+ assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(2).get("@odata.id").asText());
+ }
+
+
+ @Test
+ public void testEntityId() throws Exception {
+ ContentResponse response = http.GET(baseURL+"/$entity?$id="+baseURL + "/People('kristakemp')");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+ assertEquals("kristakemp", node.get("UserName").asText());
+
+ // using relative URL
+ response = http.GET(baseURL+"/$entity?$id="+"People('kristakemp')");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+ assertEquals("kristakemp", node.get("UserName").asText());
+ }
+
+ @Test
+ public void testCreateReadDeleteEntity() throws Exception {
+ String payload = "{\n" +
+ " \"UserName\":\"olingodude\",\n" +
+ " \"FirstName\":\"Olingo\",\n" +
+ " \"LastName\":\"Apache\",\n" +
+ " \"Emails\":[\n" +
+ " \"olingo@apache.org\"\n" +
+ " ],\n" +
+ " \"AddressInfo\":[\n" +
+ " {\n" +
+ " \"Address\":\"100 apache Ln.\",\n" +
+ " \"City\":{\n" +
+ " \"CountryRegion\":\"United States\",\n" +
+ " \"Name\":\"Boise\",\n" +
+ " \"Region\":\"ID\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"Gender\":\"0\",\n" +
+ " \"Concurrency\":635585295719432047\n" +
+ "}";
+ ContentResponse response = http.POST(baseURL + "/People")
+ .content(content(payload), "application/json")
+ .header("Prefer", "return=minimal")
+ .send();
+ // the below woud be 204, if minimal was not supplied
+ assertEquals(204, response.getStatus());
+ assertEquals("/People('olingodude')", response.getHeaders().get("Location"));
+ assertEquals("return=minimal", response.getHeaders().get("Preference-Applied"));
+
+ String location = baseURL+response.getHeaders().get("Location");
+ response = http.GET(location);
+ assertEquals(200, response.getStatus());
+
+ response = http.newRequest(location).method(HttpMethod.DELETE).send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(location);
+ assertEquals(404, response.getStatus());
+ }
+
+
+ @Test
+ public void testCreateEntityWithLinkToRelatedEntities() throws Exception {
+ String payload = "{\n" +
+ " \"UserName\":\"olingo\",\n" +
+ " \"FirstName\":\"Olingo\",\n" +
+ " \"LastName\":\"Apache\",\n" +
+ " \"Emails\":[\n" +
+ " \"olingo@apache.org\"\n" +
+ " ],\n" +
+ " \"AddressInfo\":[\n" +
+ " {\n" +
+ " \"Address\":\"100 apache Ln.\",\n" +
+ " \"City\":{\n" +
+ " \"CountryRegion\":\"United States\",\n" +
+ " \"Name\":\"Boise\",\n" +
+ " \"Region\":\"ID\"\n" +
+ " }\n" +
+ " }\n" +
+ " ],\n" +
+ " \"Gender\":\"0\",\n" +
+ " \"Concurrency\":635585295719432047,\n" +
+ "\"Friends@odata.bind\":[\"" +
+ baseURL+"/People('russellwhyte')\",\""+
+ baseURL+"/People('scottketchum')\""+
+ "]"+
+ "}";
+ ContentResponse response = http.POST(baseURL + "/People")
+ .content(content(payload), "application/json")
+ .header("Prefer", "return=minimal")
+ .send();
+ // the below woud be 204, if minimal was not supplied
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(baseURL+"/People('olingo')/Friends");
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("scottketchum", ((ArrayNode)node.get("value")).get(1).get("UserName").asText());
+ }
+
+ @Test
+ public void testUpdatePrimitiveProperty() throws Exception {
+ String payload = "{"
+ + " \"value\":\"Pilar Ackerman\""
+ + "}";
+
+ String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content(payload), "application/json")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/FirstName", node.get("@odata.context").asText());
+ assertEquals("Pilar Ackerman", node.get("value").asText());
+ }
+
+ @Test
+ public void testUpdatePrimitiveArrayProperty() throws Exception {
+ String payload = "{"
+ + " \"value\": [\n" +
+ " \"olingo@apache.com\"\n" +
+ " ]"
+ + "}";
+
+ String editUrl = baseURL + "/People('russellwhyte')/Emails";
+ ContentResponse response = http.newRequest(editUrl)
+ .content(content(payload), "application/json")
+ .method(HttpMethod.PUT)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("olingo@apache.com", ((ArrayNode)node.get("value")).get(0).asText());
+ }
+
+ @Test
+ public void testDeleteProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("Russell", node.get("value").asText());
+
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCollection() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Friends";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People", node.get("@odata.context").asText());
+
+ JsonNode person = ((ArrayNode)node.get("value")).get(0);
+ assertEquals("scottketchum", person.get("UserName").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCollection2() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Friends('scottketchum')/Trips";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Friends('scottketchum')/Trips",
+ node.get("@odata.context").asText());
+ assertTrue(node.get("value").isArray());
+ assertEquals("1001", ((ArrayNode)node.get("value")).get(0).get("TripId").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntity() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips/$entity",
+ node.get("@odata.context").asText());
+ assertEquals("f94e9116-8bdd-4dac-ab61-08438d0d9a71", node.get("ShareId").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityNotExisting() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(9999)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntitySetNotExisting() throws Exception {
+ String editUrl = baseURL + "/People('jhondoe')/Trips";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('jhondoe')/Trips",
+ node.get("@odata.context").asText());
+ assertEquals(0, ((ArrayNode)node.get("value")).size());
+ }
+
+ @Test
+ public void testBadNavigationProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Unknown";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(404, response.getStatus());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityProperty() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode",
+ node.get("@odata.context").asText());
+
+ assertEquals("JH58494", node.get("value").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityMultipleDerivedTypes() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Flight",
+ ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityCoolectionDerivedFilter() throws Exception {
+ String editUrl = baseURL
+ + "/People('russellwhyte')/Trips(1003)/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Event";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event",
+ ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+ }
+
+ @Test
+ public void testReadNavigationPropertyEntityDerivedFilter() throws Exception {
+ String editUrl = baseURL+ "/People('russellwhyte')/Trips(1003)/PlanItems(56)/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+ + "Microsoft.OData.SampleService.Models.TripPin.Event/$entity",
+ node.get("@odata.context").asText());
+
+ assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event", node.get("@odata.type").asText());
+ assertEquals("56", node.get("PlanItemId").asText());
+ }
+
+ @Test
+ public void testUpdateReference() throws Exception {
+ ContentResponse response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("/Photos(12)", node.get("@odata.id").asText());
+
+ String msg = "{\n" +
+ "\"@odata.id\": \"/Photos(11)\"\n" +
+ "}";
+ String editUrl = baseURL + "/People('ronaldmundy')/Photo/$ref";
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.PUT)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertEquals("/Photos(11)", node.get("@odata.id").asText());
+ }
+
+ @Test
+ public void testAddDelete2ReferenceCollection() throws Exception {
+ // add
+ String msg = "{\n" +
+ "\"@odata.id\": \"/People('russellwhyte')\"\n" +
+ "}";
+ String editUrl = baseURL + "/People('vincentcalabrese')/Friends/$ref";
+ ContentResponse response = http.newRequest(editUrl)
+ .method(HttpMethod.POST)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ // get
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ JsonNode node = getJSONNode(response);
+ assertEquals("/People('russellwhyte')",
+ ((ArrayNode) node.get("value")).get(2).get("@odata.id").asText());
+
+ //delete
+ response = http.newRequest(editUrl+"?$id="+baseURL+"/People('russellwhyte')")
+ .method(HttpMethod.DELETE)
+ .content(content(msg))
+ .header("Content-Type", "application/json;odata.metadata=minimal")
+ .send();
+ assertEquals(204, response.getStatus());
+
+ // get
+ response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ node = getJSONNode(response);
+ assertNull("/People('russellwhyte')", ((ArrayNode) node.get("value")).get(2));
+ }
+
+ @Test
+ public void testDeleteReference() throws Exception {
+ String editUrl = baseURL + "/People('russellwhyte')/Photo/$ref";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+
+ response = http.newRequest(editUrl)
+ .method(HttpMethod.DELETE)
+ .send();
+ assertEquals(204, response.getStatus());
+
+ response = http.GET(editUrl);
+ assertEquals(204, response.getStatus());
+ }
+
+ @Test
+ public void testCrossJoin() throws Exception {
+ String editUrl = baseURL + "/$crossjoin(People,Airlines)";
+ ContentResponse response = http.GET(editUrl);
+ assertEquals(200, response.getStatus());
+ }
+
+ public static ContentProvider content(final String msg) {
+ return new ContentProvider() {
+ boolean hasNext = true;
+
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return hasNext;
+ }
+ @Override
+ public ByteBuffer next() {
+ hasNext = false;
+ return ByteBuffer.wrap(msg.getBytes());
+ }
+ @Override
+ public void remove() {
+ }
+ };
+ }
+ @Override
+ public long getLength() {
+ return msg.length();
+ }
+ };
+ }
+}
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
new file mode 100644
index 000000000..2c05d6522
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
@@ -0,0 +1,75 @@
+/*
+ * 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.example;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ODataHttpHandler;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.core.MetadataParser;
+import org.apache.olingo.server.core.OData4Impl;
+
+public class TripPinServlet extends HttpServlet {
+ private static final long serialVersionUID = 2663595419366214401L;
+ private TripPinDataModel dataModel;
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ super.init(config);
+ }
+
+ @Override
+ public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ OData odata = OData4Impl.newInstance();
+ MetadataParser parser = new MetadataParser();
+ EdmProvider edmProvider = null;
+
+ try {
+ edmProvider = parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
+ } catch (XMLStreamException e) {
+ throw new IOException(e);
+ }
+
+ ServiceMetadata metadata = odata.createServiceMetadata(edmProvider, Collections.EMPTY_LIST);
+
+ ODataHttpHandler handler = odata.createHandler(metadata);
+
+ if (this.dataModel == null) {
+ try {
+ this.dataModel = new TripPinDataModel(metadata);
+ } catch (Exception e) {
+ throw new IOException("Failed to load data for TripPin Service");
+ }
+ }
+
+ handler.register(new TripPinHandler(this.dataModel));
+ handler.process(request, response);
+ }
+}
diff --git a/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png
new file mode 100644
index 000000000..d614f1a46
Binary files /dev/null and b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png differ
diff --git a/lib/server-core-ext/src/test/resources/airlines.json b/lib/server-core-ext/src/test/resources/airlines.json
new file mode 100644
index 000000000..78eccdc81
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airlines.json
@@ -0,0 +1,64 @@
+{
+ "value":[
+ {
+ "AirlineCode":"AA",
+ "Name":"American Airlines"
+ },
+ {
+ "AirlineCode":"FM",
+ "Name":"Shanghai Airline"
+ },
+ {
+ "AirlineCode":"MU",
+ "Name":"China Eastern Airlines"
+ },
+ {
+ "AirlineCode":"AF",
+ "Name":"Air France"
+ },
+ {
+ "AirlineCode":"AZ",
+ "Name":"Alitalia"
+ },
+ {
+ "AirlineCode":"AC",
+ "Name":"Air Canada"
+ },
+ {
+ "AirlineCode":"OS",
+ "Name":"Austrian Airlines"
+ },
+ {
+ "AirlineCode":"TK",
+ "Name":"Turkish Airlines"
+ },
+ {
+ "AirlineCode":"JL",
+ "Name":"Japan Airlines"
+ },
+ {
+ "AirlineCode":"SQ",
+ "Name":"Singapore Airlines"
+ },
+ {
+ "AirlineCode":"KE",
+ "Name":"Korean Air"
+ },
+ {
+ "AirlineCode":"CZ",
+ "Name":"China Southern"
+ },
+ {
+ "AirlineCode":"AK",
+ "Name":"AirAsia"
+ },
+ {
+ "AirlineCode":"HX",
+ "Name":"Hong Kong Airlines"
+ },
+ {
+ "AirlineCode":"EK",
+ "Name":"Emirates"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/airports.json b/lib/server-core-ext/src/test/resources/airports.json
new file mode 100644
index 000000000..c06a5cf48
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airports.json
@@ -0,0 +1,394 @@
+{
+ "value":[
+ {
+ "IcaoCode":"KSFO",
+ "Name":"San Francisco International Airport",
+ "IataCode":"SFO",
+ "Location":{
+ "Address":"South McDonnell Road, San Francisco, CA 94128",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"San Francisco",
+ "Region":"California"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -122.374722222222,
+ 37.6188888888889
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KLAX",
+ "Name":"Los Angeles International Airport",
+ "IataCode":"LAX",
+ "Location":{
+ "Address":"1 World Way, Los Angeles, CA, 90045",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Los Angeles",
+ "Region":"California"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -118.408055555556,
+ 33.9425
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZSSS",
+ "Name":"Shanghai Hongqiao International Airport",
+ "IataCode":"SHA",
+ "Location":{
+ "Address":"Hongqiao Road 2550, Changning District",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Shanghai",
+ "Region":"Shanghai"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 121.336111111111,
+ 31.1977777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZBAA",
+ "Name":"Beijing Capital International Airport",
+ "IataCode":"PEK",
+ "Location":{
+ "Address":"Airport Road, Chaoyang District, Beijing, 100621",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Beijing",
+ "Region":"Beijing"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 116.584444444444,
+ 40.08
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KJFK",
+ "Name":"John F. Kennedy International Airport",
+ "IataCode":"JFK",
+ "Location":{
+ "Address":"Jamaica, New York, NY 11430",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"New York City",
+ "Region":"New York"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -73.7788888888889,
+ 40.6397222222222
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"LIRA",
+ "Name":"Rome Ciampino Airport",
+ "IataCode":"CIA",
+ "Location":{
+ "Address":"Via Appia Nuova, 1651",
+ "City":{
+ "CountryRegion":"Italy",
+ "Name":"Rome",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 12.5947222222222,
+ 41.7991666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"CYYZ",
+ "Name":"Toronto Pearson International Airport",
+ "IataCode":"YYZ",
+ "Location":{
+ "Address":"6301 Silver Dart Dr Mississauga",
+ "City":{
+ "CountryRegion":"Canada",
+ "Name":"Mississauga",
+ "Region":"Ontario"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -79.6305555555555,
+ 43.6772222222222
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"YSSY",
+ "Name":"Sydney Airport",
+ "IataCode":"SYD",
+ "Location":{
+ "Address":"Airport Dr Sydney NSW 2020",
+ "City":{
+ "CountryRegion":"Australia",
+ "Name":"Sydney",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 151.177222222222,
+ -33.9461111111111
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"LTBA",
+ "Name":"Istanbul Ataturk Airport",
+ "IataCode":"IST",
+ "Location":{
+ "Address":"Ye\u015filk\u00f6y Mh.34149 \u0130stanbul",
+ "City":{
+ "CountryRegion":"Turkey",
+ "Name":"Istanbul",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 28.8211111111111,
+ 40.9766666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"WSSS",
+ "Name":"Singapore Changi Airport",
+ "IataCode":"SIN",
+ "Location":{
+ "Address":"Airport Blvd, Singapore",
+ "City":{
+ "CountryRegion":"Singapore",
+ "Name":"Changi",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 103.987222222222,
+ 1.35555555555556
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"OMAA",
+ "Name":"Abu Dhabi International Airport",
+ "IataCode":"AUH",
+ "Location":{
+ "Address":"Sheik Maktoum Bin Rashid Rd Abu Dhabi",
+ "City":{
+ "CountryRegion":"United Arab Emirates",
+ "Name":"Abu Dhabi",
+ "Region":""
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 54.6511111111111,
+ 24.4327777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"ZGGG",
+ "Name":"Guangzhou Baiyun International Airport",
+ "IataCode":"CAN",
+ "Location":{
+ "Address":"Jichang Road, Renhezhen, Huadu",
+ "City":{
+ "CountryRegion":"China",
+ "Name":"Guangzhou",
+ "Region":"Guangdong"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ 113.265833333333,
+ 23.1841666666667
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KORD",
+ "Name":"O'Hare International Airport",
+ "IataCode":"ORD",
+ "Location":{
+ "Address":"10000 W O'Hare Ave",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Chicago",
+ "Region":"Illinois"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -87.9044444444445,
+ 41.9794444444444
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KATL",
+ "Name":"Hartsfield-Jackson Atlanta International Airport",
+ "IataCode":"ATL",
+ "Location":{
+ "Address":"6000 N Terminal Pkwy",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Atlanta",
+ "Region":"Georgia"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -84.4269444444444,
+ 33.6402777777778
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ },
+ {
+ "IcaoCode":"KSEA",
+ "Name":"Seattle-Tacoma International Airport",
+ "IataCode":"SEA",
+ "Location":{
+ "Address":"17801 International Blvd",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"SeaTac",
+ "Region":"Washington"
+ },
+ "Loc":{
+ "type":"Point",
+ "coordinates":[
+ -122.309166666667,
+ 47.4488888888889
+ ],
+ "crs":{
+ "type":"name",
+ "properties":{
+ "name":"EPSG:4326"
+ }
+ }
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/event.json b/lib/server-core-ext/src/test/resources/event.json
new file mode 100644
index 000000000..eb19dde83
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/event.json
@@ -0,0 +1,157 @@
+{
+ "value":[
+ {
+ "PlanItemId": 50,
+ "Description": "Client Meeting",
+ "ConfirmationCode": "4372899DD",
+ "StartsAt": "2014-01-02T13:00:00Z",
+ "EndsAt": "2014-01-02T16:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Regus Business Center",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "100 Church Street, 8th Floor, Manhattan, 10007"
+ }
+ },
+ {
+ "PlanItemId": 51,
+ "Description": "Visit the Brooklyn Bridge Park",
+ "ConfirmationCode": "4372899AA",
+ "StartsAt": "2014-01-01T15:00:00Z",
+ "EndsAt": "2014-01-01T16:00:00Z",
+ "Duration": "PT1H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Brooklyn Bridge Park, at Fulton Ferry Landing",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Main St Dumbo Brooklyn 11201"
+ }
+ },
+ {
+ "PlanItemId": 52,
+ "Description": "Empire State Building",
+ "ConfirmationCode": "4372899BB",
+ "StartsAt": "2014-01-03T10:00:00Z",
+ "EndsAt": "2014-01-03T12:00:00Z",
+ "Duration": "PT2H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Empire State Building",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Empire State Building, 350 5th Ave"
+ }
+ },
+ {
+ "PlanItemId": 53,
+ "Description": "Coney Island",
+ "ConfirmationCode": "4372899CC",
+ "StartsAt": "2014-01-03T14:00:00Z",
+ "EndsAt": "2014-01-03T20:00:00Z",
+ "Duration": "PT6H",
+ "OccursAt":
+ {
+ "BuildingInfo": "",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "1208 Surf Ave, Brooklyn"
+ }
+ },
+ {
+ "PlanItemId": 54,
+ "Description": "Shopping at Times Square",
+ "ConfirmationCode": "4372899DD",
+ "StartsAt": "2014-01-04T10:00:00Z",
+ "EndsAt": "2014-01-04T15:00:00Z",
+ "Duration": "PT5H",
+ "OccursAt":
+ {
+ "BuildingInfo": "",
+ "City":
+ {
+ "Name": "New York City",
+ "CountryRegion": "United States",
+ "Region": "New York"
+ },
+ "Address": "Broadway, 7th Avenue, 42nd and 47th Streets"
+ }
+ },
+ {
+ "PlanItemId": 55,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899EE",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "Address": "10 Beijing Street, 100000",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "BuildingInfo": "Beijing Restaurant"
+ }
+ },
+ {
+ "PlanItemId": 56,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899FF",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Beijing Restaurant",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "Address": "10 Beijing Street, 100000"
+ }
+ },
+ {
+ "PlanItemId": 57,
+ "Description": "Dinner",
+ "ConfirmationCode": "4372899GG",
+ "StartsAt": "2014-02-02T18:00:00Z",
+ "EndsAt": "2014-02-02T21:00:00Z",
+ "Duration": "PT3H",
+ "OccursAt":
+ {
+ "BuildingInfo": "Beijing Restaurant",
+ "City":
+ {
+ "Name": "Beijing",
+ "CountryRegion": "China",
+ "Region": "Beijing"
+ },
+ "Address": "10 Beijing Street, 100000"
+ }
+ }
+ ]
+}
+
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/flight-links.json b/lib/server-core-ext/src/test/resources/flight-links.json
new file mode 100644
index 000000000..17741d0b0
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight-links.json
@@ -0,0 +1,52 @@
+{
+ "value":[
+ {
+ "PlanItemId": 1,
+ "Airline": "AA",
+ "From": "ORD",
+ "To": "JFK"
+ },
+ {
+ "PlanItemId": 2,
+ "Airline": "AA",
+ "From": "JFK",
+ "To": "ORD"
+ },
+ {
+ "PlanItemId": 3,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 4,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ },
+ {
+ "PlanItemId": 5,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 6,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ },
+ {
+ "PlanItemId": 7,
+ "Airline": "FM",
+ "From": "SHA",
+ "To": "PEK"
+ },
+ {
+ "PlanItemId": 8,
+ "Airline": "MU",
+ "From": "PEK",
+ "To": "SHA"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/flight.json b/lib/server-core-ext/src/test/resources/flight.json
new file mode 100644
index 000000000..af0699857
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight.json
@@ -0,0 +1,66 @@
+{
+ "value":[
+ {
+ "PlanItemId": 1,
+ "ConfirmationCode": "JH58493",
+ "FlightNumber": "AA26",
+ "StartsAt": "2014-01-01T06:15:00Z",
+ "EndsAt": "2014-01-01T11:35:00Z"
+ },
+ {
+ "PlanItemId": 2,
+ "ConfirmationCode": "JH38143",
+ "FlightNumber": "AA4035",
+ "StartsAt": "2014-01-04T17:55:00Z",
+ "EndsAt": "2014-01-04T20:45:00Z"
+ },
+ {
+ "PlanItemId": 3,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B11"
+ },
+ {
+ "PlanItemId": 4,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T15:00:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A32"
+ },
+ {
+ "PlanItemId": 5,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B11"
+ },
+ {
+ "PlanItemId": 6,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T15:00:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A32"
+ },
+ {
+ "PlanItemId": 7,
+ "ConfirmationCode": "JH58494",
+ "FlightNumber": "FM1930",
+ "StartsAt": "2014-02-01T08:00:00Z",
+ "EndsAt": "2014-02-01T09:20:00Z",
+ "SeatNumber": "B12"
+ },
+ {
+ "PlanItemId": 8,
+ "ConfirmationCode": "JH58495",
+ "FlightNumber": "MU1930",
+ "StartsAt": "2014-02-10T16:30:00Z",
+ "EndsAt": "2014-02-10T16:30:00Z",
+ "SeatNumber": "A33"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/people-links.json b/lib/server-core-ext/src/test/resources/people-links.json
new file mode 100644
index 000000000..878d6ce23
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/people-links.json
@@ -0,0 +1,94 @@
+{
+ "value":[
+ {
+ "UserName":"russellwhyte",
+ "Friends": ["scottketchum", "ronaldmundy", "javieralfred", "angelhuffman"],
+ "Trips": [1001, 1003, 1007],
+ "Photo": 1
+ },
+ {
+ "UserName":"scottketchum",
+ "Friends": ["russellwhyte", "ronaldmundy"],
+ "Trips": [1001, 2004],
+ "Photo": 11
+ },
+ {
+ "UserName":"ronaldmundy",
+ "Friends": ["russellwhyte", "scottketchum"],
+ "Trips": [3009],
+ "Photo": 12
+ },
+ {
+ "UserName":"javieralfred",
+ "Friends": ["willieashmore", "vincentcalabrese", "georginabarlow"],
+ "Trips": [4005]
+ },
+ {
+ "UserName":"willieashmore",
+ "Friends": ["javieralfred", "vincentcalabrese"],
+ "Trips": [5007, 5008]
+ },
+ {
+ "UserName":"vincentcalabrese",
+ "Friends": ["javieralfred", "willieashmore"],
+ "Trips": [7010]
+ },
+ {
+ "UserName":"clydeguess",
+ "Friends": ["keithpinckney", "ursulabright"],
+ "Trips": [8011]
+ },
+ {
+ "UserName":"keithpinckney",
+ "Friends": ["clydeguess", "marshallgaray"],
+ "Trips": []
+ },
+ {
+ "UserName":"marshallgaray",
+ "Friends": ["keithpinckney", "elainestewart", "jonirosales"]
+ },
+ {
+ "UserName":"elainestewart",
+ "Friends": ["marshallgaray"]
+ },
+ {
+ "UserName":"salliesampson",
+ "Friends": [""],
+ "Trips": [13012]
+ },
+ {
+ "UserName":"jonirosales",
+ "Friends": ["marshallgaray"],
+ "Trips": [14013]
+ },
+ {
+ "UserName":"georginabarlow",
+ "Friends": ["javieralfred"]
+ },
+ {
+ "UserName":"angelhuffman",
+ "Friends": ["russellwhyte"],
+ "Trips": [16014]
+ },
+ {
+ "UserName":"laurelosborn",
+ "Friends": ["sandyosborn"]
+ },
+ {
+ "UserName":"sandyosborn",
+ "Friends": ["laurelosborn"]
+ },
+ {
+ "UserName":"ursulabright",
+ "Friends": ["keithpinckney"]
+ },
+ {
+ "UserName":"genevievereeves",
+ "Friends": ["kristakemp"]
+ },
+ {
+ "UserName":"kristakemp",
+ "Friends": ["genevievereeves"]
+ }
+ ]
+}
diff --git a/lib/server-core-ext/src/test/resources/people.json b/lib/server-core-ext/src/test/resources/people.json
new file mode 100644
index 000000000..64699bbc2
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/people.json
@@ -0,0 +1,323 @@
+{
+ "value":[
+ {
+ "UserName":"russellwhyte",
+ "FirstName":"Russell",
+ "LastName":"Whyte",
+ "Emails":[
+ "Russell@example.com",
+ "Russell@contoso.com"
+ ],
+ "AddressInfo":[
+ {
+ "Address":"187 Suffolk Ln.",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Boise",
+ "Region":"ID"
+ }
+ }
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"scottketchum",
+ "FirstName":"Scott",
+ "LastName":"Ketchum",
+ "Emails":[
+ "Scott@example.com"
+ ],
+ "AddressInfo":[
+ {
+ "Address":"2817 Milton Dr.",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Albuquerque",
+ "Region":"NM"
+ }
+ }
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"ronaldmundy",
+ "FirstName":"Ronald",
+ "LastName":"Mundy",
+ "Emails":[
+ "Ronald@example.com",
+ "Ronald@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"javieralfred",
+ "FirstName":"Javier",
+ "LastName":"Alfred",
+ "Emails":[
+ "Javier@example.com",
+ "Javier@contoso.com"
+ ],
+ "AddressInfo":[
+ {
+ "Address":"89 Jefferson Way Suite 2",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Portland",
+ "Region":"WA"
+ }
+ }
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"willieashmore",
+ "FirstName":"Willie",
+ "LastName":"Ashmore",
+ "Emails":[
+ "Willie@example.com",
+ "Willie@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"vincentcalabrese",
+ "FirstName":"Vincent",
+ "LastName":"Calabrese",
+ "Emails":[
+ "Vincent@example.com",
+ "Vincent@contoso.com"
+ ],
+ "AddressInfo":[
+ {
+ "Address":"55 Grizzly Peak Rd.",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Butte",
+ "Region":"MT"
+ }
+ }
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"clydeguess",
+ "FirstName":"Clyde",
+ "LastName":"Guess",
+ "Emails":[
+ "Clyde@example.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"keithpinckney",
+ "FirstName":"Keith",
+ "LastName":"Pinckney",
+ "Emails":[
+ "Keith@example.com",
+ "Keith@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"marshallgaray",
+ "FirstName":"Marshall",
+ "LastName":"Garay",
+ "Emails":[
+ "Marshall@example.com",
+ "Marshall@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"ryantheriault",
+ "FirstName":"Ryan",
+ "LastName":"Theriault",
+ "Emails":[
+ "Ryan@example.com",
+ "Ryan@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"0",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"elainestewart",
+ "FirstName":"Elaine",
+ "LastName":"Stewart",
+ "Emails":[
+ "Elaine@example.com",
+ "Elaine@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"salliesampson",
+ "FirstName":"Sallie",
+ "LastName":"Sampson",
+ "Emails":[
+ "Sallie@example.com",
+ "Sallie@contoso.com"
+ ],
+ "AddressInfo":[
+ {
+ "Address":"87 Polk St. Suite 5",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"San Francisco",
+ "Region":"CA"
+ }
+ },
+ {
+ "Address":"89 Chiaroscuro Rd.",
+ "City":{
+ "CountryRegion":"United States",
+ "Name":"Portland",
+ "Region":"OR"
+ }
+ }
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"jonirosales",
+ "FirstName":"Joni",
+ "LastName":"Rosales",
+ "Emails":[
+ "Joni@example.com",
+ "Joni@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"georginabarlow",
+ "FirstName":"Georgina",
+ "LastName":"Barlow",
+ "Emails":[
+ "Georgina@example.com",
+ "Georgina@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"angelhuffman",
+ "FirstName":"Angel",
+ "LastName":"Huffman",
+ "Emails":[
+ "Angel@example.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"laurelosborn",
+ "FirstName":"Laurel",
+ "LastName":"Osborn",
+ "Emails":[
+ "Laurel@example.com",
+ "Laurel@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"sandyosborn",
+ "FirstName":"Sandy",
+ "LastName":"Osborn",
+ "Emails":[
+ "Sandy@example.com",
+ "Sandy@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"ursulabright",
+ "FirstName":"Ursula",
+ "LastName":"Bright",
+ "Emails":[
+ "Ursula@example.com",
+ "Ursula@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"genevievereeves",
+ "FirstName":"Genevieve",
+ "LastName":"Reeves",
+ "Emails":[
+ "Genevieve@example.com",
+ "Genevieve@contoso.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ },
+ {
+ "UserName":"kristakemp",
+ "FirstName":"Krista",
+ "LastName":"Kemp",
+ "Emails":[
+ "Krista@example.com"
+ ],
+ "AddressInfo":[
+
+ ],
+ "Gender":"1",
+ "Concurrency":635585295719432047
+ }
+ ]
+}
diff --git a/lib/server-core-ext/src/test/resources/photos.json b/lib/server-core-ext/src/test/resources/photos.json
new file mode 100644
index 000000000..127d19532
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/photos.json
@@ -0,0 +1,64 @@
+{
+ "value":[
+ {
+ "Id":1,
+ "Name":"My Photo 1"
+ },
+ {
+ "Id":11,
+ "Name":"Trip Photo 11"
+ },
+ {
+ "Id":12,
+ "Name":"Trip Photo 12"
+ },
+ {
+ "Id":13,
+ "Name":"Trip Photo 13"
+ },
+ {
+ "Id":14,
+ "Name":"Trip Photo 14"
+ },
+ {
+ "Id":2,
+ "Name":"My Photo 2"
+ },
+ {
+ "Id":21,
+ "Name":"Trip Photo 21"
+ },
+ {
+ "Id":22,
+ "Name":"Trip Photo 22"
+ },
+ {
+ "Id":23,
+ "Name":"Trip Photo 23"
+ },
+ {
+ "Id":24,
+ "Name":"Trip Photo 24"
+ },
+ {
+ "Id":3,
+ "Name":"My Photo 3"
+ },
+ {
+ "Id":31,
+ "Name":"Trip Photo 31"
+ },
+ {
+ "Id":32,
+ "Name":"Trip Photo 32"
+ },
+ {
+ "Id":33,
+ "Name":"Trip Photo 33"
+ },
+ {
+ "Id":34,
+ "Name":"Trip Photo 34"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/lib/server-core-ext/src/test/resources/trip-links.json b/lib/server-core-ext/src/test/resources/trip-links.json
new file mode 100644
index 000000000..df8f9b8d3
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/trip-links.json
@@ -0,0 +1,28 @@
+{
+ "value":[
+ {
+ "TripId": 1001,
+ "Flights": [1, 2],
+ "Events": [51, 52, 53, 54, 55],
+ "Photos": [21, 22]
+ },
+ {
+ "TripId":2,
+ "Flights": [3, 4],
+ "Events": [55],
+ "Photos": [13, 14]
+ },
+ {
+ "TripId": 1003,
+ "Flights": [5, 6],
+ "Events": [56, 57],
+ "Photos": [23, 24]
+ },
+ {
+ "TripId": 2004,
+ "Flights": [7, 8],
+ "Events": [55, 57],
+ "Photos": [33, 34]
+ }
+ ]
+}
diff --git a/lib/server-core-ext/src/test/resources/trip.json b/lib/server-core-ext/src/test/resources/trip.json
new file mode 100644
index 000000000..79c610403
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/trip.json
@@ -0,0 +1,224 @@
+{
+ "value":[
+ {
+ "TripId": 1001,
+ "ShareId": "9d9b2fa0-efbf-490e-a5e3-bac8f7d47354",
+ "Description": "Trip from San Francisco to New York City. Nice trip with two friends. It is a 4 days' trip. We actually had a client meeting, but we also took one to go sightseeings in New York.",
+ "Name": "Trip in US",
+ "Budget": 3000.0,
+ "StartsAt":"2014-01-01T00:00:00Z",
+ "EndsAt": "2014-01-04T00:00:00Z",
+ "Tags": ["Trip in New York", "business","sightseeing"]
+ },
+ {
+ "TripId":2,
+ "ShareId": "f94e9116-8bdd-4dac-ab61-08438d0d9a71",
+ "Description": "Trip from Shanghai to Beijing",
+ "Name":"Trip in Beijing",
+ "Budget": 3000.0,
+ "Tags": ["Travel", "Beijing"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 3,
+ "ShareId": "9ce142c3-5fd6-4a71-848e-5220ebf1e9f3",
+ "Name": "Honeymoon",
+ "Budget": 800.0,
+ "Description": "Happy honeymoon trip",
+ "Tags": ["Travel", "honeymoon"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 4,
+ "ShareId": "4CCFB043-C79C-44EF-8CFE-CD493CED6654",
+ "Name": "Business trip to OData",
+ "Budget": 324.6,
+ "Description": "Business trip to OData",
+ "Tags": ["business", "odata"],
+ "StartsAt": "2013-01-01T00:00:00Z",
+ "EndsAt": "2013-01-04T00:00:00Z"
+ },
+ {
+ "TripId": 5,
+ "ShareId": "4546F419-0070-45F7-BA2C-19E4BC3647E1",
+ "Name": "Travel trip in US",
+ "Budget": 1250.0,
+ "Description": "Travel trip in US",
+ "Tags": ["travel", "overseas"],
+ "StartsAt": "2013-01-19T00:00:00Z",
+ "EndsAt": "2013-01-28T00:00:00Z"
+ },
+ {
+ "TripId": 6,
+ "ShareId": "26F0E8F6-657A-4561-BF3B-719366EF04FA",
+ "Name": "Study music in Europe",
+ "Budget": 3200.0,
+ "Description": "Study music in Europe",
+ "Tags": ["study", "overseas"],
+ "StartsAt": "2013-03-01T00:00:00Z",
+ "EndsAt": "2013-05-04T00:00:00Z"
+ },
+ {
+ "TripId": 7,
+ "ShareId": "2E77BF06-A354-454B-8BCA-5F004C1AFB59",
+ "Name": "Conference talk about OData",
+ "Budget": 2120.55,
+ "Description": "Conference talk about ODatan",
+ "Tags": ["odata", "overseas"],
+ "StartsAt": "2013-07-02T00:00:00Z",
+ "EndsAt": "2013-07-05T00:00:00Z"
+ },
+ {
+ "TripId": 8,
+ "ShareId": "E6E23FB2-C428-439E-BDAB-9283482F49F0",
+ "Name": "Vocation at hometown",
+ "Budget": 1500.0,
+ "Description": "Vocation at hometown",
+ "Tags": ["voaction"],
+ "StartsAt": "2013-10-01T00:00:00Z",
+ "EndsAt": "2013-10-05T00:00:00Z"
+ },
+ {
+ "TripId": 9,
+ "ShareId": "FAE31279-35CE-4119-9BDC-53F6E19DD1C5",
+ "Name": "Business trip for tech training",
+ "Budget": 100.0,
+ "Description": "Business trip for tech training",
+ "Tags": ["business"],
+ "StartsAt": "2013-09-01T00:00:00Z",
+ "EndsAt": "2013-09-04T00:00:00Z"
+ },
+ {
+ "TripId": 3009,
+ "ShareId": "dd6a09c0-e59b-4745-8612-f4499b676c47",
+ "Name": "Gradutaion trip",
+ "Budget": 6000.0,
+ "Description": "Gradution trip with friends",
+ "Tags": ["Travel"],
+ "StartsAt": "2013-05-01T00:00:00Z",
+ "EndsAt": "2013-05-08T00:00:00Z"
+ },
+ {
+ "TripId": 2004,
+ "ShareId": "f94e9116-8bdd-4dac-ab61-08438d0d9a71",
+ "Name": "Trip in Beijing",
+ "Budget": 11000.0,
+ "Description": "Trip from Shanghai to Beijing",
+ "Tags": ["Travel", "Beijing"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2013-02-02T00:00:00Z"
+ },
+ {
+ "TripId": 4005,
+ "ShareId": "f94e9116-8bdd-4dac-ab61-08438d0d9a71",
+ "Name": "Trip in Beijing",
+ "Budget": 800.0,
+ "Description": "Trip from Shanghai to Beijing",
+ "Tags": ["Travel", "Beijing"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 5007,
+ "ShareId": "5ae142c3-5ad6-4a71-768e-5220ebf1e9f3",
+ "Name": "Business Trip",
+ "Budget": 3800.5,
+ "Description": "This is my first business trip",
+ "Tags": ["business", "first"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 5008,
+ "ShareId": "9ce32ac3-5fd6-4a72-848e-2250ebf1e9f3",
+ "Name": "Trip in Europe",
+ "Budget": 2000.0,
+ "Description": "The trip is currently in plan.",
+ "Tags": ["Travel", "plan"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 1003,
+ "Name": "Trip in Beijing",
+ "Budget": 2000.0,
+ "ShareId": "f94e9116-8bdd-4dac-ab61-08438d0d9a71",
+ "Description": "Trip from Shanghai to Beijing",
+ "Tags": ["Travel", "Beijing"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 2004,
+ "ShareId": "f94e9116-8bdd-4dac-ab61-08438d0d9a71",
+ "Name": "Trip in Beijing",
+ "Budget": 11000.0,
+ "Description": "Trip from Shanghai to Beijing",
+ "Tags": ["Travel", "Beijing"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 1007,
+ "ShareId": "9ce142c3-5fd6-4a71-848e-5220ebf1e9f3",
+ "Name": "Honeymoon",
+ "Budget": 2650.0,
+ "Description": "Happy honeymoon trip",
+ "Tags": ["Travel", "honeymoon"],
+ "StartsAt": "2014-02-01T00:00:00Z",
+ "EndsAt": "2014-02-04T00:00:00Z"
+ },
+ {
+ "TripId": 7010,
+ "ShareId": "dd6a09c0-e59b-4745-8612-f4499b676c47",
+ "Name": "Gradutaion trip",
+ "Budget": 1000.0,
+ "Description": "Gradution trip with friends",
+ "Tags": ["Travel"],
+ "StartsAt": "2013-05-01T00:00:00Z",
+ "EndsAt": "2013-05-08T00:00:00Z"
+ },
+ {
+ "TripId": 8011,
+ "ShareId": "a88f675d-9199-4392-9656-b08e3b46df8a",
+ "Name": "Study trip",
+ "Budget": 1550.3,
+ "Description": "This is a 2 weeks study trip",
+ "Tags": ["study"],
+ "StartsAt": "2014-01-01T00:00:00Z",
+ "EndsAt": "2013-01-14T00:00:00Z"
+ },
+ {
+ "TripId": 13012,
+ "ShareId": "a88f675d-9199-4392-9656-b08e3b46df8a",
+ "Name": "Study trip",
+ "Budget": 600.0,
+ "Description": "This is a 2 weeks study trip",
+ "Tags": ["study"],
+ "StartsAt": "2014-01-01T00:00:00Z",
+ "EndsAt": "2013-01-14T00:00:00Z"
+ },
+ {
+ "TripId": 14013,
+ "ShareId": "a88f675d-9199-4392-9656-b08e3b46df8a",
+ "Name": "Study trip",
+ "Budget": 2000.0,
+ "Description": "This is a 2 weeks study trip",
+ "Tags": ["study"],
+ "StartsAt": "2014-01-01T00:00:00Z",
+ "EndsAt": "2013-01-14T00:00:00Z"
+ },
+ {
+ "TripId": 16014,
+ "ShareId": "cb0b8acb-79cb-4127-8316-772bc4302824",
+ "Name": "DIY Trip",
+ "Budget": 1500.3,
+ "Description": "This is a DIY trip",
+ "Tags": ["Travel", "DIY"],
+ "StartsAt": "2011-02-11T00:00:00Z",
+ "EndsAt": "2011-02-14T00:00:00Z"
+ }
+ ]
+}
diff --git a/lib/server-core-ext/src/test/resources/trippin.xml b/lib/server-core-ext/src/test/resources/trippin.xml
new file mode 100644
index 000000000..5970793ef
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/trippin.xml
@@ -0,0 +1,356 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+ image/jpeg
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Core.V1.Permission/Read
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.SearchExpressions/none
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Concurrency
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.NavigationType/None
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.NavigationType/Recursive
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.SearchExpressions/none
+
+
+
+
+
+
+
+
+ Trips
+ Friends
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.SearchExpressions/none
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.SearchExpressions/none
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Org.OData.Capabilities.V1.ConformanceLevelType/Advanced
+
+
+
+ application/json;odata.metadata=full;IEEE754Compatible=false;odata.streaming=true
+ application/json;odata.metadata=minimal;IEEE754Compatible=false;odata.streaming=true
+ application/json;odata.metadata=none;IEEE754Compatible=false;odata.streaming=true
+
+
+
+
+
+
+ contains
+ endswith
+ startswith
+ length
+ indexof
+ substring
+ tolower
+ toupper
+ trim
+ concat
+ year
+ month
+ day
+ hour
+ minute
+ second
+ round
+ floor
+ ceiling
+ cast
+ isof
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
index 74c73c8f1..a66570f0f 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializer.java
@@ -20,6 +20,8 @@ package org.apache.olingo.server.core.deserializer.json;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -65,6 +67,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
public class ODataJsonDeserializer implements ODataDeserializer {
@@ -267,8 +270,8 @@ public class ODataJsonDeserializer implements ODataDeserializer {
node.remove(toRemove);
}
- private void consumeEntityProperties(final EdmEntityType edmEntityType, final ObjectNode node, final EntityImpl
- entity) throws DeserializerException {
+ private void consumeEntityProperties(final EdmEntityType edmEntityType, final ObjectNode node,
+ final EntityImpl entity) throws DeserializerException {
List propertyNames = edmEntityType.getPropertyNames();
for (String propertyName : propertyNames) {
JsonNode jsonNode = node.get(propertyName);
@@ -409,7 +412,7 @@ public class ODataJsonDeserializer implements ODataDeserializer {
case ENUM:
value = readEnumValue(name, type, isNullable, maxLength, precision, scale, isUnicode, mapping,
jsonNode);
- property.setValue(ValueType.PRIMITIVE, value);
+ property.setValue(ValueType.ENUM, value);
break;
case COMPLEX:
value = readComplexNode(name, type, isNullable, jsonNode);
@@ -706,4 +709,81 @@ public class ODataJsonDeserializer implements ODataDeserializer {
DeserializerException.MessageKeys.NOT_IMPLEMENTED);
}
}
+
+ @Override
+ public Property property(InputStream stream, EdmProperty edmProperty)
+ throws DeserializerException {
+ try {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, true);
+ JsonParser parser = new JsonFactory(objectMapper).createParser(stream);
+ final ObjectNode tree = parser.getCodec().readTree(parser);
+
+ Property property = null;
+ JsonNode jsonNode = tree.get(Constants.VALUE);
+ if (jsonNode != null) {
+ property = consumePropertyNode(edmProperty.getName(), edmProperty.getType(),
+ edmProperty.isCollection(),
+ edmProperty.isNullable(), edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(),
+ edmProperty.isUnicode(), edmProperty.getMapping(),
+ jsonNode);
+ tree.remove(Constants.VALUE);
+ } else {
+ property = consumePropertyNode(edmProperty.getName(), edmProperty.getType(),
+ edmProperty.isCollection(),
+ edmProperty.isNullable(), edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(),
+ edmProperty.isUnicode(), edmProperty.getMapping(),
+ tree);
+ }
+ return property;
+ } catch (JsonParseException e) {
+ throw new DeserializerException("An JsonParseException occurred", e,
+ DeserializerException.MessageKeys.JSON_SYNTAX_EXCEPTION);
+ } catch (JsonMappingException e) {
+ throw new DeserializerException("Duplicate property detected", e,
+ DeserializerException.MessageKeys.DUPLICATE_PROPERTY);
+ } catch (IOException e) {
+ throw new DeserializerException("An IOException occurred", e, DeserializerException.MessageKeys.IO_EXCEPTION);
+ }
+ }
+
+ public List entityReferences(InputStream stream) throws DeserializerException {
+ try {
+ ArrayList parsedValues = new ArrayList();
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY, true);
+ JsonParser parser = new JsonFactory(objectMapper).createParser(stream);
+ final ObjectNode tree = parser.getCodec().readTree(parser);
+ final String key = "@odata.id";
+ JsonNode jsonNode = tree.get(Constants.VALUE);
+ if (jsonNode != null) {
+ if (jsonNode.isArray()) {
+ ArrayNode arrayNode = (ArrayNode)jsonNode;
+ Iterator it = arrayNode.iterator();
+ while(it.hasNext()) {
+ parsedValues.add(new URI(it.next().get(key).asText()));
+ }
+ } else {
+ parsedValues.add(new URI(jsonNode.asText()));
+ }
+ tree.remove(Constants.VALUE);
+ // if this is value there can be only one property
+ return parsedValues;
+ }
+ parsedValues.add(new URI(tree.get(key).asText()));
+ return parsedValues;
+ } catch (JsonParseException e) {
+ throw new DeserializerException("An JsonParseException occurred", e,
+ DeserializerException.MessageKeys.JSON_SYNTAX_EXCEPTION);
+ } catch (JsonMappingException e) {
+ throw new DeserializerException("Duplicate property detected", e,
+ DeserializerException.MessageKeys.DUPLICATE_PROPERTY);
+ } catch (IOException e) {
+ throw new DeserializerException("An IOException occurred", e,
+ DeserializerException.MessageKeys.IO_EXCEPTION);
+ } catch (URISyntaxException e) {
+ throw new DeserializerException("failed to read @odata.id", e,
+ DeserializerException.MessageKeys.UNKOWN_CONTENT);
+ }
+ }
}
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
index aab662454..5956e8286 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializer.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
@@ -41,6 +41,7 @@ import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmStructuredType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.ODataServerError;
@@ -127,7 +128,8 @@ public class ODataJsonSerializer implements ODataSerializer {
}
@Override
- public InputStream entityCollection(final EdmEntityType entityType, final EntitySet entitySet,
+ public InputStream entityCollection(final ServiceMetadata metadata,
+ final EdmEntityType entityType, final EntitySet entitySet,
final EntityCollectionSerializerOptions options) throws SerializerException {
CircleStreamBuffer buffer = new CircleStreamBuffer();
try {
@@ -145,8 +147,9 @@ public class ODataJsonSerializer implements ODataSerializer {
json.writeNumberField(Constants.JSON_COUNT, entitySet.getCount());
}
json.writeFieldName(Constants.VALUE);
- writeEntitySet(entityType, entitySet,
- options == null ? null : options.getExpand(), options == null ? null : options.getSelect(), json);
+ writeEntitySet(metadata, entityType, entitySet, options == null ? null : options.getExpand(),
+ options == null ? null : options.getSelect(),
+ options == null ? false : options.onlyReferences(), json);
if (entitySet.getNext() != null) {
json.writeStringField(Constants.JSON_NEXT_LINK, entitySet.getNext().toASCIIString());
}
@@ -159,14 +162,16 @@ public class ODataJsonSerializer implements ODataSerializer {
}
@Override
- public InputStream entity(final EdmEntityType entityType, final Entity entity,
- final EntitySerializerOptions options) throws SerializerException {
+ public InputStream entity(final ServiceMetadata metadata, final EdmEntityType entityType,
+ final Entity entity, final EntitySerializerOptions options) throws SerializerException {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
CircleStreamBuffer buffer = new CircleStreamBuffer();
try {
JsonGenerator json = new JsonFactory().createGenerator(buffer.getOutputStream());
- writeEntity(entityType, entity, contextURL,
- options == null ? null : options.getExpand(), options == null ? null : options.getSelect(), json);
+ writeEntity(metadata, entityType, entity, contextURL,
+ options == null ? null : options.getExpand(),
+ options == null ? null : options.getSelect(),
+ options == null ? false: options.onlyReferences(), json);
json.close();
} catch (final IOException e) {
throw new SerializerException("An I/O exception occurred.", e,
@@ -184,18 +189,26 @@ public class ODataJsonSerializer implements ODataSerializer {
return contextURL;
}
- protected void writeEntitySet(final EdmEntityType entityType, final EntitySet entitySet,
- final ExpandOption expand, final SelectOption select, final JsonGenerator json)
- throws IOException, SerializerException {
+ protected void writeEntitySet(final ServiceMetadata metadata, final EdmEntityType entityType,
+ final EntitySet entitySet, final ExpandOption expand, final SelectOption select,
+ final boolean onlyReference, final JsonGenerator json) throws IOException,
+ SerializerException {
json.writeStartArray();
for (final Entity entity : entitySet.getEntities()) {
- writeEntity(entityType, entity, null, expand, select, json);
+ if (onlyReference) {
+ json.writeStartObject();
+ json.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString());
+ json.writeEndObject();
+ } else {
+ writeEntity(metadata, entityType, entity, null, expand, select, false, json);
+ }
}
json.writeEndArray();
}
- protected void writeEntity(final EdmEntityType entityType, final Entity entity, final ContextURL contextURL,
- final ExpandOption expand, final SelectOption select, final JsonGenerator json)
+ protected void writeEntity(final ServiceMetadata metadata, final EdmEntityType entityType,
+ final Entity entity, final ContextURL contextURL, final ExpandOption expand,
+ final SelectOption select, boolean onlyReference, final JsonGenerator json)
throws IOException, SerializerException {
json.writeStartObject();
if (format != ODataFormat.JSON_NO_METADATA) {
@@ -214,9 +227,63 @@ public class ODataJsonSerializer implements ODataSerializer {
}
}
}
- writeProperties(entityType, entity.getProperties(), select, json);
- writeNavigationProperties(entityType, entity, expand, json);
- json.writeEndObject();
+ if (onlyReference) {
+ json.writeStringField(Constants.JSON_ID, entity.getId().toASCIIString());
+ } else {
+ EdmEntityType resolvedType = resolveEntityType(metadata, entityType, entity.getType());
+ if (!resolvedType.equals(entityType)) {
+ json.writeStringField(Constants.JSON_TYPE, "#"+entity.getType());
+ }
+ writeProperties(resolvedType, entity.getProperties(), select, json);
+ writeNavigationProperties(metadata, resolvedType, entity, expand, json);
+ json.writeEndObject();
+ }
+ }
+
+ protected EdmEntityType resolveEntityType(ServiceMetadata metadata, EdmEntityType baseType,
+ String derivedTypeName) throws SerializerException {
+ if (baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) {
+ return baseType;
+ }
+ EdmEntityType derivedType = metadata.getEdm().getEntityType(new FullQualifiedName(derivedTypeName));
+ if (derivedType == null) {
+ throw new SerializerException("EntityType not found",
+ SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+ }
+ EdmEntityType type = derivedType.getBaseType();
+ while (type != null) {
+ if (type.getFullQualifiedName().getFullQualifiedNameAsString()
+ .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) {
+ return derivedType;
+ }
+ type = type.getBaseType();
+ }
+ throw new SerializerException("Wrong base type",
+ SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType
+ .getFullQualifiedName().getFullQualifiedNameAsString());
+ }
+
+ protected EdmComplexType resolveComplexType(ServiceMetadata metadata, EdmComplexType baseType,
+ String derivedTypeName) throws SerializerException {
+ if (baseType.getFullQualifiedName().getFullQualifiedNameAsString().equals(derivedTypeName)) {
+ return baseType;
+ }
+ EdmComplexType derivedType = metadata.getEdm().getComplexType(new FullQualifiedName(derivedTypeName));
+ if (derivedType == null) {
+ throw new SerializerException("Complex Type not found",
+ SerializerException.MessageKeys.UNKNOWN_TYPE, derivedTypeName);
+ }
+ EdmComplexType type = derivedType.getBaseType();
+ while (type != null) {
+ if (type.getFullQualifiedName().getFullQualifiedNameAsString()
+ .equals(baseType.getFullQualifiedName().getFullQualifiedNameAsString())) {
+ return derivedType;
+ }
+ type = type.getBaseType();
+ }
+ throw new SerializerException("Wrong base type",
+ SerializerException.MessageKeys.WRONG_BASE_TYPE, derivedTypeName, baseType
+ .getFullQualifiedName().getFullQualifiedNameAsString());
}
protected void writeProperties(final EdmStructuredType type, final List properties,
@@ -235,8 +302,9 @@ public class ODataJsonSerializer implements ODataSerializer {
}
}
- protected void writeNavigationProperties(final EdmStructuredType type, final Linked linked,
- final ExpandOption expand, final JsonGenerator json) throws SerializerException, IOException {
+ protected void writeNavigationProperties(final ServiceMetadata metadata,
+ final EdmStructuredType type, final Linked linked, final ExpandOption expand,
+ final JsonGenerator json) throws SerializerException, IOException {
if (ExpandSelectHelper.hasExpand(expand)) {
final boolean expandAll = ExpandSelectHelper.isExpandAll(expand);
final Set expanded = expandAll ? null :
@@ -251,7 +319,7 @@ public class ODataJsonSerializer implements ODataSerializer {
throw new SerializerException("Expand options $ref and $levels are not supported.",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
}
- writeExpandedNavigationProperty(property, navigationLink,
+ writeExpandedNavigationProperty(metadata, property, navigationLink,
innerOptions == null ? null : innerOptions.getExpandOption(),
innerOptions == null ? null : innerOptions.getSelectOption(),
json);
@@ -260,7 +328,8 @@ public class ODataJsonSerializer implements ODataSerializer {
}
}
- protected void writeExpandedNavigationProperty(final EdmNavigationProperty property, final Link navigationLink,
+ protected void writeExpandedNavigationProperty(final ServiceMetadata metadata,
+ final EdmNavigationProperty property, final Link navigationLink,
final ExpandOption innerExpand, final SelectOption innerSelect, JsonGenerator json)
throws IOException, SerializerException {
json.writeFieldName(property.getName());
@@ -269,13 +338,15 @@ public class ODataJsonSerializer implements ODataSerializer {
json.writeStartArray();
json.writeEndArray();
} else {
- writeEntitySet(property.getType(), navigationLink.getInlineEntitySet(), innerExpand, innerSelect, json);
+ writeEntitySet(metadata, property.getType(), navigationLink.getInlineEntitySet(), innerExpand,
+ innerSelect, false, json);
}
} else {
if (navigationLink == null || navigationLink.getInlineEntity() == null) {
json.writeNull();
} else {
- writeEntity(property.getType(), navigationLink.getInlineEntity(), null, innerExpand, innerSelect, json);
+ writeEntity(metadata, property.getType(), navigationLink.getInlineEntity(), null,
+ innerExpand, innerSelect, false, json);
}
}
}
@@ -316,6 +387,11 @@ public class ODataJsonSerializer implements ODataSerializer {
} else if (property.isComplex()) {
writeComplexValue((EdmComplexType) edmProperty.getType(), property.asComplex().getValue(),
selectedPaths, json);
+ } else if (property.isEnum()) {
+ writePrimitive((EdmPrimitiveType) edmProperty.getType(), property,
+ edmProperty.isNullable(), edmProperty.getMaxLength(),
+ edmProperty.getPrecision(), edmProperty.getScale(), edmProperty.isUnicode(),
+ json);
} else {
throw new SerializerException("Property type not yet supported!",
SerializerException.MessageKeys.UNSUPPORTED_PROPERTY_TYPE, edmProperty.getName());
@@ -467,8 +543,8 @@ public class ODataJsonSerializer implements ODataSerializer {
}
@Override
- public InputStream complex(final EdmComplexType type, final Property property,
- final ComplexSerializerOptions options) throws SerializerException {
+ public InputStream complex(final ServiceMetadata metadata, final EdmComplexType type,
+ final Property property, final ComplexSerializerOptions options) throws SerializerException {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
CircleStreamBuffer buffer = new CircleStreamBuffer();
try {
@@ -477,11 +553,15 @@ public class ODataJsonSerializer implements ODataSerializer {
if (contextURL != null) {
json.writeStringField(Constants.JSON_CONTEXT, ContextURLBuilder.create(contextURL).toASCIIString());
}
+ EdmComplexType resolvedType = resolveComplexType(metadata, type, property.getType());
+ if (!resolvedType.equals(type)) {
+ json.writeStringField(Constants.JSON_TYPE, "#"+property.getType());
+ }
final List values =
property.isNull() ? Collections. emptyList() : property.asComplex().getValue();
writeProperties(type, values, options == null ? null : options.getSelect(), json);
if (!property.isNull() && property.isComplex()) {
- writeNavigationProperties(type, property.asComplex(),
+ writeNavigationProperties(metadata, type, property.asComplex(),
options == null ? null : options.getExpand(), json);
}
json.writeEndObject();
@@ -523,8 +603,8 @@ public class ODataJsonSerializer implements ODataSerializer {
}
@Override
- public InputStream complexCollection(final EdmComplexType type, final Property property,
- final ComplexSerializerOptions options) throws SerializerException {
+ public InputStream complexCollection(final ServiceMetadata metadata, final EdmComplexType type,
+ final Property property, final ComplexSerializerOptions options) throws SerializerException {
final ContextURL contextURL = checkContextURL(options == null ? null : options.getContextURL());
CircleStreamBuffer buffer = new CircleStreamBuffer();
try {
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
index 4a3f82aae..ac3375976 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/utils/ContextURLBuilder.java
@@ -66,7 +66,14 @@ public final class ContextURLBuilder {
if (contextURL.getEntitySetOrSingletonOrType() != null) {
throw new IllegalArgumentException("ContextURL: $ref with Entity Set");
}
- result.append('#').append(ContextURL.Suffix.REFERENCE.getRepresentation());
+ if(contextURL.isCollection()) {
+ result.append('#');
+ result.append("Collection(")
+ .append(ContextURL.Suffix.REFERENCE.getRepresentation())
+ .append(")");
+ } else {
+ result.append('#').append(ContextURL.Suffix.REFERENCE.getRepresentation());
+ }
} else if (contextURL.getSuffix() != null) {
if (contextURL.getEntitySetOrSingletonOrType() == null) {
throw new IllegalArgumentException("ContextURL: Suffix without preceding Entity Set!");
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
index acd1dedd0..34756c106 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/xml/ODataXmlSerializerImpl.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
@@ -87,14 +87,15 @@ public class ODataXmlSerializerImpl implements ODataSerializer {
}
@Override
- public InputStream entity(final EdmEntityType entityType, final Entity entity,
- final EntitySerializerOptions options) throws SerializerException {
+ public InputStream entity(final ServiceMetadata metadata, final EdmEntityType entityType,
+ final Entity entity, final EntitySerializerOptions options) throws SerializerException {
throw new SerializerException("Entity serialization not implemented for XML format",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
}
@Override
- public InputStream entityCollection(final EdmEntityType entityType, final EntitySet entitySet,
+ public InputStream entityCollection(final ServiceMetadata metadata,
+ final EdmEntityType entityType, final EntitySet entitySet,
final EntityCollectionSerializerOptions options) throws SerializerException {
throw new SerializerException("Entityset serialization not implemented for XML format",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
@@ -114,8 +115,8 @@ public class ODataXmlSerializerImpl implements ODataSerializer {
}
@Override
- public InputStream complex(final EdmComplexType type, final Property property,
- final ComplexSerializerOptions options) throws SerializerException {
+ public InputStream complex(final ServiceMetadata metadata, final EdmComplexType type,
+ final Property property, final ComplexSerializerOptions options) throws SerializerException {
throw new SerializerException("Serialization not implemented for XML format.",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
}
@@ -128,8 +129,8 @@ public class ODataXmlSerializerImpl implements ODataSerializer {
}
@Override
- public InputStream complexCollection(final EdmComplexType type, final Property property,
- final ComplexSerializerOptions options) throws SerializerException {
+ public InputStream complexCollection(final ServiceMetadata metadata, final EdmComplexType type,
+ final Property property, final ComplexSerializerOptions options) throws SerializerException {
throw new SerializerException("Serialization not implemented for XML format.",
SerializerException.MessageKeys.NOT_IMPLEMENTED);
}
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceActionImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceActionImpl.java
index 82fe74392..a78d79f1a 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceActionImpl.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/UriResourceActionImpl.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
@@ -56,12 +56,18 @@ public class UriResourceActionImpl extends UriResourceTypedImpl implements UriRe
@Override
public boolean isCollection() {
- return action.getReturnType() !=null && action.getReturnType().isCollection();
+ if (action.getReturnType() != null) {
+ return action.getReturnType().isCollection();
+ }
+ return false;
}
@Override
public EdmType getType() {
- return action.getReturnType() == null ? null : action.getReturnType().getType();
+ if (action.getReturnType() != null) {
+ return action.getReturnType().getType();
+ }
+ return null;
}
@Override
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java
index 0b3a5f924..cf5493873 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/uri/validator/UriValidator.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
@@ -60,7 +60,7 @@ public class UriValidator {
/* entitySetCount 7 */ { true , false, false, false, false, false, true, false, false, false, false },
/* entity 8 */ { false, true , true , false, false, false, false, true , false, false, false },
/* mediaStream 9 */ { false, false, false, false, false, false, false, false, false, false, false },
- /* references 10 */ { true , true , false, false, false, true , true , false, true , true , true },
+ /* references 10 */ { true , true , false, true, false, true , true , false, true , true , true },
/* reference 11 */ { false, true , false, false, false, false, false, false, false, false, false },
/* propertyComplex 12 */ { false, true , true , false, false, false, false, true , false, false, false },
/* propertyComplexCollection 13 */ { true , true , true , false, true , true , false, true , true , true , true },
@@ -78,7 +78,7 @@ public class UriValidator {
/* GET 0 */ { true , true , true , true, true , true , true , true , true , true , true },
/* POST 0 */ { true , false , true , false, false , true , false , true , false , false , false },
/* PUT 0 */ { false , false , false , false, false , false , false , false , false , false , false },
- /* DELETE 0 */ { false , false , false , false, false , false, false , false, false , false , false },
+ /* DELETE 0 */ { false , false , false , true, false , false, false , false, false , false , false },
/* PATCH 0 */ { false , false , false , false, false , false , false , false , false , false , false }
};
//CHECKSTYLE:ON
diff --git a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
index 76266ea9c..b8254c3d2 100644
--- a/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
+++ b/lib/server-core/src/main/resources/server-core-exceptions-i18n.properties
@@ -96,6 +96,8 @@ SerializerException.INCONSISTENT_PROPERTY_TYPE=An inconsistency has been detecte
SerializerException.MISSING_PROPERTY=The non-nullable property '%1$s' is missing.
SerializerException.WRONG_PROPERTY_VALUE=The value '%2$s' is not valid for property '%1$s'.
SerializerException.WRONG_PRIMITIVE_VALUE=The value '%2$s' is not valid for the primitive type '%1$s' and the given facets.
+SerializerException.UNKNOWN_TYPE=Type '%1s' not found in metadata.
+SerializerException.WRONG_BASE_TYPE=Type '%1s' is not derived from '%2s'.
DeserializerException.NOT_IMPLEMENTED=The requested deserialization method has not been implemented yet.
DeserializerException.IO_EXCEPTION=An I/O exception occurred.
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java
index f3e22efca..a301c3d78 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerBasicTest.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
@@ -18,8 +18,13 @@
*/
package org.apache.olingo.server.core.deserializer.json;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import java.io.ByteArrayInputStream;
+import java.net.URI;
+import java.util.List;
+
import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
@@ -41,4 +46,33 @@ public class ODataJsonDeserializerBasicTest {
assertNotNull(deserializer);
deserializer = null;
}
+
+ @Test
+ public void testReadingCollectionProperties() throws Exception {
+ String payload = "{\n" +
+ " \"@odata.context\": \"http://host/service/$metadata#Collection($ref)\",\n" +
+ " \"value\": [\n" +
+ " { \"@odata.id\": \"Orders(10643)\" },\n" +
+ " { \"@odata.id\": \"Orders(10759)\" }\n" +
+ " ]\n" +
+ "}";
+ ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON);
+ List values = deserializer.entityReferences(new ByteArrayInputStream(payload.getBytes()));
+ assertEquals(2, values.size());
+ assertEquals("Orders(10643)", values.get(0).toASCIIString());
+ assertEquals("Orders(10759)", values.get(1).toASCIIString());
+ }
+
+ @Test
+ public void testReadingProperties() throws Exception {
+ String payload = "{\n" +
+ " \"@odata.context\": \"http://host/service/$metadata#$ref\",\n" +
+ " \"@odata.id\": \"Orders(10643)\"\n" +
+ "}";
+ ODataDeserializer deserializer = OData.newInstance().createDeserializer(ODataFormat.JSON);
+ List values = deserializer.entityReferences(new ByteArrayInputStream(payload
+ .getBytes()));
+ assertEquals(1, values.size());
+ assertEquals("Orders(10643)", values.get(0).toASCIIString());
+ }
}
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
index 742c5d54e..1e4537f8d 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.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
@@ -49,14 +49,17 @@ public class ODataJsonSerializerTest {
final ODataJsonSerializer serializer = new ODataJsonSerializer(ODataFormat.APPLICATION_JSON);
final ComplexSerializerOptions options = ComplexSerializerOptions.with()
.contextURL(ContextURL.with().selectList("ComplexCollection").build()).build();
- final InputStream in = serializer.complexCollection(ComplexTypeHelper.createType(), complexCollection, options);
+ final InputStream in = serializer.complexCollection(null, ComplexTypeHelper.createType(),
+ complexCollection, options);
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("value")) {
- assertEquals("{\"@odata.context\":\"$metadata(ComplexCollection)\",\"value\":"
- + "[{\"prop1\":\"test1\",\"prop2\":\"test11\"},{\"prop1\":\"test2\",\"prop2\":\"test22\"}]}", line);
+ assertEquals(
+ "{\"@odata.context\":\"$metadata(ComplexCollection)\",\"value\":"
+ + "[{\"prop1\":\"test1\",\"prop2\":\"test11\"},{\"prop1\":\"test2\",\"prop2\":\"test22\"}]}",
+ line);
}
}
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
index 7fa981b44..4137db09c 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/TechnicalServlet.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
@@ -66,8 +66,8 @@ public class TechnicalServlet extends HttpServlet {
}
ODataHttpHandler handler = odata.createHandler(serviceMetadata);
- handler.register(new TechnicalEntityProcessor(dataProvider));
- handler.register(new TechnicalPrimitiveComplexProcessor(dataProvider));
+ handler.register(new TechnicalEntityProcessor(dataProvider, serviceMetadata));
+ handler.register(new TechnicalPrimitiveComplexProcessor(dataProvider, serviceMetadata));
handler.register(new TechnicalBatchProcessor(dataProvider));
handler.process(req, resp);
} catch (RuntimeException e) {
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
index e6193dff5..1f28900f4 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataCreator.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
@@ -41,11 +41,14 @@ import org.apache.olingo.commons.core.data.EntityImpl;
import org.apache.olingo.commons.core.data.EntitySetImpl;
import org.apache.olingo.commons.core.data.LinkImpl;
import org.apache.olingo.commons.core.data.PropertyImpl;
+import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
+import org.apache.olingo.server.tecsvc.provider.EntityTypeProvider;
+
public class DataCreator {
private static final UUID GUID = UUID.fromString("01234567-89ab-cdef-0123-456789abcdef");
-
+ private static final String ctPropComp = ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString();
private final Map data;
public DataCreator() {
@@ -96,7 +99,9 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyInt16", i))
.addProperty(createPrimitive("PropertyString", "Number:" + i)));
}
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETServerSidePaging.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -106,7 +111,9 @@ public class DataCreator {
entitySet.getEntities().add(createETKeyNavEntity(1, "I am String Property 1"));
entitySet.getEntities().add(createETKeyNavEntity(2, "I am String Property 2"));
entitySet.getEntities().add(createETKeyNavEntity(3, "I am String Property 3"));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETKeyNav.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -115,10 +122,12 @@ public class DataCreator {
return new EntityImpl()
.addProperty(createPrimitive("PropertyInt16", propertyInt16))
.addProperty(createPrimitive("PropertyString", propertyString))
- .addProperty(createComplex("PropertyCompNav",
+ .addProperty(createComplex("PropertyCompNav", ctPropComp,
createPrimitive("PropertyInt16", 1)))
.addProperty(createKeyNavAllPrimComplexValue("PropertyCompAllPrim"))
- .addProperty(createComplex("PropertyCompTwoPrim",
+ .addProperty(
+ createComplex("PropertyCompTwoPrim",
+ ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString(),
createPrimitive("PropertyInt16", 16),
createPrimitive("PropertyString", "Test123")))
.addProperty(createPrimitiveCollection("CollPropertyString",
@@ -126,7 +135,9 @@ public class DataCreator {
"Employee2@company.example",
"Employee3@company.example"))
.addProperty(createPrimitiveCollection("CollPropertyInt16", 1000, 2000, 30112))
- .addProperty(createComplexCollection("CollPropertyComp",
+ .addProperty(
+ createComplexCollection("CollPropertyComp", ComplexTypeProvider.nameCTPrimComp
+ .getFullQualifiedNameAsString(),
Arrays.asList(
createPrimitive("PropertyInt16", 1),
createKeyNavAllPrimComplexValue("PropertyComp")),
@@ -136,9 +147,11 @@ public class DataCreator {
Arrays.asList(
createPrimitive("PropertyInt16", 3),
createKeyNavAllPrimComplexValue("PropertyComp"))))
- .addProperty(createComplex("PropertyCompCompNav",
+ .addProperty(
+ createComplex("PropertyCompCompNav",
+ ComplexTypeProvider.nameCTCompComp.getFullQualifiedNameAsString(),
createPrimitive("PropertyString", "1"),
- createComplex("PropertyComp", createPrimitive("PropertyInt16", 1))));
+ createComplex("PropertyComp", ctPropComp, createPrimitive("PropertyInt16", 1))));
}
private EntitySet createESTwoKeyNav() {
@@ -148,7 +161,9 @@ public class DataCreator {
entitySet.getEntities().add(createESTwoKeyNavEntity(1, "2"));
entitySet.getEntities().add(createESTwoKeyNavEntity(2, "1"));
entitySet.getEntities().add(createESTwoKeyNavEntity(3, "1"));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETTwoKeyNav.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -157,9 +172,9 @@ public class DataCreator {
return new EntityImpl()
.addProperty(createPrimitive("PropertyInt16", propertyInt16))
.addProperty(createPrimitive("PropertyString", propertyString))
- .addProperty(createComplex("PropertyComp",
+ .addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 11),
- createComplex("PropertyComp",
+ createComplex("PropertyComp", ctPropComp,
createPrimitive("PropertyString", "StringValue"),
createPrimitive("PropertyBinary", new byte[] { 1, 35, 69, 103, -119, -85, -51, -17 }),
createPrimitive("PropertyBoolean", true),
@@ -175,20 +190,26 @@ public class DataCreator {
createPrimitive("PropertyInt64", Long.MAX_VALUE),
createPrimitive("PropertySByte", Byte.MAX_VALUE),
createPrimitive("PropertyTimeOfDay", getTime(21, 5, 59)))))
- .addProperty(createComplex("PropertyCompNav",
+ .addProperty(
+ createComplex("PropertyCompNav",
+ ComplexTypeProvider.nameCTCompNav.getFullQualifiedNameAsString(),
createPrimitive("PropertyInt16", 1),
createKeyNavAllPrimComplexValue("PropertyComp")))
- .addProperty(createComplexCollection("CollPropertyComp"))
- .addProperty(createComplexCollection("CollPropertyCompNav",
+ .addProperty(createComplexCollection("CollPropertyComp", null))
+ .addProperty(
+ createComplexCollection("CollPropertyCompNav",
+ ComplexTypeProvider.nameCTCompNav.getFullQualifiedNameAsString(),
Arrays.asList(createPrimitive("PropertyInt16", 1))))
.addProperty(createPrimitiveCollection("CollPropertyString", 1, 2))
- .addProperty(createComplex("PropertyCompTwoPrim",
+ .addProperty(
+ createComplex("PropertyCompTwoPrim",
+ ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString(),
createPrimitive("PropertyInt16", 11),
createPrimitive("PropertyString", "11")));
}
private Property createKeyNavAllPrimComplexValue(final String name) {
- return createComplex(name,
+ return createComplex(name, ComplexTypeProvider.nameCTAllPrim.getFullQualifiedNameAsString(),
createPrimitive("PropertyString", "First Resource - positive values"),
createPrimitive("PropertyBinary", new byte[] { 1, 35, 69, 103, -119, -85, -51, -17 }),
createPrimitive("PropertyBoolean", true),
@@ -213,8 +234,9 @@ public class DataCreator {
entitySet.getEntities().add(new EntityImpl()
.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
- .addProperty(createComplex("PropertyComp",
- createComplexCollection("CollPropertyComp",
+ .addProperty(createComplex("PropertyComp", null,
+ createComplexCollection("CollPropertyComp", ComplexTypeProvider.nameCTTwoPrim
+ .getFullQualifiedNameAsString(),
Arrays.asList(
createPrimitive("PropertyInt16", 555),
createPrimitive("PropertyString", "1 Test Complex in Complex Property")),
@@ -227,8 +249,9 @@ public class DataCreator {
entitySet.getEntities().add(new EntityImpl()
.addProperty(createPrimitive("PropertyInt16", 12345))
- .addProperty(createComplex("PropertyComp",
- createComplexCollection("CollPropertyComp",
+ .addProperty(createComplex("PropertyComp",null,
+ createComplexCollection("CollPropertyComp", ComplexTypeProvider.nameCTTwoPrim
+ .getFullQualifiedNameAsString(),
Arrays.asList(
createPrimitive("PropertyInt16", 888),
createPrimitive("PropertyString", "11 Test Complex in Complex Property")),
@@ -238,7 +261,9 @@ public class DataCreator {
Arrays.asList(
createPrimitive("PropertyInt16", 0),
createPrimitive("PropertyString", "13 Test Complex in Complex Property"))))));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETCompCollComp.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -260,7 +285,9 @@ public class DataCreator {
entitySet.getEntities().add(new EntityImpl()
.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
.addProperty(createPrimitive("PropertyString", "Test String4")));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETTwoPrim.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -322,7 +349,9 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyDuration", 0))
.addProperty(createPrimitive("PropertyGuid", UUID.fromString("76543201-23ab-cdef-0123-456789cccddd")))
.addProperty(createPrimitive("PropertyTimeOfDay", getTime(0, 1, 1))));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETAllPrim.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -331,7 +360,7 @@ public class DataCreator {
Entity entity = new EntityImpl();
entity.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE));
- entity.addProperty(createComplex("PropertyComp",
+ entity.addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyString", "First Resource - first"),
createPrimitive("PropertyBinary",
new byte[] { 0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF }),
@@ -353,7 +382,7 @@ public class DataCreator {
entity = new EntityImpl();
entity.addProperty(createPrimitive("PropertyInt16", 7));
- entity.addProperty(createComplex("PropertyComp",
+ entity.addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyString", "Second Resource - second"),
createPrimitive("PropertyBinary",
new byte[] { 0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF }),
@@ -375,7 +404,7 @@ public class DataCreator {
entity = new EntityImpl();
entity.addProperty(createPrimitive("PropertyInt16", 0));
- entity.addProperty(createComplex("PropertyComp",
+ entity.addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyString", "Third Resource - third"),
createPrimitive("PropertyBinary",
new byte[] { 0x01, 0x23, 0x45, 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF }),
@@ -394,7 +423,9 @@ public class DataCreator {
createPrimitive("PropertySByte", Byte.MAX_VALUE),
createPrimitive("PropertyTimeOfDay", getTime(13, 27, 45))));
entitySet.getEntities().add(entity);
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETCompAllPrim.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -444,13 +475,15 @@ public class DataCreator {
entity.getProperties().addAll(entitySet.getEntities().get(0).getProperties());
entity.getProperties().set(0, createPrimitive("PropertyInt16", 3));
entitySet.getEntities().add(entity);
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETCollAllPrim.getFullQualifiedNameAsString());
+ }
return entitySet;
}
private EntitySet createESMixPrimCollComp() {
@SuppressWarnings("unchecked")
- final Property complexCollection = createComplexCollection("CollPropertyComp",
+ final Property complexCollection = createComplexCollection("CollPropertyComp", ctPropComp,
Arrays.asList(createPrimitive("PropertyInt16", 123), createPrimitive("PropertyString", "TEST 1")),
Arrays.asList(createPrimitive("PropertyInt16", 456), createPrimitive("PropertyString", "TEST 2")),
Arrays.asList(createPrimitive("PropertyInt16", 789), createPrimitive("PropertyString", "TEST 3")));
@@ -461,7 +494,7 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyInt16", Short.MAX_VALUE))
.addProperty(createPrimitiveCollection("CollPropertyString",
"Employee1@company.example", "Employee2@company.example", "Employee3@company.example"))
- .addProperty(createComplex("PropertyComp",
+ .addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 111),
createPrimitive("PropertyString", "TEST A")))
.addProperty(complexCollection));
@@ -470,7 +503,7 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyInt16", 7))
.addProperty(createPrimitiveCollection("CollPropertyString",
"Employee1@company.example", "Employee2@company.example", "Employee3@company.example"))
- .addProperty(createComplex("PropertyComp",
+ .addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 222),
createPrimitive("PropertyString", "TEST B")))
.addProperty(complexCollection));
@@ -479,11 +512,13 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyInt16", 0))
.addProperty(createPrimitiveCollection("CollPropertyString",
"Employee1@company.example", "Employee2@company.example", "Employee3@company.example"))
- .addProperty(createComplex("PropertyComp",
+ .addProperty(createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 333),
createPrimitive("PropertyString", "TEST C")))
.addProperty(complexCollection));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETMixPrimCollComp.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -519,7 +554,9 @@ public class DataCreator {
.addProperty(createPrimitive("PropertyDuration", 6))
.addProperty(createPrimitive("PropertyGuid", GUID))
.addProperty(createPrimitive("PropertyTimeOfDay", getTime(2, 48, 21))));
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETAllKey.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -528,20 +565,22 @@ public class DataCreator {
Entity entity = new EntityImpl();
entity.addProperty(createPrimitive("PropertyInt16", 1));
- entity.addProperty(createComplex("PropertyComp",
- createComplex("PropertyComp",
+ entity.addProperty(createComplex("PropertyComp", null,
+ createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 123),
createPrimitive("PropertyString", "String 1"))));
entitySet.getEntities().add(entity);
entity = new EntityImpl();
entity.addProperty(createPrimitive("PropertyInt16", 2));
- entity.addProperty(createComplex("PropertyComp",
- createComplex("PropertyComp",
+ entity.addProperty(createComplex("PropertyComp", null,
+ createComplex("PropertyComp",ctPropComp,
createPrimitive("PropertyInt16", 987),
createPrimitive("PropertyString", "String 2"))));
entitySet.getEntities().add(entity);
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETCompComp.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -571,7 +610,9 @@ public class DataCreator {
.addProperty(createPrimitive(DataProvider.MEDIA_PROPERTY_NAME, createImage("black")));
entity.setMediaContentType("image/svg+xml");
entitySet.getEntities().add(entity);
-
+ for (Entity en:entitySet.getEntities()) {
+ en.setType(EntityTypeProvider.nameETMedia.getFullQualifiedNameAsString());
+ }
return entitySet;
}
@@ -677,22 +718,23 @@ public class DataCreator {
return new PropertyImpl(null, name, ValueType.COLLECTION_PRIMITIVE, Arrays.asList(values));
}
- protected static Property createComplex(final String name, final Property... properties) {
+ protected static Property createComplex(final String name, String type, final Property... properties) {
ComplexValue complexValue = new ComplexValueImpl();
for (final Property property : properties) {
complexValue.getValue().add(property);
}
- return new PropertyImpl(null, name, ValueType.COMPLEX, complexValue);
+ return new PropertyImpl(type, name, ValueType.COMPLEX, complexValue);
}
- protected static Property createComplexCollection(final String name, final List... propertiesList) {
+ protected static Property createComplexCollection(final String name, String type,
+ final List... propertiesList) {
List complexCollection = new ArrayList();
for (final List properties : propertiesList) {
ComplexValue complexValue = new ComplexValueImpl();
complexValue.getValue().addAll(properties);
complexCollection.add(complexValue);
}
- return new PropertyImpl(null, name, ValueType.COLLECTION_COMPLEX, complexCollection);
+ return new PropertyImpl(type, name, ValueType.COLLECTION_COMPLEX, complexCollection);
}
private Calendar getDateTime(final int year, final int month, final int day,
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
index fed499fef..4fc9300e6 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/DataProvider.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
@@ -138,7 +138,7 @@ public class DataProvider {
final List entities = entitySet.getEntities();
final Map newKey = findFreeComposedKey(entities, edmEntitySet.getEntityType());
final Entity newEntity = new EntityImpl();
-
+ newEntity.setType(edmEntityType.getFullQualifiedName().getFullQualifiedNameAsString());
for (final String keyName : edmEntityType.getKeyPredicateNames()) {
newEntity.addProperty(DataCreator.createPrimitive(keyName, newKey.get(keyName)));
}
@@ -194,7 +194,8 @@ public class DataProvider {
return true;
}
- private void createProperties(final EdmStructuredType type, List properties) throws DataProviderException {
+ private void createProperties(final EdmStructuredType type, List properties)
+ throws DataProviderException {
final List keyNames = type instanceof EdmEntityType ?
((EdmEntityType) type).getKeyPredicateNames() : Collections. emptyList();
for (final String propertyName : type.getPropertyNames()) {
@@ -204,11 +205,11 @@ public class DataProvider {
}
}
}
-
- private Property createProperty(final EdmProperty edmProperty, final String propertyName)
+
+ private Property createProperty(final EdmProperty edmProperty, final String propertyName)
throws DataProviderException {
Property newProperty;
-
+
if (edmProperty.isPrimitive()) {
newProperty = edmProperty.isCollection() ?
DataCreator.createPrimitiveCollection(propertyName) :
@@ -216,17 +217,19 @@ public class DataProvider {
} else {
if (edmProperty.isCollection()) {
@SuppressWarnings("unchecked")
- Property newProperty2 = DataCreator.createComplexCollection(propertyName);
+ Property newProperty2 = DataCreator.createComplexCollection(propertyName, edmProperty
+ .getType().getFullQualifiedName().getFullQualifiedNameAsString());
newProperty = newProperty2;
} else {
- newProperty = DataCreator.createComplex(propertyName);
+ newProperty = DataCreator.createComplex(propertyName, edmProperty.getType()
+ .getFullQualifiedName().getFullQualifiedNameAsString());
createProperties((EdmComplexType) edmProperty.getType(), newProperty.asComplex().getValue());
}
}
-
+
return newProperty;
}
-
+
public void update(final String rawBaseUri, final EdmEntitySet edmEntitySet, Entity entity,
final Entity changedEntity, final boolean patch, final boolean isInsert) throws DataProviderException {
@@ -433,7 +436,7 @@ public class DataProvider {
}
}
- private ComplexValue createComplexValue(final EdmProperty edmProperty, final ComplexValue complexValue,
+ private ComplexValue createComplexValue(final EdmProperty edmProperty, final ComplexValue complexValue,
final boolean patch) throws DataProviderException {
final ComplexValueImpl result = new ComplexValueImpl();
final EdmComplexType edmType = (EdmComplexType) edmProperty.getType();
@@ -445,7 +448,7 @@ public class DataProvider {
final Property currentProperty = findProperty(propertyName, givenProperties);
final Property newProperty = createProperty(innerEdmProperty, propertyName);
result.getValue().add(newProperty);
-
+
if (currentProperty != null) {
updateProperty(innerEdmProperty, newProperty, currentProperty, patch);
} else {
@@ -459,7 +462,7 @@ public class DataProvider {
}
}
}
-
+
return result;
}
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/FunctionData.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/FunctionData.java
index 5451d5dd4..316a7b0d0 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/FunctionData.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/data/FunctionData.java
@@ -31,6 +31,7 @@ import org.apache.olingo.commons.core.data.EntitySetImpl;
import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.tecsvc.data.DataProvider.DataProviderException;
+import org.apache.olingo.server.tecsvc.provider.ComplexTypeProvider;
public class FunctionData {
@@ -80,12 +81,12 @@ public class FunctionData {
} else if (name.equals("UFCRTCollString")) {
return data.get("ESCollAllPrim").getEntities().get(0).getProperty("CollPropertyString");
} else if (name.equals("UFCRTCTTwoPrim")) {
- return DataCreator.createComplex(name,
+ return DataCreator.createComplex(name, ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString(),
DataCreator.createPrimitive("PropertyInt16", 16),
DataCreator.createPrimitive("PropertyString", "UFCRTCTTwoPrim string value"));
} else if (name.equals("UFCRTCTTwoPrimParam")) {
try {
- return DataCreator.createComplex(name,
+ return DataCreator.createComplex(name,ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString(),
DataCreator.createPrimitive("PropertyInt16",
EdmPrimitiveTypeFactory.getInstance(EdmPrimitiveTypeKind.Int16).valueOfString(
getParameterText("ParameterInt16", parameters),
@@ -99,7 +100,7 @@ public class FunctionData {
throw new DataProviderException("Error in function " + name + ".", e);
}
} else if (name.equals("UFCRTCollCTTwoPrim")) {
- return DataCreator.createComplexCollection(name,
+ return DataCreator.createComplexCollection(name,ComplexTypeProvider.nameCTTwoPrim.getFullQualifiedNameAsString(),
Arrays.asList(DataCreator.createPrimitive("PropertyInt16", 16),
DataCreator.createPrimitive("PropertyString", "Test123")),
Arrays.asList(DataCreator.createPrimitive("PropertyInt16", 17),
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
index f610fc2bb..841eabe11 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalEntityProcessor.java
@@ -6,9 +6,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -38,6 +38,7 @@ import org.apache.olingo.commons.core.data.EntitySetImpl;
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.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
import org.apache.olingo.server.api.processor.ActionEntityCollectionProcessor;
@@ -74,8 +75,11 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
EntityProcessor, ActionEntityProcessor, MediaEntityProcessor,
ActionVoidProcessor {
- public TechnicalEntityProcessor(final DataProvider dataProvider) {
+ private final ServiceMetadata serviceMetadata;
+
+ public TechnicalEntityProcessor(final DataProvider dataProvider, ServiceMetadata serviceMetadata) {
super(dataProvider);
+ this.serviceMetadata = serviceMetadata;
}
@Override
@@ -109,21 +113,24 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
entitySet,
edmEntitySet,
request.getRawRequestUri());
-
+
// Apply expand system query option
final ODataFormat format = ODataFormat.fromContentType(requestedContentType);
ODataSerializer serializer = odata.createSerializer(format);
final ExpandOption expand = uriInfo.getExpandOption();
final SelectOption select = uriInfo.getSelectOption();
-
+
// Create a shallow copy of each entity. So the expanded navigation properties can be modified for serialization,
// without affecting the data stored in the database.
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
final EntitySet entitySetSerialization = expandHandler.copyEntitySetShallowRekursive(entitySet);
expandHandler.applyExpandQueryOptions(entitySetSerialization, edmEntitySet, expand);
-
+
// Serialize
- response.setContent(serializer.entityCollection(edmEntityType, entitySetSerialization,
+ response.setContent(serializer.entityCollection(
+ this.serviceMetadata,
+ edmEntityType,
+ entitySetSerialization,
EntityCollectionSerializerOptions.with()
.contextURL(format == ODataFormat.JSON_NO_METADATA ? null :
getContextUrl(edmEntitySet, edmEntityType, false, expand, select))
@@ -170,17 +177,20 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
edmEntitySet.getEntityType();
final Entity entity = readEntity(uriInfo);
-
+
final ODataFormat format = ODataFormat.fromContentType(requestedContentType);
ODataSerializer serializer = odata.createSerializer(format);
final ExpandOption expand = uriInfo.getExpandOption();
final SelectOption select = uriInfo.getSelectOption();
-
+
final ExpandSystemQueryOptionHandler expandHandler = new ExpandSystemQueryOptionHandler();
final Entity entitySerialization = expandHandler.copyEntityShallowRekursive(entity);
expandHandler.applyExpandQueryOptions(entitySerialization, edmEntitySet, expand);
-
- response.setContent(serializer.entity(edmEntitySet.getEntityType(), entitySerialization,
+
+ response.setContent(serializer.entity(
+ this.serviceMetadata,
+ edmEntitySet.getEntityType(),
+ entitySerialization,
EntitySerializerOptions.with()
.contextURL(format == ODataFormat.JSON_NO_METADATA ? null :
getContextUrl(edmEntitySet, edmEntityType, true, expand, select))
@@ -233,7 +243,7 @@ public class TechnicalEntityProcessor extends TechnicalProcessor
final ODataFormat format = ODataFormat.fromContentType(responseFormat);
ODataSerializer serializer = odata.createSerializer(format);
- response.setContent(serializer.entity(edmEntityType, entity,
+ response.setContent(serializer.entity(this.serviceMetadata, edmEntityType, entity,
EntitySerializerOptions.with()
.contextURL(format == ODataFormat.JSON_NO_METADATA ? null :
getContextUrl(edmEntitySet, edmEntityType, true, null, null))
diff --git a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
index e36dc6b7c..b853e488f 100644
--- a/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.java
+++ b/lib/server-tecsvc/src/main/java/org/apache/olingo/server/tecsvc/processor/TechnicalPrimitiveComplexProcessor.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
@@ -44,6 +44,7 @@ import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
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.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.processor.ActionComplexCollectionProcessor;
import org.apache.olingo.server.api.processor.ActionComplexProcessor;
@@ -81,8 +82,12 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
ComplexProcessor, ActionComplexProcessor,
ComplexCollectionProcessor, ActionComplexCollectionProcessor {
- public TechnicalPrimitiveComplexProcessor(final DataProvider dataProvider) {
+ private final ServiceMetadata serviceMetadata;
+
+ public TechnicalPrimitiveComplexProcessor(final DataProvider dataProvider,
+ ServiceMetadata serviceMetadata) {
super(dataProvider);
+ this.serviceMetadata = serviceMetadata;
}
@Override
@@ -246,7 +251,7 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
.build()));
break;
case COMPLEX:
- response.setContent(serializer.complex((EdmComplexType) type, property,
+ response.setContent(serializer.complex(this.serviceMetadata,(EdmComplexType) type, property,
ComplexSerializerOptions.with().contextURL(contextURL)
.expand(expand).select(select)
.build()));
@@ -262,7 +267,7 @@ public class TechnicalPrimitiveComplexProcessor extends TechnicalProcessor
.build()));
break;
case COLLECTION_COMPLEX:
- response.setContent(serializer.complexCollection((EdmComplexType) type, property,
+ response.setContent(serializer.complexCollection(this.serviceMetadata, (EdmComplexType) type, property,
ComplexSerializerOptions.with().contextURL(contextURL)
.expand(expand).select(select)
.build()));
diff --git a/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java b/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java
index 76edb3333..4549a0349 100644
--- a/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java
+++ b/lib/server-tecsvc/src/test/java/org/apache/olingo/server/tecsvc/data/DataProviderTest.java
@@ -22,9 +22,9 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntitySet;
-import org.apache.olingo.commons.api.data.ComplexValue;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntityContainer;
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java
index cd421f43e..870f7c3bb 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/deserializer/json/ODataJsonDeserializerEntityTest.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
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
index 038c6686b..ee3868473 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/serializer/json/ODataJsonSerializerTest.java
@@ -41,11 +41,12 @@ import org.apache.olingo.commons.api.edm.FullQualifiedName;
import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.commons.core.data.PropertyImpl;
import org.apache.olingo.server.api.OData;
+import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.edmx.EdmxReference;
import org.apache.olingo.server.api.serializer.ComplexSerializerOptions;
import org.apache.olingo.server.api.serializer.EntityCollectionSerializerOptions;
-import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.EntitySerializerOptions;
+import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.PrimitiveSerializerOptions;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.uri.UriHelper;
@@ -64,9 +65,9 @@ import org.junit.Test;
import org.mockito.Mockito;
public class ODataJsonSerializerTest {
-
- private static final Edm edm = OData.newInstance().createServiceMetadata(
- new EdmTechProvider(), Collections. emptyList()).getEdm();
+ private static final ServiceMetadata metadata = OData.newInstance().createServiceMetadata(
+ new EdmTechProvider(), Collections. emptyList());
+ private static final Edm edm = metadata.getEdm();
private static final EdmEntityContainer entityContainer = edm.getEntityContainer(
new FullQualifiedName("olingo.odata.test1", "Container"));
private final DataProvider data = new DataProvider();
@@ -77,7 +78,7 @@ public class ODataJsonSerializerTest {
public void entitySimple() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build());
@@ -109,7 +110,8 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.getProperties().retainAll(Arrays.asList(entity.getProperties().get(0)));
- final String resultString = IOUtils.toString(serializer.entity(edmEntitySet.getEntityType(), entity,
+ final String resultString = IOUtils.toString(serializer.entity(metadata, edmEntitySet.getEntityType(),
+ entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build()));
@@ -130,7 +132,7 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESAllPrim");
Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.getProperties().clear();
- serializer.entity(edmEntitySet.getEntityType(), entity,
+ serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build());
@@ -142,7 +144,7 @@ public class ODataJsonSerializerTest {
Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.getProperties().get(0).setValue(ValueType.PRIMITIVE, false);
try {
- serializer.entity(edmEntitySet.getEntityType(), entity,
+ serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build());
@@ -163,7 +165,7 @@ public class ODataJsonSerializerTest {
entitySet.setNext(URI.create("/next"));
CountOption countOption = Mockito.mock(CountOption.class);
Mockito.when(countOption.getValue()).thenReturn(true);
- InputStream result = serializer.entityCollection(edmEntitySet.getEntityType(), entitySet,
+ InputStream result = serializer.entityCollection(metadata, edmEntitySet.getEntityType(), entitySet,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build())
.count(countOption)
@@ -188,7 +190,7 @@ public class ODataJsonSerializerTest {
public void entityCollAllPrim() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCollAllPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().serviceRoot(URI.create("http://host/service/"))
.entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
@@ -224,7 +226,7 @@ public class ODataJsonSerializerTest {
public void entityCompAllPrim() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESCompAllPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build());
@@ -257,7 +259,7 @@ public class ODataJsonSerializerTest {
public void entityMixPrimCollComp() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build());
@@ -280,7 +282,7 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMixPrimCollComp");
Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.getProperties().retainAll(Arrays.asList(entity.getProperties().get(0)));
- final String resultString = IOUtils.toString(serializer.entity(edmEntitySet.getEntityType(), entity,
+ final String resultString = IOUtils.toString(serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build()));
@@ -295,7 +297,7 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim");
final Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
InputStream result = new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA)
- .entity(edmEntitySet.getEntityType(), entity, null);
+ .entity(metadata, edmEntitySet.getEntityType(), entity, null);
final String resultString = IOUtils.toString(result);
final String expectedResult = "{\"PropertyInt16\":32766,\"PropertyString\":\"Test String1\"}";
Assert.assertEquals(expectedResult, resultString);
@@ -306,7 +308,7 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESTwoPrim");
final EntitySet entitySet = data.readAll(edmEntitySet);
InputStream result = new ODataJsonSerializer(ODataFormat.JSON_NO_METADATA)
- .entityCollection(edmEntitySet.getEntityType(), entitySet,
+ .entityCollection(metadata, edmEntitySet.getEntityType(), entitySet,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build()).build());
final String resultString = IOUtils.toString(result);
@@ -323,7 +325,8 @@ public class ODataJsonSerializerTest {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMedia");
Entity entity = data.readAll(edmEntitySet).getEntities().get(0);
entity.setMediaETag("theMediaETag");
- final String resultString = IOUtils.toString(serializer.entity(edmEntitySet.getEntityType(), entity,
+ final String resultString = IOUtils.toString(serializer.entity(metadata, edmEntitySet.getEntityType(),
+ entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.build()));
@@ -337,7 +340,8 @@ public class ODataJsonSerializerTest {
public void entitySetMedia() throws Exception {
final EdmEntitySet edmEntitySet = entityContainer.getEntitySet("ESMedia");
final EntitySet entitySet = data.readAll(edmEntitySet);
- final String resultString = IOUtils.toString(serializer.entityCollection(edmEntitySet.getEntityType(), entitySet,
+ final String resultString = IOUtils.toString(serializer.entityCollection(metadata,
+ edmEntitySet.getEntityType(), entitySet,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).build()).build()));
final String expectedResult = "{\"@odata.context\":\"$metadata#ESMedia\",\"value\":["
@@ -358,7 +362,7 @@ public class ODataJsonSerializerTest {
final SelectOption select = ExpandSelectMock.mockSelectOption(Arrays.asList(
selectItem1, selectItem2, selectItem2));
InputStream result = serializer
- .entity(entityType, entity,
+ .entity(metadata, entityType, entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, null, select))
@@ -380,7 +384,7 @@ public class ODataJsonSerializerTest {
SelectItem selectItem2 = Mockito.mock(SelectItem.class);
Mockito.when(selectItem2.isStar()).thenReturn(true);
final SelectOption select = ExpandSelectMock.mockSelectOption(Arrays.asList(selectItem1, selectItem2));
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.select(select)
@@ -399,7 +403,7 @@ public class ODataJsonSerializerTest {
final SelectOption select = ExpandSelectMock.mockSelectOption(Arrays.asList(
ExpandSelectMock.mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp", "PropertyString")));
InputStream result = serializer
- .entityCollection(entityType, entitySet,
+ .entityCollection(metadata, entityType, entitySet,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, null, select))
@@ -424,7 +428,7 @@ public class ODataJsonSerializerTest {
ExpandSelectMock.mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp", "PropertyString"),
ExpandSelectMock.mockSelectItem(edmEntitySet, "PropertyComp", "PropertyComp")));
final String resultString = IOUtils.toString(serializer
- .entityCollection(entityType, entitySet,
+ .entityCollection(metadata, entityType, entitySet,
EntityCollectionSerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, null, select))
@@ -445,7 +449,7 @@ public class ODataJsonSerializerTest {
final Entity entity = data.readAll(edmEntitySet).getEntities().get(3);
final ExpandOption expand = ExpandSelectMock.mockExpandOption(Arrays.asList(
ExpandSelectMock.mockExpandItem(edmEntitySet, "NavPropertyETAllPrimOne")));
- InputStream result = serializer.entity(edmEntitySet.getEntityType(), entity,
+ InputStream result = serializer.entity(metadata, edmEntitySet.getEntityType(), entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet).suffix(Suffix.ENTITY).build())
.expand(expand)
@@ -484,7 +488,7 @@ public class ODataJsonSerializerTest {
Mockito.when(expandItem.getSelectOption()).thenReturn(select);
final ExpandOption expand = ExpandSelectMock.mockExpandOption(Arrays.asList(expandItem));
final String resultString = IOUtils.toString(serializer
- .entity(entityType, entity,
+ .entity(metadata, entityType, entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, expand, select))
@@ -511,7 +515,7 @@ public class ODataJsonSerializerTest {
final SelectOption select = ExpandSelectMock.mockSelectOption(Arrays.asList(
ExpandSelectMock.mockSelectItem(edmEntitySet, "PropertySByte")));
final String resultString = IOUtils.toString(serializer
- .entity(entityType, entity,
+ .entity(metadata, entityType, entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, expand, select))
@@ -538,7 +542,7 @@ public class ODataJsonSerializerTest {
final SelectOption select = ExpandSelectMock.mockSelectOption(Arrays.asList(
ExpandSelectMock.mockSelectItem(edmEntitySet, "PropertyTimeOfDay")));
final String resultString = IOUtils.toString(serializer
- .entity(entityType, entity,
+ .entity(metadata, entityType, entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, expand, select))
@@ -569,7 +573,7 @@ public class ODataJsonSerializerTest {
Mockito.when(expandItemFirst.getSelectOption()).thenReturn(select);
final ExpandOption expand = ExpandSelectMock.mockExpandOption(Arrays.asList(expandItemFirst));
final String resultString = IOUtils.toString(serializer
- .entity(entityType, entity,
+ .entity(metadata, entityType, entity,
EntitySerializerOptions.with()
.contextURL(ContextURL.with().entitySet(edmEntitySet)
.selectList(helper.buildContextURLSelectList(entityType, expand, select))
@@ -646,7 +650,7 @@ public class ODataJsonSerializerTest {
final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty("PropertyComp");
final String resultString = IOUtils.toString(serializer
- .complex((EdmComplexType) edmProperty.getType(), property,
+ .complex(metadata, (EdmComplexType) edmProperty.getType(), property,
ComplexSerializerOptions.with()
.contextURL(ContextURL.with()
.entitySet(edmEntitySet).keyPath("32767").navOrPropertyPath(edmProperty.getName())
@@ -665,7 +669,7 @@ public class ODataJsonSerializerTest {
final Property property = data.readAll(edmEntitySet).getEntities().get(0).getProperty(edmProperty.getName());
final String resultString = IOUtils.toString(serializer
- .complexCollection((EdmComplexType) edmProperty.getType(), property,
+ .complexCollection(metadata, (EdmComplexType) edmProperty.getType(), property,
ComplexSerializerOptions.with()
.contextURL(ContextURL.with()
.entitySet(edmEntitySet).keyPath("32767").navOrPropertyPath(edmProperty.getName())
diff --git a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
index 798c5c0a5..86cbf0eb1 100644
--- a/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.java
+++ b/lib/server-test/src/test/java/org/apache/olingo/server/core/uri/antlr/TestUriParserImpl.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
@@ -1064,13 +1064,16 @@ public class TestUriParserImpl {
public void testAlias() throws Exception {
testUri.run("ESAllPrim", "$filter=PropertyInt16 eq @p1&@p1=1)")
.goFilter().is("< eq <@p1>>");
- }
-
+ }
+
@Test
public void testLambda() throws Exception {
testUri.run("ESTwoKeyNav", "$filter=CollPropertyComp/all( l : true )")
.goFilter().is("