[OLINGO-713] Batch and Deep Insert Tutorial

This commit is contained in:
Christian Holzer 2015-11-03 17:09:12 +01:00
parent 734ea91988
commit a16c9d9c06
40 changed files with 5863 additions and 274 deletions

View File

@ -22,54 +22,77 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import myservice.mynamespace.service.DemoEdmProvider;
import myservice.mynamespace.util.Util;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.Edm;
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.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpMethod;
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.ServiceMetadata;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.uri.UriParameter;
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 myservice.mynamespace.service.DemoEdmProvider;
import myservice.mynamespace.util.Util;
public class Storage {
/** Special property to store the media content **/
private static final String MEDIA_PROPERTY_NAME = "$value";
// represent our database
private List<Entity> productList;
private List<Entity> categoryList;
private List<Entity> advertisements;
final private TransactionalEntityManager manager;
final private Edm edm;
final private OData odata;
public Storage() {
productList = new ArrayList<Entity>();
categoryList = new ArrayList<Entity>();
advertisements = new ArrayList<Entity>();
// represent our database
public Storage(final OData odata, final Edm edm) {
this.odata = odata;
this.edm = edm;
manager = new TransactionalEntityManager(edm);
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
// creating some sample data
initProductSampleData();
initCategorySampleData();
initAdvertisementSampleData();
linkProductsAndCategories(productList.size());
}
/* PUBLIC FACADE */
public void beginTransaction() {
manager.beginTransaction();
}
public void rollbackTranscation() {
manager.rollbackTransaction();
}
public void commitTransaction() {
manager.commitTransaction();
}
public Entity readFunctionImportEntity(final UriResourceFunction uriResourceFunction,
final ServiceMetadata serviceMetadata) throws ODataApplicationException {
@ -95,12 +118,11 @@ public class Storage {
.getStatusCode(), Locale.ENGLISH);
}
final EdmEntityType productEntityType = serviceMetadata.getEdm().getEntityType(DemoEdmProvider.ET_PRODUCT_FQN);
final List<Entity> resultEntityList = new ArrayList<Entity>();
// Loop over all categories and check how many products are linked
for (final Entity category : categoryList) {
final EntityCollection products = getRelatedEntityCollection(category, productEntityType);
for (final Entity category : manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME)) {
final EntityCollection products = getRelatedEntityCollection(category, DemoEdmProvider.NAV_TO_PRODUCTS);
if (products.getEntities().size() == amount) {
resultEntityList.add(category);
}
@ -121,31 +143,39 @@ public class Storage {
public void resetDataSet(final int amount) {
// Replace the old lists with empty ones
productList = new ArrayList<Entity>();
categoryList = new ArrayList<Entity>();
manager.clear();
// Create new sample data
initProductSampleData();
initCategorySampleData();
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
// Truncate the lists
if (amount < productList.size()) {
productList = productList.subList(0, amount);
final List<Entity> newProductList = new ArrayList<Entity>(productList.subList(0, amount));
productList.clear();
productList.addAll(newProductList);
// 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);
final List<Entity> newCategoryList = new ArrayList<Entity>(categoryList.subList(0, (amount / 2) + 1));
categoryList.clear();
categoryList.addAll(newCategoryList);
}
linkProductsAndCategories(amount);
}
public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet) throws ODataApplicationException {
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntityCollection(productList);
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntityCollection(categoryList);
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_ADVERTISEMENTS_NAME)) {
return getEntityCollection(advertisements);
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME));
}
return null;
@ -157,76 +187,52 @@ public class Storage {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntity(edmEntityType, keyParams, productList);
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntity(edmEntityType, keyParams, categoryList);
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_ADVERTISEMENTS_NAME)) {
return getEntity(edmEntityType, keyParams, advertisements);
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME));
}
return null;
}
// Navigation
public Entity getRelatedEntity(Entity entity, EdmEntityType relatedEntityType) {
EntityCollection collection = getRelatedEntityCollection(entity, relatedEntityType);
if (collection.getEntities().isEmpty()) {
return null;
public Entity getRelatedEntity(Entity entity, UriResourceNavigation navigationResource)
throws ODataApplicationException {
final EdmNavigationProperty edmNavigationProperty = navigationResource.getProperty();
if(edmNavigationProperty.isCollection()) {
return Util.findEntity(edmNavigationProperty.getType(), getRelatedEntityCollection(entity, navigationResource),
navigationResource.getKeyPredicates());
} else {
final Link link = entity.getNavigationLink(edmNavigationProperty.getName());
return link == null ? null : link.getInlineEntity();
}
return collection.getEntities().get(0);
}
public Entity getRelatedEntity(Entity entity, EdmEntityType relatedEntityType, List<UriParameter> keyPredicates) {
EntityCollection relatedEntities = getRelatedEntityCollection(entity, relatedEntityType);
return Util.findEntity(relatedEntityType, relatedEntities, keyPredicates);
public EntityCollection getRelatedEntityCollection(Entity entity, UriResourceNavigation navigationResource) {
return getRelatedEntityCollection(entity, navigationResource.getProperty().getName());
}
public EntityCollection getRelatedEntityCollection(Entity sourceEntity, EdmEntityType targetEntityType) {
EntityCollection navigationTargetEntityCollection = new EntityCollection();
FullQualifiedName relatedEntityFqn = targetEntityType.getFullQualifiedName();
String sourceEntityFqn = sourceEntity.getType();
if (sourceEntityFqn.equals(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString())
&& relatedEntityFqn.equals(DemoEdmProvider.ET_CATEGORY_FQN)) {
// relation Products->Category (result all categories)
int productID = (Integer) sourceEntity.getProperty("ID").getValue();
if (productID == 0 || productID == 1) {
navigationTargetEntityCollection.getEntities().add(categoryList.get(0));
} else if (productID == 2 || productID == 3) {
navigationTargetEntityCollection.getEntities().add(categoryList.get(1));
} 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)) {
// relation Category->Products (result all products)
int categoryID = (Integer) sourceEntity.getProperty("ID").getValue();
if (categoryID == 0) {
// the first 2 products are notebooks
navigationTargetEntityCollection.getEntities().addAll(productList.subList(0, 2));
} else if (categoryID == 1) {
// the next 2 products are organizers
navigationTargetEntityCollection.getEntities().addAll(productList.subList(2, 4));
} else if (categoryID == 2) {
// the first 2 products are monitors
navigationTargetEntityCollection.getEntities().addAll(productList.subList(4, 6));
}
}
return navigationTargetEntityCollection;
public EntityCollection getRelatedEntityCollection(Entity entity, String navigationPropertyName) {
final Link link = entity.getNavigationLink(navigationPropertyName);
return link == null ? new EntityCollection() : link.getInlineEntitySet();
}
public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate) {
public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate, String rawServiceUri)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return createEntity(edmEntityType, entityToCreate, productList);
} else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return createEntity(edmEntityType, entityToCreate, categoryList);
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME), rawServiceUri);
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME), rawServiceUri);
}
return null;
@ -241,11 +247,14 @@ public class Storage {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod, productList);
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod, categoryList);
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_ADVERTISEMENTS_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod, advertisements);
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME));
}
}
@ -255,11 +264,11 @@ public class Storage {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
deleteEntity(edmEntityType, keyParams, productList);
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if (edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
deleteEntity(edmEntityType, keyParams, categoryList);
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_ADVERTISEMENTS_NAME)) {
deleteEntity(edmEntityType, keyParams, advertisements);
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME));
}
}
@ -286,7 +295,7 @@ public class Storage {
entity.setMediaContentType(mediaContentType);
entity.addProperty(new Property(null, MEDIA_PROPERTY_NAME, ValueType.PRIMITIVE, data));
advertisements.add(entity);
manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME).add(entity);
}
return entity;
@ -294,27 +303,83 @@ public class Storage {
/* INTERNAL */
private Entity createEntity(EdmEntityType edmEntityType, Entity entity, List<Entity> entityList) {
// the ID of the newly created entity is generated automatically
private Entity createEntity(EdmEntitySet edmEntitySet, EdmEntityType edmEntityType, Entity entity,
List<Entity> entityList, final String rawServiceUri) throws ODataApplicationException {
// 1.) Create the entity
final Entity newEntity = new Entity();
newEntity.setType(entity.getType());
// Create the new key of the entity
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));
// Add all provided properties
newEntity.getProperties().addAll(entity.getProperties());
// Add the key property
newEntity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId));
newEntity.setId(createId(newEntity, "ID"));
// 2.1.) Apply binding links
for(final Link link : entity.getNavigationBindings()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() && link.getBindingLinks() != null) {
for(final String bindingLink : link.getBindingLinks()) {
final Entity relatedEntity = readEntityByBindingLink(bindingLink, targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
} else if(!edmNavigationProperty.isCollection() && link.getBindingLink() != null) {
final Entity relatedEntity = readEntityByBindingLink(link.getBindingLink(), targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
}
entity.setId(createId(entity, "ID"));
entityList.add(entity);
return entity;
// 2.2.) Create nested entities
for(final Link link : entity.getNavigationLinks()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() && link.getInlineEntitySet() != null) {
for(final Entity nestedEntity : link.getInlineEntitySet().getEntities()) {
final Entity newNestedEntity = createEntityData(targetEntitySet, nestedEntity, rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
} else if(!edmNavigationProperty.isCollection() && link.getInlineEntity() != null){
final Entity newNestedEntity = createEntityData(targetEntitySet, link.getInlineEntity(), rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
}
entityList.add(newEntity);
return newEntity;
}
private Entity readEntityByBindingLink(final String entityId, final EdmEntitySet edmEntitySet,
final String rawServiceUri) throws ODataApplicationException {
UriResourceEntitySet entitySetResource = null;
try {
entitySetResource = odata.createUriHelper().parseEntityId(edm, entityId, rawServiceUri);
if(!entitySetResource.getEntitySet().getName().equals(edmEntitySet.getName())) {
throw new ODataApplicationException("Execpted an entity-id for entity set " + edmEntitySet.getName()
+ " but found id for entity set " + entitySetResource.getEntitySet().getName(),
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
} catch (DeserializerException e) {
throw new ODataApplicationException(entityId + " is not a valid entity-Id",
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
return readEntityData(entitySetResource.getEntitySet(), entitySetResource.getKeyPredicates());
}
private EntityCollection getEntityCollection(final List<Entity> entityList) {
EntityCollection retEntitySet = new EntityCollection();
@ -417,7 +482,7 @@ public class Storage {
}
private void initProductSampleData() {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
Entity entity = new Entity();
entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE, 0));
@ -475,22 +540,22 @@ public class Storage {
}
private void initCategorySampleData() {
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
Entity entity = new Entity();
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, 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, 2));
entity.addProperty(new Property(null, "Name", ValueType.PRIMITIVE, "Monitors"));
@ -500,7 +565,8 @@ public class Storage {
}
private void initAdvertisementSampleData() {
final List<Entity> advertisements = manager.getEntityCollection(DemoEdmProvider.ES_ADVERTISEMENTS_NAME);
Entity entity = new Entity();
entity.addProperty(new Property(null, "ID", ValueType.PRIMITIVE,
UUID.fromString("f89dee73-af9f-4cd4-b330-db93c25ff3c7")));
@ -520,6 +586,43 @@ private void initAdvertisementSampleData() {
advertisements.add(entity);
}
private void linkProductsAndCategories(final int numberOfProducts) {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
if(numberOfProducts >= 1) {
setLink(productList.get(0), "Category", categoryList.get(0));
}
if(numberOfProducts >= 2) {
setLink(productList.get(1), "Category", categoryList.get(0));
}
if(numberOfProducts >= 3) {
setLink(productList.get(2), "Category", categoryList.get(1));
}
if(numberOfProducts >= 4) {
setLink(productList.get(3), "Category", categoryList.get(1));
}
if(numberOfProducts >= 5) {
setLink(productList.get(4), "Category", categoryList.get(2));
}
if(numberOfProducts >= 6) {
setLink(productList.get(5), "Category", categoryList.get(2));
}
if (numberOfProducts >= 1) {
setLinks(categoryList.get(0), "Products",
productList.subList(0, Math.min(2, numberOfProducts)).toArray(new Entity[0]));
}
if (numberOfProducts >= 3) {
setLinks(categoryList.get(1), "Products",
productList.subList(2, Math.min(4, numberOfProducts)).toArray(new Entity[0]));
}
if (numberOfProducts >= 5) {
setLinks(categoryList.get(2), "Products",
productList.subList(4, Math.min(6, numberOfProducts)).toArray(new Entity[0]));
}
}
private URI createId(Entity entity, String idPropertyName) {
return createId(entity, idPropertyName, null);
}
@ -546,4 +649,60 @@ private void initAdvertisementSampleData() {
}
return entity.getType();
}
private void createLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity destEntity) {
setLink(navigationProperty, srcEntity, destEntity);
final EdmNavigationProperty partnerNavigationProperty = navigationProperty.getPartner();
if (partnerNavigationProperty != null) {
setLink(partnerNavigationProperty, destEntity, srcEntity);
}
}
private void setLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity targetEntity) {
if (navigationProperty.isCollection()) {
setLinks(srcEntity, navigationProperty.getName(), targetEntity);
} else {
setLink(srcEntity, navigationProperty.getName(), targetEntity);
}
}
private void setLink(final Entity entity, final String navigationPropertyName, final Entity target) {
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(target.getId().toASCIIString());
entity.getNavigationLinks().add(link);
}
link.setInlineEntity(target);
}
private void setLinks(final Entity entity, final String navigationPropertyName, final Entity... targets) {
if(targets.length == 0) {
return;
}
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(entity.getId().toASCIIString() + "/" + navigationPropertyName);
EntityCollection target = new EntityCollection();
target.getEntities().addAll(Arrays.asList(targets));
link.setInlineEntitySet(target);
entity.getNavigationLinks().add(link);
} else {
link.getInlineEntitySet().getEntities().addAll(Arrays.asList(targets));
}
}
}

View File

@ -0,0 +1,185 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package myservice.mynamespace.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
public class TransactionalEntityManager {
private Map<String, List<Entity>> entities = new HashMap<String, List<Entity>>();
private Map<String, List<Entity>> backupEntities = new HashMap<String, List<Entity>>();
private Map<String, IdentityHashMap<Entity, Entity>> copyMap = new HashMap<String, IdentityHashMap<Entity, Entity>>();
private boolean isInTransaction = false;
private Edm edm;
public TransactionalEntityManager(final Edm edm) {
this.edm = edm;
}
public List<Entity> getEntityCollection(final String entitySetName) {
if(!entities.containsKey(entitySetName)) {
entities.put(entitySetName, new ArrayList<Entity>());
}
return entities.get(entitySetName);
}
public void beginTransaction() {
if(!isInTransaction) {
isInTransaction = true;
copyCurrentState();
}
}
public void rollbackTransaction() {
if(isInTransaction) {
entities = backupEntities;
backupEntities = new HashMap<String, List<Entity>>();
isInTransaction = false;
}
}
public void commitTransaction() {
if(isInTransaction) {
backupEntities.clear();
isInTransaction = false;
}
}
public void clear() {
entities.clear();
backupEntities.clear();
}
private void copyCurrentState() {
copyMap.clear();
backupEntities.clear();
for(final String entitySetName : entities.keySet()) {
final List<Entity> entityList = entities.get(entitySetName);
backupEntities.put(entitySetName, new ArrayList<Entity>());
final List<Entity> backupEntityList = backupEntities.get(entitySetName);
for(final Entity entity : entityList) {
final EdmEntitySet entitySet = edm.getEntityContainer().getEntitySet(entitySetName);
backupEntityList.add(copyEntityRecursively(entitySet, entity));
}
}
}
private Entity copyEntityRecursively(final EdmEntitySet edmEntitySet, final Entity entity) {
// Check if entity is already copied
if(containsEntityInCopyMap(edmEntitySet.getName(), entity)) {
return getEntityFromCopyMap(edmEntitySet.getName(), entity);
} else {
final Entity newEntity = copyEntity(entity);
addEntityToCopyMap(edmEntitySet.getName(), entity, newEntity);
// Create nested entities recursively
for(final Link link : entity.getNavigationLinks()) {
newEntity.getNavigationLinks().add(copyLink(edmEntitySet, link));
}
return newEntity;
}
}
private Link copyLink(final EdmEntitySet edmEntitySet, final Link link) {
final Link newLink = new Link();
newLink.setBindingLink(link.getBindingLink());
newLink.setBindingLinks(new ArrayList<String>(link.getBindingLinks()));
newLink.setHref(link.getHref());
newLink.setMediaETag(link.getMediaETag());
newLink.setRel(link.getRel());
newLink.setTitle(link.getTitle());
newLink.setType(link.getType());
// Single navigation link
if(link.getInlineEntity() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
newLink.setInlineEntity(copyEntityRecursively(linkedEdmEntitySet, link.getInlineEntity()));
}
// Collection navigation link
if(link.getInlineEntitySet() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
final EntityCollection inlineEntitySet = link.getInlineEntitySet();
final EntityCollection newInlineEntitySet = new EntityCollection();
newInlineEntitySet.setBaseURI(inlineEntitySet.getBaseURI());
newInlineEntitySet.setCount(inlineEntitySet.getCount());
newInlineEntitySet.setDeltaLink(inlineEntitySet.getDeltaLink());
newInlineEntitySet.setId(inlineEntitySet.getId());
newInlineEntitySet.setNext(inlineEntitySet.getNext());
for(final Entity inlineEntity : inlineEntitySet.getEntities()) {
newInlineEntitySet.getEntities().add(copyEntityRecursively(linkedEdmEntitySet, inlineEntity));
}
newLink.setInlineEntitySet(newInlineEntitySet);
}
return newLink;
}
private Entity copyEntity(final Entity entity) {
final Entity newEntity = new Entity();
newEntity.setBaseURI(entity.getBaseURI());
newEntity.setEditLink(entity.getEditLink());
newEntity.setETag(entity.getETag());
newEntity.setId(entity.getId());
newEntity.setMediaContentSource(entity.getMediaContentSource());
newEntity.setMediaContentType(entity.getMediaContentType());
newEntity.setSelfLink(entity.getSelfLink());
newEntity.setMediaETag(entity.getMediaETag());
newEntity.setType(entity.getType());
newEntity.getProperties().addAll(entity.getProperties());
return newEntity;
}
private void addEntityToCopyMap(final String entitySetName, final Entity srcEntity, final Entity destEntity) {
if(!copyMap.containsKey(entitySetName)) {
copyMap.put(entitySetName, new IdentityHashMap<Entity, Entity>());
}
copyMap.get(entitySetName).put(srcEntity, destEntity);
}
private boolean containsEntityInCopyMap(final String entitySetName, final Entity srcEntity) {
return getEntityFromCopyMap(entitySetName, srcEntity) != null;
}
private Entity getEntityFromCopyMap(final String entitySetName, final Entity srcEntity) {
if(!copyMap.containsKey(entitySetName)) {
return null;
}
return copyMap.get(entitySetName).get(srcEntity);
}
}

View File

@ -0,0 +1,163 @@
/*
* 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.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.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.batch.BatchFacade;
import org.apache.olingo.server.api.deserializer.batch.BatchOptions;
import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart;
import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart;
import org.apache.olingo.server.api.processor.BatchProcessor;
import myservice.mynamespace.data.Storage;
public class DemoBatchProcessor implements BatchProcessor {
private OData odata;
private Storage storage;
public DemoBatchProcessor(final Storage storage) {
this.storage = storage;
}
@Override
public void init(final OData odata, final ServiceMetadata serviceMetadata) {
this.odata = odata;
}
@Override
public void processBatch(final BatchFacade facade, final ODataRequest request, final ODataResponse response)
throws ODataApplicationException, ODataLibraryException {
// 1. Extract the boundary
final String boundary = facade.extractBoundaryFromContentType(request.getHeader(HttpHeader.CONTENT_TYPE));
// 2. Prepare the batch options
final BatchOptions options = BatchOptions.with().rawBaseUri(request.getRawBaseUri())
.rawServiceResolutionUri(request.getRawServiceResolutionUri())
.build();
// 3. Deserialize the batch request
final List<BatchRequestPart> requestParts = odata.createFixedFormatDeserializer()
.parseBatchRequest(request.getBody(), boundary, options);
// 4. Execute the batch request parts
final List<ODataResponsePart> responseParts = new ArrayList<ODataResponsePart>();
for (final BatchRequestPart part : requestParts) {
responseParts.add(facade.handleBatchRequest(part));
}
// 5. Serialize the response content
final InputStream responseContent = odata.createFixedFormatSerializer().batchResponse(responseParts, boundary);
// 6. Create a new boundary for the response
final String responseBoundary = "batch_" + UUID.randomUUID().toString();
// 7. Setup response
response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.MULTIPART_MIXED + ";boundary=" + responseBoundary);
response.setContent(responseContent);
response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
}
@Override
public ODataResponsePart processChangeSet(final BatchFacade facade, final List<ODataRequest> requests)
throws ODataApplicationException, ODataLibraryException {
/*
* OData Version 4.0 Part 1: Protocol Plus Errata 02
* 11.7.4 Responding to a Batch Request
*
* All operations in a change set represent a single change unit so a service MUST successfully process and
* apply all the requests in the change set or else apply none of them. It is up to the service implementation
* to define rollback semantics to undo any requests within a change set that may have been applied before
* another request in that same change set failed and thereby apply this all-or-nothing requirement.
* The service MAY execute the requests within a change set in any order and MAY return the responses to the
* individual requests in any order. The service MUST include the Content-ID header in each response with the
* same value that the client specified in the corresponding request, so clients can correlate requests
* and responses.
*
* To keep things simple, we dispatch the requests within the change set to the other processor interfaces.
*/
final List<ODataResponse> responses = new ArrayList<ODataResponse>();
try {
storage.beginTransaction();
for(final ODataRequest request : requests) {
// Actual request dispatching to the other processor interfaces.
final ODataResponse response = facade.handleODataRequest(request);
// Determine if an error occurred while executing the request.
// Exceptions thrown by the processors get caught and result in a proper OData response.
final int statusCode = response.getStatusCode();
if(statusCode < 400) {
// The request has been executed successfully. Return the response as a part of the change set
responses.add(response);
} else {
// Something went wrong. Undo all previous requests in this Change Set
storage.rollbackTranscation();
/*
* In addition the response must be provided as follows:
*
* OData Version 4.0 Part 1: Protocol Plus Errata 02
* 11.7.4 Responding to a Batch Request
*
* When a request within a change set fails, the change set response is not represented using
* the multipart/mixed media type. Instead, a single response, using the application/http media type
* and a Content-Transfer-Encoding header with a value of binary, is returned that applies to all requests
* in the change set and MUST be formatted according to the Error Handling defined
* for the particular response format.
*
* This can be simply done by passing the response of the failed ODataRequest to a new instance of
* ODataResponsePart and setting the second parameter "isChangeSet" to false.
*/
return new ODataResponsePart(response, false);
}
}
// Everything went well, so commit the changes.
storage.commitTransaction();
return new ODataResponsePart(responses, true);
} catch(ODataApplicationException e) {
// See below
storage.rollbackTranscation();
throw e;
} catch(ODataLibraryException e) {
// The request is malformed or the processor implementation is not correct.
// Throwing an exception will stop the whole batch request not only the change set!
storage.rollbackTranscation();
throw e;
}
}
}

View File

@ -19,26 +19,18 @@
package myservice.mynamespace.service;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import myservice.mynamespace.data.Storage;
import org.apache.olingo.commons.api.Constants;
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.data.Link;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
@ -55,6 +47,7 @@ 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.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;
import org.apache.olingo.server.api.uri.UriResourceFunction;
@ -73,6 +66,9 @@ 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.Member;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
public class DemoEntityCollectionProcessor implements EntityCollectionProcessor {
private OData odata;
@ -135,17 +131,59 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor
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<UriResource> resourcePaths = uriInfo.getUriResourceParts();
// in our example, the first segment is the EntitySet
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0);
EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
// Read the collection or process ONE navigation property
EdmEntitySet edmEntitySet = null; // we'll need this to build the ContextURL
EntityCollection entityCollection = null; // we'll need this to set the response body
// 2nd: fetch the data from backend for this requested EntitySetName and deliver as EntitySet
EntityCollection entityCollection = storage.readEntitySetData(edmEntitySet);
// 1st retrieve the requested EntitySet from the uriInfo (representation of the parsed URI)
List<UriResource> resourceParts = uriInfo.getUriResourceParts();
int segmentCount = resourceParts.size();
UriResource uriResource = resourceParts.get(0); // in our example, the first segment is the EntitySet
if (!(uriResource instanceof UriResourceEntitySet)) {
throw new ODataApplicationException("Only EntitySet is supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
if (segmentCount == 1) { // this is the case for: DemoService/DemoService.svc/Categories
edmEntitySet = startEdmEntitySet; // the response body is built from the first (and only) entitySet
// 2nd: fetch the data from backend for this requested EntitySetName and deliver as EntitySet
entityCollection = storage.readEntitySetData(startEdmEntitySet);
} else if (segmentCount == 2) { // in case of navigation: DemoService.svc/Categories(3)/Products
UriResource lastSegment = resourceParts.get(1); // in our example we don't support more complex URIs
if (lastSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) lastSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
// from Categories(1) to Products
edmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
// 2nd: fetch the data from backend
// first fetch the entity where the first segment of the URI points to
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// e.g. for Categories(3)/Products we have to find the single entity: Category with ID 3
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
// error handling for e.g. DemoService.svc/Categories(99)/Products
if (sourceEntity == null) {
throw new ODataApplicationException("Entity not found.",
HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
}
// then fetch the entity collection where the entity navigates to
// note: we don't need to check uriResourceNavigation.isCollection(),
// because we are the EntityCollectionProcessor
entityCollection = storage.getRelatedEntityCollection(sourceEntity, uriResourceNavigation);
}
} else { // this would be the case for e.g. Products(1)/Category/Products
throw new ODataApplicationException("Not supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
List<Entity> modifiedEntityList = entityCollection.getEntities();
EntityCollection modifiedEntityCollection = new EntityCollection();
List<Entity> modifiedEntityList = new ArrayList<Entity>();
modifiedEntityList.addAll(entityCollection.getEntities());
// 3rd: Apply system query option
// The system query options have to be applied in a defined order
@ -161,7 +199,8 @@ 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(modifiedEntityList, edmEntitySet, uriInfo.getExpandOption());
// Nested system query options are not implemented
validateNestedExpxandSystemQueryOptions(uriInfo.getExpandOption());
// 3.8.) $select
SelectOption selectOption = uriInfo.getSelectOption();
@ -198,73 +237,6 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
}
private List<Entity> applyExpandQueryOption(List<Entity> modifiedEntityList,
EdmEntitySet edmEntitySet, ExpandOption expandOption) {
// in our example: http://localhost:8080/DemoService/DemoService.svc/Categories/$expand=Products
// or http://localhost:8080/DemoService/DemoService.svc/Products?$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<EdmNavigationPropertyBinding> bindings = edmEntitySet.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 = edmEntitySet.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 uriResource = expandItem.getResourcePath().getUriResourceParts().get(0);
// we don't need to handle error cases, as it is done in the Olingo library
if (uriResource instanceof UriResourceNavigation) {
edmNavigationProperty = ((UriResourceNavigation) uriResource).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) {
String navPropName = edmNavigationProperty.getName();
EdmEntityType expandEdmEntityType = edmNavigationProperty.getType();
for (Entity entity : modifiedEntityList) {
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);
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 != null ? expandEntity.getId().toASCIIString() : null);
}
// set the link - containing the expanded data - to the current entity
entity.getNavigationLinks().add(link);
}
}
}
return modifiedEntityList;
}
private List<Entity> applyTopQueryOption(List<Entity> entityList, TopOption topOption)
throws ODataApplicationException {
@ -364,7 +336,29 @@ public class DemoEntityCollectionProcessor implements EntityCollectionProcessor
return entityList;
}
private void validateNestedExpxandSystemQueryOptions(final ExpandOption expandOption)
throws ODataApplicationException {
if(expandOption == null) {
return;
}
for(final ExpandItem item : expandOption.getExpandItems()) {
if( item.getCountOption() != null
|| item.getFilterOption() != null
|| item.getLevelsOption() != null
|| item.getOrderByOption() != null
|| item.getSearchOption() != null
|| item.getSelectOption() != null
|| item.getSkipOption() != null
|| item.getTopOption() != null) {
throw new ODataApplicationException("Nested expand system query options are not implemented",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),Locale.ENGLISH);
}
}
}
private List<Entity> applyFilterQueryOption(List<Entity> entityList, FilterOption filterOption)
throws ODataApplicationException {

View File

@ -22,17 +22,12 @@ import java.io.InputStream;
import java.util.List;
import java.util.Locale;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.edm.EdmElement;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.EdmNavigationPropertyBinding;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
@ -134,7 +129,6 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
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
@ -163,7 +157,6 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
if (navSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) navSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
responseEdmEntityType = edmNavigationProperty.getType();
// contextURL displays the last segment
responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
@ -173,17 +166,7 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
// e.g. for Products(1)/Category we have to find first the Products(1)
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
// now we have to check if the navigation is
// a) to-one: e.g. Products(1)/Category
// b) to-many with key: e.g. Categories(3)/Products(5)
// the key for nav is used in this case: Categories(3)/Products(5)
List<UriParameter> navKeyPredicates = uriResourceNavigation.getKeyPredicates();
if (navKeyPredicates.isEmpty()) { // e.g. DemoService.svc/Products(1)/Category
responseEntity = storage.getRelatedEntity(sourceEntity, responseEdmEntityType);
} else { // e.g. DemoService.svc/Categories(3)/Products(5)
responseEntity = storage.getRelatedEntity(sourceEntity, responseEdmEntityType, navKeyPredicates);
}
responseEntity = storage.getRelatedEntity(sourceEntity, uriResourceNavigation);
}
} else {
// this would be the case for e.g. Products(1)/Category/Products(1)/Category
@ -204,67 +187,9 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
// 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<EdmNavigationPropertyBinding> 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();
// 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 =
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
responseEntity.getNavigationLinks().add(link);
}
}
// Nested system query options are not implemented
validateNestedExpxandSystemQueryOptions(expandOption);
// 4. serialize
EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType();
// we need the property names of the $select, in order to build the context URL
@ -290,6 +215,28 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
}
private void validateNestedExpxandSystemQueryOptions(final ExpandOption expandOption)
throws ODataApplicationException {
if(expandOption == null) {
return;
}
for(final ExpandItem item : expandOption.getExpandItems()) {
if( item.getCountOption() != null
|| item.getFilterOption() != null
|| item.getLevelsOption() != null
|| item.getOrderByOption() != null
|| item.getSearchOption() != null
|| item.getSelectOption() != null
|| item.getSkipOption() != null
|| item.getTopOption() != null) {
throw new ODataApplicationException("Nested expand system query options are not implemented",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(),Locale.ENGLISH);
}
}
}
/*
* Example request:
*
@ -317,7 +264,16 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the creation in backend, which returns the newly created entity
Entity createdEntity = storage.createEntityData(edmEntitySet, requestEntity);
Entity createdEntity = null;
try {
storage.beginTransaction();
createdEntity = storage.createEntityData(edmEntitySet, requestEntity, request.getRawBaseUri());
storage.commitTransaction();
} catch(ODataApplicationException e) {
storage.rollbackTranscation();
throw e;
}
// 3. serialize the response (we have to return the created entity)
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build();
@ -329,6 +285,10 @@ public class DemoEntityProcessor implements EntityProcessor, MediaEntityProcesso
SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options);
// 4. configure the response object
final String location = request.getRawBaseUri() + '/'
+ odata.createUriHelper().buildCanonicalURL(edmEntitySet, createdEntity);
response.setHeader(HttpHeader.LOCATION, location);
response.setContent(serializedResponse.getContent());
response.setStatusCode(HttpStatusCode.CREATED.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());

View File

@ -29,6 +29,7 @@ import javax.servlet.http.HttpSession;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.service.DemoActionProcessor;
import myservice.mynamespace.service.DemoBatchProcessor;
import myservice.mynamespace.service.DemoEdmProvider;
import myservice.mynamespace.service.DemoEntityCollectionProcessor;
import myservice.mynamespace.service.DemoEntityProcessor;
@ -49,22 +50,24 @@ public class DemoServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
OData odata = OData.newInstance();
ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>());
try {
HttpSession session = req.getSession(true);
Storage storage = (Storage) session.getAttribute(Storage.class.getName());
if (storage == null) {
storage = new Storage();
storage = new Storage(odata, edm.getEdm());
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));
handler.register(new DemoActionProcessor(storage));
handler.register(new DemoBatchProcessor(storage));
// let the handler do the work
handler.process(req, resp);

View File

@ -0,0 +1,85 @@
<?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-Batch</artifactId>
<packaging>war</packaging>
<version>4.0.0</version>
<name>${project.artifactId}-Webapp</name>
<build>
<finalName>DemoService</finalName>
</build>
<properties>
<javax.version>2.5</javax.version>
<odata.version>4.1.0-SNAPSHOT</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>
</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>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.11</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,316 @@
/*
* 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 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.edm.EdmKeyPropertyRef;
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.uri.UriParameter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public class Storage {
private List<Entity> productList;
private List<Entity> productListBeforeTransaction;
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;
}
public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate) {
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 null;
}
/**
* This method is invoked for PATCH or PUT requests
* */
public void updateEntityData(EdmEntitySet edmEntitySet, List<UriParameter> 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);
}
}
public void deleteEntityData(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)) {
deleteProduct(edmEntityType, keyParams);
}
}
public void beginTransaction() {
if(productListBeforeTransaction == null) {
productListBeforeTransaction = cloneEntityCollection(productList);
}
}
public void commitTransaction() {
if(productListBeforeTransaction != null) {
productListBeforeTransaction = null;
}
}
public void rollbackTranscation() {
if(productListBeforeTransaction != null) {
productList = productListBeforeTransaction;
productListBeforeTransaction = null;
}
}
/* INTERNAL */
private List<Entity> cloneEntityCollection(final List<Entity> entities) {
final List<Entity> clonedEntities = new ArrayList<Entity>();
for(final Entity entity : entities) {
final Entity clonedEntity = new Entity();
clonedEntity.setId(entity.getId());
for(final Property property : entity.getProperties()) {
clonedEntity.addProperty(new Property(property.getType(),
property.getName(),
property.getValueType(),
property.getValue()));
}
clonedEntities.add(clonedEntity);
}
return clonedEntities;
}
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;
}
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("Products", newId));
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;
}
private void updateProduct(EdmEntityType edmEntityType, List<UriParameter> keyParams, Entity entity,
HttpMethod httpMethod) throws ODataApplicationException {
Entity productEntity = getProduct(edmEntityType, keyParams);
if (productEntity == 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<Property> existingProperties = productEntity.getProperties();
for (Property existingProp : existingProperties) {
String propName = existingProp.getName();
// ignore the key properties, they aren't updateable
if (isKey(edmEntityType, propName)) {
continue;
}
Property updateProperty = entity.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
// depending on the HttpMethod, our behavior is different
if (httpMethod.equals(HttpMethod.PATCH)) {
// as of the OData spec, in case of PATCH, the existing property is not touched
continue; // do nothing
} else if (httpMethod.equals(HttpMethod.PUT)) {
// as of the OData spec, in case of PUT, the existing property is set to null (or to default value)
existingProp.setValue(existingProp.getValueType(), null);
continue;
}
}
// change the value of the properties
existingProp.setValue(existingProp.getValueType(), updateProperty.getValue());
}
}
private void deleteProduct(EdmEntityType edmEntityType, List<UriParameter> keyParams)
throws ODataApplicationException {
Entity productEntity = getProduct(edmEntityType, keyParams);
if (productEntity == null) {
throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
}
this.productList.remove(productEntity);
}
/* HELPER */
private boolean isKey(EdmEntityType edmEntityType, String propertyName) {
List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs();
for (EdmKeyPropertyRef propRef : keyPropertyRefs) {
String keyPropertyName = propRef.getName();
if (keyPropertyName.equals(propertyName)) {
return true;
}
}
return false;
}
private void initSampleData(){
// add some sample product entities
final Entity e1 = 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"));
e1.setId(createId("Products", 1));
productList.add(e1);
final Entity e2 = 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"));
e2.setId(createId("Products", 2));
productList.add(e2);
final Entity e3 = 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"));
e3.setId(createId("Products", 3));
productList.add(e3);
}
private URI createId(String entitySetName, Object id) {
try {
return new URI(entitySetName + "(" + String.valueOf(id) + ")");
} catch (URISyntaxException e) {
throw new ODataRuntimeException("Unable to create id for entity: " + entitySetName, e);
}
}
}

