diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/data/Storage.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/data/Storage.java index 04911e764..5a41ee9cd 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/data/Storage.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/data/Storage.java @@ -18,6 +18,8 @@ */ package myservice.mynamespace.data; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -33,6 +35,7 @@ import org.apache.olingo.commons.api.edm.EdmEntitySet; import org.apache.olingo.commons.api.edm.EdmEntityType; import org.apache.olingo.commons.api.edm.EdmKeyPropertyRef; import org.apache.olingo.commons.api.edm.FullQualifiedName; +import org.apache.olingo.commons.api.ex.ODataRuntimeException; import org.apache.olingo.commons.api.http.HttpMethod; import org.apache.olingo.commons.api.http.HttpStatusCode; import org.apache.olingo.server.api.ODataApplicationException; @@ -106,6 +109,7 @@ public class Storage { if (sourceEntityFqn.equals(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()) && relatedEntityFqn.equals(DemoEdmProvider.ET_CATEGORY_FQN)) { + navigationTargetEntityCollection.setId(createId(sourceEntity, "ID", DemoEdmProvider.NAV_TO_CATEGORY)); // relation Products->Category (result all categories) int productID = (Integer) sourceEntity.getProperty("ID").getValue(); if (productID == 1 || productID == 2) { @@ -117,6 +121,7 @@ public class Storage { } } else if (sourceEntityFqn.equals(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString()) && relatedEntityFqn.equals(DemoEdmProvider.ET_PRODUCT_FQN)) { + navigationTargetEntityCollection.setId(createId(sourceEntity, "ID", DemoEdmProvider.NAV_TO_PRODUCTS)); // relation Category->Products (result all products) int categoryID = (Integer) sourceEntity.getProperty("ID").getValue(); if (categoryID == 1) { @@ -137,7 +142,7 @@ public class Storage { return navigationTargetEntityCollection; } - + public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate) { EdmEntityType edmEntityType = edmEntitySet.getEntityType(); @@ -321,6 +326,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Notebook Basic, 1.7GHz - 15 XGA - 1024MB DDR2 SDRAM - 40GB")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); entity = new Entity(); @@ -329,6 +335,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Notebook Professional, 2.8GHz - 15 XGA - 8GB DDR3 RAM - 500GB")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); entity = new Entity(); @@ -337,6 +344,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Ultrafast 3G UMTS/HSDPA Pocket PC, supports GSM network")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); entity = new Entity(); @@ -345,6 +353,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "32 GB Digital Assitant with high-resolution color screen")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); entity = new Entity(); @@ -353,6 +362,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "19 Optimum Resolution 1024 x 768 @ 85Hz, resolution 1280 x 960")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); entity = new Entity(); @@ -361,6 +371,7 @@ public class Storage { entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Optimum Hi-Resolution max. 1600 x 1200 @ 85Hz, Dot Pitch: 0.24mm")); entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); productList.add(entity); } @@ -371,19 +382,48 @@ public class Storage { entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 1)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Notebooks")); entity.setType(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); categoryList.add(entity); entity = new Entity(); entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 2)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Organizers")); entity.setType(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); categoryList.add(entity); entity = new Entity(); entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 3)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Monitors")); entity.setType(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString()); + entity.setId(createId(entity, "ID")); categoryList.add(entity); } + private URI createId(Entity entity, String idPropertyName) { + return createId(entity, idPropertyName, null); + } + + private URI createId(Entity entity, String idPropertyName, String navigationName) { + try { + StringBuilder sb = new StringBuilder(getEntitySetName(entity)).append("("); + final Property property = entity.getProperty(idPropertyName); + sb.append(property.asPrimitive()).append(")"); + if(navigationName != null) { + sb.append("/").append(navigationName); + } + return new URI(sb.toString()); + } catch (URISyntaxException e) { + throw new ODataRuntimeException("Unable to create (Atom) id for entity: " + entity, e); + } + } + + private String getEntitySetName(Entity entity) { + if(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString().equals(entity.getType())) { + return DemoEdmProvider.ES_CATEGORIES_NAME; + } else if(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString().equals(entity.getType())) { + return DemoEdmProvider.ES_PRODUCTS_NAME; + } + return entity.getType(); + } } diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java index 8ce9baced..ea79de6b3 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEdmProvider.java @@ -54,6 +54,8 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { // Entity Set Names public static final String ES_PRODUCTS_NAME = "Products"; public static final String ES_CATEGORIES_NAME = "Categories"; + public static final String NAV_TO_CATEGORY = "Category"; + public static final String NAV_TO_PRODUCTS = "Products"; @Override public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) { @@ -75,7 +77,7 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { propertyRef.setName("ID"); // navigation property: many-to-one, null not allowed (product must have a category) - CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Category") + CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName(NAV_TO_CATEGORY) .setType(ET_CATEGORY_FQN).setNullable(false).setPartner("Products"); List navPropList = new ArrayList(); navPropList.add(navProp); @@ -99,7 +101,7 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { propertyRef.setName("ID"); // navigation property: one-to-many - CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Products") + CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName(NAV_TO_PRODUCTS) .setType(ET_PRODUCT_FQN).setCollection(true).setPartner("Category"); List navPropList = new ArrayList(); navPropList.add(navProp); diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java index f3b208544..422ba2b4b 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityCollectionProcessor.java @@ -115,8 +115,7 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor modifiedEntityList = applyTopQueryOption(modifiedEntityList, uriInfo.getTopOption()); // 3.6.) Server driven paging (not part of this tutorial) // 3.7.) $expand - modifiedEntityList = applyExpandQueryOption(modifiedEntityCollection, modifiedEntityList, edmEntitySet, - uriInfo.getExpandOption()); + modifiedEntityList = applyExpandQueryOption(modifiedEntityList, edmEntitySet, uriInfo.getExpandOption()); // 3.8.) $select SelectOption selectOption = uriInfo.getSelectOption(); @@ -133,13 +132,15 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).selectList(selectList).build(); // adding the selectOption to the serializerOpts will actually tell the lib to do the job + final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName(); EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with() .contextURL(contextUrl) .count(uriInfo.getCountOption()) .select(selectOption) .expand(uriInfo.getExpandOption()) + .setId(id) .build(); - + // and serialize the content: transform from the EntitySet object to InputStream SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, modifiedEntityCollection, opts); @@ -151,7 +152,7 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); } - private List applyExpandQueryOption(EntityCollection entityCollection, List modifiedEntityList, + private List applyExpandQueryOption(List modifiedEntityList, EdmEntitySet edmEntitySet, ExpandOption expandOption) { // in our example: http://localhost:8080/DemoService/DemoService.svc/Categories/$expand=Products @@ -193,16 +194,19 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor Link link = new Link(); link.setTitle(navPropName); link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + link.setRel(Constants.NS_ASSOCIATION_LINK_REL + navPropName); if (edmNavigationProperty.isCollection()) { // in case of Categories/$expand=Products // fetch the data for the $expand (to-many navigation) from backend EntityCollection expandEntityCollection = storage.getRelatedEntityCollection(entity, expandEdmEntityType); link.setInlineEntitySet(expandEntityCollection); + link.setHref(expandEntityCollection.getId().toASCIIString()); } else { // in case of Products?$expand=Category // fetch the data for the $expand (to-one navigation) from backend // here we get the data for the expand Entity expandEntity = storage.getRelatedEntity(entity, expandEdmEntityType); link.setInlineEntity(expandEntity); + link.setHref(expandEntity.getId().toASCIIString()); } // set the link - containing the expanded data - to the current entity diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java index 097e26d00..c176ae2f9 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoEntityProcessor.java @@ -99,7 +99,6 @@ public class DemoEntityProcessor implements EntityProcessor { // Analyze the URI segments if (segmentCount == 1) { // no navigation - responseEdmEntityType = startEdmEntitySet.getEntityType(); responseEdmEntitySet = startEdmEntitySet; // since we have only one segment // 2. step: retrieve the data from backend @@ -190,17 +189,21 @@ public class DemoEntityProcessor implements EntityProcessor { Link link = new Link(); link.setTitle(navPropName); link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + link.setRel(Constants.NS_ASSOCIATION_LINK_REL + navPropName); - if(edmNavigationProperty.isCollection()){ // in case of Categories(1)/$expand=Products + if(edmNavigationProperty.isCollection()) { // in case of Categories(1)/$expand=Products // fetch the data for the $expand (to-many navigation) from backend // here we get the data for the expand - EntityCollection expandEntityCollection = storage.getRelatedEntityCollection(responseEntity, expandEdmEntityType); + EntityCollection expandEntityCollection = + storage.getRelatedEntityCollection(responseEntity, expandEdmEntityType); link.setInlineEntitySet(expandEntityCollection); + link.setHref(expandEntityCollection.getId().toASCIIString()); } else { // in case of Products(1)?$expand=Category // fetch the data for the $expand (to-one navigation) from backend // here we get the data for the expand Entity expandEntity = storage.getRelatedEntity(responseEntity, expandEdmEntityType); link.setInlineEntity(expandEntity); + link.setHref(expandEntity.getId().toASCIIString()); } // set the link - containing the expanded data - to the current entity diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java index 12d6e301a..811833a52 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/FilterExpressionVisitor.java @@ -46,7 +46,6 @@ public class FilterExpressionVisitor implements ExpressionVisitor { 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 @@ -73,7 +72,6 @@ public class FilterExpressionVisitor implements ExpressionVisitor { } } - @Override public Object visitLiteral(Literal 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. @@ -100,8 +98,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { } } - @Override - public Object visitUnaryOperator(UnaryOperatorKind operator, Object operand) + 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 @@ -119,8 +116,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); } - @Override - public Object visitBinaryOperator(BinaryOperatorKind operator, Object left, Object right) + 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 @@ -181,11 +177,11 @@ public class FilterExpressionVisitor implements ExpressionVisitor { // Luckily all used types String, Boolean and also Integer support the interface Comparable int result; if(left instanceof Integer) { - result = ((Comparable) (Integer)left).compareTo((Integer) right); + result = ((Integer) left).compareTo((Integer) right); } else if(left instanceof String) { - result = ((Comparable) (String)left).compareTo((String) right); + result = ((String) left).compareTo((String) right); } else if(left instanceof Boolean) { - result = ((Comparable) (Boolean)left).compareTo((Boolean) right); + result = ((Boolean) left).compareTo((Boolean) right); } else { throw new ODataApplicationException("Class " + left.getClass().getCanonicalName() + " not expected", HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH); @@ -207,7 +203,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { } } else { - throw new ODataApplicationException("Comparision needs two equal types", + throw new ODataApplicationException("Comparison needs two equal types", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); } } @@ -239,8 +235,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { } } - @Override - public Object visitMethodCall(MethodKind methodCall, List parameters) + public Object visitMethodCall(MethodKind methodCall, List parameters) throws ExpressionVisitException, ODataApplicationException { // To keep this tutorial small and simple, we implement only one method call @@ -248,7 +243,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { // "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. + // First the method visitMember 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 // @@ -259,7 +254,7 @@ public class FilterExpressionVisitor implements ExpressionVisitor { return valueParam1.contains(valueParam2); } else { - throw new ODataApplicationException("Contains needs two parametes of type Edm.String", + throw new ODataApplicationException("Contains needs two parameters of type Edm.String", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH); } } else { @@ -268,34 +263,29 @@ public class FilterExpressionVisitor implements ExpressionVisitor { } } - @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 { + + 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 enumValues) + public Object visitEnum(EdmEnumType type, List 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) + 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) + public Object visitLambdaReference(String variableName) throws ExpressionVisitException, ODataApplicationException { throw new ODataApplicationException("Lamdba references are not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); diff --git a/samples/tutorials/p0_all/src/main/webapp/index.jsp b/samples/tutorials/p0_all/src/main/webapp/index.jsp index c0fe23e9c..ea65784e5 100644 --- a/samples/tutorials/p0_all/src/main/webapp/index.jsp +++ b/samples/tutorials/p0_all/src/main/webapp/index.jsp @@ -1,24 +1,129 @@ -/* - * 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. - */ + -

Hello World!

+

OData Olingo V4 Demo Service

OData Olingo V4 Demo Service +

Sample Links

+