Repository: olingo-odata4 Updated Branches: refs/heads/master 74fe42e54 -> c0e032d48
[OLINGO-713] Proposal Tutorial - System Query Option $filter Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/c0e032d4 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/c0e032d4 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/c0e032d4 Branch: refs/heads/master Commit: c0e032d48505c6d50bf42f3008fdd6f120eaf619 Parents: 74fe42e Author: Christian Holzer <[email protected]> Authored: Fri Aug 21 14:56:45 2015 +0200 Committer: Christian Holzer <[email protected]> Committed: Fri Aug 21 15:04:20 2015 +0200 ---------------------------------------------------------------------- samples/tutorials/p7_queryoptions-f/pom.xml | 80 +++++ .../myservice/mynamespace/data/Storage.java | 125 ++++++++ .../mynamespace/service/DemoEdmProvider.java | 153 +++++++++ .../service/DemoEntityCollectionProcessor.java | 134 ++++++++ .../service/DemoEntityProcessor.java | 126 ++++++++ .../service/DemoPrimitiveProcessor.java | 147 +++++++++ .../service/FilterExpressionVisitor.java | 308 +++++++++++++++++++ .../java/myservice/mynamespace/util/Util.java | 120 ++++++++ .../myservice/mynamespace/web/DemoServlet.java | 76 +++++ .../src/main/webapp/WEB-INF/web.xml | 40 +++ .../p7_queryoptions-f/src/main/webapp/index.jsp | 24 ++ 11 files changed, 1333 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/pom.xml ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/pom.xml b/samples/tutorials/p7_queryoptions-f/pom.xml new file mode 100644 index 0000000..439a37c --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/pom.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>my.group.id</groupId> + <artifactId>DemoService-QueryOptions-F</artifactId> + <packaging>war</packaging> + <version>0.0.1</version> + + <name>${project.artifactId} Webapp</name> + + <build> + <finalName>DemoService</finalName> + </build> + + <properties> + <javax.version>2.5</javax.version> + <odata.version>4.0.0-beta-03</odata.version> + <slf4j.version>1.7.7</slf4j.version> + </properties> + + <dependencies> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>${javax.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.olingo</groupId> + <artifactId>odata-server-api</artifactId> + <version>${odata.version}</version> + </dependency> + <dependency> + <groupId>org.apache.olingo</groupId> + <artifactId>odata-server-core</artifactId> + <version>${odata.version}</version> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.apache.olingo</groupId> + <artifactId>odata-commons-api</artifactId> + <version>${odata.version}</version> + </dependency> + <dependency> + <groupId>org.apache.olingo</groupId> + <artifactId>odata-commons-core</artifactId> + <version>${odata.version}</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <version>${slf4j.version}</version> + <scope>runtime</scope> + </dependency> + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/data/Storage.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/data/Storage.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/data/Storage.java new file mode 100644 index 0000000..d5d16c2 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/data/Storage.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 myservice.mynamespace.data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import myservice.mynamespace.service.DemoEdmProvider; +import myservice.mynamespace.util.Util; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.data.ValueType; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmEntityType; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.uri.UriParameter; + +public class Storage { + + private List<Entity> productList; + + public Storage() { + productList = new ArrayList<Entity>(); + initSampleData(); + } + + /* PUBLIC FACADE */ + + public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet)throws ODataApplicationException{ + + // actually, this is only required if we have more than one Entity Sets + if(edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)){ + return getProducts(); + } + + return null; + } + + public Entity readEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams) throws ODataApplicationException{ + + EdmEntityType edmEntityType = edmEntitySet.getEntityType(); + + // actually, this is only required if we have more than one Entity Type + if(edmEntityType.getName().equals(DemoEdmProvider.ET_PRODUCT_NAME)){ + return getProduct(edmEntityType, keyParams); + } + + return null; + } + + + + /* INTERNAL */ + + private EntityCollection getProducts(){ + EntityCollection retEntitySet = new EntityCollection(); + + for(Entity productEntity : this.productList){ + retEntitySet.getEntities().add(productEntity); + } + + return retEntitySet; + } + + + private Entity getProduct(EdmEntityType edmEntityType, List<UriParameter> keyParams) throws ODataApplicationException{ + + // the list of entities at runtime + EntityCollection entitySet = getProducts(); + + /* generic approach to find the requested entity */ + Entity requestedEntity = Util.findEntity(edmEntityType, entitySet, keyParams); + + if(requestedEntity == null){ + // this variable is null if our data doesn't contain an entity for the requested key + // Throw suitable exception + throw new ODataApplicationException("Entity for requested key doesn't exist", + HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH); + } + + return requestedEntity; + } + + /* HELPER */ + + private void initSampleData(){ + + // add some sample product entities + productList.add(new Entity() + .addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 1)) + .addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Notebook Basic 15")) + .addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Notebook Basic, 1.7GHz - 15 XGA - 1024MB DDR2 SDRAM - 40GB"))); + + productList.add(new Entity() + .addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 2)) + .addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "1UMTS PDA")) + .addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Ultrafast 3G UMTS/HSDPA Pocket PC, supports GSM network"))); + + productList.add(new Entity() + .addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 3)) + .addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Ergo Screen")) + .addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "19 Optimum Resolution 1024 x 768 @ 85Hz, resolution 1280 x 960"))); + + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java new file mode 100644 index 0000000..15140e1 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java @@ -0,0 +1,153 @@ +/* + * 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 myservice.mynamespace.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.olingo.commons.api.ODataException; +import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; +import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.edm.provider.CsdlAbstractEdmProvider; +import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainer; +import org.apache.olingo.commons.api.edm.provider.CsdlEntityContainerInfo; +import org.apache.olingo.commons.api.edm.provider.CsdlEntitySet; +import org.apache.olingo.commons.api.edm.provider.CsdlEntityType; +import org.apache.olingo.commons.api.edm.provider.CsdlProperty; +import org.apache.olingo.commons.api.edm.provider.CsdlPropertyRef; +import org.apache.olingo.commons.api.edm.provider.CsdlSchema; + +public class DemoEdmProvider extends CsdlAbstractEdmProvider { + + // Service Namespace + public static final String NAMESPACE = "OData.Demo"; + + // EDM Container + public static final String CONTAINER_NAME = "Container"; + public static final FullQualifiedName CONTAINER = new FullQualifiedName(NAMESPACE, CONTAINER_NAME); + + // Entity Types Names + public static final String ET_PRODUCT_NAME = "Product"; + public static final FullQualifiedName ET_PRODUCT_FQN = new FullQualifiedName(NAMESPACE, ET_PRODUCT_NAME); + + // Entity Set Names + public static final String ES_PRODUCTS_NAME = "Products"; + + + @Override + public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) + throws ODataException { + // this method is called for one of the EntityTypes that are configured in the Schema + if(ET_PRODUCT_FQN.equals(entityTypeName)){ + + //create EntityType properties + CsdlProperty id = new CsdlProperty().setName("ID").setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName()); + CsdlProperty name = new CsdlProperty().setName("Name").setType(EdmPrimitiveTypeKind.String.getFullQualifiedName()); + CsdlProperty description = new CsdlProperty().setName("Description").setType(EdmPrimitiveTypeKind.String.getFullQualifiedName()); + + // create PropertyRef for Key element + CsdlPropertyRef propertyRef = new CsdlPropertyRef(); + propertyRef.setName("ID"); + + // configure EntityType + CsdlEntityType entityType = new CsdlEntityType(); + entityType.setName(ET_PRODUCT_NAME); + entityType.setProperties(Arrays.asList(id, name, description)); + entityType.setKey(Collections.singletonList(propertyRef)); + + return entityType; + } + + return null; + + } + + @Override + public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, + String entitySetName) throws ODataException { + if(entityContainer.equals(CONTAINER)){ + if(entitySetName.equals(ES_PRODUCTS_NAME)){ + CsdlEntitySet entitySet = new CsdlEntitySet(); + entitySet.setName(ES_PRODUCTS_NAME); + entitySet.setType(ET_PRODUCT_FQN); + + return entitySet; + } + } + + return null; + + } + + @Override + public CsdlEntityContainerInfo getEntityContainerInfo( + FullQualifiedName entityContainerName) throws ODataException { + // This method is invoked when displaying the service document at e.g. http://localhost:8080/DemoService/DemoService.svc + if(entityContainerName == null || entityContainerName.equals(CONTAINER)){ + CsdlEntityContainerInfo entityContainerInfo = new CsdlEntityContainerInfo(); + entityContainerInfo.setContainerName(CONTAINER); + return entityContainerInfo; + } + + return null; + + } + + @Override + public List<CsdlSchema> getSchemas() throws ODataException { + // create Schema + CsdlSchema schema = new CsdlSchema(); + schema.setNamespace(NAMESPACE); + + // add EntityTypes + List<CsdlEntityType> entityTypes = new ArrayList<CsdlEntityType>(); + entityTypes.add(getEntityType(ET_PRODUCT_FQN)); + schema.setEntityTypes(entityTypes); + + // add EntityContainer + schema.setEntityContainer(getEntityContainer()); + + // finally + List<CsdlSchema> schemas = new ArrayList<CsdlSchema>(); + schemas.add(schema); + + return schemas; + + } + + @Override + public CsdlEntityContainer getEntityContainer() throws ODataException { + // create EntitySets + List<CsdlEntitySet> entitySets = new ArrayList<CsdlEntitySet>(); + entitySets.add(getEntitySet(CONTAINER, ES_PRODUCTS_NAME)); + + // create EntityContainer + CsdlEntityContainer entityContainer = new CsdlEntityContainer(); + entityContainer.setName(CONTAINER_NAME); + entityContainer.setEntitySets(entitySets); + + return entityContainer; + + } + + + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java new file mode 100644 index 0000000..292a0c4 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java @@ -0,0 +1,134 @@ +/* + * 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 myservice.mynamespace.service; + +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import myservice.mynamespace.data.Storage; + +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +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.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.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.processor.EntityCollectionProcessor; +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.api.serializer.SerializerResult; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.queryoption.FilterOption; +import org.apache.olingo.server.api.uri.queryoption.expression.Expression; +import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException; + +public class DemoEntityCollectionProcessor implements EntityCollectionProcessor { + + private OData odata; + private ServiceMetadata serviceMetadata; + private Storage storage; + + public DemoEntityCollectionProcessor(Storage storage) { + this.storage = storage; + } + + public void init(OData odata, ServiceMetadata serviceMetadata) { + this.odata = odata; + this.serviceMetadata = serviceMetadata; + } + + public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) throws ODataApplicationException, SerializerException { + + // 1st: retrieve the requested EntitySet from the uriInfo (representation of the parsed URI) + List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); + UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); // in our example, the first segment is the EntitySet + EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet(); + + // 2nd: fetch the data from backend for this requested EntitySetName and deliver as EntitySet + EntityCollection entityCollection = storage.readEntitySetData(edmEntitySet); + + // 3rd: Check if filter system query option is provided and apply the expression if necessary + FilterOption filterOption = uriInfo.getFilterOption(); + if(filterOption != null) { + // Apply $filter system query option + try { + List<Entity> entityList = entityCollection.getEntities(); + Iterator<Entity> entityIterator = entityList.iterator(); + + // Evaluate the expression for each entity + // If the expression is evaluated to "true", keep the entity otherwise remove it from the entityList + while (entityIterator.hasNext()) { + // To evaluate the the expression, create an instance of the Filter Expression Visitor and pass + // the current entity to the constructor + Entity currentEntity = entityIterator.next(); + Expression filterExpression = filterOption.getExpression(); + FilterExpressionVisitor expressionVisitor = new FilterExpressionVisitor(currentEntity); + + // Start evaluating the expression + Object visitorResult = filterExpression.accept(expressionVisitor); + + // The result of the filter expression must be of type Edm.Boolean + if(visitorResult instanceof Boolean) { + if(visitorResult != null && Boolean.FALSE.equals(visitorResult)) { + // The expression evaluated to false, so we have to remove the currentEntity from entityList + entityIterator.remove(); + } + } else { + throw new ODataApplicationException("A filter expression must evaulate to type Edm.Boolean", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + } + + } catch (ExpressionVisitException e) { + throw new ODataApplicationException("Exception in filter evaluation", + HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH); + } + } + + // 4th: create a serializer based on the requested format (json) + ODataFormat format = ODataFormat.fromContentType(responseFormat); + ODataSerializer serializer = odata.createSerializer(format); + + // and serialize the content: transform from the EntitySet object to InputStream + EdmEntityType edmEntityType = edmEntitySet.getEntityType(); + ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build(); + + EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with().contextURL(contextUrl).build(); + SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, entityCollection, opts); + InputStream serializedContent = serializerResult.getContent(); + + // 5th: configure the response object: set the body, headers and status code + response.setContent(serializedContent); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java new file mode 100644 index 0000000..e3c8767 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package myservice.mynamespace.service; + +import java.io.InputStream; +import java.util.List; +import java.util.Locale; + +import myservice.mynamespace.data.Storage; + +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +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.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.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.EntityProcessor; +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.serializer.SerializerResult; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriParameter; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; + +public class DemoEntityProcessor implements EntityProcessor { + + + private OData odata; + private ServiceMetadata serviceMetadata; + private Storage storage; + + + + public DemoEntityProcessor(Storage storage) { + this.storage = storage; + } + + + public void init(OData odata, ServiceMetadata serviceMetadata) { + this.odata = odata; + this.serviceMetadata = serviceMetadata; + } + + + public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) + throws ODataApplicationException, SerializerException { + + // 1. retrieve the Entity Type + List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); + // Note: only in our example we can assume that the first segment is the EntitySet + UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); + EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet(); + + // 2. retrieve the data from backend + List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates(); + Entity entity = storage.readEntityData(edmEntitySet, keyPredicates); + + // 3. serialize + EdmEntityType entityType = edmEntitySet.getEntityType(); + + ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).suffix(ContextURL.Suffix.ENTITY).build(); + // expand and select currently not supported + EntitySerializerOptions options = EntitySerializerOptions.with().contextURL(contextUrl).build(); + + ODataFormat oDataFormat = ODataFormat.fromContentType(responseFormat); + ODataSerializer serializer = this.odata.createSerializer(oDataFormat); + SerializerResult serializerResult = serializer.entity(serviceMetadata, entityType, entity, options); + InputStream entityStream = serializerResult.getContent(); + + //4. configure the response object + response.setContent(entityStream); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } + + + + + /* + * These processor methods are not handled in this tutorial + * */ + + + public void createEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestFormat, ContentType responseFormat) + throws ODataApplicationException, DeserializerException, SerializerException { + throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + + public void updateEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestFormat, ContentType responseFormat) + throws ODataApplicationException, DeserializerException, SerializerException { + throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + + public void deleteEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException { + throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoPrimitiveProcessor.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoPrimitiveProcessor.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoPrimitiveProcessor.java new file mode 100644 index 0000000..1ba0c75 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/DemoPrimitiveProcessor.java @@ -0,0 +1,147 @@ +/* + * 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 myservice.mynamespace.service; + +import java.io.InputStream; +import java.util.List; +import java.util.Locale; + +import myservice.mynamespace.data.Storage; + +import org.apache.olingo.commons.api.data.ContextURL; +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.Property; +import org.apache.olingo.commons.api.edm.EdmEntitySet; +import org.apache.olingo.commons.api.edm.EdmPrimitiveType; +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.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.ServiceMetadata; +import org.apache.olingo.server.api.deserializer.DeserializerException; +import org.apache.olingo.server.api.processor.PrimitiveProcessor; +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.serializer.SerializerResult; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriParameter; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourceEntitySet; +import org.apache.olingo.server.api.uri.UriResourceProperty; + +public class DemoPrimitiveProcessor implements PrimitiveProcessor { + + private OData odata; + private Storage storage; + + public DemoPrimitiveProcessor(Storage storage) { + this.storage = storage; + } + + public void init(OData odata, ServiceMetadata serviceMetadata) { + this.odata = odata; + + } + + /* + * In our example, the URL would be: http://localhost:8080/DemoService/DemoService.svc/Products(1)/Name + * and the response: + * { + * @odata.context: "$metadata#Products/Name", + * value: "Notebook Basic 15" + * } + * */ + public void readPrimitive(ODataRequest request, ODataResponse response, + UriInfo uriInfo, ContentType responseFormat) + throws ODataApplicationException, SerializerException { + + // 1. Retrieve info from URI + // 1.1. retrieve the info about the requested entity set + List<UriResource> resourceParts = uriInfo.getUriResourceParts(); + // Note: only in our example we can rely that the first segment is the EntitySet + UriResourceEntitySet uriEntityset = (UriResourceEntitySet) resourceParts.get(0); + EdmEntitySet edmEntitySet = uriEntityset.getEntitySet(); + // the key for the entity + List<UriParameter> keyPredicates = uriEntityset.getKeyPredicates(); + + // 1.2. retrieve the requested (Edm) property + UriResourceProperty uriProperty = (UriResourceProperty)resourceParts.get(resourceParts.size() -1); // the last segment is the Property + EdmProperty edmProperty = uriProperty.getProperty(); + String edmPropertyName = edmProperty.getName(); + // in our example, we know we have only primitive types in our model + EdmPrimitiveType edmPropertyType = (EdmPrimitiveType) edmProperty.getType(); + + + // 2. retrieve data from backend + // 2.1. retrieve the entity data, for which the property has to be read + Entity entity = storage.readEntityData(edmEntitySet, keyPredicates); + if (entity == null) { // Bad request + throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH); + } + + // 2.2. retrieve the property data from the entity + Property property = entity.getProperty(edmPropertyName); + if (property == null) { + throw new ODataApplicationException("Property not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH); + } + + // 3. serialize + Object value = property.getValue(); + if (value != null) { + // 3.1. configure the serializer + ODataFormat format = ODataFormat.fromContentType(responseFormat); + ODataSerializer serializer = odata.createSerializer(format); + + ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build(); + PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build(); + // 3.2. serialize + SerializerResult serializerResult = serializer.primitive(edmPropertyType, property, options); + InputStream propertyStream = serializerResult.getContent(); + + //4. configure the response object + response.setContent(propertyStream); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } else { + // in case there's no value for the property, we can skip the serialization + response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); + } + } + + + + /* + * These processor methods are not handled in this tutorial + * */ + + public void updatePrimitive(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType requestFormat, ContentType responseFormat) + throws ODataApplicationException, DeserializerException, SerializerException { + throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } + + public void deletePrimitive(ODataRequest request, ODataResponse response, UriInfo uriInfo) throws ODataApplicationException { + throw new ODataApplicationException("Not supported.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java new file mode 100644 index 0000000..7550497 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java @@ -0,0 +1,308 @@ +/* + * 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 myservice.mynamespace.service; + +import java.util.List; +import java.util.Locale; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.edm.EdmEnumType; +import org.apache.olingo.commons.api.edm.EdmType; +import org.apache.olingo.commons.api.http.HttpStatusCode; +import org.apache.olingo.server.api.ODataApplicationException; +import org.apache.olingo.server.api.uri.UriInfoResource; +import org.apache.olingo.server.api.uri.UriResource; +import org.apache.olingo.server.api.uri.UriResourcePrimitiveProperty; +import org.apache.olingo.server.api.uri.queryoption.expression.BinaryOperatorKind; +import org.apache.olingo.server.api.uri.queryoption.expression.Expression; +import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitException; +import org.apache.olingo.server.api.uri.queryoption.expression.ExpressionVisitor; +import org.apache.olingo.server.api.uri.queryoption.expression.MethodKind; +import org.apache.olingo.server.api.uri.queryoption.expression.UnaryOperatorKind; + +public class FilterExpressionVisitor implements ExpressionVisitor<Object> { + + private Entity currentEntity; + + public FilterExpressionVisitor(Entity currentEntity) { + this.currentEntity = currentEntity; + } + + @Override + public Object visitMember(UriInfoResource member) throws ExpressionVisitException, ODataApplicationException { + // To keeps things simple, this tutorial allows only primitive properties. + // We have faith that the java type of Edm.Int32 is Integer + + final List<UriResource> uriResourceParts = member.getUriResourceParts(); + + // Make sure that the resource path of the property contains only a single segment and a primitive property + // has been addressed. We can be sure, that the property exists because the UriParser checks if the + // property has been defined in service metadata document. + + if(uriResourceParts.size() == 1 && uriResourceParts.get(0) instanceof UriResourcePrimitiveProperty) { + UriResourcePrimitiveProperty uriResourceProperty = (UriResourcePrimitiveProperty) uriResourceParts.get(0); + return currentEntity.getProperty(uriResourceProperty.getProperty().getName()).getValue(); + + } else { + throw new ODataApplicationException("Only primitive properties are implemented in filter expressions", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + } + + @Override + public Object visitLiteral(String literal) throws ExpressionVisitException, ODataApplicationException { + // To keep this tutorial simple, our filter expression visitor supports only Edm.Int32 and Edm.String + // In real world scenarios it can be difficult to guess the type of an literal. + // We can be sure, that the literal is a valid OData literal because the URI Parser checks + // the lexicographical structure + + // String literals start and end with an single quotation mark + if(literal.startsWith("'") && literal.endsWith("'")) { + String stringLiteral = ""; + if(literal.length() > 2) { + stringLiteral = literal.substring(1, literal.length() - 1); + } + + return stringLiteral; + } else { + // Try to convert the literal into an Java Integer + try { + return Integer.parseInt(literal); + } catch(NumberFormatException e) { + throw new ODataApplicationException("Only Edm.Int32 and Edm.String literals are implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + } + } + + @Override + public Object visitUnaryOperator(UnaryOperatorKind operator, Object operand) + throws ExpressionVisitException, ODataApplicationException { + // OData allows two different unary operators. We have to take care, that the type of the operand fits to + // operand + + if(operator == UnaryOperatorKind.NOT && operand instanceof Boolean) { + // 1.) boolean negation + return !(Boolean) operand; + } else if(operator == UnaryOperatorKind.MINUS && operand instanceof Integer){ + // 2.) arithmetic minus + return -(Integer) operand; + } + + // Operation not processed, throw an exception + throw new ODataApplicationException("Invalid type for unary operator", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + + @Override + public Object visitBinaryOperator(BinaryOperatorKind operator, Object left, Object right) + throws ExpressionVisitException, ODataApplicationException { + + // Binary Operators are split up in three different kinds. Up to the kind of the operator it can be applied + // to different types + // - Arithmetic operations like add, minus, modulo, etc. are allowed on numeric types like Edm.Int32 + // - Logical operations are allowed on numeric types and also Edm.String + // - Boolean operations like and, or are allowed on Edm.Boolean + // A detailed explanation can be found in OData Version 4.0 Part 2: URL Conventions + + switch (operator) { + + // Arithmetic operations + case ADD: + /** Fall through **/ + case MOD: + /** Fall through **/ + case MUL: + /** Fall through **/ + case DIV: + /** Fall through **/ + case SUB: + return evaluateArithmeticOperation(operator, left, right); + + // Logical operations + case EQ: + /** Fall through **/ + case NE: + /** Fall through **/ + case GE: + /** Fall through **/ + case GT: + /** Fall through **/ + case LE: + /** Fall through **/ + case LT: + return evaluateLogicalOperation(operator, left, right); + + // Boolean operations + case AND: + /** Fall through **/ + case OR: + return evaluateBooleanOperation(operator, left, right); + + case HAS: + // Has operation is not supported. We do not use enums in our service. + /** fall through **/ + default: + throw new ODataApplicationException("Binary operation " + operator.name() + " is not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + } + + private Object evaluateBooleanOperation(BinaryOperatorKind operator, Object left, Object right) + throws ODataApplicationException { + + // First check that both operands are of type Boolean + if(left instanceof Boolean && right instanceof Boolean) { + Boolean valueLeft = (Boolean) left; + Boolean valueRight = (Boolean) right; + + // Than calculate the result value + if(operator == BinaryOperatorKind.AND) { + return valueLeft && valueRight; + } else { + // OR + return valueLeft || valueRight; + } + } else { + throw new ODataApplicationException("Boolean operations needs two numeric operands", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + } + + @SuppressWarnings("unchecked") + private Object evaluateLogicalOperation(BinaryOperatorKind operator, Object left, Object right) + throws ODataApplicationException { + + // All types in our tutorial supports all logical operations, but we have to make sure that the types are equals + if(left.getClass().equals(right.getClass())) { + // Luckily all used types String, Boolean and also Integer supports the interface Comparable + // TODO: Is this OK? Otherwise we can infer generic arguments after using an if statement + @SuppressWarnings("rawtypes") + int result = ((Comparable) left).compareTo(right); + + if (operator == BinaryOperatorKind.EQ) { + return result == 0; + } else if (operator == BinaryOperatorKind.NE) { + return result != 0; + } else if (operator == BinaryOperatorKind.GE) { + return result >= 0; + } else if (operator == BinaryOperatorKind.GT) { + return result > 0; + } else if (operator == BinaryOperatorKind.LE) { + return result <= 0; + } else { + // BinaryOperatorKind.LT + return result < 0; + } + + } else { + throw new ODataApplicationException("Comparision needs to equal types", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + } + + private Object evaluateArithmeticOperation(BinaryOperatorKind operator, Object left, + Object right) throws ODataApplicationException { + + // First check if the type of of both operands is numerical + if(left instanceof Integer && right instanceof Integer) { + Integer valueLeft = (Integer) left; + Integer valueRight = (Integer) right; + + // Than calculate the result value + if(operator == BinaryOperatorKind.ADD) { + return valueLeft + valueRight; + } else if(operator == BinaryOperatorKind.SUB) { + return valueLeft - valueRight; + } else if(operator == BinaryOperatorKind.MUL) { + return valueLeft * valueRight; + } else if(operator == BinaryOperatorKind.DIV) { + return valueLeft / valueRight; + } else { + // BinaryOperatorKind,MOD + return valueLeft % valueRight; + } + } else { + throw new ODataApplicationException("Arithmetic operations needs two numeric operands", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + } + + @Override + public Object visitMethodCall(MethodKind methodCall, List<Object> parameters) + throws ExpressionVisitException, ODataApplicationException { + + // To keep this tutorial small and simple, we implement only one method call + if(methodCall == MethodKind.CONTAINS) { + // "Contains" gets two parameters, both have to be of type String + // e.g. /Products?$filter=contains(Description, '1024 MB') + // + // First the method visistMember is called, which returns the current String value of the property. + // After that the method visitLiteral is called with the string literal '1024 MB', + // which returns a String + // + // Both String values are passed to visitMethodCall. + if(parameters.get(0) instanceof String && parameters.get(1) instanceof String) { + String valueParam1 = (String) parameters.get(0); + String valueParam2 = (String) parameters.get(1); + + return valueParam1.contains(valueParam2); + } else { + throw new ODataApplicationException("Contains needs two parametes of type Edm.String", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); + } + } else { + throw new ODataApplicationException("Method call " + methodCall + " not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + } + + @Override + public Object visitTypeLiteral(EdmType type) throws ExpressionVisitException, ODataApplicationException { + throw new ODataApplicationException("Type literals are not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + + @Override + public Object visitAlias(String aliasName) throws ExpressionVisitException, ODataApplicationException { + throw new ODataApplicationException("Aliases are not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + + @Override + public Object visitEnum(EdmEnumType type, List<String> enumValues) + throws ExpressionVisitException, ODataApplicationException { + throw new ODataApplicationException("Enums are not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + + @Override + public Object visitLambdaExpression(String lambdaFunction, String lambdaVariable, Expression expression) + throws ExpressionVisitException, ODataApplicationException { + throw new ODataApplicationException("Lamdba expressions are not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + + @Override + public Object visitLambdaReference(String variableName) + throws ExpressionVisitException, ODataApplicationException { + throw new ODataApplicationException("Lamdba references are not implemented", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/util/Util.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/util/Util.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/util/Util.java new file mode 100644 index 0000000..16c7745 --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/util/Util.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 myservice.mynamespace.util; + +import java.util.List; +import java.util.Locale; + +import org.apache.olingo.commons.api.data.Entity; +import org.apache.olingo.commons.api.data.EntityCollection; +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.http.HttpStatusCode; +import org.apache.olingo.server.api.ODataApplicationException; +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.UriResourceEntitySet; + +public class Util { + + public static EdmEntitySet getEdmEntitySet(UriInfoResource uriInfo) throws ODataApplicationException { + List<UriResource> resourcePaths = uriInfo.getUriResourceParts(); + // To get the entity set we have to interpret all URI segments + if (!(resourcePaths.get(0) instanceof UriResourceEntitySet)) { + // Here we should interpret the whole URI but in this example we do not support navigation so we throw an exception + throw new ODataApplicationException("Invalid resource type for first segment.", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),Locale.ENGLISH); + } + + UriResourceEntitySet uriResource = (UriResourceEntitySet) resourcePaths.get(0); + + return uriResource.getEntitySet(); + } + + + public static Entity findEntity(EdmEntityType edmEntityType, EntityCollection entitySet, List<UriParameter> keyParams) throws ODataApplicationException { + + List<Entity> entityList = entitySet.getEntities(); + + // loop over all entities in order to find that one that matches all keys in request e.g. contacts(ContactID=1, CompanyID=1) + for(Entity entity : entityList){ + boolean foundEntity = entityMatchesAllKeys(edmEntityType, entity, keyParams); + if(foundEntity) { + return entity; + } + } + + return null; + } + + + public static boolean + entityMatchesAllKeys(EdmEntityType edmEntityType, Entity rt_entity, List<UriParameter> keyParams) + throws ODataApplicationException { + + // loop over all keys + for (final UriParameter key : keyParams) { + // key + String keyName = key.getName(); + String keyText = key.getText(); + + // Edm: we need this info for the comparison below + EdmProperty edmKeyProperty = (EdmProperty) edmEntityType.getProperty(keyName); + Boolean isNullable = edmKeyProperty.isNullable(); + Integer maxLength = edmKeyProperty.getMaxLength(); + Integer precision = edmKeyProperty.getPrecision(); + Boolean isUnicode = edmKeyProperty.isUnicode(); + Integer scale = edmKeyProperty.getScale(); + // get the EdmType in order to compare + EdmType edmType = edmKeyProperty.getType(); + // if(EdmType instanceof EdmPrimitiveType) // do we need this? + EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) edmType; + + // Runtime data: the value of the current entity + // don't need to check for null, this is done in olingo library + Object valueObject = rt_entity.getProperty(keyName).getValue(); + + // now need to compare the valueObject with the keyText String + // this is done using type.valueToString + String valueAsString = null; + try { + valueAsString = edmPrimitiveType.valueToString(valueObject, isNullable, maxLength, precision, scale, isUnicode); + } catch (EdmPrimitiveTypeException e) { + throw new ODataApplicationException("Failed to retrieve String value", + HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH, e); + } + + if (valueAsString == null) { + return false; + } + + boolean matches = valueAsString.equals(keyText); + if (!matches) { + // if any of the key properties is not found in the entity, we don't need to search further + return false; + } + } + + return true; + } +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/web/DemoServlet.java ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/web/DemoServlet.java b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/web/DemoServlet.java new file mode 100644 index 0000000..fe5cdbb --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/java/myservice/mynamespace/web/DemoServlet.java @@ -0,0 +1,76 @@ +/* + * 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 myservice.mynamespace.web; + +import java.io.IOException; +import java.lang.Override;import java.lang.RuntimeException;import java.util.ArrayList; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import myservice.mynamespace.data.Storage; +import myservice.mynamespace.service.DemoEdmProvider; +import myservice.mynamespace.service.DemoEntityCollectionProcessor; +import myservice.mynamespace.service.DemoEntityProcessor; +import myservice.mynamespace.service.DemoPrimitiveProcessor; + +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.api.edmx.EdmxReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DemoServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + private static final Logger LOG = LoggerFactory.getLogger(DemoServlet.class); + + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + try { + HttpSession session = req.getSession(true); + Storage storage = (Storage) session.getAttribute(Storage.class.getName()); + if (storage == null) { + storage = new Storage(); + session.setAttribute(Storage.class.getName(), storage); + } + + // create odata handler and configure it with EdmProvider and Processor + OData odata = OData.newInstance(); + ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>()); + ODataHttpHandler handler = odata.createHandler(edm); + handler.register(new DemoEntityCollectionProcessor(storage)); + handler.register(new DemoEntityProcessor(storage)); + handler.register(new DemoPrimitiveProcessor(storage)); + + // let the handler do the work + handler.process(req, resp); + } catch (RuntimeException e) { + LOG.error("Server Error occurred in ExampleServlet", e); + throw new ServletException(e); + } + + } + +} http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/webapp/WEB-INF/web.xml ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/webapp/WEB-INF/web.xml b/samples/tutorials/p7_queryoptions-f/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..21de52a --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" + id="WebApp_ID" version="2.5"> + + <!-- Register the HttpServlet implementation --> + <servlet> + <servlet-name>DemoServlet</servlet-name> + <servlet-class>myservice.mynamespace.web.DemoServlet</servlet-class> + <load-on-startup>1</load-on-startup> + </servlet> + + <!-- + Our OData service can be invoked at + http://localhost:8080/DemoService/DemoService.svc + --> + <servlet-mapping> + <servlet-name>DemoServlet</servlet-name> + <url-pattern>/DemoService.svc/*</url-pattern> + </servlet-mapping> +</web-app> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/c0e032d4/samples/tutorials/p7_queryoptions-f/src/main/webapp/index.jsp ---------------------------------------------------------------------- diff --git a/samples/tutorials/p7_queryoptions-f/src/main/webapp/index.jsp b/samples/tutorials/p7_queryoptions-f/src/main/webapp/index.jsp new file mode 100644 index 0000000..c0fe23e --- /dev/null +++ b/samples/tutorials/p7_queryoptions-f/src/main/webapp/index.jsp @@ -0,0 +1,24 @@ +/* + * 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. + */ +<html> +<body> +<h2>Hello World!</h2> +<a href="DemoService.svc/">OData Olingo V4 Demo Service</a> +</body> +</html>