View File

@ -0,0 +1,209 @@
/*
* 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.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.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.batch.BatchFacade;
import org.apache.olingo.server.api.deserializer.batch.BatchOptions;
import org.apache.olingo.server.api.deserializer.batch.BatchRequestPart;
import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart;
import org.apache.olingo.server.api.processor.BatchProcessor;
import myservice.mynamespace.data.Storage;
/**
* --abc123
Content-Type: application/http
Content-Transfer-Encoding: binary
GET Products HTTP/1.1
Content-Type: application/json
--abc123
Content-Type: multipart/mixed;boundary=changeset_abc
--changeset_abc
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-Id: abc1234
POST Products HTTP/1.1
Content-Type: application/json
{"Name": "Test", "Description": "This is a test product"}
--changeset_abc
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2
PATCH $abc1234 HTTP/1.1
Content-Type: application/json
Accept: application/json
{"Name": "Bäääääm"}
--changeset_abc--
--abc123
Content-Type: application/http
Content-Transfer-Encoding: binary
GET Products HTTP/1.1
Content-Type: application/json
--abc123--
*
*/
public class DemoBatchProcessor implements BatchProcessor {
private OData odata;
private Storage storage;
public DemoBatchProcessor(final Storage storage) {
this.storage = storage;
}
@Override
public void init(final OData odata, final ServiceMetadata serviceMetadata) {
this.odata = odata;
}
@Override
public void processBatch(final BatchFacade facade, final ODataRequest request, final ODataResponse response)
throws ODataApplicationException, ODataLibraryException {
// 1. Extract the boundary
final String boundary = facade.extractBoundaryFromContentType(request.getHeader(HttpHeader.CONTENT_TYPE));
// 2. Prepare the batch options
final BatchOptions options = BatchOptions.with().rawBaseUri(request.getRawBaseUri())
.rawServiceResolutionUri(request.getRawServiceResolutionUri())
.build();
// 3. Deserialize the batch request
final List<BatchRequestPart> requestParts = odata.createFixedFormatDeserializer()
.parseBatchRequest(request.getBody(), boundary, options);
// 4. Execute the batch request parts
final List<ODataResponsePart> responseParts = new ArrayList<ODataResponsePart>();
for (final BatchRequestPart part : requestParts) {
responseParts.add(facade.handleBatchRequest(part));
}
// 5. Serialize the response content
final InputStream responseContent = odata.createFixedFormatSerializer().batchResponse(responseParts, boundary);
// 6. Create a new boundary for the response
final String responseBoundary = "batch_" + UUID.randomUUID().toString();
// 7. Setup response
response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.MULTIPART_MIXED + ";boundary=" + responseBoundary);
response.setContent(responseContent);
response.setStatusCode(HttpStatusCode.ACCEPTED.getStatusCode());
}
@Override
public ODataResponsePart processChangeSet(final BatchFacade facade, final List<ODataRequest> requests)
throws ODataApplicationException, ODataLibraryException {
/*
* OData Version 4.0 Part 1: Protocol Plus Errata 02
* 11.7.4 Responding to a Batch Request
*
* All operations in a change set represent a single change unit so a service MUST successfully process and
* apply all the requests in the change set or else apply none of them. It is up to the service implementation
* to define rollback semantics to undo any requests within a change set that may have been applied before
* another request in that same change set failed and thereby apply this all-or-nothing requirement.
* The service MAY execute the requests within a change set in any order and MAY return the responses to the
* individual requests in any order. The service MUST include the Content-ID header in each response with the
* same value that the client specified in the corresponding request, so clients can correlate requests
* and responses.
*
* To keep things simple, we dispatch the requests within the change set to the other processor interfaces.
*/
final List<ODataResponse> responses = new ArrayList<ODataResponse>();
try {
storage.beginTransaction();
for(final ODataRequest request : requests) {
// Actual request dispatching to the other processor interfaces.
final ODataResponse response = facade.handleODataRequest(request);
// Determine if an error occurred while executing the request.
// Exceptions thrown by the processors get caught and result in a proper OData response.
final int statusCode = response.getStatusCode();
if(statusCode < 400) {
// The request has been executed successfully. Return the response as a part of the change set
responses.add(response);
} else {
// Something went wrong. Undo all previous requests in this Change Set
storage.rollbackTranscation();
/*
* In addition the response must be provided as follows:
*
* OData Version 4.0 Part 1: Protocol Plus Errata 02
* 11.7.4 Responding to a Batch Request
*
* When a request within a change set fails, the change set response is not represented using
* the multipart/mixed media type. Instead, a single response, using the application/http media type
* and a Content-Transfer-Encoding header with a value of binary, is returned that applies to all requests
* in the change set and MUST be formatted according to the Error Handling defined
* for the particular response format.
*
* This can be simply done by passing the response of the failed ODataRequest to a new instance of
* ODataResponsePart and setting the second parameter "isChangeSet" to false.
*/
return new ODataResponsePart(response, false);
}
}
// Everything went well, so commit the changes.
storage.commitTransaction();
return new ODataResponsePart(responses, true);
} catch(ODataApplicationException e) {
// See below
storage.rollbackTranscation();
throw e;
} catch(ODataLibraryException e) {
// The request is malformed or the processor implementation is not correct.
// Throwing an exception will stop the whole batch request not only the change set!
storage.rollbackTranscation();
throw e;
}
}
}

