blob: 4dbf74e86026b9d81dc6b5dfa76b0e10fe710f14 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.server.example;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import org.apache.olingo.commons.api.data.Entity;
import org.apache.olingo.commons.api.data.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.EdmAction;
import org.apache.olingo.commons.api.edm.EdmEntitySet;
import org.apache.olingo.commons.api.edm.EdmEntityType;
import org.apache.olingo.commons.api.edm.EdmFunction;
import org.apache.olingo.commons.api.edm.EdmProperty;
import org.apache.olingo.commons.api.edm.EdmSingleton;
import org.apache.olingo.commons.api.ex.ODataNotSupportedException;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.http.HttpMethod;
import org.apache.olingo.server.api.OData;
import org.apache.olingo.server.api.ODataApplicationException;
import org.apache.olingo.server.api.ODataLibraryException;
import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ODataServerError;
import org.apache.olingo.server.api.ServiceMetadata;
import org.apache.olingo.server.api.uri.UriParameter;
import org.apache.olingo.server.api.uri.UriResourceNavigation;
import org.apache.olingo.server.core.ServiceHandler;
import org.apache.olingo.server.core.requests.ActionRequest;
import org.apache.olingo.server.core.requests.DataRequest;
import org.apache.olingo.server.core.requests.FunctionRequest;
import org.apache.olingo.server.core.requests.MediaRequest;
import org.apache.olingo.server.core.requests.MetadataRequest;
import org.apache.olingo.server.core.requests.ServiceDocumentRequest;
import org.apache.olingo.server.core.responses.CountResponse;
import org.apache.olingo.server.core.responses.EntityResponse;
import org.apache.olingo.server.core.responses.EntitySetResponse;
import org.apache.olingo.server.core.responses.ErrorResponse;
import org.apache.olingo.server.core.responses.MetadataResponse;
import org.apache.olingo.server.core.responses.NoContentResponse;
import org.apache.olingo.server.core.responses.PrimitiveValueResponse;
import org.apache.olingo.server.core.responses.PropertyResponse;
import org.apache.olingo.server.core.responses.ServiceDocumentResponse;
import org.apache.olingo.server.core.responses.ServiceResponse;
import org.apache.olingo.server.core.responses.ServiceResponseVisior;
import org.apache.olingo.server.core.responses.StreamResponse;
public class TripPinHandler implements ServiceHandler {
private OData odata;
private ServiceMetadata serviceMetadata;
private final TripPinDataModel dataModel;
public TripPinHandler(TripPinDataModel datamodel) {
this.dataModel = datamodel;
}
@Override
public void init(OData odata, ServiceMetadata serviceMetadata) {
this.odata = odata;
this.serviceMetadata = serviceMetadata;
}
@Override
public void readMetadata(MetadataRequest request, MetadataResponse response)
throws ODataLibraryException, ODataApplicationException {
response.writeMetadata();
}
@Override
public void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
throws ODataLibraryException, ODataApplicationException {
response.writeServiceDocument(request.getODataRequest().getRawBaseUri());
}
static class EntityDetails {
EntityCollection entitySet = null;
Entity entity = null;
EdmEntityType entityType;
String navigationProperty;
Entity parentEntity = null;
}
private EntityDetails process(final DataRequest request) throws ODataApplicationException {
EntityCollection entitySet = null;
Entity entity = null;
EdmEntityType entityType;
Entity parentEntity = null;
if (request.isSingleton()) {
EdmSingleton singleton = request.getUriResourceSingleton().getSingleton();
entityType = singleton.getEntityType();
if (singleton.getName().equals("Me")) {
entitySet = this.dataModel.getEntitySet("People");
entity = entitySet.getEntities().get(0);
}
} else {
final EdmEntitySet edmEntitySet = request.getEntitySet();
entityType = edmEntitySet.getEntityType();
List<UriParameter> keys = request.getKeyPredicates();
// TODO: This example so far ignores all system options; but a real
// service should not
if (keys != null && !keys.isEmpty()) {
entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
} else {
int skip = 0;
if (request.getUriInfo().getSkipTokenOption() != null) {
skip = Integer.parseInt(request.getUriInfo().getSkipTokenOption().getValue());
}
int pageSize = getPageSize(request);
entitySet = this.dataModel.getEntitySet(edmEntitySet.getName(), skip, pageSize);
if (entitySet.getEntities().size() == pageSize) {
try {
entitySet.setNext(new URI(request.getODataRequest().getRawRequestUri() + "?$skiptoken="
+ (skip + pageSize)));
} catch (URISyntaxException e) {
throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
}
}
}
}
EntityDetails details = new EntityDetails();
if (!request.getNavigations().isEmpty() && entity != null) {
UriResourceNavigation lastNavigation = request.getNavigations().getLast();
for (UriResourceNavigation nav : request.getNavigations()) {
entityType = nav.getProperty().getType();
if (nav.isCollection()) {
entitySet = this.dataModel.getNavigableEntitySet(entity, nav);
} else {
parentEntity = entity;
entity = this.dataModel.getNavigableEntity(parentEntity, nav);
}
}
details.navigationProperty = lastNavigation.getProperty().getName();
}
details.entity = entity;
details.entitySet = entitySet;
details.entityType = entityType;
details.parentEntity = parentEntity;
return details;
}
@Override
public <T extends ServiceResponse> void read(final DataRequest request, final T response)
throws ODataLibraryException, ODataApplicationException {
final EntityDetails details = process(request);
response.accepts(new ServiceResponseVisior() {
@Override
public void visit(CountResponse response) throws ODataLibraryException, ODataApplicationException {
response.writeCount(details.entitySet.getCount());
}
@Override
public void visit(PrimitiveValueResponse response) throws ODataLibraryException,
ODataApplicationException {
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
Property property = details.entity.getProperty(edmProperty.getName());
response.write(property.getValue());
}
@Override
public void visit(PropertyResponse response) throws ODataLibraryException,
ODataApplicationException {
EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
Property property = details.entity.getProperty(edmProperty.getName());
response.writeProperty(edmProperty.getType(), property);
}
@Override
public void visit(StreamResponse response) throws ODataLibraryException,
ODataApplicationException {
// stream property response
response.writeStreamResponse(new ByteArrayInputStream("dummy".getBytes()),
ContentType.APPLICATION_OCTET_STREAM);
}
@Override
public void visit(EntitySetResponse response) throws ODataLibraryException,
ODataApplicationException {
if (request.getPreference("odata.maxpagesize") != null) {
response.writeHeader("Preference-Applied", "odata.maxpagesize="+request.getPreference("odata.maxpagesize"));
}
if (details.entity == null && !request.getNavigations().isEmpty()) {
response.writeReadEntitySet(details.entityType, new EntityCollection());
} else {
response.writeReadEntitySet(details.entityType, details.entitySet);
}
}
@Override
public void visit(EntityResponse response) throws ODataLibraryException,
ODataApplicationException {
if (details.entity == null && !request.getNavigations().isEmpty()) {
response.writeNoContent(true);
} else {
response.writeReadEntity(details.entityType, details.entity);
}
}
});
}
private int getPageSize(DataRequest request) {
String size = request.getPreference("odata.maxpagesize");
if (size == null) {
return 8;
}
return Integer.parseInt(size);
}
@Override
public void createEntity(DataRequest request, Entity entity, EntityResponse response)
throws ODataLibraryException, ODataApplicationException {
EdmEntitySet edmEntitySet = request.getEntitySet();
Entity created = this.dataModel.createEntity(edmEntitySet, entity, request.getODataRequest().getRawBaseUri());
try {
// create references, they come in "@odata.bind" value
List<Link> bindings = entity.getNavigationBindings();
if (bindings != null & !bindings.isEmpty()) {
for (Link link : bindings) {
String navigationProperty = link.getTitle();
String uri = link.getBindingLink();
if (uri != null) {
DataRequest bindingRequest = request.parseLink(new URI(uri));
Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
bindingRequest.getKeyPredicates());
this.dataModel.addNavigationLink(navigationProperty, created, reference);
} else {
for (String binding : link.getBindingLinks()) {
DataRequest bindingRequest = request.parseLink(new URI(binding));
Entity reference = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
bindingRequest.getKeyPredicates());
this.dataModel.addNavigationLink(navigationProperty, created, reference);
}
}
}
}
} catch (URISyntaxException e) {
throw new ODataApplicationException(e.getMessage(), 500, Locale.getDefault());
}
response.writeCreatedEntity(edmEntitySet, created);
}
@Override
public void updateEntity(DataRequest request, Entity entity, boolean merge, String eTag,
EntityResponse response) throws ODataLibraryException, ODataApplicationException {
EdmEntitySet edmEntitySet = request.getEntitySet();
Entity currentEntity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
if (currentEntity == null) {
response.writeNotFound(true);
return;
}
String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
String baseURL = request.getODataRequest().getRawBaseUri();
boolean updated = this.dataModel.updateEntity(edmEntitySet, eTag, key, currentEntity
.getProperty(key).getValue(), merge, entity, baseURL);
if (updated) {
response.writeUpdatedEntity();
} else {
response.writeNotModified();
}
}
@Override
public void deleteEntity(DataRequest request, String eTag, EntityResponse response)
throws ODataLibraryException, ODataApplicationException {
EdmEntitySet edmEntitySet = request.getEntitySet();
Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
if (entity == null) {
response.writeNotFound(true);
return;
}
String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
boolean removed = this.dataModel.deleteEntity(edmEntitySet.getName(), eTag, key, entity
.getProperty(key).getValue());
if (removed) {
response.writeDeletedEntityOrReference();
} else {
response.writeNotFound(true);
}
}
@Override
public void updateProperty(DataRequest request, final Property property, boolean rawValue, boolean merge,
String entityETag, PropertyResponse response) throws ODataLibraryException,
ODataApplicationException {
if (rawValue && property.getValue() != null) {
// this is more generic, stricter conversion rules must taken in a real service
byte[] value = (byte[])property.getValue();
property.setValue(ValueType.PRIMITIVE, new String(value));
}
EdmEntitySet edmEntitySet = request.getEntitySet();
Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
if (entity == null) {
response.writeNotFound(true);
return;
}
String key = edmEntitySet.getEntityType().getKeyPredicateNames().get(0);
boolean replaced = this.dataModel.updateProperty(edmEntitySet.getName(), entityETag, key,
entity.getProperty(key).getValue(), property);
if (replaced) {
if (property.getValue() == null) {
response.writePropertyDeleted();
} else {
response.writePropertyUpdated();
}
} else {
response.writeServerError(true);
}
}
@Override
public <T extends ServiceResponse> void invoke(FunctionRequest request, HttpMethod method,
T response) throws ODataLibraryException, ODataApplicationException {
EdmFunction function = request.getFunction();
if (function.getName().equals("GetNearestAirport")) {
final EdmEntityType type = serviceMetadata.getEdm().getEntityContainer(null)
.getEntitySet("Airports").getEntityType();
EntityCollection es = this.dataModel.getEntitySet("Airports");
int i = new Random().nextInt(es.getEntities().size());
final Entity entity = es.getEntities().get(i);
response.accepts(new ServiceResponseVisior() {
@Override
public void visit(EntityResponse response) throws ODataLibraryException,
ODataApplicationException {
response.writeReadEntity(type, entity);
}
});
}
}
@Override
public <T extends ServiceResponse> void invoke(ActionRequest request, String eTag, T response)
throws ODataLibraryException, ODataApplicationException {
EdmAction action = request.getAction();
if (action.getName().equals("ResetDataSource")) {
try {
this.dataModel.loadData();
response.accepts(new ServiceResponseVisior() {
@Override
public void visit(NoContentResponse response) throws ODataLibraryException,
ODataApplicationException {
response.writeNoContent();
}
});
} catch (Exception e) {
response.writeServerError(true);
}
} else {
response.writeServerError(true);
}
}
@Override
public void readMediaStream(MediaRequest request, StreamResponse response)
throws ODataLibraryException, ODataApplicationException {
final EdmEntitySet edmEntitySet = request.getEntitySet();
List<UriParameter> keys = request.getKeyPredicates();
Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
InputStream contents = this.dataModel.readMedia(entity);
response.writeStreamResponse(contents, request.getResponseContentType());
}
@Override
public void upsertMediaStream(MediaRequest request, String entityETag, InputStream mediaContent,
NoContentResponse response) throws ODataLibraryException, ODataApplicationException {
final EdmEntitySet edmEntitySet = request.getEntitySet();
List<UriParameter> keys = request.getKeyPredicates();
Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
if (mediaContent == null) {
boolean deleted = this.dataModel.deleteMedia(entity);
if (deleted) {
response.writeNoContent();
} else {
response.writeNotFound();
}
} else {
boolean updated = this.dataModel.updateMedia(entity, mediaContent);
if (updated) {
response.writeNoContent();
} else {
response.writeServerError(true);
}
}
}
@Override
public void upsertStreamProperty(DataRequest request, String entityETag, InputStream streamContent,
NoContentResponse response) throws ODataLibraryException, ODataApplicationException {
final EdmEntitySet edmEntitySet = request.getEntitySet();
List<UriParameter> keys = request.getKeyPredicates();
Entity entity = this.dataModel.getEntity(edmEntitySet.getName(), keys);
EdmProperty property = request.getUriResourceProperty().getProperty();
if (streamContent == null) {
boolean deleted = this.dataModel.deleteStream(entity, property);
if (deleted) {
response.writeNoContent();
} else {
response.writeNotFound();
}
} else {
boolean updated = this.dataModel.updateStream(entity, property, streamContent);
if (updated) {
response.writeNoContent();
} else {
response.writeServerError(true);
}
}
}
@Override
public void addReference(DataRequest request, String entityETag, URI referenceId,
NoContentResponse response) throws ODataLibraryException, ODataApplicationException {
final EntityDetails details = process(request);
DataRequest bindingRequest = request.parseLink(referenceId);
Entity linkEntity = this.dataModel.getEntity(bindingRequest.getEntitySet().getName(),
bindingRequest.getKeyPredicates());
this.dataModel.addNavigationLink(details.navigationProperty, details.entity, linkEntity);
response.writeNoContent();
}
@Override
public void updateReference(DataRequest request, String entityETag, URI updateId,
NoContentResponse response) throws ODataLibraryException, ODataApplicationException {
// this single valued navigation.
boolean updated = false;
final EntityDetails details = process(request);
DataRequest updateRequest = request.parseLink(updateId);
Entity updateEntity = this.dataModel.getEntity(updateRequest.getEntitySet().getName(),
updateRequest.getKeyPredicates());
if (updateEntity != null) {
updated = this.dataModel.updateNavigationLink(details.navigationProperty,
details.parentEntity, updateEntity);
}
if (updated) {
response.writeNoContent();
} else {
response.writeServerError(true);
}
}
@Override
public void deleteReference(DataRequest request, URI deleteId, String entityETag,
NoContentResponse response) throws ODataLibraryException, ODataApplicationException {
boolean removed = false;
if (deleteId != null) {
final EntityDetails details = process(request);
DataRequest deleteRequest = request.parseLink(deleteId);
Entity deleteEntity = this.dataModel.getEntity(deleteRequest.getEntitySet().getName(),
deleteRequest.getKeyPredicates());
if (deleteEntity != null) {
removed = this.dataModel.removeNavigationLink(details.navigationProperty, details.entity,
deleteEntity);
}
} else {
// this single valued navigation.
final EntityDetails details = process(request);
removed = this.dataModel.removeNavigationLink(details.navigationProperty,
details.parentEntity, details.entity);
}
if (removed) {
response.writeNoContent();
} else {
response.writeServerError(true);
}
}
@Override
public void anyUnsupported(ODataRequest request, ODataResponse response)
throws ODataLibraryException, ODataApplicationException {
response.setStatusCode(500);
}
@Override
public String startTransaction() {
return null;
}
@Override
public void commit(String txnId) {
}
@Override
public void rollback(String txnId) {
}
@Override
public void crossJoin(DataRequest dataRequest, List<String> entitySetNames, ODataResponse response) {
response.setStatusCode(501);
}
@Override
public void apply(DataRequest dataRequest, ODataResponse response)
throws ODataLibraryException, ODataApplicationException {
response.setStatusCode(501);
}
@Override
public void upsertEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
EntityResponse response) throws ODataLibraryException, ODataApplicationException {
EdmEntitySet edmEntitySet = request.getEntitySet();
Entity currentEntity = this.dataModel.getEntity(edmEntitySet.getName(), request.getKeyPredicates());
if(currentEntity == null) {
createEntity(request, entity, response);
} else {
updateEntity(request, entity, merge, entityETag, response);
}
}
@Override
public boolean supportsDataIsolation() {
return false;
}
@Override
public void processError(ODataServerError error, ErrorResponse response) {
error.getException().printStackTrace();
response.writeError(error);
}
}