From 1051f9b6cf29e8c6947a29f3c5f663027b395b50 Mon Sep 17 00:00:00 2001 From: Christian Holzer Date: Tue, 6 Oct 2015 15:00:23 +0200 Subject: [PATCH] [OLINGO-713] Updated Tutorial p0 with action/function importrs --- .../myservice/mynamespace/data/Storage.java | 280 ++++++++++------- .../service/DemoActionProcessor.java | 85 ++++++ .../mynamespace/service/DemoEdmProvider.java | 127 +++++++- .../DemoEntityCollectionProcessor.java | 75 ++++- .../service/DemoEntityProcessor.java | 287 +++++++++++------- .../mynamespace/web/DemoServlet.java | 2 + .../myservice/mynamespace/data/Storage.java | 2 +- .../service/DemoActionProcessor.java | 2 +- 8 files changed, 609 insertions(+), 251 deletions(-) create mode 100644 samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java 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 208bc538d..2cf43eca4 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 @@ -39,7 +39,9 @@ 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; +import org.apache.olingo.server.api.ServiceMetadata; import org.apache.olingo.server.api.uri.UriParameter; +import org.apache.olingo.server.api.uri.UriResourceFunction; public class Storage { @@ -59,30 +61,97 @@ public class Storage { /* PUBLIC FACADE */ - public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet) { - EntityCollection entitySet = null; + public Entity readFunctionImportEntity(final UriResourceFunction uriResourceFunction, + final ServiceMetadata serviceMetadata) throws ODataApplicationException { - if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) { - entitySet = getProducts(); - } else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) { - entitySet = getCategories(); - } + final EntityCollection entityCollection = readFunctionImportCollection(uriResourceFunction, serviceMetadata); + final EdmEntityType edmEntityType = (EdmEntityType) uriResourceFunction.getFunction().getReturnType().getType(); - return entitySet; + return Util.findEntity(edmEntityType, entityCollection, uriResourceFunction.getKeyPredicates()); } - public Entity readEntityData(EdmEntitySet edmEntitySet, List keyParams) { - Entity entity = null; + public EntityCollection readFunctionImportCollection(final UriResourceFunction uriResourceFunction, + final ServiceMetadata serviceMetadata) throws ODataApplicationException { + + if (DemoEdmProvider.FUNCTION_COUNT_CATEGORIES.equals(uriResourceFunction.getFunctionImport().getName())) { + // Get the parameter of the function + final UriParameter parameterAmount = uriResourceFunction.getParameters().get(0); + // Try to convert the parameter to an Integer. + // We have to take care, that the type of parameter fits to its EDM declaration + int amount; + try { + amount = Integer.parseInt(parameterAmount.getText()); + } catch (NumberFormatException e) { + throw new ODataApplicationException("Type of parameter Amount must be Edm.Int32", HttpStatusCode.BAD_REQUEST + .getStatusCode(), Locale.ENGLISH); + } + + final EdmEntityType productEntityType = serviceMetadata.getEdm().getEntityType(DemoEdmProvider.ET_PRODUCT_FQN); + final List resultEntityList = new ArrayList(); + + // Loop over all categories and check how many products are linked + for (final Entity category : categoryList) { + final EntityCollection products = getRelatedEntityCollection(category, productEntityType); + if (products.getEntities().size() == amount) { + resultEntityList.add(category); + } + } + + final EntityCollection resultCollection = new EntityCollection(); + resultCollection.getEntities().addAll(resultEntityList); + return resultCollection; + } else { + throw new ODataApplicationException("Function not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), + Locale.ROOT); + } + } + + public void resetDataSet() { + resetDataSet(Integer.MAX_VALUE); + } + + public void resetDataSet(final int amount) { + // Replace the old lists with empty ones + productList = new ArrayList(); + categoryList = new ArrayList(); + + // Create new sample data + initProductSampleData(); + initCategorySampleData(); + + // Truncate the lists + if (amount < productList.size()) { + productList = productList.subList(0, amount); + // Products 0, 1 are linked to category 0 + // Products 2, 3 are linked to category 1 + // Products 4, 5 are linked to category 2 + categoryList = categoryList.subList(0, (amount / 2) + 1); + } + } + + public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet) throws ODataApplicationException { + + if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) { + return getEntityCollection(productList); + } else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) { + return getEntityCollection(categoryList); + } + + return null; + } + + public Entity readEntityData(EdmEntitySet edmEntitySet, List keyParams) + throws ODataApplicationException { EdmEntityType edmEntityType = edmEntitySet.getEntityType(); if (edmEntityType.getName().equals(DemoEdmProvider.ET_PRODUCT_NAME)) { - entity = getProduct(edmEntityType, keyParams); + return getEntity(edmEntityType, keyParams, productList); } else if (edmEntityType.getName().equals(DemoEdmProvider.ET_CATEGORY_NAME)) { - entity = getCategory(edmEntityType, keyParams); + return getEntity(edmEntityType, keyParams, categoryList); } - return entity; + return null; } // Navigation @@ -109,37 +178,31 @@ 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) { + if (productID == 0 || productID == 1) { navigationTargetEntityCollection.getEntities().add(categoryList.get(0)); - } else if (productID == 3 || productID == 4) { + } else if (productID == 2 || productID == 3) { navigationTargetEntityCollection.getEntities().add(categoryList.get(1)); - } else if (productID == 5 || productID == 6) { + } else if (productID == 4 || productID == 5) { navigationTargetEntityCollection.getEntities().add(categoryList.get(2)); } } 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) { + if (categoryID == 0) { // the first 2 products are notebooks navigationTargetEntityCollection.getEntities().addAll(productList.subList(0, 2)); - } else if (categoryID == 2) { + } else if (categoryID == 1) { // the next 2 products are organizers navigationTargetEntityCollection.getEntities().addAll(productList.subList(2, 4)); - } else if (categoryID == 3) { + } else if (categoryID == 2) { // the first 2 products are monitors navigationTargetEntityCollection.getEntities().addAll(productList.subList(4, 6)); } } - if (navigationTargetEntityCollection.getEntities().isEmpty()) { - return null; - } - return navigationTargetEntityCollection; } @@ -147,9 +210,10 @@ public class Storage { 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 createProduct(edmEntityType, entityToCreate); + return createEntity(edmEntityType, entityToCreate, productList); + } else if (edmEntityType.getName().equals(DemoEdmProvider.ET_CATEGORY_NAME)) { + return createEntity(edmEntityType, entityToCreate, categoryList); } return null; @@ -157,15 +221,16 @@ public class Storage { /** * This method is invoked for PATCH or PUT requests - * */ + */ public void updateEntityData(EdmEntitySet edmEntitySet, List keyParams, Entity updateEntity, HttpMethod httpMethod) 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)) { - updateProduct(edmEntityType, keyParams, updateEntity, httpMethod); + updateEntity(edmEntityType, keyParams, updateEntity, httpMethod, productList); + } else if (edmEntityType.getName().equals(DemoEdmProvider.ET_CATEGORY_NAME)) { + updateEntity(edmEntityType, keyParams, updateEntity, httpMethod, categoryList); } } @@ -174,63 +239,86 @@ public class Storage { 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)) { - deleteProduct(edmEntityType, keyParams); + deleteEntity(edmEntityType, keyParams, productList); + } else if (edmEntityType.getName().equals(DemoEdmProvider.ET_CATEGORY_NAME)) { + deleteEntity(edmEntityType, keyParams, categoryList); } } - + /* INTERNAL */ - private EntityCollection getProducts() { - EntityCollection retEntitySet = new EntityCollection(); + private Entity createEntity(EdmEntityType edmEntityType, Entity entity, List entityList) { - for (Entity productEntity : this.productList) { - retEntitySet.getEntities().add(productEntity); + // the ID of the newly created entity is generated automatically + int newId = 1; + while (entityIdExists(newId, entityList)) { + newId++; } + Property idProperty = entity.getProperty("ID"); + if (idProperty != null) { + idProperty.setValue(ValueType.PRIMITIVE, Integer.valueOf(newId)); + } else { + // as of OData v4 spec, the key property can be omitted from the POST request body + entity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId)); + } + entity.setId(createId(entity, "ID")); + entityList.add(entity); + + return entity; + } + + private EntityCollection getEntityCollection(final List entityList) { + + EntityCollection retEntitySet = new EntityCollection(); + retEntitySet.getEntities().addAll(entityList); + return retEntitySet; } - private Entity getProduct(EdmEntityType edmEntityType, List keyParams) { + private Entity getEntity(EdmEntityType edmEntityType, List keyParams, List entityList) + throws ODataApplicationException { // the list of entities at runtime - EntityCollection entityCollection = getProducts(); + EntityCollection entitySet = getEntityCollection(entityList); /* generic approach to find the requested entity */ - return Util.findEntity(edmEntityType, entityCollection, keyParams); - } + Entity requestedEntity = Util.findEntity(edmEntityType, entitySet, keyParams); - private EntityCollection getCategories() { - EntityCollection entitySet = new EntityCollection(); - - for (Entity categoryEntity : this.categoryList) { - entitySet.getEntities().add(categoryEntity); + 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 entitySet; + return requestedEntity; } - private Entity getCategory(EdmEntityType edmEntityType, List keyParams) { + private boolean entityIdExists(int id, List entityList) { - // the list of entities at runtime - EntityCollection entitySet = getCategories(); + for (Entity entity : entityList) { + Integer existingID = (Integer) entity.getProperty("ID").getValue(); + if (existingID.intValue() == id) { + return true; + } + } - /* generic approach to find the requested entity */ - return Util.findEntity(edmEntityType, entitySet, keyParams); + return false; } - private void updateProduct(EdmEntityType edmEntityType, List keyParams, Entity entity, - HttpMethod httpMethod) throws ODataApplicationException { - - Entity productEntity = getProduct(edmEntityType, keyParams); - if (productEntity == null) { + private void updateEntity(EdmEntityType edmEntityType, List keyParams, Entity updateEntity, + HttpMethod httpMethod, List entityList) throws ODataApplicationException { + + Entity entity = getEntity(edmEntityType, keyParams, entityList); + if (entity == null) { throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH); } // loop over all properties and replace the values with the values of the given payload // Note: ignoring ComplexType, as we don't have it in our odata model - List existingProperties = productEntity.getProperties(); + List existingProperties = entity.getProperties(); for (Property existingProp : existingProperties) { String propName = existingProp.getName(); @@ -239,7 +327,7 @@ public class Storage { continue; } - Property updateProperty = entity.getProperty(propName); + Property updateProperty = updateEntity.getProperty(propName); // the request payload might not consider ALL properties, so it can be null if (updateProperty == null) { // if a property has NOT been added to the request payload @@ -258,54 +346,18 @@ public class Storage { existingProp.setValue(existingProp.getValueType(), updateProperty.getValue()); } } - - private void deleteProduct(EdmEntityType edmEntityType, List keyParams) + + private void deleteEntity(EdmEntityType edmEntityType, List keyParams, List entityList) throws ODataApplicationException { - - Entity productEntity = getProduct(edmEntityType, keyParams); - if (productEntity == null) { + + Entity entity = getEntity(edmEntityType, keyParams, entityList); + if (entity == null) { throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH); } - this.productList.remove(productEntity); + entityList.remove(entity); } - private Entity createProduct(EdmEntityType edmEntityType, Entity entity) { - - // the ID of the newly created product entity is generated automatically - int newId = 1; - while (productIdExists(newId)) { - newId++; - } - - Property idProperty = entity.getProperty("ID"); - if (idProperty != null) { - idProperty.setValue(ValueType.PRIMITIVE, Integer.valueOf(newId)); - } else { - // as of OData v4 spec, the key property can be omitted from the POST request body - entity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId)); - } - entity.setId(createId(entity, "ID")); - this.productList.add(entity); - - return entity; - - } - - private boolean productIdExists(int id) { - - for (Entity entity : this.productList) { - Integer existingID = (Integer) entity.getProperty("ID").getValue(); - if (existingID.intValue() == id) { - return true; - } - } - - return false; - } - - /* HELPER */ - private boolean isKey(EdmEntityType edmEntityType, String propertyName) { List keyPropertyRefs = edmEntityType.getKeyPropertyRefs(); for (EdmKeyPropertyRef propRef : keyPropertyRefs) { @@ -316,12 +368,12 @@ public class Storage { } return false; } - + private void initProductSampleData() { Entity entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 1)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 0)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Notebook Basic 15")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Notebook Basic, 1.7GHz - 15 XGA - 1024MB DDR2 SDRAM - 40GB")); @@ -330,7 +382,7 @@ public class Storage { productList.add(entity); entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 2)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 1)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Notebook Professional 17")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Notebook Professional, 2.8GHz - 15 XGA - 8GB DDR3 RAM - 500GB")); @@ -339,7 +391,7 @@ public class Storage { productList.add(entity); entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 3)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 2)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "1UMTS PDA")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Ultrafast 3G UMTS/HSDPA Pocket PC, supports GSM network")); @@ -348,7 +400,7 @@ public class Storage { productList.add(entity); entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 4)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 3)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Comfort Easy")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "32 GB Digital Assitant with high-resolution color screen")); @@ -357,7 +409,7 @@ public class Storage { productList.add(entity); entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 5)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 4)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Ergo Screen")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "19 Optimum Resolution 1024 x 768 @ 85Hz, resolution 1280 x 960")); @@ -366,7 +418,7 @@ public class Storage { productList.add(entity); entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 6)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 5)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Flat Basic")); entity.addProperty(new Property(null, "Description", ValueType.PRIMITIVE, "Optimum Hi-Resolution max. 1600 x 1200 @ 85Hz, Dot Pitch: 0.24mm")); @@ -379,21 +431,21 @@ public class Storage { Entity entity = new Entity(); - entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 1)); + entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 0)); 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, "ID", ValueType.PRIMITIVE, 1)); 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, "ID", ValueType.PRIMITIVE, 2)); entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Monitors")); entity.setType(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString()); entity.setId(createId(entity, "ID")); @@ -409,7 +461,7 @@ public class Storage { StringBuilder sb = new StringBuilder(getEntitySetName(entity)).append("("); final Property property = entity.getProperty(idPropertyName); sb.append(property.asPrimitive()).append(")"); - if(navigationName != null) { + if (navigationName != null) { sb.append("/").append(navigationName); } return new URI(sb.toString()); @@ -419,9 +471,9 @@ public class Storage { } private String getEntitySetName(Entity entity) { - if(DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString().equals(entity.getType())) { + if (DemoEdmProvider.ET_CATEGORY_FQN.getFullQualifiedNameAsString().equals(entity.getType())) { return DemoEdmProvider.ES_CATEGORIES_NAME; - } else if(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString().equals(entity.getType())) { + } 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/DemoActionProcessor.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java new file mode 100644 index 000000000..e2aaed19c --- /dev/null +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java @@ -0,0 +1,85 @@ +/* + * 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.Locale; +import java.util.Map; + +import org.apache.olingo.commons.api.data.Parameter; +import org.apache.olingo.commons.api.edm.EdmAction; +import org.apache.olingo.commons.api.format.ContentType; +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.ODataLibraryException; +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.ODataDeserializer; +import org.apache.olingo.server.api.processor.ActionVoidProcessor; +import org.apache.olingo.server.api.uri.UriInfo; +import org.apache.olingo.server.api.uri.UriResourceAction; + +import myservice.mynamespace.data.Storage; + +public class DemoActionProcessor implements ActionVoidProcessor { + + private OData odata; + private Storage storage; + + public DemoActionProcessor(final Storage storage) { + this.storage = storage; + } + + @Override + public void init(final OData odata, final ServiceMetadata serviceMetadata) { + this.odata = odata; + } + + @Override + public void processActionVoid(ODataRequest request, ODataResponse response, UriInfo uriInfo, + ContentType requestFormat) throws ODataApplicationException, ODataLibraryException { + + // 1st Get the action from the resource path + final EdmAction edmAction = ((UriResourceAction) uriInfo.asUriInfoResource().getUriResourceParts() + .get(0)).getAction(); + + // 2nd Deserialize the parameter + // In our case there is only one action. So we can be sure that parameter "Amount" has been provided by the client + if (requestFormat == null) { + throw new ODataApplicationException("The content type has not been set in the request.", + HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT); + } + + final ODataDeserializer deserializer = odata.createDeserializer(requestFormat); + final Map actionParameter = deserializer.actionParameters(request.getBody(), edmAction) + .getActionParameters(); + final Parameter parameterAmount = actionParameter.get(DemoEdmProvider.PARAMETER_AMOUNT); + + // The parameter amount is nullable + if(parameterAmount.isNull()) { + storage.resetDataSet(); + } else { + final Integer amount = (Integer) parameterAmount.asPrimitive(); + storage.resetDataSet(amount); + } + + response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); + } +} 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 ea79de6b3..6e4bae2fd 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 @@ -25,14 +25,20 @@ import java.util.List; 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.CsdlAction; +import org.apache.olingo.commons.api.edm.provider.CsdlActionImport; 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.CsdlFunction; +import org.apache.olingo.commons.api.edm.provider.CsdlFunctionImport; import org.apache.olingo.commons.api.edm.provider.CsdlNavigationProperty; import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding; +import org.apache.olingo.commons.api.edm.provider.CsdlParameter; 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.CsdlReturnType; import org.apache.olingo.commons.api.edm.provider.CsdlSchema; public class DemoEdmProvider extends CsdlAbstractEdmProvider { @@ -57,6 +63,101 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { public static final String NAV_TO_CATEGORY = "Category"; public static final String NAV_TO_PRODUCTS = "Products"; + //Action + public static final String ACTION_RESET = "Reset"; + public static final FullQualifiedName ACTION_RESET_FQN = new FullQualifiedName(NAMESPACE, ACTION_RESET); + + // Function + public static final String FUNCTION_COUNT_CATEGORIES = "CountCategories"; + public static final FullQualifiedName FUNCTION_COUNT_CATEGORIES_FQN + = new FullQualifiedName(NAMESPACE, FUNCTION_COUNT_CATEGORIES); + + // Function/Action Parameters + public static final String PARAMETER_AMOUNT = "Amount"; + + @Override + public List getActions(final FullQualifiedName actionName) { + if(actionName.equals(ACTION_RESET_FQN)) { + // It is allowed to overload actions, so we have to provide a list of Actions for each action name + final List actions = new ArrayList(); + + // Create parameters + final List parameters = new ArrayList(); + final CsdlParameter parameter = new CsdlParameter(); + parameter.setName(PARAMETER_AMOUNT); + parameter.setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName()); + parameters.add(parameter); + + // Create the Csdl Action + final CsdlAction action = new CsdlAction(); + action.setName(ACTION_RESET_FQN.getName()); + action.setParameters(parameters); + actions.add(action); + + return actions; + } + + return null; + } + + @Override + public CsdlActionImport getActionImport(final FullQualifiedName entityContainer, final String actionImportName) { + if(entityContainer.equals(CONTAINER)) { + if(actionImportName.equals(ACTION_RESET_FQN.getName())) { + return new CsdlActionImport() + .setName(actionImportName) + .setAction(ACTION_RESET_FQN); + } + } + + return null; + } + + @Override + public List getFunctions(final FullQualifiedName functionName) { + if (functionName.equals(FUNCTION_COUNT_CATEGORIES_FQN)) { + // It is allowed to overload functions, so we have to provide a list of functions for each function name + final List functions = new ArrayList(); + + // Create the parameter for the function + final CsdlParameter parameterAmount = new CsdlParameter(); + parameterAmount.setName(PARAMETER_AMOUNT); + parameterAmount.setNullable(false); + parameterAmount.setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName()); + + // Create the return type of the function + final CsdlReturnType returnType = new CsdlReturnType(); + returnType.setCollection(true); + returnType.setType(ET_CATEGORY_FQN); + + // Create the function + final CsdlFunction function = new CsdlFunction(); + function.setName(FUNCTION_COUNT_CATEGORIES_FQN.getName()) + .setParameters(Arrays.asList(parameterAmount)) + .setReturnType(returnType); + functions.add(function); + + return functions; + } + + return null; + } + + @Override + public CsdlFunctionImport getFunctionImport(FullQualifiedName entityContainer, String functionImportName) { + if(entityContainer.equals(CONTAINER)) { + if(functionImportName.equals(FUNCTION_COUNT_CATEGORIES_FQN.getName())) { + return new CsdlFunctionImport() + .setName(functionImportName) + .setFunction(FUNCTION_COUNT_CATEGORIES_FQN) + .setEntitySet(ES_CATEGORIES_NAME) + .setIncludeInServiceDocument(true); + } + } + + return null; + } + @Override public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) { @@ -78,7 +179,7 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { // navigation property: many-to-one, null not allowed (product must have a category) CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName(NAV_TO_CATEGORY) - .setType(ET_CATEGORY_FQN).setNullable(false).setPartner("Products"); + .setType(ET_CATEGORY_FQN).setNullable(true).setPartner("Products"); List navPropList = new ArrayList(); navPropList.add(navProp); @@ -183,7 +284,17 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { entityTypes.add(getEntityType(ET_PRODUCT_FQN)); entityTypes.add(getEntityType(ET_CATEGORY_FQN)); schema.setEntityTypes(entityTypes); - + + // add actions + List actions = new ArrayList(); + actions.addAll(getActions(ACTION_RESET_FQN)); + schema.setActions(actions); + + // add functions + List functions = new ArrayList(); + functions.addAll(getFunctions(FUNCTION_COUNT_CATEGORIES_FQN)); + schema.setFunctions(functions); + // add EntityContainer schema.setEntityContainer(getEntityContainer()); @@ -201,10 +312,20 @@ public class DemoEdmProvider extends CsdlAbstractEdmProvider { List entitySets = new ArrayList(); entitySets.add(getEntitySet(CONTAINER, ES_PRODUCTS_NAME)); entitySets.add(getEntitySet(CONTAINER, ES_CATEGORIES_NAME)); - + + // Create function imports + List functionImports = new ArrayList(); + functionImports.add(getFunctionImport(CONTAINER, FUNCTION_COUNT_CATEGORIES)); + + // Create action imports + List actionImports = new ArrayList(); + actionImports.add(getActionImport(CONTAINER, ACTION_RESET)); + // create EntityContainer CsdlEntityContainer entityContainer = new CsdlEntityContainer(); entityContainer.setName(CONTAINER_NAME); + entityContainer.setActionImports(actionImports); + entityContainer.setFunctionImports(functionImports); entityContainer.setEntitySets(entitySets); return entityContainer; 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 22b9f54b6..d16115d33 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 @@ -19,6 +19,7 @@ package myservice.mynamespace.service; import java.io.InputStream; +import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -56,6 +57,7 @@ import org.apache.olingo.server.api.uri.UriInfo; import org.apache.olingo.server.api.uri.UriInfoResource; import org.apache.olingo.server.api.uri.UriResource; 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.queryoption.CountOption; @@ -86,10 +88,54 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor this.serviceMetadata = serviceMetadata; } - public void readEntityCollection(ODataRequest request, ODataResponse response, UriInfo uriInfo, - ContentType responseFormat) throws ODataApplicationException, SerializerException { + public void readEntityCollection(ODataRequest request, ODataResponse response, + UriInfo uriInfo, ContentType responseFormat) + throws ODataApplicationException, SerializerException { + + final UriResource firstResourceSegment = uriInfo.getUriResourceParts().get(0); + + if(firstResourceSegment instanceof UriResourceEntitySet) { + readEntityCollectionInternal(request, response, uriInfo, responseFormat); + } else if(firstResourceSegment instanceof UriResourceFunction) { + readFunctionImportCollection(request, response, uriInfo, responseFormat); + } else { + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), + Locale.ENGLISH); + } + } + + private void readFunctionImportCollection(final ODataRequest request, final ODataResponse response, + final UriInfo uriInfo, final ContentType responseFormat) throws ODataApplicationException, SerializerException { + + // 1st step: Analyze the URI and fetch the entity collection returned by the function import + // Function Imports are always the first segment of the resource path + final UriResource firstSegment = uriInfo.getUriResourceParts().get(0); + + if(!(firstSegment instanceof UriResourceFunction)) { + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), + Locale.ENGLISH); + } + + final UriResourceFunction uriResourceFunction = (UriResourceFunction) firstSegment; + final EntityCollection entityCol = storage.readFunctionImportCollection(uriResourceFunction, serviceMetadata); + + // 2nd step: Serialize the response entity + final EdmEntityType edmEntityType = (EdmEntityType) uriResourceFunction.getFunction().getReturnType().getType(); + final ContextURL contextURL = ContextURL.with().asCollection().type(edmEntityType).build(); + EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with().contextURL(contextURL).build(); + final ODataSerializer serializer = odata.createSerializer(responseFormat); + final SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType, entityCol, + opts); - // 1st: retrieve the requested EntitySet from the uriInfo (representation of the parsed URI) + // 3rd configure the response object + response.setContent(serializerResult.getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } + + private void readEntityCollectionInternal(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 resourcePaths = uriInfo.getUriResourceParts(); // in our example, the first segment is the EntitySet UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); @@ -100,10 +146,10 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor EntityCollection modifiedEntityCollection = new EntityCollection(); List modifiedEntityList = new ArrayList(); modifiedEntityList.addAll(entityCollection.getEntities()); - - // 3rd: Apply system query option - // The system query options have to be applied in a defined order - // 3.1.) $filter + + // 3rd: Apply system query option + // The system query options have to be applied in a defined order + // 3.1.) $filter modifiedEntityList = applyFilterQueryOption(modifiedEntityList, uriInfo.getFilterOption()); // 3.2.) $orderby modifiedEntityList = applyOrderQueryOption(modifiedEntityList, uriInfo.getOrderByOption()); @@ -124,9 +170,9 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor // 4th: create a serializer based on the requested format (json) ODataSerializer serializer = odata.createSerializer(responseFormat); - - // we need the property names of the $select, in order to build the context URL - EdmEntityType edmEntityType = edmEntitySet.getEntityType(); + + // we need the property names of the $select, in order to build the context URL + EdmEntityType edmEntityType = edmEntitySet.getEntityType(); String selectList = odata.createUriHelper() .buildContextURLSelectList(edmEntityType, uriInfo.getExpandOption(), selectOption); ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).selectList(selectList).build(); @@ -150,8 +196,8 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor response.setContent(serializedContent); response.setStatusCode(HttpStatusCode.OK.getStatusCode()); response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); - } - + } + private List applyExpandQueryOption(List modifiedEntityList, EdmEntitySet edmEntitySet, ExpandOption expandOption) { @@ -200,13 +246,14 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor // 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()); + final URI entityId = expandEntityCollection.getId(); + link.setHref(entityId != null ? entityId.toASCIIString() : null); } 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()); + link.setHref(expandEntity != null ? expandEntity.getId().toASCIIString() : null); } // 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 c176ae2f9..97a4dc531 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 @@ -54,6 +54,7 @@ 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.UriResourceFunction; import org.apache.olingo.server.api.uri.UriResourceNavigation; import org.apache.olingo.server.api.uri.queryoption.ExpandItem; import org.apache.olingo.server.api.uri.queryoption.ExpandOption; @@ -77,10 +78,61 @@ public class DemoEntityProcessor implements EntityProcessor { this.serviceMetadata = serviceMetadata; } - public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) - throws ODataApplicationException, SerializerException { + public void readEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, ContentType responseFormat) + throws ODataApplicationException, SerializerException { - EdmEntityType responseEdmEntityType = null; // we'll need this to build the ContextURL + // The sample service supports only functions imports and entity sets. + // We do not care about bound functions and composable functions. + + UriResource uriResource = uriInfo.getUriResourceParts().get(0); + + if (uriResource instanceof UriResourceEntitySet) { + readEntityInternal(request, response, uriInfo, responseFormat); + } else if (uriResource instanceof UriResourceFunction) { + readFunctionImportInternal(request, response, uriInfo, responseFormat); + } else { + throw new ODataApplicationException("Only EntitySet is supported", + HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ENGLISH); + } + } + + private void readFunctionImportInternal(final ODataRequest request, final ODataResponse response, + final UriInfo uriInfo, final ContentType responseFormat) throws ODataApplicationException, SerializerException { + + // 1st step: Analyze the URI and fetch the entity returned by the function import + // Function Imports are always the first segment of the resource path + final UriResource firstSegment = uriInfo.getUriResourceParts().get(0); + + if (!(firstSegment instanceof UriResourceFunction)) { + throw new ODataApplicationException("Not implemented", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), + Locale.ENGLISH); + } + + final UriResourceFunction uriResourceFunction = (UriResourceFunction) firstSegment; + final Entity entity = storage.readFunctionImportEntity(uriResourceFunction, serviceMetadata); + + if (entity == null) { + throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); + } + + // 2nd step: Serialize the response entity + final EdmEntityType edmEntityType = (EdmEntityType) uriResourceFunction.getFunction().getReturnType().getType(); + final ContextURL contextURL = ContextURL.with().type(edmEntityType).build(); + final EntitySerializerOptions opts = EntitySerializerOptions.with().contextURL(contextURL).build(); + final ODataSerializer serializer = odata.createSerializer(responseFormat); + final SerializerResult serializerResult = serializer.entity(serviceMetadata, edmEntityType, entity, opts); + + // 3rd configure the response object + response.setContent(serializerResult.getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } + + private void readEntityInternal(ODataRequest request, ODataResponse response, UriInfo uriInfo, + ContentType responseFormat) + throws ODataApplicationException, SerializerException { + + EdmEntityType responseEdmEntityType = null; // we'll need this to build the ContextURL Entity responseEntity = null; // required for serialization of the response body EdmEntitySet responseEdmEntitySet = null; // we need this for building the contextUrl @@ -141,123 +193,122 @@ public class DemoEntityProcessor implements EntityProcessor { throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT); } - // 3. apply system query options + // 3. apply system query options - // handle $select - SelectOption selectOption = uriInfo.getSelectOption(); - // in our example, we don't have performance issues, so we can rely upon the handling in the Olingo lib - // nothing else to be done + // handle $select + SelectOption selectOption = uriInfo.getSelectOption(); + // in our example, we don't have performance issues, so we can rely upon the handling in the Olingo lib + // nothing else to be done - // handle $expand - ExpandOption expandOption = uriInfo.getExpandOption(); - // in our example: http://localhost:8080/DemoService/DemoService.svc/Categories(1)/$expand=Products - // or http://localhost:8080/DemoService/DemoService.svc/Products(1)?$expand=Category - if(expandOption != null) { - // retrieve the EdmNavigationProperty from the expand expression - // Note: in our example, we have only one NavigationProperty, so we can directly access it - EdmNavigationProperty edmNavigationProperty = null; - ExpandItem expandItem = expandOption.getExpandItems().get(0); - if(expandItem.isStar()) { - List bindings = responseEdmEntitySet.getNavigationPropertyBindings(); - // we know that there are navigation bindings - // however normally in this case a check if navigation bindings exists is done - if(!bindings.isEmpty()) { - // can in our case only be 'Category' or 'Products', so we can take the first - EdmNavigationPropertyBinding binding = bindings.get(0); - EdmElement property = responseEdmEntitySet.getEntityType().getProperty(binding.getPath()); - // we don't need to handle error cases, as it is done in the Olingo library - if(property instanceof EdmNavigationProperty) { - edmNavigationProperty = (EdmNavigationProperty) property; - } - } - } else { - // can be 'Category' or 'Products', no path supported - UriResource expandUriResource = expandItem.getResourcePath().getUriResourceParts().get(0); - // we don't need to handle error cases, as it is done in the Olingo library - if(expandUriResource instanceof UriResourceNavigation) { - edmNavigationProperty = ((UriResourceNavigation) expandUriResource).getProperty(); - } - } + // handle $expand + ExpandOption expandOption = uriInfo.getExpandOption(); + // in our example: http://localhost:8080/DemoService/DemoService.svc/Categories(1)/$expand=Products + // or http://localhost:8080/DemoService/DemoService.svc/Products(1)?$expand=Category + if (expandOption != null) { + // retrieve the EdmNavigationProperty from the expand expression + // Note: in our example, we have only one NavigationProperty, so we can directly access it + EdmNavigationProperty edmNavigationProperty = null; + ExpandItem expandItem = expandOption.getExpandItems().get(0); + if (expandItem.isStar()) { + List bindings = responseEdmEntitySet.getNavigationPropertyBindings(); + // we know that there are navigation bindings + // however normally in this case a check if navigation bindings exists is done + if (!bindings.isEmpty()) { + // can in our case only be 'Category' or 'Products', so we can take the first + EdmNavigationPropertyBinding binding = bindings.get(0); + EdmElement property = responseEdmEntitySet.getEntityType().getProperty(binding.getPath()); + // we don't need to handle error cases, as it is done in the Olingo library + if (property instanceof EdmNavigationProperty) { + edmNavigationProperty = (EdmNavigationProperty) property; + } + } + } else { + // can be 'Category' or 'Products', no path supported + UriResource expandUriResource = expandItem.getResourcePath().getUriResourceParts().get(0); + // we don't need to handle error cases, as it is done in the Olingo library + if (expandUriResource instanceof UriResourceNavigation) { + edmNavigationProperty = ((UriResourceNavigation) expandUriResource).getProperty(); + } + } - // can be 'Category' or 'Products', no path supported - // we don't need to handle error cases, as it is done in the Olingo library - if(edmNavigationProperty != null) { - EdmEntityType expandEdmEntityType = edmNavigationProperty.getType(); - String navPropName = edmNavigationProperty.getName(); + // can be 'Category' or 'Products', no path supported + // we don't need to handle error cases, as it is done in the Olingo library + if (edmNavigationProperty != null) { + EdmEntityType expandEdmEntityType = edmNavigationProperty.getType(); + String navPropName = edmNavigationProperty.getName(); - // build the inline data - Link link = new Link(); - link.setTitle(navPropName); - link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE); + // build the inline data + 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 - // fetch the data for the $expand (to-many navigation) from backend - // here we get the data for the expand - EntityCollection expandEntityCollection = + 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); - link.setInlineEntitySet(expandEntityCollection); + 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); + } 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 - responseEntity.getNavigationLinks().add(link); - } - } + // set the link - containing the expanded data - to the current entity + responseEntity.getNavigationLinks().add(link); + } + } + // 4. serialize + EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType(); + // we need the property names of the $select, in order to build the context URL + String selectList = odata.createUriHelper().buildContextURLSelectList(edmEntityType, expandOption, selectOption); + ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet) + .selectList(selectList) + .suffix(Suffix.ENTITY).build(); - // 4. serialize - EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType(); - // we need the property names of the $select, in order to build the context URL - String selectList = odata.createUriHelper().buildContextURLSelectList(edmEntityType, expandOption, selectOption); - ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet) - .selectList(selectList) - .suffix(Suffix.ENTITY).build(); + // make sure that $expand and $select are considered by the serializer + // adding the selectOption to the serializerOpts will actually tell the lib to do the job + EntitySerializerOptions opts = EntitySerializerOptions.with() + .contextURL(contextUrl) + .select(selectOption) + .expand(expandOption) + .build(); - // make sure that $expand and $select are considered by the serializer - // adding the selectOption to the serializerOpts will actually tell the lib to do the job - EntitySerializerOptions opts = EntitySerializerOptions.with() - .contextURL(contextUrl) - .select(selectOption) - .expand(expandOption) - .build(); + ODataSerializer serializer = this.odata.createSerializer(responseFormat); + SerializerResult serializerResult = serializer.entity(serviceMetadata, edmEntityType, responseEntity, opts); - ODataSerializer serializer = this.odata.createSerializer(responseFormat); - SerializerResult serializerResult = serializer.entity(serviceMetadata, edmEntityType, responseEntity, opts); + // 5. configure the response object + response.setContent(serializerResult.getContent()); + response.setStatusCode(HttpStatusCode.OK.getStatusCode()); + response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); + } - // 5. configure the response object - response.setContent(serializerResult.getContent()); - response.setStatusCode(HttpStatusCode.OK.getStatusCode()); - response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); - } - - /* + /* * Example request: * * POST URL: http://localhost:8080/DemoService/DemoService.svc/Products * Header: Content-Type: application/json; odata.metadata=minimal * Request body: - { - "ID":3, - "Name":"Ergo Screen", - "Description":"17 Optimum Resolution 1024 x 768 @ 85Hz, resolution 1280 x 960" - } - * */ + * { + * "ID":3, + * "Name":"Ergo Screen", + * "Description":"17 Optimum Resolution 1024 x 768 @ 85Hz, resolution 1280 x 960" + * } + */ public void createEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, - ContentType requestFormat, ContentType responseFormat) - throws ODataApplicationException, DeserializerException, SerializerException { - - // 1. Retrieve the entity type from the URI + ContentType requestFormat, ContentType responseFormat) + throws ODataApplicationException, DeserializerException, SerializerException { + + // 1. Retrieve the entity type from the URI EdmEntitySet edmEntitySet = Util.getEdmEntitySet(uriInfo); EdmEntityType edmEntityType = edmEntitySet.getEntityType(); - // 2. create the data in backend + // 2. create the data in backend // 2.1. retrieve the payload from the POST request for the entity to create and deserialize it InputStream requestInputStream = request.getBody(); ODataDeserializer deserializer = this.odata.createDeserializer(requestFormat); @@ -265,34 +316,35 @@ public class DemoEntityProcessor implements EntityProcessor { Entity requestEntity = result.getEntity(); // 2.2 do the creation in backend, which returns the newly created entity Entity createdEntity = storage.createEntityData(edmEntitySet, requestEntity); - + // 3. serialize the response (we have to return the created entity) - ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build(); - EntitySerializerOptions options = EntitySerializerOptions.with().contextURL(contextUrl).build(); // expand and select currently not supported - + ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build(); + EntitySerializerOptions options = EntitySerializerOptions.with().contextURL(contextUrl).build(); // expand and + // select currently + // not supported + ODataSerializer serializer = this.odata.createSerializer(responseFormat); SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options); - - //4. configure the response object + + // 4. configure the response object response.setContent(serializedResponse.getContent()); response.setStatusCode(HttpStatusCode.CREATED.getStatusCode()); response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString()); } - public void updateEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo, - ContentType requestFormat, ContentType responseFormat) - throws ODataApplicationException, DeserializerException, SerializerException { - - // 1. Retrieve the entity set which belongs to the requested entity + ContentType requestFormat, ContentType responseFormat) + throws ODataApplicationException, DeserializerException, SerializerException { + + // 1. Retrieve the entity set which belongs to the requested entity List resourcePaths = uriInfo.getUriResourceParts(); // Note: only in our example we can assume that the first segment is the EntitySet - UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); + UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet(); EdmEntityType edmEntityType = edmEntitySet.getEntityType(); // 2. update the data in backend - // 2.1. retrieve the payload from the PUT request for the entity to be updated + // 2.1. retrieve the payload from the PUT request for the entity to be updated InputStream requestInputStream = request.getBody(); ODataDeserializer deserializer = this.odata.createDeserializer(requestFormat); DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType); @@ -302,26 +354,25 @@ public class DemoEntityProcessor implements EntityProcessor { // Note that this updateEntity()-method is invoked for both PUT or PATCH operations HttpMethod httpMethod = request.getMethod(); storage.updateEntityData(edmEntitySet, keyPredicates, requestEntity, httpMethod); - - //3. configure the response object + + // 3. configure the response object response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); } - public void deleteEntity(ODataRequest request, ODataResponse response, UriInfo uriInfo) - throws ODataApplicationException { - - // 1. Retrieve the entity set which belongs to the requested entity + throws ODataApplicationException { + + // 1. Retrieve the entity set which belongs to the requested entity List resourcePaths = uriInfo.getUriResourceParts(); // Note: only in our example we can assume that the first segment is the EntitySet - UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); + UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0); EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet(); // 2. delete the data in backend List keyPredicates = uriResourceEntitySet.getKeyPredicates(); storage.deleteEntityData(edmEntitySet, keyPredicates); - - //3. configure the response object + + // 3. configure the response object response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode()); } } diff --git a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/web/DemoServlet.java b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/web/DemoServlet.java index fe5cdbb38..a07f991d4 100644 --- a/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/web/DemoServlet.java +++ b/samples/tutorials/p0_all/src/main/java/myservice/mynamespace/web/DemoServlet.java @@ -28,6 +28,7 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import myservice.mynamespace.data.Storage; +import myservice.mynamespace.service.DemoActionProcessor; import myservice.mynamespace.service.DemoEdmProvider; import myservice.mynamespace.service.DemoEntityCollectionProcessor; import myservice.mynamespace.service.DemoEntityProcessor; @@ -63,6 +64,7 @@ public class DemoServlet extends HttpServlet { handler.register(new DemoEntityCollectionProcessor(storage)); handler.register(new DemoEntityProcessor(storage)); handler.register(new DemoPrimitiveProcessor(storage)); + handler.register(new DemoActionProcessor(storage)); // let the handler do the work handler.process(req, resp); diff --git a/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/data/Storage.java b/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/data/Storage.java index 62efa7f16..97d9f5069 100644 --- a/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/data/Storage.java +++ b/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/data/Storage.java @@ -108,7 +108,7 @@ public class Storage { resetDataSet(Integer.MAX_VALUE); } - public void resetDataSet(final Integer amount) { + public void resetDataSet(final int amount) { // Replace the old lists with empty ones productList = new ArrayList(); categoryList = new ArrayList(); diff --git a/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java b/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java index 0de668185..e2aaed19c 100644 --- a/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java +++ b/samples/tutorials/p9_action/src/main/java/myservice/mynamespace/service/DemoActionProcessor.java @@ -61,7 +61,7 @@ public class DemoActionProcessor implements ActionVoidProcessor { .get(0)).getAction(); // 2nd Deserialize the parameter - // In our case there is only one action. So we can be sure that parameter has been provided by the client + // In our case there is only one action. So we can be sure that parameter "Amount" has been provided by the client if (requestFormat == null) { throw new ODataApplicationException("The content type has not been set in the request.", HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ROOT);