View File

@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package 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.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;
/*
* this class is supposed to declare the metadata of the OData service
* it is invoked by the Olingo framework e.g. when the metadata document of the service is invoked
* e.g. http://localhost:8080/ExampleService1/ExampleService1.svc/$metadata
*/
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 List<CsdlSchema> getSchemas() {
// 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 CsdlEntityType getEntityType(FullQualifiedName entityTypeName) {
// this method is called for one of the EntityTypes that are configured in the Schema
if(entityTypeName.equals(ET_PRODUCT_FQN)){
//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 CsdlPropertyRef 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) {
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;
}
public CsdlEntityContainer getEntityContainer() {
// 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;
}
@Override
public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) {
// 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;
}
}

View File

@ -0,0 +1,93 @@
/*
* 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 myservice.mynamespace.data.Storage;
import org.apache.olingo.commons.api.data.ContextURL;
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.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;
public class DemoEntityCollectionProcessor implements EntityCollectionProcessor {
private OData odata;
private Storage storage;
private ServiceMetadata serviceMetadata;
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();
// in our example, the first segment is the EntitySet
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) resourcePaths.get(0);
EdmEntitySet edmEntitySet = uriResourceEntitySet.getEntitySet();
// 2nd: fetch the data from backend for this requested EntitySetName
// it has to be delivered as EntitySet object
EntityCollection entitySet = storage.readEntitySetData(edmEntitySet);
// 3rd: create a serializer based on the requested format (json)
ODataSerializer serializer = odata.createSerializer(responseFormat);
// and serialize the content: transform from the EntitySet object to InputStream
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).build();
final String id = request.getRawBaseUri() + "/" + edmEntitySet.getName();
EntityCollectionSerializerOptions opts =
EntityCollectionSerializerOptions.with().id(id).contextURL(contextUrl).build();
SerializerResult serializedContent = serializer.entityCollection(serviceMetadata, edmEntityType, entitySet, opts);
// Finally: configure the response object: set the body, headers and status code
response.setContent(serializedContent.getContent());
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
}
}

View File

@ -0,0 +1,189 @@
/*
* 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 myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
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.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
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.deserializer.DeserializerResult;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
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 Storage storage;
private ServiceMetadata serviceMetadata;
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();
ODataSerializer serializer = this.odata.createSerializer(responseFormat);
SerializerResult result = serializer.entity(serviceMetadata, entityType, entity, options);
//4. configure the response object
response.setContent(result.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"
}
* */
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
EdmEntitySet edmEntitySet = Util.getEdmEntitySet(uriInfo);
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
// 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);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
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
ODataSerializer serializer = this.odata.createSerializer(responseFormat);
SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options);
//4. configure the response object
final String location = request.getRawBaseUri() + '/'
+ odata.createUriHelper().buildCanonicalURL(edmEntitySet, createdEntity);
response.setHeader(HttpHeader.LOCATION, location);
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
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();
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
InputStream requestInputStream = request.getBody();
ODataDeserializer deserializer = this.odata.createDeserializer(requestFormat);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the modification in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// 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
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
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. delete the data in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
storage.deleteEntityData(edmEntitySet, keyPredicates);
//3. configure the response object
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
}
}

