You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ra...@apache.org on 2015/03/31 18:35:52 UTC

[06/11] olingo-odata4 git commit: OLINGO-573: New processing framework on server side with single interface with TripPin as example

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
new file mode 100644
index 0000000..19a0387
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinHandler.java
@@ -0,0 +1,546 @@
+/*
+ * 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.EntitySet;
+import org.apache.olingo.commons.api.data.Link;
+import org.apache.olingo.commons.api.data.Property;
+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.format.ContentType;
+import org.apache.olingo.commons.api.http.HttpMethod;
+import org.apache.olingo.commons.core.data.EntitySetImpl;
+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.ODataTranslatedException;
+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.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 ODataTranslatedException, ODataApplicationException {
+    response.writeMetadata();
+  }
+
+  @Override
+  public void readServiceDocument(ServiceDocumentRequest request, ServiceDocumentResponse response)
+      throws ODataTranslatedException, ODataApplicationException {
+    response.writeServiceDocument(request.getODataRequest().getRawBaseUri());
+  }
+
+  static class EntityDetails {
+    EntitySet entitySet = null;
+    Entity entity = null;
+    EdmEntityType entityType;
+    String navigationProperty;
+    Entity parentEntity = null;
+  }
+
+  private EntityDetails process(final DataRequest request) throws ODataApplicationException {
+    EntitySet 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 ODataTranslatedException, ODataApplicationException {
+
+    final EntityDetails details = process(request);
+
+    response.accepts(new ServiceResponseVisior() {
+      @Override
+      public void visit(CountResponse response) throws ODataTranslatedException, ODataApplicationException {
+        response.writeCount(details.entitySet.getCount());
+      }
+
+      @Override
+      public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        EdmProperty edmProperty = request.getUriResourceProperty().getProperty();
+        Property property = details.entity.getProperty(edmProperty.getName());
+        response.write(property.getValue());
+      }
+
+      @Override
+      public void visit(PropertyResponse response) throws ODataTranslatedException,
+          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 ODataTranslatedException,
+          ODataApplicationException {
+        // stream property response
+        response.writeStreamResponse(new ByteArrayInputStream("dummy".getBytes()),
+            ContentType.APPLICATION_OCTET_STREAM);
+      }
+
+      @Override
+      public void visit(EntitySetResponse response) throws ODataTranslatedException,
+          ODataApplicationException {
+        if (request.getPreference("odata.maxpagesize") != null) {
+          response.writeHeader("Preference-Applied", request.getPreference("odata.maxpagesize"));
+        }
+        if (details.entity == null && !request.getNavigations().isEmpty()) {
+          response.writeReadEntitySet(details.entityType, new EntitySetImpl());
+        } else {
+          response.writeReadEntitySet(details.entityType, details.entitySet);
+        }
+      }
+
+      @Override
+      public void visit(EntityResponse response) throws ODataTranslatedException,
+          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 ODataTranslatedException, ODataApplicationException {
+    EdmEntitySet edmEntitySet = request.getEntitySet();
+
+    String location = buildLocation(entity, edmEntitySet.getName(), edmEntitySet.getEntityType());
+    Entity created = this.dataModel.createEntity(edmEntitySet.getName(), entity, location);
+
+    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.getEntityType(), created, location);
+  }
+
+  static String buildLocation(Entity entity, String name, EdmEntityType type) {
+    String location = "/" + name + "(";
+    int i = 0;
+    boolean usename = type.getKeyPredicateNames().size() > 1;
+
+    for (String key : type.getKeyPredicateNames()) {
+      if (i > 0) {
+        location += ",";
+      }
+      i++;
+      if (usename) {
+        location += (key + "=");
+      }
+      if (entity.getProperty(key).getType().equals("Edm.String")) {
+        location = location + "'" + entity.getProperty(key).getValue().toString() + "'";
+      } else {
+        location = location + entity.getProperty(key).getValue().toString();
+      }
+    }
+    location += ")";
+    return location;
+  }
+
+  @Override
+  public void updateEntity(DataRequest request, Entity entity, boolean merge, String entityETag,
+      EntityResponse response) throws ODataTranslatedException, ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  @Override
+  public void deleteEntity(DataRequest request, String eTag, EntityResponse response)
+      throws ODataTranslatedException, 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 merge,
+      String entityETag, PropertyResponse response) throws ODataTranslatedException,
+      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 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 ODataTranslatedException, ODataApplicationException {
+    EdmFunction function = request.getFunction();
+    if (function.getName().equals("GetNearestAirport")) {
+
+      final EdmEntityType type = serviceMetadata.getEdm().getEntityContainer(null)
+          .getEntitySet("Airports").getEntityType();
+
+      EntitySet 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 ODataTranslatedException,
+            ODataApplicationException {
+          response.writeReadEntity(type, entity);
+        }
+      });
+    }
+  }
+
+  @Override
+  public <T extends ServiceResponse> void invoke(ActionRequest request, String eTag, T response)
+      throws ODataTranslatedException, 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 ODataTranslatedException,
+              ODataApplicationException {
+            response.writeNoContent();
+          }
+        });
+      } catch (Exception e) {
+        response.writeServerError(true);
+      }
+    } else {
+      response.writeServerError(true);
+    }
+  }
+
+  @Override
+  public void readMediaStream(MediaRequest request, StreamResponse response)
+      throws ODataTranslatedException, 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 ODataTranslatedException, 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 ODataTranslatedException, 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, List<URI> references,
+      NoContentResponse response) throws ODataTranslatedException, ODataApplicationException {
+
+    final EntityDetails details = process(request);
+
+    for (URI reference : references) {
+      DataRequest bindingRequest = request.parseLink(reference);
+      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 ODataTranslatedException, ODataApplicationException {
+    // this single valued navigation.
+    final EntityDetails details = process(request);
+    DataRequest updateRequest = request.parseLink(updateId);
+    Entity updateEntity = this.dataModel.getEntity(updateRequest.getEntitySet().getName(),
+        updateRequest.getKeyPredicates());
+    boolean updated = false;
+    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 ODataTranslatedException, 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 ODataTranslatedException, 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(200);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
new file mode 100644
index 0000000..4b26b8e
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServiceTest.java
@@ -0,0 +1,756 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+
+import org.apache.olingo.commons.core.Encoder;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.api.ContentProvider;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+
+/**
+ * Please note that NONE of the system query options are developed in the sample
+ * service like $filter, $orderby etc. So using those options will be ignored
+ * right now. These tests designed to test the framework, all options are responsibilities
+ * of service developer.
+ */
+public class TripPinServiceTest {
+  private static Server server = new Server();
+  private static String baseURL;
+  private static HttpClient http = new HttpClient();
+
+  @BeforeClass
+  public static void beforeTest() throws Exception {
+    ServerConnector connector = new ServerConnector(server);
+    server.setConnectors(new Connector[] { connector });
+
+    ServletContextHandler context = new ServletContextHandler();
+    context.setContextPath("/trippin");
+    context.addServlet(new ServletHolder(new TripPinServlet()), "/*");
+    server.setHandler(context);
+    server.start();
+    int port = connector.getLocalPort();
+    http.start();
+    baseURL = "http://localhost:"+port+"/trippin";
+  }
+
+  @AfterClass
+  public static void afterTest() throws Exception {
+    server.stop();
+  }
+
+  @Test
+  public void testEntitySet() throws Exception {
+    ContentResponse response = http.newRequest(baseURL + "/People")
+    .header("Content-Type", "application/json;odata.metadata=minimal")
+    .method(HttpMethod.GET)
+    .send();
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+
+    assertEquals("$metadata#People", node.get("@odata.context").asText());
+    assertEquals(baseURL+"/People?$skiptoken=8", node.get("@odata.nextLink").asText());
+
+    JsonNode person = ((ArrayNode)node.get("value")).get(0);
+    assertEquals("russellwhyte", person.get("UserName").asText());
+  }
+
+  private JsonNode getJSONNode(ContentResponse response) throws IOException,
+      JsonProcessingException {
+    ObjectMapper objectMapper = new ObjectMapper();
+    JsonNode node = objectMapper.readTree(response.getContent());
+    return node;
+  }
+
+  @Test
+  public void testReadEntitySetWithPaging() throws Exception {
+    ContentResponse response = http.newRequest(baseURL + "/People")
+        .header("Prefer", "odata.maxpagesize=10").send();
+
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People", node.get("@odata.context").asText());
+    assertEquals(baseURL+"/People?$skiptoken=10", node.get("@odata.nextLink").asText());
+
+    JsonNode person = ((ArrayNode)node.get("value")).get(0);
+    assertEquals("russellwhyte", person.get("UserName").asText());
+
+    assertNotNull(response.getHeaders().get("Preference-Applied"));
+  }
+
+  @Test
+  public void testReadEntityWithKey() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines('AA')");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#Airlines/$entity", node.get("@odata.context").asText());
+    assertEquals("American Airlines", node.get("Name").asText());
+  }
+
+  @Test
+  public void testReadEntityWithNonExistingKey() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines('OO')");
+    assertEquals(404, response.getStatus());
+  }
+
+  @Test
+  public void testRead$Count() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines/$count");
+    assertEquals(200, response.getStatus());
+    assertEquals("15", response.getContentAsString());
+  }
+
+  @Test
+  public void testReadPrimitiveProperty() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#Airlines('AA')/Name", node.get("@odata.context").asText());
+    assertEquals("American Airlines", node.get("value").asText());
+  }
+
+  @Test
+  public void testReadNonExistentProperty() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Unknown");
+    assertEquals(404, response.getStatus());
+  }
+
+  @Test
+  public void testReadPrimitiveArrayProperty() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Emails");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("Russell@example.com", ((ArrayNode)node.get("value")).get(0).asText());
+    assertEquals("Russell@contoso.com", ((ArrayNode)node.get("value")).get(1).asText());
+  }
+
+  @Test
+  public void testReadPrimitivePropertyValue() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airlines('AA')/Name/$value");
+    assertEquals(200, response.getStatus());
+    assertEquals("American Airlines", response.getContentAsString());
+  }
+
+  @Test @Ignore
+  // TODO: Support geometry types to make this run
+  public void testReadComplexProperty() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Airports('KSFO')/Location");
+    fail("support geometry type");
+  }
+
+  @Test
+  public void testReadComplexArrayProperty() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/AddressInfo");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/AddressInfo", node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("187 Suffolk Ln.", ((ArrayNode)node.get("value")).get(0).get("Address").asText());
+  }
+
+  @Test
+  public void testReadMedia() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Photos(1)/$value");
+    assertEquals(200, response.getStatus());
+  }
+
+  @Test
+  public void testCreateMedia() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Photos(1)/$value";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content("bytecontents"), "image/jpeg")
+        .method(HttpMethod.PUT)
+        .send();
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testDeleteMedia() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Photos(1)/$value";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content("bytecontents"), "image/jpeg")
+        .method(HttpMethod.DELETE)
+        .send();
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testCreateStream() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Airlines('AA')/Picture";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content("bytecontents"), "image/jpeg")
+        .method(HttpMethod.POST)
+        .send();
+    // method not allowed
+    assertEquals(405, response.getStatus());
+  }
+
+  @Test
+  public void testCreateStream2() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Airlines('AA')/Picture";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content("bytecontents"), "image/jpeg")
+        .method(HttpMethod.PUT)
+        .send();
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testDeleteStream() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Airlines('AA')/Picture";
+    ContentResponse response = http.newRequest(editUrl)
+        .method(HttpMethod.DELETE)
+        .send();
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testReadStream() throws Exception {
+    // treating update and create as same for now, as there is details about
+    // how entity payload and media payload can be sent at same time in request's body
+    String editUrl = baseURL + "/Airlines('AA')/Picture";
+    ContentResponse response = http.newRequest(editUrl)
+        .method(HttpMethod.GET)
+        .send();
+    assertEquals(200, response.getStatus());
+  }
+
+  @Test
+  public void testLambdaAny() throws Exception {
+    // this is just testing to see the labba expresions are going through the
+    // framework, none of the system options are not implemented in example service
+    String query = "Friends/any(d:d/UserName eq 'foo')";
+    ContentResponse response = http.newRequest(baseURL + "/People?$filter="+Encoder.encode(query))
+        .method(HttpMethod.GET)
+        .send();
+    assertEquals(200, response.getStatus());
+  }
+
+  @Test
+  public void testSingleton() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/Me");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#Me", node.get("@odata.context").asText());
+    assertEquals("russellwhyte", node.get("UserName").asText());
+  }
+
+  @Test
+  public void testSelectOption() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/People('russellwhyte')?$select=FirstName,LastName");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People(FirstName,LastName)/$entity", node.get("@odata.context").asText());
+    assertEquals("Russell", node.get("FirstName").asText());
+  }
+
+  @Test
+  public void testActionImportWithNoResponse() throws Exception {
+    ContentResponse response = http.POST(baseURL + "/ResetDataSource").send();
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testFunctionImport() throws Exception {
+    //TODO: fails because of lack of geometery support
+    ContentResponse response = http.GET(baseURL + "/GetNearestAirport(lat=23.0,lon=34.0)");
+  }
+
+  @Test
+  public void testBadReferences() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/People('russelwhyte')/$ref");
+    assertEquals(405, response.getStatus());
+  }
+
+  @Test
+  public void testReadReferences() throws Exception {
+    ContentResponse response = http.GET(baseURL + "/People('russellwhyte')/Friends/$ref");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#Collection($ref)", node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+  }
+
+  @Test
+  public void testAddCollectionReferences() throws Exception {
+    //GET
+    ContentResponse response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+
+    assertTrue(node.get("value").isArray());
+    assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+    assertNull(((ArrayNode)node.get("value")).get(1));
+
+    //ADD
+    String payload = "{\n" +
+        "  \"@odata.context\": \""+baseURL+"/$metadata#Collection($ref)\",\n" +
+        "  \"value\": [\n" +
+        "    { \"@odata.id\": \"People('russellwhyte')\" },\n" +
+        "    { \"@odata.id\": \"People('scottketchum')\" } \n" +
+        "  ]\n" +
+        "}";
+    response = http.POST(baseURL + "/People('kristakemp')/Friends/$ref")
+        .content(content(payload), "application/json")
+        .send();
+    assertEquals(204, response.getStatus());
+
+    //GET
+    response = http.GET(baseURL + "/People('kristakemp')/Friends/$ref");
+    assertEquals(200, response.getStatus());
+    node = getJSONNode(response);
+
+    assertTrue(node.get("value").isArray());
+    assertEquals("/People('genevievereeves')", ((ArrayNode)node.get("value")).get(0).get("@odata.id").asText());
+    assertEquals("/People('russellwhyte')", ((ArrayNode)node.get("value")).get(1).get("@odata.id").asText());
+    assertEquals("/People('scottketchum')", ((ArrayNode)node.get("value")).get(2).get("@odata.id").asText());
+  }
+
+
+  @Test
+  public void testEntityId() throws Exception {
+    ContentResponse response = http.GET(baseURL+"/$entity?$id="+baseURL + "/People('kristakemp')");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+    assertEquals("kristakemp", node.get("UserName").asText());
+
+    // using relative URL
+    response = http.GET(baseURL+"/$entity?$id="+"People('kristakemp')");
+    assertEquals(200, response.getStatus());
+    node = getJSONNode(response);
+    assertEquals("$metadata#People/$entity", node.get("@odata.context").asText());
+    assertEquals("kristakemp", node.get("UserName").asText());
+  }
+
+  @Test
+  public void testCreateReadDeleteEntity() throws Exception {
+    String payload = "{\n" +
+        "         \"UserName\":\"olingodude\",\n" +
+        "         \"FirstName\":\"Olingo\",\n" +
+        "         \"LastName\":\"Apache\",\n" +
+        "         \"Emails\":[\n" +
+        "            \"olingo@apache.org\"\n" +
+        "         ],\n" +
+        "         \"AddressInfo\":[\n" +
+        "            {\n" +
+        "               \"Address\":\"100 apache Ln.\",\n" +
+        "               \"City\":{\n" +
+        "                  \"CountryRegion\":\"United States\",\n" +
+        "                  \"Name\":\"Boise\",\n" +
+        "                  \"Region\":\"ID\"\n" +
+        "               }\n" +
+        "            }\n" +
+        "         ],\n" +
+        "         \"Gender\":\"0\",\n" +
+        "         \"Concurrency\":635585295719432047\n" +
+        "}";
+    ContentResponse response = http.POST(baseURL + "/People")
+        .content(content(payload), "application/json")
+        .header("Prefer", "return=minimal")
+        .send();
+    // the below woud be 204, if minimal was not supplied
+    assertEquals(204, response.getStatus());
+    assertEquals("/People('olingodude')", response.getHeaders().get("Location"));
+    assertEquals("return=minimal", response.getHeaders().get("Preference-Applied"));
+
+    String location = baseURL+response.getHeaders().get("Location");
+    response = http.GET(location);
+    assertEquals(200, response.getStatus());
+
+    response = http.newRequest(location).method(HttpMethod.DELETE).send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(location);
+    assertEquals(404, response.getStatus());
+  }
+
+
+  @Test
+  public void testCreateEntityWithLinkToRelatedEntities() throws Exception {
+    String payload = "{\n" +
+        "         \"UserName\":\"olingo\",\n" +
+        "         \"FirstName\":\"Olingo\",\n" +
+        "         \"LastName\":\"Apache\",\n" +
+        "         \"Emails\":[\n" +
+        "            \"olingo@apache.org\"\n" +
+        "         ],\n" +
+        "         \"AddressInfo\":[\n" +
+        "            {\n" +
+        "               \"Address\":\"100 apache Ln.\",\n" +
+        "               \"City\":{\n" +
+        "                  \"CountryRegion\":\"United States\",\n" +
+        "                  \"Name\":\"Boise\",\n" +
+        "                  \"Region\":\"ID\"\n" +
+        "               }\n" +
+        "            }\n" +
+        "         ],\n" +
+        "         \"Gender\":\"0\",\n" +
+        "         \"Concurrency\":635585295719432047,\n" +
+        "\"Friends@odata.bind\":[\"" +
+         baseURL+"/People('russellwhyte')\",\""+
+         baseURL+"/People('scottketchum')\""+
+        "]"+
+        "}";
+    ContentResponse response = http.POST(baseURL + "/People")
+        .content(content(payload), "application/json")
+        .header("Prefer", "return=minimal")
+        .send();
+    // the below woud be 204, if minimal was not supplied
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(baseURL+"/People('olingo')/Friends");
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People", node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("scottketchum", ((ArrayNode)node.get("value")).get(1).get("UserName").asText());
+  }
+
+  @Test
+  public void testUpdatePrimitiveProperty() throws Exception {
+    String payload = "{"
+        + " \"value\":\"Pilar Ackerman\""
+        + "}";
+
+    String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content(payload), "application/json")
+        .method(HttpMethod.PUT)
+        .send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/FirstName", node.get("@odata.context").asText());
+    assertEquals("Pilar Ackerman", node.get("value").asText());
+  }
+
+  @Test
+  public void testUpdatePrimitiveArrayProperty() throws Exception {
+    String payload = "{"
+        + " \"value\": [\n" +
+        "       \"olingo@apache.com\"\n" +
+        "    ]"
+        + "}";
+
+    String editUrl = baseURL + "/People('russellwhyte')/Emails";
+    ContentResponse response = http.newRequest(editUrl)
+        .content(content(payload), "application/json")
+        .method(HttpMethod.PUT)
+        .send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Emails", node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("olingo@apache.com", ((ArrayNode)node.get("value")).get(0).asText());
+  }
+
+  @Test
+  public void testDeleteProperty() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/FirstName";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("Russell", node.get("value").asText());
+
+    response = http.newRequest(editUrl)
+        .method(HttpMethod.DELETE)
+        .send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(editUrl);
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityCollection() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Friends";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People", node.get("@odata.context").asText());
+
+    JsonNode person = ((ArrayNode)node.get("value")).get(0);
+    assertEquals("scottketchum", person.get("UserName").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityCollection2() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Friends('scottketchum')/Trips";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Friends('scottketchum')/Trips",
+        node.get("@odata.context").asText());
+    assertTrue(node.get("value").isArray());
+    assertEquals("1001", ((ArrayNode)node.get("value")).get(0).get("TripId").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntity() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Trips/$entity",
+        node.get("@odata.context").asText());
+    assertEquals("f94e9116-8bdd-4dac-ab61-08438d0d9a71", node.get("ShareId").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityNotExisting() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Trips(9999)";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntitySetNotExisting() throws Exception {
+    String editUrl = baseURL + "/People('jhondoe')/Trips";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('jhondoe')/Trips",
+        node.get("@odata.context").asText());
+    assertEquals(0, ((ArrayNode)node.get("value")).size());
+  }
+
+  @Test
+  public void testBadNavigationProperty() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Unknown";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(404, response.getStatus());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityProperty() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems(5)/ConfirmationCode",
+        node.get("@odata.context").asText());
+
+    assertEquals("JH58494", node.get("value").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityMultipleDerivedTypes() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Trips(1003)/PlanItems";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems",
+        node.get("@odata.context").asText());
+
+    assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Flight",
+        ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityCoolectionDerivedFilter() throws Exception {
+    String editUrl = baseURL
+        + "/People('russellwhyte')/Trips(1003)/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Event";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+        + "Microsoft.OData.SampleService.Models.TripPin.Event",
+        node.get("@odata.context").asText());
+
+    assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event",
+        ((ArrayNode) node.get("value")).get(0).get("@odata.type").asText());
+  }
+
+  @Test
+  public void testReadNavigationPropertyEntityDerivedFilter() throws Exception {
+    String editUrl = baseURL+ "/People('russellwhyte')/Trips(1003)/PlanItems(56)/"
+        + "Microsoft.OData.SampleService.Models.TripPin.Event";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("$metadata#People('russellwhyte')/Trips(1003)/PlanItems/"
+        + "Microsoft.OData.SampleService.Models.TripPin.Event/$entity",
+        node.get("@odata.context").asText());
+
+    assertEquals("#Microsoft.OData.SampleService.Models.TripPin.Event", node.get("@odata.type").asText());
+    assertEquals("56", node.get("PlanItemId").asText());
+  }
+
+  @Test
+  public void testUpdateReference() throws Exception {
+    ContentResponse response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("/Photos(12)", node.get("@odata.id").asText());
+
+    String msg = "{\n" +
+        "\"@odata.id\": \"/Photos(11)\"\n" +
+        "}";
+    String editUrl = baseURL + "/People('ronaldmundy')/Photo/$ref";
+    response = http.newRequest(editUrl)
+        .method(HttpMethod.PUT)
+        .content(content(msg))
+        .header("Content-Type", "application/json;odata.metadata=minimal")
+        .send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(baseURL+"/People('ronaldmundy')/Photo/$ref");
+    assertEquals(200, response.getStatus());
+    node = getJSONNode(response);
+    assertEquals("/Photos(11)", node.get("@odata.id").asText());
+  }
+
+  @Test
+  public void testAddDelete2ReferenceCollection() throws Exception {
+    // add
+    String msg = "{\n" +
+        "\"@odata.id\": \"/People('russellwhyte')\"\n" +
+        "}";
+    String editUrl = baseURL + "/People('vincentcalabrese')/Friends/$ref";
+    ContentResponse response = http.newRequest(editUrl)
+        .method(HttpMethod.POST)
+        .content(content(msg))
+        .header("Content-Type", "application/json;odata.metadata=minimal")
+        .send();
+    assertEquals(204, response.getStatus());
+
+    // get
+    response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    JsonNode node = getJSONNode(response);
+    assertEquals("/People('russellwhyte')",
+        ((ArrayNode) node.get("value")).get(2).get("@odata.id").asText());
+
+    //delete
+    response = http.newRequest(editUrl+"?$id="+baseURL+"/People('russellwhyte')")
+        .method(HttpMethod.DELETE)
+        .content(content(msg))
+        .header("Content-Type", "application/json;odata.metadata=minimal")
+        .send();
+    assertEquals(204, response.getStatus());
+
+    // get
+    response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+    node = getJSONNode(response);
+    assertNull("/People('russellwhyte')", ((ArrayNode) node.get("value")).get(2));
+  }
+
+  @Test
+  public void testDeleteReference() throws Exception {
+    String editUrl = baseURL + "/People('russellwhyte')/Photo/$ref";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+
+    response = http.newRequest(editUrl)
+        .method(HttpMethod.DELETE)
+        .send();
+    assertEquals(204, response.getStatus());
+
+    response = http.GET(editUrl);
+    assertEquals(204, response.getStatus());
+  }
+
+  @Test
+  public void testCrossJoin() throws Exception {
+    String editUrl = baseURL + "/$crossjoin(People,Airlines)";
+    ContentResponse response = http.GET(editUrl);
+    assertEquals(200, response.getStatus());
+  }
+
+  public static ContentProvider content(final String msg) {
+    return new ContentProvider() {
+      boolean hasNext = true;
+
+      @Override
+      public Iterator<ByteBuffer> iterator() {
+        return new Iterator<ByteBuffer>() {
+          @Override
+          public boolean hasNext() {
+            return hasNext;
+          }
+          @Override
+          public ByteBuffer next() {
+            hasNext = false;
+            return ByteBuffer.wrap(msg.getBytes());
+          }
+          @Override
+          public void remove() {
+          }
+        };
+      }
+      @Override
+      public long getLength() {
+        return msg.length();
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
new file mode 100644
index 0000000..2c05d65
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinServlet.java
@@ -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 org.apache.olingo.server.example;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+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.core.MetadataParser;
+import org.apache.olingo.server.core.OData4Impl;
+
+public class TripPinServlet extends HttpServlet {
+  private static final long serialVersionUID = 2663595419366214401L;
+  private TripPinDataModel dataModel;
+
+  @Override
+  public void init(ServletConfig config) throws ServletException {
+    super.init(config);
+  }
+
+  @Override
+  public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+    OData odata = OData4Impl.newInstance();
+    MetadataParser parser = new MetadataParser();
+    EdmProvider edmProvider = null;
+
+    try {
+      edmProvider = parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
+    } catch (XMLStreamException e) {
+      throw new IOException(e);
+    }
+
+    ServiceMetadata metadata = odata.createServiceMetadata(edmProvider, Collections.EMPTY_LIST);
+
+    ODataHttpHandler handler = odata.createHandler(metadata);
+
+    if (this.dataModel == null) {
+      try {
+        this.dataModel = new TripPinDataModel(metadata);
+      } catch (Exception e) {
+        throw new IOException("Failed to load data for TripPin Service");
+      }
+    }
+
+    handler.register(new TripPinHandler(this.dataModel));
+    handler.process(request, response);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png
new file mode 100644
index 0000000..d614f1a
Binary files /dev/null and b/lib/server-core-ext/src/test/resources/OlingoOrangeTM.png differ

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/airlines.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/airlines.json b/lib/server-core-ext/src/test/resources/airlines.json
new file mode 100644
index 0000000..78eccdc
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airlines.json
@@ -0,0 +1,64 @@
+{
+   "value":[
+      {
+         "AirlineCode":"AA",
+         "Name":"American Airlines"
+      },
+      {
+         "AirlineCode":"FM",
+         "Name":"Shanghai Airline"
+      },
+      {
+         "AirlineCode":"MU",
+         "Name":"China Eastern Airlines"
+      },
+      {
+         "AirlineCode":"AF",
+         "Name":"Air France"
+      },
+      {
+         "AirlineCode":"AZ",
+         "Name":"Alitalia"
+      },
+      {
+         "AirlineCode":"AC",
+         "Name":"Air Canada"
+      },
+      {
+         "AirlineCode":"OS",
+         "Name":"Austrian Airlines"
+      },
+      {
+         "AirlineCode":"TK",
+         "Name":"Turkish Airlines"
+      },
+      {
+         "AirlineCode":"JL",
+         "Name":"Japan Airlines"
+      },
+      {
+         "AirlineCode":"SQ",
+         "Name":"Singapore Airlines"
+      },
+      {
+         "AirlineCode":"KE",
+         "Name":"Korean Air"
+      },
+      {
+         "AirlineCode":"CZ",
+         "Name":"China Southern"
+      },
+      {
+         "AirlineCode":"AK",
+         "Name":"AirAsia"
+      },
+      {
+         "AirlineCode":"HX",
+         "Name":"Hong Kong Airlines"
+      },
+      {
+         "AirlineCode":"EK",
+         "Name":"Emirates"
+      }      
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/airports.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/airports.json b/lib/server-core-ext/src/test/resources/airports.json
new file mode 100644
index 0000000..c06a5cf
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/airports.json
@@ -0,0 +1,394 @@
+{
+   "value":[
+      {
+         "IcaoCode":"KSFO",
+         "Name":"San Francisco International Airport",
+         "IataCode":"SFO",
+         "Location":{
+            "Address":"South McDonnell Road, San Francisco, CA 94128",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"San Francisco",
+               "Region":"California"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -122.374722222222,
+                  37.6188888888889
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"KLAX",
+         "Name":"Los Angeles International Airport",
+         "IataCode":"LAX",
+         "Location":{
+            "Address":"1 World Way, Los Angeles, CA, 90045",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"Los Angeles",
+               "Region":"California"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -118.408055555556,
+                  33.9425
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"ZSSS",
+         "Name":"Shanghai Hongqiao International Airport",
+         "IataCode":"SHA",
+         "Location":{
+            "Address":"Hongqiao Road 2550, Changning District",
+            "City":{
+               "CountryRegion":"China",
+               "Name":"Shanghai",
+               "Region":"Shanghai"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  121.336111111111,
+                  31.1977777777778
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"ZBAA",
+         "Name":"Beijing Capital International Airport",
+         "IataCode":"PEK",
+         "Location":{
+            "Address":"Airport Road, Chaoyang District, Beijing, 100621",
+            "City":{
+               "CountryRegion":"China",
+               "Name":"Beijing",
+               "Region":"Beijing"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  116.584444444444,
+                  40.08
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"KJFK",
+         "Name":"John F. Kennedy International Airport",
+         "IataCode":"JFK",
+         "Location":{
+            "Address":"Jamaica, New York, NY 11430",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"New York City",
+               "Region":"New York"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -73.7788888888889,
+                  40.6397222222222
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"LIRA",
+         "Name":"Rome Ciampino Airport",
+         "IataCode":"CIA",
+         "Location":{
+            "Address":"Via Appia Nuova, 1651",
+            "City":{
+               "CountryRegion":"Italy",
+               "Name":"Rome",
+               "Region":""
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  12.5947222222222,
+                  41.7991666666667
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"CYYZ",
+         "Name":"Toronto Pearson International Airport",
+         "IataCode":"YYZ",
+         "Location":{
+            "Address":"6301 Silver Dart Dr Mississauga",
+            "City":{
+               "CountryRegion":"Canada",
+               "Name":"Mississauga",
+               "Region":"Ontario"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -79.6305555555555,
+                  43.6772222222222
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"YSSY",
+         "Name":"Sydney Airport",
+         "IataCode":"SYD",
+         "Location":{
+            "Address":"Airport Dr Sydney NSW 2020",
+            "City":{
+               "CountryRegion":"Australia",
+               "Name":"Sydney",
+               "Region":""
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  151.177222222222,
+                  -33.9461111111111
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"LTBA",
+         "Name":"Istanbul Ataturk Airport",
+         "IataCode":"IST",
+         "Location":{
+            "Address":"Ye\u015filk\u00f6y Mh.34149 \u0130stanbul",
+            "City":{
+               "CountryRegion":"Turkey",
+               "Name":"Istanbul",
+               "Region":""
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  28.8211111111111,
+                  40.9766666666667
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"WSSS",
+         "Name":"Singapore Changi Airport",
+         "IataCode":"SIN",
+         "Location":{
+            "Address":"Airport Blvd, Singapore",
+            "City":{
+               "CountryRegion":"Singapore",
+               "Name":"Changi",
+               "Region":""
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  103.987222222222,
+                  1.35555555555556
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"OMAA",
+         "Name":"Abu Dhabi International Airport",
+         "IataCode":"AUH",
+         "Location":{
+            "Address":"Sheik Maktoum Bin Rashid Rd Abu Dhabi",
+            "City":{
+               "CountryRegion":"United Arab Emirates",
+               "Name":"Abu Dhabi",
+               "Region":""
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  54.6511111111111,
+                  24.4327777777778
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"ZGGG",
+         "Name":"Guangzhou Baiyun International Airport",
+         "IataCode":"CAN",
+         "Location":{
+            "Address":"Jichang Road, Renhezhen, Huadu",
+            "City":{
+               "CountryRegion":"China",
+               "Name":"Guangzhou",
+               "Region":"Guangdong"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  113.265833333333,
+                  23.1841666666667
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"KORD",
+         "Name":"O'Hare International Airport",
+         "IataCode":"ORD",
+         "Location":{
+            "Address":"10000 W O'Hare Ave",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"Chicago",
+               "Region":"Illinois"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -87.9044444444445,
+                  41.9794444444444
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"KATL",
+         "Name":"Hartsfield-Jackson Atlanta International Airport",
+         "IataCode":"ATL",
+         "Location":{
+            "Address":"6000 N Terminal Pkwy",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"Atlanta",
+               "Region":"Georgia"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -84.4269444444444,
+                  33.6402777777778
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      },
+      {
+         "IcaoCode":"KSEA",
+         "Name":"Seattle-Tacoma International Airport",
+         "IataCode":"SEA",
+         "Location":{
+            "Address":"17801 International Blvd",
+            "City":{
+               "CountryRegion":"United States",
+               "Name":"SeaTac",
+               "Region":"Washington"
+            },
+            "Loc":{
+               "type":"Point",
+               "coordinates":[
+                  -122.309166666667,
+                  47.4488888888889
+               ],
+               "crs":{
+                  "type":"name",
+                  "properties":{
+                     "name":"EPSG:4326"
+                  }
+               }
+            }
+         }
+      }      
+   ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/event.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/event.json b/lib/server-core-ext/src/test/resources/event.json
new file mode 100644
index 0000000..eb19dde
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/event.json
@@ -0,0 +1,157 @@
+{
+   "value":[
+    {
+        "PlanItemId": 50,
+        "Description": "Client Meeting",
+        "ConfirmationCode": "4372899DD",
+        "StartsAt": "2014-01-02T13:00:00Z",
+        "EndsAt": "2014-01-02T16:00:00Z",
+        "Duration": "PT3H",
+        "OccursAt": 
+        {
+            "BuildingInfo": "Regus Business Center",
+            "City": 
+            {
+                "Name": "New York City",
+                "CountryRegion": "United States",
+                "Region": "New York"
+            },
+            "Address": "100 Church Street, 8th Floor, Manhattan, 10007"
+        }
+    },
+    {
+        "PlanItemId": 51,
+        "Description": "Visit the Brooklyn Bridge Park",
+        "ConfirmationCode": "4372899AA",
+        "StartsAt": "2014-01-01T15:00:00Z",
+        "EndsAt": "2014-01-01T16:00:00Z",
+        "Duration": "PT1H",
+        "OccursAt": 
+        {
+            "BuildingInfo": "Brooklyn Bridge Park, at Fulton Ferry Landing",
+            "City":
+            {
+                "Name": "New York City",
+                "CountryRegion": "United States",
+                "Region": "New York"
+            },
+            "Address": "Main St Dumbo Brooklyn 11201"
+        }
+    },
+    {
+        "PlanItemId": 52,
+        "Description": "Empire State Building",
+        "ConfirmationCode": "4372899BB",
+        "StartsAt": "2014-01-03T10:00:00Z",
+        "EndsAt": "2014-01-03T12:00:00Z",
+        "Duration": "PT2H",
+        "OccursAt": 
+        {
+            "BuildingInfo": "Empire State Building",
+            "City":
+            {
+                "Name": "New York City",
+                "CountryRegion": "United States",
+                "Region": "New York"
+            },
+            "Address": "Empire State Building, 350 5th Ave"
+        }
+    },
+    {
+        "PlanItemId": 53,
+        "Description": "Coney Island",
+        "ConfirmationCode": "4372899CC",
+        "StartsAt": "2014-01-03T14:00:00Z",
+        "EndsAt": "2014-01-03T20:00:00Z",
+        "Duration": "PT6H",
+        "OccursAt":
+        {
+            "BuildingInfo": "",
+            "City": 
+            {
+                "Name": "New York City",
+                "CountryRegion": "United States",
+                "Region": "New York"
+            },
+            "Address": "1208 Surf Ave, Brooklyn"
+        }
+    },
+    {
+        "PlanItemId": 54,
+        "Description": "Shopping at Times Square",
+        "ConfirmationCode": "4372899DD",
+        "StartsAt": "2014-01-04T10:00:00Z",
+        "EndsAt": "2014-01-04T15:00:00Z",
+        "Duration": "PT5H",
+        "OccursAt": 
+        {
+            "BuildingInfo": "",
+            "City": 
+            {
+                "Name": "New York City",
+                "CountryRegion": "United States",
+                "Region": "New York"
+            },
+            "Address": "Broadway, 7th Avenue, 42nd and 47th Streets"
+        }
+    },
+    {
+        "PlanItemId": 55,
+        "Description": "Dinner",
+        "ConfirmationCode": "4372899EE",
+        "StartsAt": "2014-02-02T18:00:00Z",
+        "EndsAt": "2014-02-02T21:00:00Z",
+        "Duration": "PT3H",
+        "OccursAt": 
+         {
+            "Address": "10 Beijing Street, 100000",
+            "City": 
+            {
+                "Name": "Beijing",
+                "CountryRegion": "China",
+                "Region": "Beijing"
+            },
+            "BuildingInfo": "Beijing Restaurant"
+         }
+    }, 
+    {
+        "PlanItemId": 56,
+        "Description": "Dinner",
+        "ConfirmationCode": "4372899FF",
+        "StartsAt": "2014-02-02T18:00:00Z",
+        "EndsAt": "2014-02-02T21:00:00Z",
+        "Duration": "PT3H",
+        "OccursAt": 
+         {
+            "BuildingInfo": "Beijing Restaurant",
+            "City":
+             {
+                "Name": "Beijing",
+                "CountryRegion": "China",
+                "Region": "Beijing"
+             },
+            "Address": "10 Beijing Street, 100000"
+         }
+    },
+    {
+        "PlanItemId": 57,
+        "Description": "Dinner",
+        "ConfirmationCode": "4372899GG",
+        "StartsAt": "2014-02-02T18:00:00Z",
+        "EndsAt": "2014-02-02T21:00:00Z",
+        "Duration": "PT3H",
+        "OccursAt":
+         {
+            "BuildingInfo": "Beijing Restaurant",
+            "City":
+             {
+                "Name": "Beijing",
+                "CountryRegion": "China",
+                "Region": "Beijing"
+             },
+            "Address": "10 Beijing Street, 100000"
+         }
+    }
+   ]
+} 
+                                                                
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/flight-links.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/flight-links.json b/lib/server-core-ext/src/test/resources/flight-links.json
new file mode 100644
index 0000000..17741d0
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight-links.json
@@ -0,0 +1,52 @@
+{
+   "value":[   
+      {
+          "PlanItemId": 1,
+          "Airline": "AA",
+          "From": "ORD",
+          "To": "JFK"
+      },
+      {
+          "PlanItemId": 2,
+          "Airline": "AA",
+          "From": "JFK",
+          "To": "ORD"
+      },
+      {
+          "PlanItemId": 3,
+          "Airline": "FM",
+          "From": "SHA",
+          "To": "PEK"
+      },
+      {
+          "PlanItemId": 4,
+          "Airline": "MU",
+          "From": "PEK",
+          "To": "SHA"
+      },     
+      {
+          "PlanItemId": 5,
+          "Airline": "FM",
+          "From": "SHA",
+          "To": "PEK"
+      },
+      {
+          "PlanItemId": 6,
+          "Airline": "MU",
+          "From": "PEK",
+          "To": "SHA"
+      },
+      {
+          "PlanItemId": 7,
+          "Airline": "FM",
+          "From": "SHA",
+          "To": "PEK"
+      },
+      {
+          "PlanItemId": 8,
+          "Airline": "MU",
+          "From": "PEK",
+          "To": "SHA"
+      }
+   ]
+}                                                                    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/flight.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/flight.json b/lib/server-core-ext/src/test/resources/flight.json
new file mode 100644
index 0000000..af06998
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/flight.json
@@ -0,0 +1,66 @@
+{
+   "value":[   
+      {
+          "PlanItemId": 1,
+          "ConfirmationCode": "JH58493",
+          "FlightNumber": "AA26",
+          "StartsAt": "2014-01-01T06:15:00Z",
+          "EndsAt": "2014-01-01T11:35:00Z"
+      },
+      {
+          "PlanItemId": 2,
+          "ConfirmationCode": "JH38143",
+          "FlightNumber": "AA4035",
+          "StartsAt": "2014-01-04T17:55:00Z", 
+          "EndsAt": "2014-01-04T20:45:00Z" 
+      },
+      {
+          "PlanItemId": 3,
+          "ConfirmationCode": "JH58494",
+          "FlightNumber": "FM1930",
+          "StartsAt": "2014-02-01T08:00:00Z", 
+          "EndsAt": "2014-02-01T09:20:00Z",
+          "SeatNumber": "B11"
+      },
+      {
+          "PlanItemId": 4,
+          "ConfirmationCode": "JH58495",
+          "FlightNumber": "MU1930",
+          "StartsAt": "2014-02-10T15:00:00Z", 
+          "EndsAt": "2014-02-10T16:30:00Z",
+          "SeatNumber": "A32"
+      },     
+      {
+          "PlanItemId": 5,
+          "ConfirmationCode": "JH58494",
+          "FlightNumber": "FM1930",
+          "StartsAt": "2014-02-01T08:00:00Z",  
+          "EndsAt": "2014-02-01T09:20:00Z",
+          "SeatNumber": "B11"
+      },
+      {
+          "PlanItemId": 6,
+          "ConfirmationCode": "JH58495",
+          "FlightNumber": "MU1930",
+          "StartsAt": "2014-02-10T15:00:00Z", 
+          "EndsAt": "2014-02-10T16:30:00Z",
+          "SeatNumber": "A32"
+      },
+      {
+          "PlanItemId": 7,
+          "ConfirmationCode": "JH58494",
+          "FlightNumber": "FM1930",
+          "StartsAt": "2014-02-01T08:00:00Z",  
+          "EndsAt": "2014-02-01T09:20:00Z",
+          "SeatNumber": "B12"
+      },
+      {
+          "PlanItemId": 8,
+          "ConfirmationCode": "JH58495",
+          "FlightNumber": "MU1930",
+          "StartsAt": "2014-02-10T16:30:00Z",  
+          "EndsAt": "2014-02-10T16:30:00Z",          
+          "SeatNumber": "A33"
+      }
+   ]
+}                                                                    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/2b73abcc/lib/server-core-ext/src/test/resources/people-links.json
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/resources/people-links.json b/lib/server-core-ext/src/test/resources/people-links.json
new file mode 100644
index 0000000..878d6ce
--- /dev/null
+++ b/lib/server-core-ext/src/test/resources/people-links.json
@@ -0,0 +1,94 @@
+{
+   "value":[
+      {
+         "UserName":"russellwhyte",
+         "Friends": ["scottketchum", "ronaldmundy", "javieralfred", "angelhuffman"],
+         "Trips": [1001, 1003, 1007],
+         "Photo": 1
+      },
+      {
+         "UserName":"scottketchum",
+         "Friends": ["russellwhyte", "ronaldmundy"],
+         "Trips": [1001, 2004],
+         "Photo": 11
+      },
+      {
+         "UserName":"ronaldmundy",
+         "Friends": ["russellwhyte", "scottketchum"],
+         "Trips": [3009],
+         "Photo": 12
+      },
+      {
+         "UserName":"javieralfred",
+         "Friends": ["willieashmore", "vincentcalabrese", "georginabarlow"],
+         "Trips": [4005]
+      },
+      {
+         "UserName":"willieashmore",
+         "Friends": ["javieralfred", "vincentcalabrese"],
+         "Trips": [5007, 5008]
+      },
+      {
+         "UserName":"vincentcalabrese",
+         "Friends": ["javieralfred", "willieashmore"],
+         "Trips": [7010]
+      },
+      {
+         "UserName":"clydeguess",
+         "Friends": ["keithpinckney", "ursulabright"],
+         "Trips": [8011]
+      },
+      {
+         "UserName":"keithpinckney",
+         "Friends": ["clydeguess", "marshallgaray"],
+         "Trips": []
+      },
+      {
+         "UserName":"marshallgaray",
+         "Friends": ["keithpinckney", "elainestewart", "jonirosales"]
+      },
+      {
+         "UserName":"elainestewart",
+         "Friends": ["marshallgaray"]
+      },
+      {
+         "UserName":"salliesampson",
+         "Friends": [""],
+         "Trips": [13012]
+      },
+      {
+         "UserName":"jonirosales",
+         "Friends": ["marshallgaray"],
+         "Trips": [14013]
+      },
+      {
+         "UserName":"georginabarlow",
+         "Friends": ["javieralfred"]
+      },
+      {
+         "UserName":"angelhuffman",
+         "Friends": ["russellwhyte"],
+         "Trips": [16014]
+      },
+      {
+         "UserName":"laurelosborn",
+         "Friends": ["sandyosborn"]
+      },
+      {
+         "UserName":"sandyosborn",
+         "Friends": ["laurelosborn"]
+      },
+      {
+         "UserName":"ursulabright",
+         "Friends": ["keithpinckney"]
+      },
+      {
+         "UserName":"genevievereeves",
+         "Friends": ["kristakemp"]
+      },
+      {
+         "UserName":"kristakemp",
+         "Friends": ["genevievereeves"]
+      }            
+   ]
+}