View File

@ -0,0 +1,146 @@
/*
* 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 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.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;
private ServiceMetadata serviceMetadata;
public DemoPrimitiveProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/*
* 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
ODataSerializer serializer = odata.createSerializer(responseFormat);
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build();
PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build();
// 3.2. serialize
SerializerResult result = serializer.primitive(serviceMetadata, edmPropertyType, property, options);
//4. configure the response object
response.setContent(result.getContent());
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);
}
}

View File

@ -0,0 +1,121 @@
/*
* 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 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();
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 = entity.getProperty(keyName).getValue();
// now need to compare the valueObject with the keyText String
// this is done using the 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;
}
}

View File

@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package 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.DemoBatchProcessor;
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));
handler.register(new DemoBatchProcessor(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);
}
}
}

View File

@ -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>

View File

@ -0,0 +1,26 @@
<!--
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>

View File

@ -0,0 +1,85 @@
<?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-DeepInsert</artifactId>
<packaging>war</packaging>
<version>4.0.0</version>
<name>${project.artifactId}-Webapp</name>
<build>
<finalName>DemoService</finalName>
</build>
<properties>
<javax.version>2.5</javax.version>
<odata.version>4.1.0-SNAPSHOT</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>
</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>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.11</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,534 @@
/*
* 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.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.Edm;
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.EdmNavigationProperty;
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.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.deserializer.DeserializerException;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.service.DemoEdmProvider;
import myservice.mynamespace.util.Util;
public class Storage {
private OData odata;
private Edm edm;
final private TransactionalEntityManager manager;
public Storage(final OData odata, final Edm edm) {
this.odata = odata;
this.edm = edm;
this.manager = new TransactionalEntityManager(edm);
initProductSampleData();
initCategorySampleData();
linkProductsAndCategories();
}
/* PUBLIC FACADE */
public void beginTransaction() throws ODataApplicationException {
manager.beginTransaction();
}
public void rollbackTransaction() throws ODataApplicationException {
manager.rollbackTransaction();
}
public void commitTransaction() throws ODataApplicationException {
manager.commitTransaction();
}
public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet) throws ODataApplicationException {
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
return null;
}
public Entity readEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
return null;
}
public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate, String rawServiceUri)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME), rawServiceUri);
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME), rawServiceUri);
}
return null;
}
/**
* This method is invoked for PATCH or PUT requests
* */
public void updateEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams, Entity updateEntity,
HttpMethod httpMethod) throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
}
public void deleteEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
}
// Navigation
public Entity getRelatedEntity(Entity entity, UriResourceNavigation navigationResource)
throws ODataApplicationException {
final EdmNavigationProperty edmNavigationProperty = navigationResource.getProperty();
if(edmNavigationProperty.isCollection()) {
return Util.findEntity(edmNavigationProperty.getType(), getRelatedEntityCollection(entity, navigationResource),
navigationResource.getKeyPredicates());
} else {
final Link link = entity.getNavigationLink(edmNavigationProperty.getName());
return link == null ? null : link.getInlineEntity();
}
}
public EntityCollection getRelatedEntityCollection(Entity entity, UriResourceNavigation navigationResource) {
final Link link = entity.getNavigationLink(navigationResource.getProperty().getName());
return link == null ? new EntityCollection() : link.getInlineEntitySet();
}
/* INTERNAL */
private EntityCollection getEntityCollection(final List<Entity> entityList) {
EntityCollection retEntitySet = new EntityCollection();
retEntitySet.getEntities().addAll(entityList);
return retEntitySet;
}
private Entity getEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, List<Entity> entityList)
throws ODataApplicationException {
// the list of entities at runtime
EntityCollection entitySet = getEntityCollection(entityList);
/* 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;
}
private Entity createEntity(EdmEntitySet edmEntitySet, EdmEntityType edmEntityType, Entity entity,
List<Entity> entityList, final String rawServiceUri) throws ODataApplicationException {
// 1.) Create the entity
final Entity newEntity = new Entity();
newEntity.setType(entity.getType());
// Create the new key of the entity
int newId = 1;
while (entityIdExists(newId, entityList)) {
newId++;
}
// Add all provided properties
newEntity.getProperties().addAll(entity.getProperties());
// Add the key property
newEntity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId));
newEntity.setId(createId(newEntity, "ID"));
// 2.1.) Apply binding links
for(final Link link : entity.getNavigationBindings()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() && link.getBindingLinks() != null) {
for(final String bindingLink : link.getBindingLinks()) {
final Entity relatedEntity = readEntityByBindingLink(bindingLink, targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
} else if(!edmNavigationProperty.isCollection() && link.getBindingLink() != null) {
final Entity relatedEntity = readEntityByBindingLink(link.getBindingLink(), targetEntitySet, rawServiceUri);
createLink(edmNavigationProperty, newEntity, relatedEntity);
}
}
// 2.2.) Create nested entities
for(final Link link : entity.getNavigationLinks()) {
final EdmNavigationProperty edmNavigationProperty = edmEntityType.getNavigationProperty(link.getTitle());
final EdmEntitySet targetEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
if(edmNavigationProperty.isCollection() && link.getInlineEntitySet() != null) {
for(final Entity nestedEntity : link.getInlineEntitySet().getEntities()) {
final Entity newNestedEntity = createEntityData(targetEntitySet, nestedEntity, rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
} else if(!edmNavigationProperty.isCollection() && link.getInlineEntity() != null){
final Entity newNestedEntity = createEntityData(targetEntitySet, link.getInlineEntity(), rawServiceUri);
createLink(edmNavigationProperty, newEntity, newNestedEntity);
}
}
entityList.add(newEntity);
return newEntity;
}
private Entity readEntityByBindingLink(final String entityId, final EdmEntitySet edmEntitySet,
final String rawServiceUri) throws ODataApplicationException {
UriResourceEntitySet entitySetResource = null;
try {
entitySetResource = odata.createUriHelper().parseEntityId(edm, entityId, rawServiceUri);
if(!entitySetResource.getEntitySet().getName().equals(edmEntitySet.getName())) {
throw new ODataApplicationException("Execpted an entity-id for entity set " + edmEntitySet.getName()
+ " but found id for entity set " + entitySetResource.getEntitySet().getName(),
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
} catch (DeserializerException e) {
throw new ODataApplicationException(entityId + " is not a valid entity-Id",
HttpStatusCode.BAD_REQUEST.getStatusCode(), Locale.ENGLISH);
}
return readEntityData(entitySetResource.getEntitySet(), entitySetResource.getKeyPredicates());
}
private boolean entityIdExists(int id, List<Entity> entityList) {
for (Entity entity : entityList) {
Integer existingID = (Integer) entity.getProperty("ID").getValue();
if (existingID.intValue() == id) {
return true;
}
}
return false;
}
private void updateEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, Entity updateEntity,
HttpMethod httpMethod, List<Entity> 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<Property> existingProperties = entity.getProperties();
for (Property existingProp : existingProperties) {
String propName = existingProp.getName();
// ignore the key properties, they aren't updateable
if (isKey(edmEntityType, propName)) {
continue;
}
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
// depending on the HttpMethod, our behavior is different
if (httpMethod.equals(HttpMethod.PATCH)) {
// as of the OData spec, in case of PATCH, the existing property is not touched
continue; // do nothing
} else if (httpMethod.equals(HttpMethod.PUT)) {
// as of the OData spec, in case of PUT, the existing property is set to null (or to default value)
existingProp.setValue(existingProp.getValueType(), null);
continue;
}
}
// change the value of the properties
existingProp.setValue(existingProp.getValueType(), updateProperty.getValue());
}
}
private void deleteEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, List<Entity> entityList)
throws ODataApplicationException {
Entity entity = getEntity(edmEntityType, keyParams, entityList);
if (entity == null) {
throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
}
entityList.remove(entity);
}
/* HELPER */
private boolean isKey(EdmEntityType edmEntityType, String propertyName) {
List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs();
for (EdmKeyPropertyRef propRef : keyPropertyRefs) {
String keyPropertyName = propRef.getName();
if (keyPropertyName.equals(propertyName)) {
return true;
}
}
return false;
}
private void initProductSampleData() {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
Entity entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
}
private void initCategorySampleData() {
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
Entity entity = new Entity();
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, 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, 2));
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 void linkProductsAndCategories() {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
setLink(productList.get(0), "Category", categoryList.get(0));
setLink(productList.get(1), "Category", categoryList.get(0));
setLink(productList.get(2), "Category", categoryList.get(1));
setLink(productList.get(3), "Category", categoryList.get(1));
setLink(productList.get(4), "Category", categoryList.get(2));
setLink(productList.get(5), "Category", categoryList.get(2));
setLinks(categoryList.get(0), "Products", productList.subList(0, 2).toArray(new Entity[0]));
setLinks(categoryList.get(1), "Products", productList.subList(2, 4).toArray(new Entity[0]));
setLinks(categoryList.get(2), "Products", productList.subList(4, 6).toArray(new Entity[0]));
}
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();
}
private void createLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity destEntity) {
setLink(navigationProperty, srcEntity, destEntity);
final EdmNavigationProperty partnerNavigationProperty = navigationProperty.getPartner();
if (partnerNavigationProperty != null) {
setLink(partnerNavigationProperty, destEntity, srcEntity);
}
}
private void setLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity targetEntity) {
if (navigationProperty.isCollection()) {
setLinks(srcEntity, navigationProperty.getName(), targetEntity);
} else {
setLink(srcEntity, navigationProperty.getName(), targetEntity);
}
}
private void setLink(final Entity entity, final String navigationPropertyName, final Entity target) {
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(target.getId().toASCIIString());
entity.getNavigationLinks().add(link);
}
link.setInlineEntity(target);
}
private void setLinks(final Entity entity, final String navigationPropertyName, final Entity... targets) {
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(entity.getId().toASCIIString() + "/" + navigationPropertyName);
EntityCollection target = new EntityCollection();
target.getEntities().addAll(Arrays.asList(targets));
link.setInlineEntitySet(target);
entity.getNavigationLinks().add(link);
} else {
link.getInlineEntitySet().getEntities().addAll(Arrays.asList(targets));
}
}
}

View File

@ -0,0 +1,192 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package myservice.mynamespace.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.ODataApplicationException;
public class TransactionalEntityManager {
private Map<String, List<Entity>> entities = new HashMap<String, List<Entity>>();
private Map<String, List<Entity>> backupEntities = new HashMap<String, List<Entity>>();
private Map<String, IdentityHashMap<Entity, Entity>> copyMap = new HashMap<String, IdentityHashMap<Entity, Entity>>();
private boolean isInTransaction = false;
private Edm edm;
public TransactionalEntityManager(final Edm edm) {
this.edm = edm;
}
public List<Entity> getEntityCollection(final String entitySetName) {
if(!entities.containsKey(entitySetName)) {
entities.put(entitySetName, new ArrayList<Entity>());
}
return entities.get(entitySetName);
}
public void beginTransaction() throws ODataApplicationException {
if(!isInTransaction) {
isInTransaction = true;
copyCurrentState();
} else {
throw new ODataApplicationException("Transaction already in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
public void rollbackTransaction() throws ODataApplicationException {
if(isInTransaction) {
entities = backupEntities;
backupEntities = new HashMap<String, List<Entity>>();
isInTransaction = false;
} else {
throw new ODataApplicationException("No transaction in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
public void commitTransaction() throws ODataApplicationException {
if(isInTransaction) {
backupEntities.clear();
isInTransaction = false;
} else {
throw new ODataApplicationException("No transaction in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
private void copyCurrentState() {
copyMap.clear();
backupEntities.clear();
for(final String entitySetName : entities.keySet()) {
final List<Entity> entityList = entities.get(entitySetName);
backupEntities.put(entitySetName, new ArrayList<Entity>());
final List<Entity> backupEntityList = backupEntities.get(entitySetName);
for(final Entity entity : entityList) {
final EdmEntitySet entitySet = edm.getEntityContainer().getEntitySet(entitySetName);
backupEntityList.add(copyEntityRecursively(entitySet, entity));
}
}
}
private Entity copyEntityRecursively(final EdmEntitySet edmEntitySet, final Entity entity) {
// Check if entity is already copied
if(containsEntityInCopyMap(edmEntitySet.getName(), entity)) {
return getEntityFromCopyMap(edmEntitySet.getName(), entity);
} else {
final Entity newEntity = copyEntity(entity);
addEntityToCopyMap(edmEntitySet.getName(), entity, newEntity);
// Create nested entities recursively
for(final Link link : entity.getNavigationLinks()) {
newEntity.getNavigationLinks().add(copyLink(edmEntitySet, link));
}
return newEntity;
}
}
private Link copyLink(final EdmEntitySet edmEntitySet, final Link link) {
final Link newLink = new Link();
newLink.setBindingLink(link.getBindingLink());
newLink.setBindingLinks(new ArrayList<String>(link.getBindingLinks()));
newLink.setHref(link.getHref());
newLink.setMediaETag(link.getMediaETag());
newLink.setRel(link.getRel());
newLink.setTitle(link.getTitle());
newLink.setType(link.getType());
// Single navigation link
if(link.getInlineEntity() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
newLink.setInlineEntity(copyEntityRecursively(linkedEdmEntitySet, link.getInlineEntity()));
}
// Collection navigation link
if(link.getInlineEntitySet() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
final EntityCollection inlineEntitySet = link.getInlineEntitySet();
final EntityCollection newInlineEntitySet = new EntityCollection();
newInlineEntitySet.setBaseURI(inlineEntitySet.getBaseURI());
newInlineEntitySet.setCount(inlineEntitySet.getCount());
newInlineEntitySet.setDeltaLink(inlineEntitySet.getDeltaLink());
newInlineEntitySet.setId(inlineEntitySet.getId());
newInlineEntitySet.setNext(inlineEntitySet.getNext());
for(final Entity inlineEntity : inlineEntitySet.getEntities()) {
newInlineEntitySet.getEntities().add(copyEntityRecursively(linkedEdmEntitySet, inlineEntity));
}
newLink.setInlineEntitySet(newInlineEntitySet);
}
return newLink;
}
private Entity copyEntity(final Entity entity) {
final Entity newEntity = new Entity();
newEntity.setBaseURI(entity.getBaseURI());
newEntity.setEditLink(entity.getEditLink());
newEntity.setETag(entity.getETag());
newEntity.setId(entity.getId());
newEntity.setMediaContentSource(entity.getMediaContentSource());
newEntity.setMediaContentType(entity.getMediaContentType());
newEntity.setSelfLink(entity.getSelfLink());
newEntity.setMediaETag(entity.getMediaETag());
newEntity.setType(entity.getType());
newEntity.getProperties().addAll(entity.getProperties());
return newEntity;
}
private void addEntityToCopyMap(final String entitySetName, final Entity srcEntity, final Entity destEntity) {
if(!copyMap.containsKey(entitySetName)) {
copyMap.put(entitySetName, new IdentityHashMap<Entity, Entity>());
}
copyMap.get(entitySetName).put(srcEntity, destEntity);
}
private boolean containsEntityInCopyMap(final String entitySetName, final Entity srcEntity) {
return getEntityFromCopyMap(entitySetName, srcEntity) != null;
}
private Entity getEntityFromCopyMap(final String entitySetName, final Entity srcEntity) {
if(!copyMap.containsKey(entitySetName)) {
return null;
}
return copyMap.get(entitySetName).get(srcEntity);
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.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.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.CsdlNavigationProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding;
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;
/*
* this class is supposed to declare the metadata of the OData service
* it is invoked by the Olingo framework e.g. when the metadata document of the service is invoked
* e.g. http://localhost:8080/ExampleService1/ExampleService1.svc/$metadata
*/
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);
public static final String ET_CATEGORY_NAME = "Category";
public static final FullQualifiedName ET_CATEGORY_FQN = new FullQualifiedName(NAMESPACE, ET_CATEGORY_NAME);
// Entity Set Names
public static final String ES_PRODUCTS_NAME = "Products";
public static final String ES_CATEGORIES_NAME = "Categories";
public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) {
// this method is called for each EntityType that are configured in the Schema
CsdlEntityType entityType = null;
if (entityTypeName.equals(ET_PRODUCT_FQN)) {
// 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");
// navigation property: many-to-one, null not allowed (product must have a category)
CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Category")
.setType(ET_CATEGORY_FQN).setNullable(true)
.setPartner("Products");
List<CsdlNavigationProperty> navPropList = new ArrayList<CsdlNavigationProperty>();
navPropList.add(navProp);
// configure EntityType
entityType = new CsdlEntityType();
entityType.setName(ET_PRODUCT_NAME);
entityType.setProperties(Arrays.asList(id, name, description));
entityType.setKey(Arrays.asList(propertyRef));
entityType.setNavigationProperties(navPropList);
} else if (entityTypeName.equals(ET_CATEGORY_FQN)) {
// create EntityType properties
CsdlProperty id = new CsdlProperty().setName("ID")
.setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName());
CsdlProperty name = new CsdlProperty().setName("Name")
.setType(EdmPrimitiveTypeKind.String.getFullQualifiedName());
// create PropertyRef for Key element
CsdlPropertyRef propertyRef = new CsdlPropertyRef();
propertyRef.setName("ID");
// navigation property: one-to-many
CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Products")
.setType(ET_PRODUCT_FQN).setCollection(true)
.setPartner("Category");
List<CsdlNavigationProperty> navPropList = new ArrayList<CsdlNavigationProperty>();
navPropList.add(navProp);
// configure EntityType
entityType = new CsdlEntityType();
entityType.setName(ET_CATEGORY_NAME);
entityType.setProperties(Arrays.asList(id, name));
entityType.setKey(Arrays.asList(propertyRef));
entityType.setNavigationProperties(navPropList);
}
return entityType;
}
@Override
public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, String entitySetName) {
CsdlEntitySet entitySet = null;
if (entityContainer.equals(CONTAINER)) {
if (entitySetName.equals(ES_PRODUCTS_NAME)) {
entitySet = new CsdlEntitySet();
entitySet.setName(ES_PRODUCTS_NAME);
entitySet.setType(ET_PRODUCT_FQN);
// navigation
CsdlNavigationPropertyBinding navPropBinding = new CsdlNavigationPropertyBinding();
navPropBinding.setTarget("Categories"); // the target entity set, where the navigation property points to
navPropBinding.setPath("Category"); // the path from entity type to navigation property
List<CsdlNavigationPropertyBinding> navPropBindingList = new ArrayList<CsdlNavigationPropertyBinding>();
navPropBindingList.add(navPropBinding);
entitySet.setNavigationPropertyBindings(navPropBindingList);
} else if (entitySetName.equals(ES_CATEGORIES_NAME)) {
entitySet = new CsdlEntitySet();
entitySet.setName(ES_CATEGORIES_NAME);
entitySet.setType(ET_CATEGORY_FQN);
// navigation
CsdlNavigationPropertyBinding navPropBinding = new CsdlNavigationPropertyBinding();
navPropBinding.setTarget("Products"); // the target entity set, where the navigation property points to
navPropBinding.setPath("Products"); // the path from entity type to navigation property
List<CsdlNavigationPropertyBinding> navPropBindingList = new ArrayList<CsdlNavigationPropertyBinding>();
navPropBindingList.add(navPropBinding);
entitySet.setNavigationPropertyBindings(navPropBindingList);
}
}
return entitySet;
}
@Override
public List<CsdlSchema> getSchemas() {
// create Schema
CsdlSchema schema = new CsdlSchema();
schema.setNamespace(NAMESPACE);
// add EntityTypes
List<CsdlEntityType> entityTypes = new ArrayList<CsdlEntityType>();
entityTypes.add(getEntityType(ET_PRODUCT_FQN));
entityTypes.add(getEntityType(ET_CATEGORY_FQN));
schema.setEntityTypes(entityTypes);
// add EntityContainer
schema.setEntityContainer(getEntityContainer());
// finally
List<CsdlSchema> schemas = new ArrayList<CsdlSchema>();
schemas.add(schema);
return schemas;
}
public CsdlEntityContainer getEntityContainer() {
// create EntitySets
List<CsdlEntitySet> entitySets = new ArrayList<CsdlEntitySet>();
entitySets.add(getEntitySet(CONTAINER, ES_PRODUCTS_NAME));
entitySets.add(getEntitySet(CONTAINER, ES_CATEGORIES_NAME));
// create EntityContainer
CsdlEntityContainer entityContainer = new CsdlEntityContainer();
entityContainer.setName(CONTAINER_NAME);
entityContainer.setEntitySets(entitySets);
return entityContainer;
}
@Override
public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) {
// 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;
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.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.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.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.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
public class DemoEntityCollectionProcessor implements EntityCollectionProcessor {
private OData odata;
private ServiceMetadata serviceMetadata;
// our database-mock
private Storage storage;
public DemoEntityCollectionProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/*
* This method is invoked when a collection of entities has to be read.
* In our example, this can be either a "normal" read operation, or a navigation:
*
* Example for "normal" read entity set operation:
* http://localhost:8080/DemoService/DemoService.svc/Categories
*
* Example for navigation
* http://localhost:8080/DemoService/DemoService.svc/Categories(3)/Products
*/
public void readEntityCollection(ODataRequest request, ODataResponse response,
UriInfo uriInfo, ContentType responseFormat)
throws ODataApplicationException, SerializerException {
EdmEntitySet responseEdmEntitySet = null; // we'll need this to build the ContextURL
EntityCollection responseEntityCollection = null; // we'll need this to set the response body
// 1st retrieve the requested EntitySet from the uriInfo (representation of the parsed URI)
List<UriResource> resourceParts = uriInfo.getUriResourceParts();
int segmentCount = resourceParts.size();
UriResource uriResource = resourceParts.get(0); // in our example, the first segment is the EntitySet
if (!(uriResource instanceof UriResourceEntitySet)) {
throw new ODataApplicationException("Only EntitySet is supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
if (segmentCount == 1) { // this is the case for: DemoService/DemoService.svc/Categories
responseEdmEntitySet = startEdmEntitySet; // the response body is built from the first (and only) entitySet
// 2nd: fetch the data from backend for this requested EntitySetName and deliver as EntitySet
responseEntityCollection = storage.readEntitySetData(startEdmEntitySet);
} else if (segmentCount == 2) { // in case of navigation: DemoService.svc/Categories(3)/Products
UriResource lastSegment = resourceParts.get(1); // in our example we don't support more complex URIs
if (lastSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) lastSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
// from Categories(1) to Products
responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
// 2nd: fetch the data from backend
// first fetch the entity where the first segment of the URI points to
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// e.g. for Categories(3)/Products we have to find the single entity: Category with ID 3
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
// error handling for e.g. DemoService.svc/Categories(99)/Products
if (sourceEntity == null) {
throw new ODataApplicationException("Entity not found.",
HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
}
// then fetch the entity collection where the entity navigates to
// note: we don't need to check uriResourceNavigation.isCollection(),
// because we are the EntityCollectionProcessor
responseEntityCollection = storage.getRelatedEntityCollection(sourceEntity, uriResourceNavigation);
}
} else { // this would be the case for e.g. Products(1)/Category/Products
throw new ODataApplicationException("Not supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
// 3rd: create and configure a serializer
ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).build();
final String id = request.getRawBaseUri() + "/" + responseEdmEntitySet.getName();
EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with()
.contextURL(contextUrl).id(id).build();
EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType();
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType,
responseEntityCollection, opts);
// 4th: configure the response object: set the body, headers and status code
response.setContent(serializerResult.getContent());
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
}
}

View File

@ -0,0 +1,242 @@
/*
* 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 org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
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.deserializer.DeserializerResult;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
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;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
public class DemoEntityProcessor implements EntityProcessor {
private OData odata;
private Storage storage;
private ServiceMetadata serviceMetadata;
public DemoEntityProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/**
* This method is invoked when a single entity has to be read.
* In our example, this can be either a "normal" read operation, or a navigation:
*
* Example for "normal" read operation:
* http://localhost:8080/DemoService/DemoService.svc/Products(1)
*
* Example for navigation
* http://localhost:8080/DemoService/DemoService.svc/Products(1)/Category
*/
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
Entity responseEntity = null; // required for serialization of the response body
EdmEntitySet responseEdmEntitySet = null; // we need this for building the contextUrl
// 1st step: retrieve the requested Entity: can be "normal" read operation, or navigation (to-one)
List<UriResource> resourceParts = uriInfo.getUriResourceParts();
int segmentCount = resourceParts.size();
UriResource uriResource = resourceParts.get(0); // in our example, the first segment is the EntitySet
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
// 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
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
responseEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
} else if (segmentCount == 2) { // navigation
UriResource navSegment = resourceParts.get(1); // in our example we don't support more complex URIs
if (navSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) navSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
responseEdmEntityType = edmNavigationProperty.getType();
// contextURL displays the last segment
responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
// 2nd: fetch the data from backend.
// e.g. for the URI: Products(1)/Category we have to find the correct Category entity
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// e.g. for Products(1)/Category we have to find first the Products(1)
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
responseEntity = storage.getRelatedEntity(sourceEntity, uriResourceNavigation);
}
} else {
// this would be the case for e.g. Products(1)/Category/Products(1)/Category
throw new ODataApplicationException("Not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
if (responseEntity == null) {
// this is the case for e.g. DemoService.svc/Categories(4) or DemoService.svc/Categories(3)/Products(999)
throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
}
// 3. serialize
ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).suffix(Suffix.ENTITY).build();
EntitySerializerOptions opts = EntitySerializerOptions.with().contextURL(contextUrl).build();
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializerResult = serializer.entity(serviceMetadata,
responseEdmEntityType, responseEntity, opts);
// 4. 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"
}
* */
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
EdmEntitySet edmEntitySet = Util.getEdmEntitySet(uriInfo);
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
// 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 = odata.createDeserializer(requestFormat);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the creation in backend, which returns the newly created entity
Entity createdEntity = null;
try {
storage.beginTransaction();
createdEntity = storage.createEntityData(edmEntitySet, requestEntity, request.getRawBaseUri());
storage.commitTransaction();
} catch( ODataApplicationException e ) {
storage.rollbackTransaction();
throw e;
}
// 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
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options);
//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
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();
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
InputStream requestInputStream = request.getBody();
ODataDeserializer deserializer = odata.createDeserializer(requestFormat);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the modification in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// 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
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
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. delete the data in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
storage.deleteEntityData(edmEntitySet, keyPredicates);
//3. configure the response object
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
}
}

View File

@ -0,0 +1,146 @@
/*
* 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 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.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;
private ServiceMetadata serviceMetadata;
public DemoPrimitiveProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/*
* 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
ODataSerializer serializer = odata.createSerializer(responseFormat);
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build();
PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build();
// 3.2. serialize
SerializerResult result = serializer.primitive(serviceMetadata, edmPropertyType, property, options);
//4. configure the response object
response.setContent(result.getContent());
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);
}
}

View File

@ -0,0 +1,161 @@
/*
* 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.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.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 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();
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 = entity.getProperty(keyName).getValue();
// now need to compare the valueObject with the keyText String
// this is done using the 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;
}
/**
* Example:
* For the following navigation: DemoService.svc/Categories(1)/Products
* we need the EdmEntitySet for the navigation property "Products"
*
* This is defined as follows in the metadata:
* <code>
*
* <EntitySet Name="Categories" EntityType="OData.Demo.Category">
* <NavigationPropertyBinding Path="Products" Target="Products"/>
* </EntitySet>
* </code>
* The "Target" attribute specifies the target EntitySet
* Therefore we need the startEntitySet "Categories" in order to retrieve the target EntitySet "Products"
*/
public static EdmEntitySet getNavigationTargetEntitySet(EdmEntitySet startEdmEntitySet,
EdmNavigationProperty edmNavigationProperty)
throws ODataApplicationException {
EdmEntitySet navigationTargetEntitySet = null;
String navPropName = edmNavigationProperty.getName();
EdmBindingTarget edmBindingTarget = startEdmEntitySet.getRelatedBindingTarget(navPropName);
if (edmBindingTarget == null) {
throw new ODataApplicationException("Not supported.",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
if (edmBindingTarget instanceof EdmEntitySet) {
navigationTargetEntitySet = (EdmEntitySet) edmBindingTarget;
} else {
throw new ODataApplicationException("Not supported.",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
return navigationTargetEntitySet;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.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 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;
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;
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 {
OData odata = OData.newInstance();
ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>());
HttpSession session = req.getSession(true);
Storage storage = (Storage) session.getAttribute(Storage.class.getName());
if (storage == null) {
storage = new Storage(odata, edm.getEdm());
session.setAttribute(Storage.class.getName(), storage);
}
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);
}
}
}

View File

@ -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>

View File

@ -0,0 +1,26 @@
<!--
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>

View File

@ -0,0 +1,85 @@
<?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-DeepInsertPreparation</artifactId>
<packaging>war</packaging>
<version>4.0.0</version>
<name>${project.artifactId}-Webapp</name>
<build>
<finalName>DemoService</finalName>
</build>
<properties>
<javax.version>2.5</javax.version>
<odata.version>4.1.0-SNAPSHOT</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>
</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>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.11</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,472 @@
/*
* 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.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.data.ValueType;
import org.apache.olingo.commons.api.edm.Edm;
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.EdmNavigationProperty;
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.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.service.DemoEdmProvider;
import myservice.mynamespace.util.Util;
public class Storage {
private OData odata;
private Edm edm;
final private TransactionalEntityManager manager;
public Storage(final OData odata, final Edm edm) {
this.odata = odata;
this.edm = edm;
this.manager = new TransactionalEntityManager(edm);
initProductSampleData();
initCategorySampleData();
linkProductsAndCategories();
}
/* PUBLIC FACADE */
public void beginTransaction() throws ODataApplicationException {
manager.beginTransaction();
}
public void rollbackTransaction() throws ODataApplicationException {
manager.rollbackTransaction();
}
public void commitTransaction() throws ODataApplicationException {
manager.commitTransaction();
}
public EntityCollection readEntitySetData(EdmEntitySet edmEntitySet) throws ODataApplicationException {
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntityCollection(manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
return null;
}
public Entity readEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return getEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
return null;
}
public Entity createEntityData(EdmEntitySet edmEntitySet, Entity entityToCreate, String rawServiceUri)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME), rawServiceUri);
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
return createEntity(edmEntitySet, edmEntityType, entityToCreate,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME), rawServiceUri);
}
return null;
}
/**
* This method is invoked for PATCH or PUT requests
* */
public void updateEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams, Entity updateEntity,
HttpMethod httpMethod) throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
updateEntity(edmEntityType, keyParams, updateEntity, httpMethod,
manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
}
public void deleteEntityData(EdmEntitySet edmEntitySet, List<UriParameter> keyParams)
throws ODataApplicationException {
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
if (edmEntitySet.getName().equals(DemoEdmProvider.ES_PRODUCTS_NAME)) {
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME));
} else if(edmEntitySet.getName().equals(DemoEdmProvider.ES_CATEGORIES_NAME)) {
deleteEntity(edmEntityType, keyParams, manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME));
}
}
// Navigation
public Entity getRelatedEntity(Entity entity, UriResourceNavigation navigationResource)
throws ODataApplicationException {
final EdmNavigationProperty edmNavigationProperty = navigationResource.getProperty();
if(edmNavigationProperty.isCollection()) {
return Util.findEntity(edmNavigationProperty.getType(), getRelatedEntityCollection(entity, navigationResource),
navigationResource.getKeyPredicates());
} else {
final Link link = entity.getNavigationLink(edmNavigationProperty.getName());
return link == null ? null : link.getInlineEntity();
}
}
public EntityCollection getRelatedEntityCollection(Entity entity, UriResourceNavigation navigationResource) {
final Link link = entity.getNavigationLink(navigationResource.getProperty().getName());
return link == null ? new EntityCollection() : link.getInlineEntitySet();
}
/* INTERNAL */
private EntityCollection getEntityCollection(final List<Entity> entityList) {
EntityCollection retEntitySet = new EntityCollection();
retEntitySet.getEntities().addAll(entityList);
return retEntitySet;
}
private Entity getEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, List<Entity> entityList)
throws ODataApplicationException {
// the list of entities at runtime
EntityCollection entitySet = getEntityCollection(entityList);
/* 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;
}
private Entity createEntity(EdmEntitySet edmEntitySet, EdmEntityType edmEntityType, Entity entity,
List<Entity> entityList, final String rawServiceUri) throws ODataApplicationException {
// 1.) Create the entity
final Entity newEntity = new Entity();
newEntity.setType(entity.getType());
// Create the new key of the entity
int newId = 1;
while (entityIdExists(newId, entityList)) {
newId++;
}
// Add all provided properties
newEntity.getProperties().addAll(entity.getProperties());
// Add the key property
newEntity.getProperties().add(new Property(null, "ID", ValueType.PRIMITIVE, newId));
newEntity.setId(createId(newEntity, "ID"));
// --> Implement Deep Insert handling here <--
entityList.add(newEntity);
return newEntity;
}
private boolean entityIdExists(int id, List<Entity> entityList) {
for (Entity entity : entityList) {
Integer existingID = (Integer) entity.getProperty("ID").getValue();
if (existingID.intValue() == id) {
return true;
}
}
return false;
}
private void updateEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, Entity updateEntity,
HttpMethod httpMethod, List<Entity> 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<Property> existingProperties = entity.getProperties();
for (Property existingProp : existingProperties) {
String propName = existingProp.getName();
// ignore the key properties, they aren't updateable
if (isKey(edmEntityType, propName)) {
continue;
}
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
// depending on the HttpMethod, our behavior is different
if (httpMethod.equals(HttpMethod.PATCH)) {
// as of the OData spec, in case of PATCH, the existing property is not touched
continue; // do nothing
} else if (httpMethod.equals(HttpMethod.PUT)) {
// as of the OData spec, in case of PUT, the existing property is set to null (or to default value)
existingProp.setValue(existingProp.getValueType(), null);
continue;
}
}
// change the value of the properties
existingProp.setValue(existingProp.getValueType(), updateProperty.getValue());
}
}
private void deleteEntity(EdmEntityType edmEntityType, List<UriParameter> keyParams, List<Entity> entityList)
throws ODataApplicationException {
Entity entity = getEntity(edmEntityType, keyParams, entityList);
if (entity == null) {
throw new ODataApplicationException("Entity not found", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ENGLISH);
}
entityList.remove(entity);
}
/* HELPER */
private boolean isKey(EdmEntityType edmEntityType, String propertyName) {
List<EdmKeyPropertyRef> keyPropertyRefs = edmEntityType.getKeyPropertyRefs();
for (EdmKeyPropertyRef propRef : keyPropertyRefs) {
String keyPropertyName = propRef.getName();
if (keyPropertyName.equals(propertyName)) {
return true;
}
}
return false;
}
private void initProductSampleData() {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
Entity entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
entity = new Entity();
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"));
entity.setType(DemoEdmProvider.ET_PRODUCT_FQN.getFullQualifiedNameAsString());
entity.setId(createId(entity, "ID"));
productList.add(entity);
}
private void initCategorySampleData() {
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
Entity entity = new Entity();
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, 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, 2));
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 void linkProductsAndCategories() {
final List<Entity> productList = manager.getEntityCollection(DemoEdmProvider.ES_PRODUCTS_NAME);
final List<Entity> categoryList = manager.getEntityCollection(DemoEdmProvider.ES_CATEGORIES_NAME);
setLink(productList.get(0), "Category", categoryList.get(0));
setLink(productList.get(1), "Category", categoryList.get(0));
setLink(productList.get(2), "Category", categoryList.get(1));
setLink(productList.get(3), "Category", categoryList.get(1));
setLink(productList.get(4), "Category", categoryList.get(2));
setLink(productList.get(5), "Category", categoryList.get(2));
setLinks(categoryList.get(0), "Products", productList.subList(0, 2).toArray(new Entity[0]));
setLinks(categoryList.get(1), "Products", productList.subList(2, 4).toArray(new Entity[0]));
setLinks(categoryList.get(2), "Products", productList.subList(4, 6).toArray(new Entity[0]));
}
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();
}
private void setLink(final EdmNavigationProperty navigationProperty, final Entity srcEntity,
final Entity targetEntity) {
if (navigationProperty.isCollection()) {
setLinks(srcEntity, navigationProperty.getName(), targetEntity);
} else {
setLink(srcEntity, navigationProperty.getName(), targetEntity);
}
}
private void setLink(final Entity entity, final String navigationPropertyName, final Entity target) {
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(target.getId().toASCIIString());
entity.getNavigationLinks().add(link);
}
link.setInlineEntity(target);
}
private void setLinks(final Entity entity, final String navigationPropertyName, final Entity... targets) {
Link link = entity.getNavigationLink(navigationPropertyName);
if (link == null) {
link = new Link();
link.setRel(Constants.NS_NAVIGATION_LINK_REL + navigationPropertyName);
link.setType(Constants.ENTITY_SET_NAVIGATION_LINK_TYPE);
link.setTitle(navigationPropertyName);
link.setHref(entity.getId().toASCIIString() + "/" + navigationPropertyName);
EntityCollection target = new EntityCollection();
target.getEntities().addAll(Arrays.asList(targets));
link.setInlineEntitySet(target);
entity.getNavigationLinks().add(link);
} else {
link.getInlineEntitySet().getEntities().addAll(Arrays.asList(targets));
}
}
}

View File

@ -0,0 +1,192 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package myservice.mynamespace.data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.EntityCollection;
import org.apache.olingo.commons.api.data.Link;
import org.apache.olingo.commons.api.edm.Edm;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.ODataApplicationException;
public class TransactionalEntityManager {
private Map<String, List<Entity>> entities = new HashMap<String, List<Entity>>();
private Map<String, List<Entity>> backupEntities = new HashMap<String, List<Entity>>();
private Map<String, IdentityHashMap<Entity, Entity>> copyMap = new HashMap<String, IdentityHashMap<Entity, Entity>>();
private boolean isInTransaction = false;
private Edm edm;
public TransactionalEntityManager(final Edm edm) {
this.edm = edm;
}
public List<Entity> getEntityCollection(final String entitySetName) {
if(!entities.containsKey(entitySetName)) {
entities.put(entitySetName, new ArrayList<Entity>());
}
return entities.get(entitySetName);
}
public void beginTransaction() throws ODataApplicationException {
if(!isInTransaction) {
isInTransaction = true;
copyCurrentState();
} else {
throw new ODataApplicationException("Transaction already in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
public void rollbackTransaction() throws ODataApplicationException {
if(isInTransaction) {
entities = backupEntities;
backupEntities = new HashMap<String, List<Entity>>();
isInTransaction = false;
} else {
throw new ODataApplicationException("No transaction in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
public void commitTransaction() throws ODataApplicationException {
if(isInTransaction) {
backupEntities.clear();
isInTransaction = false;
} else {
throw new ODataApplicationException("No transaction in progress",
HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), Locale.ENGLISH);
}
}
private void copyCurrentState() {
copyMap.clear();
backupEntities.clear();
for(final String entitySetName : entities.keySet()) {
final List<Entity> entityList = entities.get(entitySetName);
backupEntities.put(entitySetName, new ArrayList<Entity>());
final List<Entity> backupEntityList = backupEntities.get(entitySetName);
for(final Entity entity : entityList) {
final EdmEntitySet entitySet = edm.getEntityContainer().getEntitySet(entitySetName);
backupEntityList.add(copyEntityRecursively(entitySet, entity));
}
}
}
private Entity copyEntityRecursively(final EdmEntitySet edmEntitySet, final Entity entity) {
// Check if entity is already copied
if(containsEntityInCopyMap(edmEntitySet.getName(), entity)) {
return getEntityFromCopyMap(edmEntitySet.getName(), entity);
} else {
final Entity newEntity = copyEntity(entity);
addEntityToCopyMap(edmEntitySet.getName(), entity, newEntity);
// Create nested entities recursively
for(final Link link : entity.getNavigationLinks()) {
newEntity.getNavigationLinks().add(copyLink(edmEntitySet, link));
}
return newEntity;
}
}
private Link copyLink(final EdmEntitySet edmEntitySet, final Link link) {
final Link newLink = new Link();
newLink.setBindingLink(link.getBindingLink());
newLink.setBindingLinks(new ArrayList<String>(link.getBindingLinks()));
newLink.setHref(link.getHref());
newLink.setMediaETag(link.getMediaETag());
newLink.setRel(link.getRel());
newLink.setTitle(link.getTitle());
newLink.setType(link.getType());
// Single navigation link
if(link.getInlineEntity() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
newLink.setInlineEntity(copyEntityRecursively(linkedEdmEntitySet, link.getInlineEntity()));
}
// Collection navigation link
if(link.getInlineEntitySet() != null) {
final EdmEntitySet linkedEdmEntitySet = (EdmEntitySet) edmEntitySet.getRelatedBindingTarget(link.getTitle());
final EntityCollection inlineEntitySet = link.getInlineEntitySet();
final EntityCollection newInlineEntitySet = new EntityCollection();
newInlineEntitySet.setBaseURI(inlineEntitySet.getBaseURI());
newInlineEntitySet.setCount(inlineEntitySet.getCount());
newInlineEntitySet.setDeltaLink(inlineEntitySet.getDeltaLink());
newInlineEntitySet.setId(inlineEntitySet.getId());
newInlineEntitySet.setNext(inlineEntitySet.getNext());
for(final Entity inlineEntity : inlineEntitySet.getEntities()) {
newInlineEntitySet.getEntities().add(copyEntityRecursively(linkedEdmEntitySet, inlineEntity));
}
newLink.setInlineEntitySet(newInlineEntitySet);
}
return newLink;
}
private Entity copyEntity(final Entity entity) {
final Entity newEntity = new Entity();
newEntity.setBaseURI(entity.getBaseURI());
newEntity.setEditLink(entity.getEditLink());
newEntity.setETag(entity.getETag());
newEntity.setId(entity.getId());
newEntity.setMediaContentSource(entity.getMediaContentSource());
newEntity.setMediaContentType(entity.getMediaContentType());
newEntity.setSelfLink(entity.getSelfLink());
newEntity.setMediaETag(entity.getMediaETag());
newEntity.setType(entity.getType());
newEntity.getProperties().addAll(entity.getProperties());
return newEntity;
}
private void addEntityToCopyMap(final String entitySetName, final Entity srcEntity, final Entity destEntity) {
if(!copyMap.containsKey(entitySetName)) {
copyMap.put(entitySetName, new IdentityHashMap<Entity, Entity>());
}
copyMap.get(entitySetName).put(srcEntity, destEntity);
}
private boolean containsEntityInCopyMap(final String entitySetName, final Entity srcEntity) {
return getEntityFromCopyMap(entitySetName, srcEntity) != null;
}
private Entity getEntityFromCopyMap(final String entitySetName, final Entity srcEntity) {
if(!copyMap.containsKey(entitySetName)) {
return null;
}
return copyMap.get(entitySetName).get(srcEntity);
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.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.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.CsdlNavigationProperty;
import org.apache.olingo.commons.api.edm.provider.CsdlNavigationPropertyBinding;
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;
/*
* this class is supposed to declare the metadata of the OData service
* it is invoked by the Olingo framework e.g. when the metadata document of the service is invoked
* e.g. http://localhost:8080/ExampleService1/ExampleService1.svc/$metadata
*/
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);
public static final String ET_CATEGORY_NAME = "Category";
public static final FullQualifiedName ET_CATEGORY_FQN = new FullQualifiedName(NAMESPACE, ET_CATEGORY_NAME);
// Entity Set Names
public static final String ES_PRODUCTS_NAME = "Products";
public static final String ES_CATEGORIES_NAME = "Categories";
public CsdlEntityType getEntityType(FullQualifiedName entityTypeName) {
// this method is called for each EntityType that are configured in the Schema
CsdlEntityType entityType = null;
if (entityTypeName.equals(ET_PRODUCT_FQN)) {
// 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");
// navigation property: many-to-one, null not allowed (product must have a category)
CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Category")
.setType(ET_CATEGORY_FQN).setNullable(true)
.setPartner("Products");
List<CsdlNavigationProperty> navPropList = new ArrayList<CsdlNavigationProperty>();
navPropList.add(navProp);
// configure EntityType
entityType = new CsdlEntityType();
entityType.setName(ET_PRODUCT_NAME);
entityType.setProperties(Arrays.asList(id, name, description));
entityType.setKey(Arrays.asList(propertyRef));
entityType.setNavigationProperties(navPropList);
} else if (entityTypeName.equals(ET_CATEGORY_FQN)) {
// create EntityType properties
CsdlProperty id = new CsdlProperty().setName("ID")
.setType(EdmPrimitiveTypeKind.Int32.getFullQualifiedName());
CsdlProperty name = new CsdlProperty().setName("Name")
.setType(EdmPrimitiveTypeKind.String.getFullQualifiedName());
// create PropertyRef for Key element
CsdlPropertyRef propertyRef = new CsdlPropertyRef();
propertyRef.setName("ID");
// navigation property: one-to-many
CsdlNavigationProperty navProp = new CsdlNavigationProperty().setName("Products")
.setType(ET_PRODUCT_FQN).setCollection(true)
.setPartner("Category");
List<CsdlNavigationProperty> navPropList = new ArrayList<CsdlNavigationProperty>();
navPropList.add(navProp);
// configure EntityType
entityType = new CsdlEntityType();
entityType.setName(ET_CATEGORY_NAME);
entityType.setProperties(Arrays.asList(id, name));
entityType.setKey(Arrays.asList(propertyRef));
entityType.setNavigationProperties(navPropList);
}
return entityType;
}
@Override
public CsdlEntitySet getEntitySet(FullQualifiedName entityContainer, String entitySetName) {
CsdlEntitySet entitySet = null;
if (entityContainer.equals(CONTAINER)) {
if (entitySetName.equals(ES_PRODUCTS_NAME)) {
entitySet = new CsdlEntitySet();
entitySet.setName(ES_PRODUCTS_NAME);
entitySet.setType(ET_PRODUCT_FQN);
// navigation
CsdlNavigationPropertyBinding navPropBinding = new CsdlNavigationPropertyBinding();
navPropBinding.setTarget("Categories"); // the target entity set, where the navigation property points to
navPropBinding.setPath("Category"); // the path from entity type to navigation property
List<CsdlNavigationPropertyBinding> navPropBindingList = new ArrayList<CsdlNavigationPropertyBinding>();
navPropBindingList.add(navPropBinding);
entitySet.setNavigationPropertyBindings(navPropBindingList);
} else if (entitySetName.equals(ES_CATEGORIES_NAME)) {
entitySet = new CsdlEntitySet();
entitySet.setName(ES_CATEGORIES_NAME);
entitySet.setType(ET_CATEGORY_FQN);
// navigation
CsdlNavigationPropertyBinding navPropBinding = new CsdlNavigationPropertyBinding();
navPropBinding.setTarget("Products"); // the target entity set, where the navigation property points to
navPropBinding.setPath("Products"); // the path from entity type to navigation property
List<CsdlNavigationPropertyBinding> navPropBindingList = new ArrayList<CsdlNavigationPropertyBinding>();
navPropBindingList.add(navPropBinding);
entitySet.setNavigationPropertyBindings(navPropBindingList);
}
}
return entitySet;
}
@Override
public List<CsdlSchema> getSchemas() {
// create Schema
CsdlSchema schema = new CsdlSchema();
schema.setNamespace(NAMESPACE);
// add EntityTypes
List<CsdlEntityType> entityTypes = new ArrayList<CsdlEntityType>();
entityTypes.add(getEntityType(ET_PRODUCT_FQN));
entityTypes.add(getEntityType(ET_CATEGORY_FQN));
schema.setEntityTypes(entityTypes);
// add EntityContainer
schema.setEntityContainer(getEntityContainer());
// finally
List<CsdlSchema> schemas = new ArrayList<CsdlSchema>();
schemas.add(schema);
return schemas;
}
public CsdlEntityContainer getEntityContainer() {
// create EntitySets
List<CsdlEntitySet> entitySets = new ArrayList<CsdlEntitySet>();
entitySets.add(getEntitySet(CONTAINER, ES_PRODUCTS_NAME));
entitySets.add(getEntitySet(CONTAINER, ES_CATEGORIES_NAME));
// create EntityContainer
CsdlEntityContainer entityContainer = new CsdlEntityContainer();
entityContainer.setName(CONTAINER_NAME);
entityContainer.setEntitySets(entitySets);
return entityContainer;
}
@Override
public CsdlEntityContainerInfo getEntityContainerInfo(FullQualifiedName entityContainerName) {
// 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;
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.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.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpStatusCode;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.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.UriParameter;
import org.apache.olingo.server.api.uri.UriResource;
import org.apache.olingo.server.api.uri.UriResourceEntitySet;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
public class DemoEntityCollectionProcessor implements EntityCollectionProcessor {
private OData odata;
private ServiceMetadata serviceMetadata;
// our database-mock
private Storage storage;
public DemoEntityCollectionProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/*
* This method is invoked when a collection of entities has to be read.
* In our example, this can be either a "normal" read operation, or a navigation:
*
* Example for "normal" read entity set operation:
* http://localhost:8080/DemoService/DemoService.svc/Categories
*
* Example for navigation
* http://localhost:8080/DemoService/DemoService.svc/Categories(3)/Products
*/
public void readEntityCollection(ODataRequest request, ODataResponse response,
UriInfo uriInfo, ContentType responseFormat)
throws ODataApplicationException, SerializerException {
EdmEntitySet responseEdmEntitySet = null; // we'll need this to build the ContextURL
EntityCollection responseEntityCollection = null; // we'll need this to set the response body
// 1st retrieve the requested EntitySet from the uriInfo (representation of the parsed URI)
List<UriResource> resourceParts = uriInfo.getUriResourceParts();
int segmentCount = resourceParts.size();
UriResource uriResource = resourceParts.get(0); // in our example, the first segment is the EntitySet
if (!(uriResource instanceof UriResourceEntitySet)) {
throw new ODataApplicationException("Only EntitySet is supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
if (segmentCount == 1) { // this is the case for: DemoService/DemoService.svc/Categories
responseEdmEntitySet = startEdmEntitySet; // the response body is built from the first (and only) entitySet
// 2nd: fetch the data from backend for this requested EntitySetName and deliver as EntitySet
responseEntityCollection = storage.readEntitySetData(startEdmEntitySet);
} else if (segmentCount == 2) { // in case of navigation: DemoService.svc/Categories(3)/Products
UriResource lastSegment = resourceParts.get(1); // in our example we don't support more complex URIs
if (lastSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) lastSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
// from Categories(1) to Products
responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
// 2nd: fetch the data from backend
// first fetch the entity where the first segment of the URI points to
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// e.g. for Categories(3)/Products we have to find the single entity: Category with ID 3
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
// error handling for e.g. DemoService.svc/Categories(99)/Products
if (sourceEntity == null) {
throw new ODataApplicationException("Entity not found.",
HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
}
// then fetch the entity collection where the entity navigates to
// note: we don't need to check uriResourceNavigation.isCollection(),
// because we are the EntityCollectionProcessor
responseEntityCollection = storage.getRelatedEntityCollection(sourceEntity, uriResourceNavigation);
}
} else { // this would be the case for e.g. Products(1)/Category/Products
throw new ODataApplicationException("Not supported",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
// 3rd: create and configure a serializer
ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).build();
final String id = request.getRawBaseUri() + "/" + responseEdmEntitySet.getName();
EntityCollectionSerializerOptions opts = EntityCollectionSerializerOptions.with()
.contextURL(contextUrl).id(id).build();
EdmEntityType edmEntityType = responseEdmEntitySet.getEntityType();
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializerResult = serializer.entityCollection(serviceMetadata, edmEntityType,
responseEntityCollection, opts);
// 4th: configure the response object: set the body, headers and status code
response.setContent(serializerResult.getContent());
response.setStatusCode(HttpStatusCode.OK.getStatusCode());
response.setHeader(HttpHeader.CONTENT_TYPE, responseFormat.toContentTypeString());
}
}

View File

@ -0,0 +1,242 @@
/*
* 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 org.apache.olingo.commons.api.data.ContextURL;
import org.apache.olingo.commons.api.data.ContextURL.Suffix;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.apache.olingo.commons.api.http.HttpMethod;
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.deserializer.DeserializerResult;
import org.apache.olingo.server.api.deserializer.ODataDeserializer;
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;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import myservice.mynamespace.data.Storage;
import myservice.mynamespace.util.Util;
public class DemoEntityProcessor implements EntityProcessor {
private OData odata;
private Storage storage;
private ServiceMetadata serviceMetadata;
public DemoEntityProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/**
* This method is invoked when a single entity has to be read.
* In our example, this can be either a "normal" read operation, or a navigation:
*
* Example for "normal" read operation:
* http://localhost:8080/DemoService/DemoService.svc/Products(1)
*
* Example for navigation
* http://localhost:8080/DemoService/DemoService.svc/Products(1)/Category
*/
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
Entity responseEntity = null; // required for serialization of the response body
EdmEntitySet responseEdmEntitySet = null; // we need this for building the contextUrl
// 1st step: retrieve the requested Entity: can be "normal" read operation, or navigation (to-one)
List<UriResource> resourceParts = uriInfo.getUriResourceParts();
int segmentCount = resourceParts.size();
UriResource uriResource = resourceParts.get(0); // in our example, the first segment is the EntitySet
UriResourceEntitySet uriResourceEntitySet = (UriResourceEntitySet) uriResource;
EdmEntitySet startEdmEntitySet = uriResourceEntitySet.getEntitySet();
// 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
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
responseEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
} else if (segmentCount == 2) { // navigation
UriResource navSegment = resourceParts.get(1); // in our example we don't support more complex URIs
if (navSegment instanceof UriResourceNavigation) {
UriResourceNavigation uriResourceNavigation = (UriResourceNavigation) navSegment;
EdmNavigationProperty edmNavigationProperty = uriResourceNavigation.getProperty();
responseEdmEntityType = edmNavigationProperty.getType();
// contextURL displays the last segment
responseEdmEntitySet = Util.getNavigationTargetEntitySet(startEdmEntitySet, edmNavigationProperty);
// 2nd: fetch the data from backend.
// e.g. for the URI: Products(1)/Category we have to find the correct Category entity
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// e.g. for Products(1)/Category we have to find first the Products(1)
Entity sourceEntity = storage.readEntityData(startEdmEntitySet, keyPredicates);
responseEntity = storage.getRelatedEntity(sourceEntity, uriResourceNavigation);
}
} else {
// this would be the case for e.g. Products(1)/Category/Products(1)/Category
throw new ODataApplicationException("Not supported", HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
if (responseEntity == null) {
// this is the case for e.g. DemoService.svc/Categories(4) or DemoService.svc/Categories(3)/Products(999)
throw new ODataApplicationException("Nothing found.", HttpStatusCode.NOT_FOUND.getStatusCode(), Locale.ROOT);
}
// 3. serialize
ContextURL contextUrl = ContextURL.with().entitySet(responseEdmEntitySet).suffix(Suffix.ENTITY).build();
EntitySerializerOptions opts = EntitySerializerOptions.with().contextURL(contextUrl).build();
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializerResult = serializer.entity(serviceMetadata,
responseEdmEntityType, responseEntity, opts);
// 4. 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"
}
* */
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
EdmEntitySet edmEntitySet = Util.getEdmEntitySet(uriInfo);
EdmEntityType edmEntityType = edmEntitySet.getEntityType();
// 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 = odata.createDeserializer(requestFormat);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the creation in backend, which returns the newly created entity
Entity createdEntity = null;
try {
storage.beginTransaction();
createdEntity = storage.createEntityData(edmEntitySet, requestEntity, request.getRawBaseUri());
storage.commitTransaction();
} catch( ODataApplicationException e ) {
storage.rollbackTransaction();
throw e;
}
// 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
ODataSerializer serializer = odata.createSerializer(responseFormat);
SerializerResult serializedResponse = serializer.entity(serviceMetadata, edmEntityType, createdEntity, options);
//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
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();
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
InputStream requestInputStream = request.getBody();
ODataDeserializer deserializer = odata.createDeserializer(requestFormat);
DeserializerResult result = deserializer.entity(requestInputStream, edmEntityType);
Entity requestEntity = result.getEntity();
// 2.2 do the modification in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
// 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
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
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. delete the data in backend
List<UriParameter> keyPredicates = uriResourceEntitySet.getKeyPredicates();
storage.deleteEntityData(edmEntitySet, keyPredicates);
//3. configure the response object
response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
}
}

View File

@ -0,0 +1,146 @@
/*
* 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 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.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;
private ServiceMetadata serviceMetadata;
public DemoPrimitiveProcessor(Storage storage) {
this.storage = storage;
}
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
/*
* 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
ODataSerializer serializer = odata.createSerializer(responseFormat);
ContextURL contextUrl = ContextURL.with().entitySet(edmEntitySet).navOrPropertyPath(edmPropertyName).build();
PrimitiveSerializerOptions options = PrimitiveSerializerOptions.with().contextURL(contextUrl).build();
// 3.2. serialize
SerializerResult result = serializer.primitive(serviceMetadata, edmPropertyType, property, options);
//4. configure the response object
response.setContent(result.getContent());
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);
}
}

View File

@ -0,0 +1,161 @@
/*
* 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.EdmBindingTarget;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmNavigationProperty;
import org.apache.olingo.commons.api.edm.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 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();
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 = entity.getProperty(keyName).getValue();
// now need to compare the valueObject with the keyText String
// this is done using the 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;
}
/**
* Example:
* For the following navigation: DemoService.svc/Categories(1)/Products
* we need the EdmEntitySet for the navigation property "Products"
*
* This is defined as follows in the metadata:
* <code>
*
* <EntitySet Name="Categories" EntityType="OData.Demo.Category">
* <NavigationPropertyBinding Path="Products" Target="Products"/>
* </EntitySet>
* </code>
* The "Target" attribute specifies the target EntitySet
* Therefore we need the startEntitySet "Categories" in order to retrieve the target EntitySet "Products"
*/
public static EdmEntitySet getNavigationTargetEntitySet(EdmEntitySet startEdmEntitySet,
EdmNavigationProperty edmNavigationProperty)
throws ODataApplicationException {
EdmEntitySet navigationTargetEntitySet = null;
String navPropName = edmNavigationProperty.getName();
EdmBindingTarget edmBindingTarget = startEdmEntitySet.getRelatedBindingTarget(navPropName);
if (edmBindingTarget == null) {
throw new ODataApplicationException("Not supported.",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
if (edmBindingTarget instanceof EdmEntitySet) {
navigationTargetEntitySet = (EdmEntitySet) edmBindingTarget;
} else {
throw new ODataApplicationException("Not supported.",
HttpStatusCode.NOT_IMPLEMENTED.getStatusCode(), Locale.ROOT);
}
return navigationTargetEntitySet;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.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 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;
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;
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 {
OData odata = OData.newInstance();
ServiceMetadata edm = odata.createServiceMetadata(new DemoEdmProvider(), new ArrayList<EdmxReference>());
HttpSession session = req.getSession(true);
Storage storage = (Storage) session.getAttribute(Storage.class.getName());
if (storage == null) {
storage = new Storage(odata, edm.getEdm());
session.setAttribute(Storage.class.getName(), storage);
}
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);
}
}
}

View File

@ -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>

View File

@ -0,0 +1,26 @@
<!--
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>

View File

@ -47,6 +47,9 @@
<module>p9_action</module>
<module>p9_action_preparation</module>
<module>p10_media</module>
<module>p11_batch</module>
<module>p12_deep_insert</module>
<module>p12_deep_insert_preparation</module>
</modules>
<build>