You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2015/04/04 19:05:26 UTC

[20/50] [abbrv] olingo-odata4 git commit: OLINGO-573: New processing framework on server side with single interface with TripPin example

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java
new file mode 100644
index 0000000..a306551
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponse.java
@@ -0,0 +1,119 @@
+/*
+ * 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.core.responses;
+
+import java.util.Map;
+
+import org.apache.olingo.commons.api.http.HttpHeader;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+
+public abstract class ServiceResponse {
+  protected ServiceMetadata metadata;
+  protected ODataResponse response;
+  protected Map<String, String> preferences;
+  private boolean closed;
+  private boolean strictApplyPreferences = true;
+
+  public ServiceResponse(ServiceMetadata metadata, ODataResponse response,
+      Map<String, String> preferences) {
+    this.metadata = metadata;
+    this.response = response;
+    this.preferences = preferences;
+  }
+
+  public ODataResponse getODataResponse() {
+    return this.response;
+  }
+
+  protected boolean isClosed() {
+    return this.closed;
+  }
+
+  protected void close() {
+    if (!this.closed) {
+      if (this.strictApplyPreferences) {
+        if (!preferences.isEmpty()) {
+          assert(this.response.getHeaders().get("Preference-Applied") != null);
+        }
+      }
+      this.closed = true;
+    }
+  }
+
+  public void writeNoContent(boolean closeResponse) {
+    this.response.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
+    if (closeResponse) {
+      close();
+    }
+  }
+
+  public void writeNotFound(boolean closeResponse) {
+    response.setStatusCode(HttpStatusCode.NOT_FOUND.getStatusCode());
+    if (closeResponse) {
+      close();
+    }
+  }
+
+  public void writeServerError(boolean closeResponse) {
+    response.setStatusCode(HttpStatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
+    if (closeResponse) {
+      close();
+    }
+  }
+
+  public void writeBadRequest(boolean closeResponse) {
+    response.setStatusCode(HttpStatusCode.BAD_REQUEST.getStatusCode());
+    if (closeResponse) {
+      close();
+    }
+  }
+
+  public void writeOK(String contentType) {
+    this.response.setStatusCode(HttpStatusCode.OK.getStatusCode());
+    this.response.setHeader(HttpHeader.CONTENT_TYPE, contentType);
+  }
+
+  public void writeHeader(String key, String value) {
+    if ("Preference-Applied".equals(key)) {
+      String previous = this.response.getHeaders().get(key);
+      if (previous != null) {
+        value = previous+";"+value;
+      }
+      this.response.setHeader(key, value);
+    } else {
+      this.response.setHeader(key, value);
+    }
+  }
+
+  /**
+   * When true; the "Preference-Applied" header is strictly checked.
+   * @param flag
+   */
+  public void setStrictlyApplyPreferences(boolean flag) {
+    this.strictApplyPreferences = flag;
+  }
+
+  public abstract void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+      ODataApplicationException;
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java
new file mode 100644
index 0000000..4f11cb8
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/ServiceResponseVisior.java
@@ -0,0 +1,71 @@
+/*
+ * 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.core.responses;
+
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataTranslatedException;
+
+@SuppressWarnings("unused")
+public class ServiceResponseVisior {
+
+  public void visit(CountResponse response) throws ODataTranslatedException,
+    ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(EntityResponse response) throws ODataTranslatedException,
+    ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(MetadataResponse response) throws ODataTranslatedException,
+    ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(NoContentResponse response) throws ODataTranslatedException,
+    ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(PrimitiveValueResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(PropertyResponse response) throws ODataTranslatedException,
+    ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(ServiceDocumentResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(StreamResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    response.writeServerError(true);
+  }
+
+  public void visit(EntitySetResponse response) throws ODataTranslatedException,
+      ODataApplicationException {
+    response.writeServerError(true);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java
new file mode 100644
index 0000000..ec7db03
--- /dev/null
+++ b/lib/server-core-ext/src/main/java/org/apache/olingo/server/core/responses/StreamResponse.java
@@ -0,0 +1,54 @@
+/*
+ * 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.core.responses;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ODataResponse;
+import org.apache.olingo.server.api.ODataTranslatedException;
+import org.apache.olingo.server.api.ServiceMetadata;
+
+public class StreamResponse extends ServiceResponse {
+
+  public StreamResponse(ServiceMetadata metadata, ODataResponse response) {
+    super(metadata, response, Collections.EMPTY_MAP);
+  }
+
+  public void writeStreamResponse(InputStream streamContent, ContentType contentType) {
+    this.response.setContent(streamContent);
+    writeOK(contentType.toContentTypeString());
+    close();
+  }
+
+  public void writeBinaryResponse(byte[] streamContent, ContentType contentType) {
+    this.response.setContent(new ByteArrayInputStream(streamContent));
+    writeOK(contentType.toContentTypeString());
+    close();
+  }
+
+  @Override
+  public void accepts(ServiceResponseVisior visitor) throws ODataTranslatedException,
+      ODataApplicationException {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java
new file mode 100644
index 0000000..9749a6c
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/MetadataParserTest.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.server.core;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.FileReader;
+import java.util.List;
+
+import org.apache.olingo.commons.api.ODataException;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.provider.Action;
+import org.apache.olingo.commons.api.edm.provider.ActionImport;
+import org.apache.olingo.commons.api.edm.provider.ComplexType;
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.commons.api.edm.provider.EntitySet;
+import org.apache.olingo.commons.api.edm.provider.EntityType;
+import org.apache.olingo.commons.api.edm.provider.EnumType;
+import org.apache.olingo.commons.api.edm.provider.Function;
+import org.apache.olingo.commons.api.edm.provider.FunctionImport;
+import org.apache.olingo.commons.api.edm.provider.NavigationPropertyBinding;
+import org.apache.olingo.commons.api.edm.provider.Parameter;
+import org.apache.olingo.commons.api.edm.provider.Property;
+import org.apache.olingo.commons.api.edm.provider.Singleton;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MetadataParserTest {
+  final String NS = "Microsoft.OData.SampleService.Models.TripPin";
+  final FullQualifiedName NSF = new FullQualifiedName(NS);
+
+  EdmProvider provider = null;
+
+  @Before
+  public void setUp() throws Exception {
+    MetadataParser parser = new MetadataParser();
+    provider = parser.buildEdmProvider(new FileReader("src/test/resources/trippin.xml"));
+  }
+
+  @Test
+  public void testAction() throws ODataException {
+    // test action
+    List<Action> actions = provider.getActions(new FullQualifiedName(NS, "ResetDataSource"));
+    assertNotNull(actions);
+    assertEquals(1, actions.size());
+  }
+
+  @Test
+  public void testFunction() throws ODataException {
+    // test function
+    List<Function> functions = provider
+        .getFunctions(new FullQualifiedName(NS, "GetFavoriteAirline"));
+    assertNotNull(functions);
+    assertEquals(1, functions.size());
+    assertEquals("GetFavoriteAirline", functions.get(0).getName());
+    assertTrue(functions.get(0).isBound());
+    assertTrue(functions.get(0).isComposable());
+    assertEquals(
+        "person/Trips/PlanItems/Microsoft.OData.SampleService.Models.TripPin.Flight/Airline",
+        functions.get(0).getEntitySetPath());
+
+    List<Parameter> parameters = functions.get(0).getParameters();
+    assertNotNull(parameters);
+    assertEquals(1, parameters.size());
+    assertEquals("person", parameters.get(0).getName());
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",parameters.get(0).getType());
+    assertFalse(parameters.get(0).isNullable());
+
+    assertNotNull(functions.get(0).getReturnType());
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Airline",
+        functions.get(0).getReturnType().getType());
+    assertFalse(functions.get(0).getReturnType().isNullable());
+  }
+
+  @Test
+  public void testEnumType() throws ODataException {
+    // test enum type
+    EnumType enumType = provider.getEnumType(new FullQualifiedName(NS, "PersonGender"));
+    assertNotNull(enumType);
+    assertEquals("Male", enumType.getMembers().get(0).getName());
+    assertEquals("Female", enumType.getMembers().get(1).getName());
+    assertEquals("Unknown", enumType.getMembers().get(2).getName());
+    assertEquals("0", enumType.getMembers().get(0).getValue());
+    assertEquals("1", enumType.getMembers().get(1).getValue());
+    assertEquals("2", enumType.getMembers().get(2).getValue());
+  }
+
+  @Test
+  public void testEntityType() throws ODataException {
+    // test Entity Type
+    EntityType et = provider.getEntityType(new FullQualifiedName(NS, "Photo"));
+    assertNotNull(et);
+    assertNotNull(et.getKey());
+    assertEquals("Id", et.getKey().get(0).getName());
+    assertTrue(et.hasStream());
+    assertEquals("Id", et.getProperties().get(0).getName());
+    assertEquals("Edm.Int64", et.getProperties().get(0).getType());
+    assertEquals("Name", et.getProperties().get(1).getName());
+    assertEquals("Edm.String", et.getProperties().get(1).getType());
+  }
+
+  @Test
+  public void testComplexType() throws ODataException {
+    // Test Complex Type
+    ComplexType ct = provider.getComplexType(new FullQualifiedName(NS, "City"));
+    assertNotNull(ct);
+    assertEquals(3, ct.getProperties().size());
+    Property p = ct.getProperties().get(0);
+    assertEquals("CountryRegion", p.getName());
+    assertEquals("Edm.String", p.getType());
+    assertEquals(false, p.isNullable());
+
+    ct = provider.getComplexType(new FullQualifiedName(NS, "Location"));
+    assertNotNull(ct);
+
+    ct = provider.getComplexType(new FullQualifiedName(NS, "EventLocation"));
+    assertNotNull(ct);
+  }
+
+  @Test
+  public void testEntitySet() throws Exception {
+    EntitySet es = provider.getEntitySet(NSF, "People");
+    assertNotNull(es);
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",es.getType());
+
+    List<NavigationPropertyBinding> bindings = es.getNavigationPropertyBindings();
+    assertNotNull(bindings);
+    assertEquals(6, bindings.size());
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Flight/From", bindings.get(2)
+        .getPath());
+    assertEquals("Airports", bindings.get(2).getTarget());
+  }
+
+  @Test
+  public void testFunctionImport() throws Exception {
+    FunctionImport fi = provider.getFunctionImport(NSF, "GetNearestAirport");
+    assertNotNull(fi);
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.GetNearestAirport", fi.getFunction());
+    assertEquals("Airports", fi.getEntitySet());
+    assertTrue(fi.isIncludeInServiceDocument());
+  }
+
+  @Test
+  public void testActionImport() throws Exception {
+    ActionImport ai = provider.getActionImport(NSF, "ResetDataSource");
+    assertNotNull(ai);
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.ResetDataSource", ai.getAction());
+    assertNull(ai.getEntitySet());
+  }
+
+  @Test
+  public void testSingleton() throws Exception {
+    Singleton single = this.provider.getSingleton(NSF, "Me");
+    assertNotNull(single);
+
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Person",single.getType());
+
+    List<NavigationPropertyBinding> bindings = single.getNavigationPropertyBindings();
+    assertNotNull(bindings);
+    assertEquals(6, bindings.size());
+    assertEquals("Microsoft.OData.SampleService.Models.TripPin.Flight/From", bindings.get(2).getPath());
+    assertEquals("Airports", bindings.get(2).getTarget());
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java
new file mode 100644
index 0000000..16caa1a
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/core/ServiceDispatcherTest.java
@@ -0,0 +1,417 @@
+/*
+ * 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.core;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.olingo.commons.api.edm.provider.EdmProvider;
+import org.apache.olingo.commons.api.http.HttpMethod;
+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.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.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.StreamResponse;
+import org.apache.olingo.server.example.TripPinServiceTest;
+import org.eclipse.jetty.client.HttpClient;
+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.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+public class ServiceDispatcherTest {
+  private Server server;
+
+  public class SampleODataServlet extends HttpServlet {
+    private final ServiceHandler handler; // must be stateless
+    private final EdmProvider provider; // must be stateless
+
+    public SampleODataServlet(ServiceHandler handler, EdmProvider provider) {
+      this.handler = handler;
+      this.provider = provider;
+    }
+
+    @Override
+    public void service(HttpServletRequest request, HttpServletResponse response)
+        throws IOException {
+      OData odata = OData4Impl.newInstance();
+      ServiceMetadata metadata = odata.createServiceMetadata(this.provider, Collections.EMPTY_LIST);
+
+      ODataHttpHandler handler = odata.createHandler(metadata);
+
+      handler.register(this.handler);
+      handler.process(request, response);
+    }
+  }
+
+  public int beforeTest(ServiceHandler serviceHandler) throws Exception {
+    MetadataParser parser = new MetadataParser();
+    EdmProvider edmProvider = parser.buildEdmProvider(new FileReader(
+        "src/test/resources/trippin.xml"));
+
+    this.server = new Server();
+
+    ServerConnector connector = new ServerConnector(this.server);
+    this.server.setConnectors(new Connector[] { connector });
+
+    ServletContextHandler context = new ServletContextHandler();
+    context.setContextPath("/trippin");
+    context
+        .addServlet(new ServletHolder(new SampleODataServlet(serviceHandler, edmProvider)), "/*");
+    this.server.setHandler(context);
+    this.server.start();
+
+    return connector.getLocalPort();
+  }
+
+  public void afterTest() throws Exception {
+    this.server.stop();
+  }
+
+  interface TestResult {
+    void validate() throws Exception;
+  }
+
+  private void helpGETTest(ServiceHandler handler, String path, TestResult validator)
+      throws Exception {
+    int port = beforeTest(handler);
+    HttpClient http = new HttpClient();
+    http.start();
+    http.GET("http://localhost:" + port + "/" + path);
+    validator.validate();
+    afterTest();
+  }
+
+  private void helpTest(ServiceHandler handler, String path, String method, String payload,
+      TestResult validator) throws Exception {
+    int port = beforeTest(handler);
+    HttpClient http = new HttpClient();
+    http.start();
+    String editUrl = "http://localhost:" + port + "/" + path;
+    http.newRequest(editUrl).method(method)
+        .header("Content-Type", "application/json;odata.metadata=minimal")
+        .content(TripPinServiceTest.content(payload)).send();
+    validator.validate();
+    afterTest();
+  }
+
+  @Test
+  public void testMetadata() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/$metadata", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<MetadataRequest> arg1 = ArgumentCaptor.forClass(MetadataRequest.class);
+        ArgumentCaptor<MetadataResponse> arg2 = ArgumentCaptor.forClass(MetadataResponse.class);
+        Mockito.verify(handler).readMetadata(arg1.capture(), arg2.capture());
+      }
+    });
+  }
+
+  @Test
+  public void testEntitySet() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<EntityResponse> arg2 = ArgumentCaptor.forClass(EntityResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        // Need toString on ContextURL class
+        // assertEquals("",
+        // request.getContextURL(request.getOdata()).toString());
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testEntitySetCount() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports/$count", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<CountResponse> arg2 = ArgumentCaptor.forClass(CountResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        // Need toString on ContextURL class
+        // assertEquals("",
+        // request.getContextURL(request.getOdata()).toString());
+        assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testEntity() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports('0')", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<EntityResponse> arg2 = ArgumentCaptor.forClass(EntityResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadProperty() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports('0')/IataCode", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<PropertyResponse> arg2 = ArgumentCaptor.forClass(PropertyResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals(true, request.isPropertyRequest());
+        assertEquals(false, request.isPropertyComplex());
+        assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadComplexProperty() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports('0')/Location", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<PropertyResponse> arg2 = ArgumentCaptor.forClass(PropertyResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals(true, request.isPropertyRequest());
+        assertEquals(true, request.isPropertyComplex());
+        assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadProperty$Value() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports('0')/IataCode/$value", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<PrimitiveValueResponse> arg2 = ArgumentCaptor
+            .forClass(PrimitiveValueResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals(true, request.isPropertyRequest());
+        assertEquals(false, request.isPropertyComplex());
+        assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+        assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadPropertyRef() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Airports('0')/IataCode/$value", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<PrimitiveValueResponse> arg2 = ArgumentCaptor
+            .forClass(PrimitiveValueResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals(true, request.isPropertyRequest());
+        assertEquals(false, request.isPropertyComplex());
+        assertEquals(1, request.getUriResourceEntitySet().getKeyPredicates().size());
+        assertEquals("text/plain", request.getResponseContentType().toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testFunctionImport() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/GetNearestAirport(lat=12.11,lon=34.23)", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<FunctionRequest> arg1 = ArgumentCaptor.forClass(FunctionRequest.class);
+        ArgumentCaptor<PropertyResponse> arg3 = ArgumentCaptor.forClass(PropertyResponse.class);
+        ArgumentCaptor<HttpMethod> arg2 = ArgumentCaptor.forClass(HttpMethod.class);
+        Mockito.verify(handler).invoke(arg1.capture(), arg2.capture(), arg3.capture());
+
+        FunctionRequest request = arg1.getValue();
+      }
+    });
+  }
+
+  @Test
+  public void testActionImport() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpTest(handler, "trippin/ResetDataSource", "POST", "", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<ActionRequest> arg1 = ArgumentCaptor.forClass(ActionRequest.class);
+        ArgumentCaptor<NoContentResponse> arg2 = ArgumentCaptor.forClass(NoContentResponse.class);
+        Mockito.verify(handler).invoke(arg1.capture(), Mockito.anyString(), arg2.capture());
+
+        ActionRequest request = arg1.getValue();
+      }
+    });
+  }
+
+  @Test
+  public void testReadMedia() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/Photos(1)/$value", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<MediaRequest> arg1 = ArgumentCaptor.forClass(MediaRequest.class);
+        ArgumentCaptor<StreamResponse> arg2 = ArgumentCaptor.forClass(StreamResponse.class);
+        Mockito.verify(handler).readMediaStream(arg1.capture(), arg2.capture());
+
+        MediaRequest request = arg1.getValue();
+        assertEquals("application/octet-stream", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadNavigation() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/People('russelwhyte')/Friends", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<EntitySetResponse> arg2 = ArgumentCaptor.forClass(EntitySetResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testReadReference() throws Exception {
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpGETTest(handler, "trippin/People('russelwhyte')/Friends/$ref", new TestResult() {
+      @Override
+      public void validate() throws Exception {
+        ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+        ArgumentCaptor<EntitySetResponse> arg2 = ArgumentCaptor.forClass(EntitySetResponse.class);
+        Mockito.verify(handler).read(arg1.capture(), arg2.capture());
+
+        DataRequest request = arg1.getValue();
+        assertEquals("application/json;odata.metadata=minimal", request.getResponseContentType()
+            .toContentTypeString());
+      }
+    });
+  }
+
+  @Test
+  public void testWriteReferenceCollection() throws Exception {
+    String payload = "{\n" + "\"@odata.id\": \"/Photos(11)\"\n" + "}";
+
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpTest(handler, "trippin/People('russelwhyte')/Friends/$ref", "POST", payload,
+        new TestResult() {
+          @Override
+          public void validate() throws Exception {
+            ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+            ArgumentCaptor<String> arg2 = ArgumentCaptor.forClass(String.class);
+            ArgumentCaptor<List> arg3 = ArgumentCaptor.forClass(List.class);
+            ArgumentCaptor<NoContentResponse> arg4 = ArgumentCaptor
+                .forClass(NoContentResponse.class);
+            Mockito.verify(handler).addReference(arg1.capture(), arg2.capture(), arg3.capture(),
+                arg4.capture());
+
+            DataRequest request = arg1.getValue();
+            assertEquals("application/json;odata.metadata=minimal", request
+                .getResponseContentType().toContentTypeString());
+          }
+        });
+  }
+
+  @Test
+  public void testWriteReference() throws Exception {
+    String payload = "{\n" + "\"@odata.id\": \"/Photos(11)\"\n" + "}";
+
+    final ServiceHandler handler = Mockito.mock(ServiceHandler.class);
+    helpTest(handler, "trippin/People('russelwhyte')/Friends('someone')/Photo/$ref", "PUT", payload,
+        new TestResult() {
+          @Override
+          public void validate() throws Exception {
+            ArgumentCaptor<DataRequest> arg1 = ArgumentCaptor.forClass(DataRequest.class);
+            ArgumentCaptor<String> arg2 = ArgumentCaptor.forClass(String.class);
+            ArgumentCaptor<URI> arg3 = ArgumentCaptor.forClass(URI.class);
+            ArgumentCaptor<NoContentResponse> arg4 = ArgumentCaptor
+                .forClass(NoContentResponse.class);
+            Mockito.verify(handler).updateReference(arg1.capture(), arg2.capture(), arg3.capture(),
+                arg4.capture());
+
+            DataRequest request = arg1.getValue();
+            assertEquals("application/json;odata.metadata=minimal", request
+                .getResponseContentType().toContentTypeString());
+          }
+        });
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/8668b097/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java
----------------------------------------------------------------------
diff --git a/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java
new file mode 100644
index 0000000..904f4d8
--- /dev/null
+++ b/lib/server-core-ext/src/test/java/org/apache/olingo/server/example/TripPinDataModel.java
@@ -0,0 +1,843 @@
+/*
+ * 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.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+
+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.EdmEntityContainer;
+import org.apache.olingo.commons.api.edm.EdmEntitySet;
+import org.apache.olingo.commons.api.edm.EdmEntityType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveType;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmProperty;
+import org.apache.olingo.commons.api.edm.EdmType;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.edm.constants.EdmTypeKind;
+import org.apache.olingo.commons.core.data.EntityImpl;
+import org.apache.olingo.commons.core.data.EntitySetImpl;
+import org.apache.olingo.commons.core.data.LinkImpl;
+import org.apache.olingo.server.api.ODataApplicationException;
+import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.deserializer.DeserializerException;
+import org.apache.olingo.server.api.uri.UriParameter;
+import org.apache.olingo.server.api.uri.UriResourceNavigation;
+import org.apache.olingo.server.core.deserializer.json.ODataJsonDeserializer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class TripPinDataModel {
+  private final ServiceMetadata metadata;
+  private HashMap<String, EntitySet> entitySetMap;
+  private Map<Integer, Map> tripLinks;
+  private Map<String, Map> peopleLinks;
+  private Map<Integer, Map> flightLinks;
+
+  public TripPinDataModel(ServiceMetadata metadata) throws Exception {
+    this.metadata = metadata;
+    loadData();
+  }
+
+  public void loadData() throws Exception {
+    this.entitySetMap = new HashMap<String, EntitySet>();
+    this.tripLinks = new HashMap<Integer, Map>();
+    this.peopleLinks = new HashMap<String, Map>();
+    this.flightLinks = new HashMap<Integer, Map>();
+
+    EdmEntityContainer ec = metadata.getEdm().getEntityContainer(null);
+    for (EdmEntitySet edmEntitySet : ec.getEntitySets()) {
+      String entitySetName = edmEntitySet.getName();
+      EntitySet set = loadEnities(entitySetName, edmEntitySet.getEntityType());
+      if (set != null) {
+        this.entitySetMap.put(entitySetName, set);
+      }
+    }
+
+    EdmEntityType type = metadata.getEdm().getEntityType(
+        new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Trip"));
+    this.entitySetMap.put("Trip", loadEnities("Trip", type));
+
+    type = metadata.getEdm().getEntityType(
+        new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Flight"));
+    this.entitySetMap.put("Flight", loadEnities("Flight", type));
+
+    type = metadata.getEdm().getEntityType(
+        new FullQualifiedName("Microsoft.OData.SampleService.Models.TripPin", "Event"));
+    this.entitySetMap.put("Event", loadEnities("Event", type));
+
+    ObjectMapper mapper = new ObjectMapper();
+    Map tripLinks = mapper.readValue(new FileInputStream(new File(
+        "src/test/resources/trip-links.json")), Map.class);
+    for (Object link : (ArrayList) tripLinks.get("value")) {
+      Map map = (Map) link;
+      this.tripLinks.put((Integer) map.get("TripId"), map);
+    }
+
+    Map peopleLinks = mapper.readValue(new FileInputStream(new File(
+        "src/test/resources/people-links.json")), Map.class);
+    for (Object link : (ArrayList) peopleLinks.get("value")) {
+      Map map = (Map) link;
+      this.peopleLinks.put((String) map.get("UserName"), map);
+    }
+
+    Map flightLinks = mapper.readValue(new FileInputStream(new File(
+        "src/test/resources/flight-links.json")), Map.class);
+    for (Object link : (ArrayList) flightLinks.get("value")) {
+      Map map = (Map) link;
+      this.flightLinks.put((Integer) map.get("PlanItemId"), map);
+    }
+  }
+
+  private EntitySet loadEnities(String entitySetName, EdmEntityType type) {
+    try {
+      ODataJsonDeserializer deserializer = new ODataJsonDeserializer();
+
+      EntitySet set = deserializer.entityCollection(new FileInputStream(new File(
+          "src/test/resources/" + entitySetName.toLowerCase() + ".json")), type);
+      // TODO: the count needs to be part of deserializer
+      set.setCount(set.getEntities().size());
+      for (Entity entity : set.getEntities()) {
+        ((EntityImpl) entity).setETag(UUID.randomUUID().toString());
+        ((EntityImpl) entity).setId(new URI(TripPinHandler.buildLocation(entity, entitySetName,
+            type)));
+        ((EntityImpl) entity).setType(type.getFullQualifiedName().getFullQualifiedNameAsString());
+      }
+      return set;
+    } catch (FileNotFoundException e) {
+      // keep going
+      e.printStackTrace();
+    } catch (DeserializerException e) {
+      // keep going
+      e.printStackTrace();
+    } catch (URISyntaxException e) {
+      // keep going
+      e.printStackTrace();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  public EntitySet getEntitySet(String name) {
+    return getEntitySet(name, -1, -1);
+  }
+
+  public EntitySet getEntitySet(String name, int skip, int pageSize) {
+    EntitySet set = this.entitySetMap.get(name);
+    if (set == null) {
+      return null;
+    }
+
+    EntitySetImpl modifiedES = new EntitySetImpl();
+    int i = 0;
+    for (Entity e : set.getEntities()) {
+      if (skip >= 0 && i >= skip && modifiedES.getEntities().size() < pageSize) {
+        modifiedES.getEntities().add(e);
+      }
+      i++;
+    }
+    modifiedES.setCount(i);
+    set.setCount(i);
+
+    if (skip == -1 && pageSize == -1) {
+      return set;
+    }
+    return modifiedES;
+  }
+
+  private List<Entity> getMatch(UriParameter param, List<Entity> es)
+      throws ODataApplicationException {
+    ArrayList<Entity> list = new ArrayList<Entity>();
+    for (Entity entity : es) {
+
+      EdmEntityType entityType = this.metadata.getEdm().getEntityType(
+          new FullQualifiedName(entity.getType()));
+
+      EdmProperty property = (EdmProperty) entityType.getProperty(param.getName());
+      EdmType type = property.getType();
+      if (type.getKind() == EdmTypeKind.PRIMITIVE) {
+        Object match = readPrimitiveValue(property, param.getText());
+        Property entityValue = entity.getProperty(param.getName());
+        if (match.equals(entityValue.asPrimitive())) {
+          list.add(entity);
+        }
+      } else {
+        throw new RuntimeException("Can not compare complex objects");
+      }
+    }
+    return list;
+  }
+
+  static Object readPrimitiveValue(EdmProperty edmProperty, String value)
+      throws ODataApplicationException {
+    if (value == null) {
+      return null;
+    }
+    try {
+      if (value.startsWith("'") && value.endsWith("'")) {
+        value = value.substring(1,value.length()-1);
+      }
+      EdmPrimitiveType edmPrimitiveType = (EdmPrimitiveType) edmProperty.getType();
+      Class<?> javaClass = getJavaClassForPrimitiveType(edmProperty, edmPrimitiveType);
+      return edmPrimitiveType.valueOfString(value, edmProperty.isNullable(),
+          edmProperty.getMaxLength(), edmProperty.getPrecision(), edmProperty.getScale(),
+          edmProperty.isUnicode(), javaClass);
+    } catch (EdmPrimitiveTypeException e) {
+      throw new ODataApplicationException("Invalid value: " + value + " for property: "
+          + edmProperty.getName(), 500, Locale.getDefault());
+    }
+  }
+
+  static Class<?> getJavaClassForPrimitiveType(EdmProperty edmProperty, EdmPrimitiveType edmPrimitiveType) {
+    Class<?> javaClass = null;
+    if (edmProperty.getMapping() != null && edmProperty.getMapping().getMappedJavaClass() != null) {
+      javaClass = edmProperty.getMapping().getMappedJavaClass();
+    } else {
+      javaClass = edmPrimitiveType.getDefaultType();
+    }
+
+    edmPrimitiveType.getDefaultType();
+    return javaClass;
+  }
+
+  public Entity getEntity(String name, List<UriParameter> keys) throws ODataApplicationException {
+    EntitySet es = getEntitySet(name);
+    return getEntity(es, keys);
+  }
+
+  public Entity getEntity(EntitySet es, List<UriParameter> keys) throws ODataApplicationException {
+    List<Entity> search = es.getEntities();
+    for (UriParameter param : keys) {
+      search = getMatch(param, search);
+    }
+    if (search.isEmpty()) {
+      return null;
+    }
+    return search.get(0);
+  }
+
+  private EntitySet getFriends(String userName) {
+    Map<String, Object> map = this.peopleLinks.get(userName);
+    if (map == null) {
+      return null;
+    }
+    ArrayList<String> friends = (ArrayList<String>) map.get("Friends");
+    EntitySet set = getEntitySet("People");
+
+    EntitySetImpl result = new EntitySetImpl();
+    int i = 0;
+    if (friends != null) {
+      for (String friend : friends) {
+        for (Entity e : set.getEntities()) {
+          if (e.getProperty("UserName").getValue().equals(friend)) {
+            result.getEntities().add(e);
+            i++;
+            break;
+          }
+        }
+      }
+    }
+    result.setCount(i);
+    return result;
+  }
+
+  private EntitySet getTrips(String userName) {
+    Map<String, Object> map = this.peopleLinks.get(userName);
+    if (map == null) {
+      return null;
+    }
+
+    ArrayList<Integer> trips = (ArrayList<Integer>) map.get("Trips");
+    EntitySet set = getEntitySet("Trip");
+
+    EntitySetImpl result = new EntitySetImpl();
+    int i = 0;
+    if (trips != null) {
+      for (int trip : trips) {
+        for (Entity e : set.getEntities()) {
+          if (e.getProperty("TripId").getValue().equals(trip)) {
+            result.getEntities().add(e);
+            i++;
+            break;
+          }
+        }
+      }
+    }
+    result.setCount(i);
+    return result;
+  }
+
+  private Entity getPhoto(String userName) {
+    Map<String, Object> map = this.peopleLinks.get(userName);
+    if (map == null) {
+      return null;
+    }
+
+    Integer photoID = (Integer) map.get("Photo");
+    EntitySet set = getEntitySet("Photos");
+    if (photoID != null) {
+      for (Entity e : set.getEntities()) {
+        if (e.getProperty("Id").getValue().equals(photoID.longValue())) {
+          return e;
+        }
+      }
+    }
+    return null;
+  }
+
+  private EntitySet getPlanItems(int tripId, EntitySetImpl result) {
+    getFlights(tripId, result);
+    getEvents(tripId, result);
+    return result;
+  }
+
+  private EntitySet getEvents(int tripId, EntitySetImpl result) {
+    Map<Integer, Object> map = this.tripLinks.get(tripId);
+    if (map == null) {
+      return null;
+    }
+
+    ArrayList<Integer> events = (ArrayList<Integer>) map.get("Events");
+    EntitySet set = getEntitySet("Event");
+    int i = result.getEntities().size();
+    if (events != null) {
+      for (int event : events) {
+        for (Entity e : set.getEntities()) {
+          if (e.getProperty("PlanItemId").getValue().equals(event)) {
+            result.getEntities().add(e);
+            i++;
+            break;
+          }
+        }
+      }
+    }
+    result.setCount(i);
+    return result;
+  }
+
+  private EntitySet getFlights(int tripId, EntitySetImpl result) {
+    Map<Integer, Object> map = this.tripLinks.get(tripId);
+    if (map == null) {
+      return null;
+    }
+
+    ArrayList<Integer> flights = (ArrayList<Integer>) map.get("Flights");
+    EntitySet set = getEntitySet("Flight");
+    int i = result.getEntities().size();
+    if (flights != null) {
+      for (int flight : flights) {
+        for (Entity e : set.getEntities()) {
+          if (e.getProperty("PlanItemId").getValue().equals(flight)) {
+            result.getEntities().add(e);
+            i++;
+            break;
+          }
+        }
+      }
+    }
+    result.setCount(i);
+    return result;
+  }
+
+  private EntitySet getTripPhotos(int tripId) {
+    Map<Integer, Object> map = this.tripLinks.get(tripId);
+    if (map == null) {
+      return null;
+    }
+
+    ArrayList<Integer> photos = (ArrayList<Integer>) map.get("Photos");
+
+    EntitySet set = getEntitySet("Photos");
+    EntitySetImpl result = new EntitySetImpl();
+    int i = 0;
+    if (photos != null) {
+      for (int photo : photos) {
+        for (Entity e : set.getEntities()) {
+          if (e.getProperty("Id").getValue().equals(photo)) {
+            result.getEntities().add(e);
+            i++;
+            break;
+          }
+        }
+      }
+    }
+    result.setCount(i);
+    return result;
+  }
+
+  private Entity getFlightFrom(int flighID) {
+    Map<String, Object> map = this.flightLinks.get(flighID);
+    if (map == null) {
+      return null;
+    }
+
+    String from = (String) map.get("From");
+    EntitySet set = getEntitySet("Airports");
+
+    if (from != null) {
+      for (Entity e : set.getEntities()) {
+        if (e.getProperty("IataCode").getValue().equals(from)) {
+          return e;
+        }
+      }
+    }
+    return null;
+  }
+
+  private Entity getFlightTo(int flighID) {
+    Map<String, Object> map = this.flightLinks.get(flighID);
+    if (map == null) {
+      return null;
+    }
+
+    String to = (String) map.get("To");
+    EntitySet set = getEntitySet("Airports");
+
+    if (to != null) {
+      for (Entity e : set.getEntities()) {
+        if (e.getProperty("IataCode").getValue().equals(to)) {
+          return e;
+        }
+      }
+    }
+    return null;
+  }
+
+  private Entity getFlightAirline(int flighID) {
+    Map<String, Object> map = this.flightLinks.get(flighID);
+    if (map == null) {
+      return null;
+    }
+
+    String airline = (String) map.get("Airline");
+    EntitySet set = getEntitySet("Airlines");
+
+    if (airline != null) {
+      for (Entity e : set.getEntities()) {
+        if (e.getProperty("AirlineCode").getValue().equals(airline)) {
+          return e;
+        }
+      }
+    }
+    return null;
+  }
+
+  public void addNavigationLink(String navigation, Entity parentEntity, Entity childEntity) {
+
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(parentEntity.getType()));
+    String key = type.getKeyPredicateNames().get(0);
+    if (type.getName().equals("Person") && navigation.equals("Friends")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+      }
+
+      ArrayList<String> friends = (ArrayList<String>) map.get("Friends");
+      if (friends == null) {
+        friends = new ArrayList<String>();
+        map.put("Friends", friends);
+      }
+      friends.add((String) childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Person") && navigation.equals("Trips")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+      }
+
+      ArrayList<Integer> trips = (ArrayList<Integer>) map.get("Trips");
+      if (trips == null) {
+        trips = new ArrayList<Integer>();
+        map.put("Trips", trips);
+      }
+      trips.add((Integer) childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Person") && navigation.equals("Photo")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.peopleLinks.put((String) parentEntity.getProperty(key).getValue(), map);
+      }
+      map.put("Photo", childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Trip") && navigation.equals("PlanItems")) {
+      Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.tripLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+      }
+      if (childEntity.getType().equals("Flight")) {
+        ArrayList<Integer> flights = (ArrayList<Integer>) map.get("Flights");
+        if (flights == null) {
+          flights = new ArrayList<Integer>();
+          map.put("Flights", flights);
+        }
+        flights.add((Integer) childEntity.getProperty(key).getValue());
+      } else {
+        ArrayList<Integer> events = (ArrayList<Integer>) map.get("Events");
+        if (events == null) {
+          events = new ArrayList<Integer>();
+          map.put("Events", events);
+        }
+        events.add((Integer) childEntity.getProperty(key).getValue());
+      }
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Trip") && navigation.equals("Photo")) {
+      Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.tripLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+      }
+      ArrayList<Integer> photos = (ArrayList<Integer>) map.get("Photos");
+      if (photos == null) {
+        photos = new ArrayList<Integer>();
+        map.put("Photos", photos);
+      }
+      photos.add((Integer) childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Flight") && navigation.equals("From")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+      }
+      map.put("From", childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Flight") && navigation.equals("To")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+      }
+      map.put("To", childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else if (type.getName().equals("Flight") && navigation.equals("Airline")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map == null) {
+        map = new HashMap();
+        this.flightLinks.put((Integer) parentEntity.getProperty(key).getValue(), map);
+      }
+      map.put("Airline", childEntity.getProperty(key).getValue());
+      setLink(parentEntity, navigation, childEntity);
+    } else {
+      throw new RuntimeException("unknown relation");
+    }
+  }
+
+  protected static void setLink(Entity entity, final String navigationPropertyName,
+      final Entity target) {
+    Link link = new LinkImpl();
+    link.setTitle(navigationPropertyName);
+    link.setInlineEntity(target);
+    entity.getNavigationLinks().add(link);
+  }
+
+  public boolean updateNavigationLink(String navigationProperty, Entity parentEntity,
+      Entity updateEntity) {
+    boolean updated = false;
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(parentEntity.getType()));
+    String key = type.getKeyPredicateNames().get(0);
+
+    EdmEntityType updateType = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(updateEntity.getType()));
+    String updateKey = updateType.getKeyPredicateNames().get(0);
+
+    if (type.getName().equals("Person") && navigationProperty.equals("Photo")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.put("Photo", ((Long) updateEntity.getProperty(updateKey).getValue()).intValue());
+        updated = true;
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("From")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.put("From", updateEntity.getProperty(updateKey).getValue());
+        updated = true;
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("To")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.put("To", updateEntity.getProperty(updateKey).getValue());
+        updated = true;
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("Airline")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.put("Airline", updateEntity.getProperty(updateKey).getValue());
+        updated = true;
+      }
+    } else {
+      throw new RuntimeException("unknown relation");
+    }
+    return updated;
+  }
+
+  public Entity createEntity(String entitySetName, Entity entity, String location)
+      throws ODataApplicationException {
+
+    EntitySet set = this.entitySetMap.get(entitySetName);
+    EntityImpl copy = new EntityImpl();
+    copy.setType(entity.getType());
+    for (Property p : entity.getProperties()) {
+      copy.addProperty(p);
+    }
+
+    try {
+      copy.setId(new URI(location));
+      copy.setETag(UUID.randomUUID().toString());
+    } catch (URISyntaxException e) {
+      throw new ODataApplicationException("Failed to create ID for entity", 500,
+          Locale.getDefault());
+    }
+
+    set.getEntities().add(copy);
+    return copy;
+  }
+
+  public boolean deleteEntity(String entitySetName, String eTag, String key, Object keyValue) {
+    EntitySet set = getEntitySet(entitySetName);
+    Iterator<Entity> it = set.getEntities().iterator();
+    boolean removed = false;
+    while (it.hasNext()) {
+      Entity entity = it.next();
+      if (entity.getProperty(key).getValue().equals(keyValue) && eTag.equals("*")
+          || eTag.equals(entity.getETag())) {
+        it.remove();
+        removed = true;
+        break;
+      }
+    }
+    return removed;
+  }
+
+  public boolean updateProperty(String entitySetName, String eTag, String key, Object keyValue,
+      Property property) {
+    EntitySet set = getEntitySet(entitySetName);
+    Iterator<Entity> it = set.getEntities().iterator();
+    boolean replaced = false;
+    while (it.hasNext()) {
+      Entity entity = it.next();
+      if (entity.getProperty(key).getValue().equals(keyValue) && eTag.equals("*")
+          || eTag.equals(entity.getETag())) {
+        entity.getProperty(property.getName()).setValue(property.getValueType(),
+            property.getValue());
+        replaced = true;
+        break;
+      }
+    }
+    return replaced;
+  }
+
+  public EntitySet getNavigableEntitySet(Entity parentEntity, UriResourceNavigation navigation) {
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(parentEntity.getType()));
+
+    String key = type.getKeyPredicateNames().get(0);
+    String linkName = navigation.getProperty().getName();
+
+    EntitySet results = null;
+    if (type.getName().equals("Person") && linkName.equals("Friends")) {
+      results = getFriends((String) parentEntity.getProperty(key).getValue());
+    } else if (type.getName().equals("Person") && linkName.equals("Trips")) {
+      results = getTrips((String) parentEntity.getProperty(key).getValue());
+    } else if (type.getName().equals("Trip") && linkName.equals("PlanItems")) {
+      EntitySetImpl planitems = new EntitySetImpl();
+      if (navigation.getTypeFilterOnCollection() == null) {
+        results = getPlanItems((Integer) parentEntity.getProperty(key).getValue(), planitems);
+      } else if (navigation.getTypeFilterOnCollection().getName().equals("Flight")) {
+        results = getFlights((Integer) parentEntity.getProperty(key).getValue(), planitems);
+      } else if (navigation.getTypeFilterOnCollection().getName().equals("Event")) {
+        results = getEvents((Integer) parentEntity.getProperty(key).getValue(), planitems);
+      } else {
+        throw new RuntimeException("unknown relation");
+      }
+    } else if (type.getName().equals("Trip") && linkName.equals("Photos")) {
+      results = getTripPhotos((Integer) parentEntity.getProperty(key).getValue());
+    }
+    return results;
+  }
+
+  public Entity getNavigableEntity(Entity parentEntity, UriResourceNavigation navigation)
+      throws ODataApplicationException {
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(parentEntity.getType()));
+
+    String key = type.getKeyPredicateNames().get(0);
+    String linkName = navigation.getProperty().getName();
+
+    EntitySet results = null;
+    if (navigation.getProperty().isCollection()) {
+      results = getNavigableEntitySet(parentEntity, navigation);
+      return this.getEntity(results, navigation.getKeyPredicates());
+    }
+    if (type.getName().equals("Person") && linkName.equals("Photo")) {
+      return getPhoto((String) parentEntity.getProperty(key).getValue());
+    } else if (type.getName().equals("Flight") && linkName.equals("From")) {
+      return getFlightFrom((Integer) parentEntity.getProperty(key).getValue());
+    } else if (type.getName().equals("Flight") && linkName.equals("To")) {
+      return getFlightTo((Integer) parentEntity.getProperty(key).getValue());
+    } else if (type.getName().equals("Flight") && linkName.equals("Airline")) {
+      return getFlightAirline((Integer) parentEntity.getProperty(key).getValue());
+    } else {
+      throw new RuntimeException("unknown relation");
+    }
+  }
+
+  public boolean removeNavigationLink(String navigationProperty, Entity parentEntity,
+      Entity deleteEntity) {
+    boolean removed = false;
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(parentEntity.getType()));
+    String key = type.getKeyPredicateNames().get(0);
+
+    if (type.getName().equals("Person") && navigationProperty.equals("Friends")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        ArrayList<String> friends = (ArrayList<String>) map.get("Friends");
+        if (friends != null) {
+          friends.remove(deleteEntity.getProperty(key).getValue());
+          removed = true;
+        }
+      }
+    } else if (type.getName().equals("Person") && navigationProperty.equals("Trips")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        ArrayList<Integer> trips = (ArrayList<Integer>) map.get("Trips");
+        if (trips != null) {
+          trips.remove(deleteEntity.getProperty(key).getValue());
+          removed = true;
+        }
+      }
+    } else if (type.getName().equals("Person") && navigationProperty.equals("Photo")) {
+      Map map = this.peopleLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.remove("Photo");
+        removed = true;
+      }
+    } else if (type.getName().equals("Trip") && navigationProperty.equals("PlanItems")) {
+      Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        if (deleteEntity.getType().equals("Flight")) {
+          ArrayList<Integer> flights = (ArrayList<Integer>) map.get("Flights");
+          if (flights != null) {
+            flights.remove(deleteEntity.getProperty(key).getValue());
+            removed = true;
+          }
+        } else {
+          ArrayList<Integer> events = (ArrayList<Integer>) map.get("Events");
+          if (events != null) {
+            events.remove(deleteEntity.getProperty(key).getValue());
+            removed = true;
+          }
+        }
+      }
+    } else if (type.getName().equals("Trip") && navigationProperty.equals("Photo")) {
+      Map map = this.tripLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        ArrayList<Integer> photos = (ArrayList<Integer>) map.get("Photos");
+        if (photos != null) {
+          photos.remove(deleteEntity.getProperty(key).getValue());
+          removed = true;
+        }
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("From")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.remove("From");
+        removed = true;
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("To")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.remove("To");
+        removed = true;
+      }
+    } else if (type.getName().equals("Flight") && navigationProperty.equals("Airline")) {
+      Map map = this.flightLinks.get(parentEntity.getProperty(key).getValue());
+      if (map != null) {
+        map.remove("Airline");
+        removed = true;
+      }
+    } else {
+      throw new RuntimeException("unknown relation");
+    }
+    return removed;
+  }
+
+  // note these are not tied to entities for simplicity sake
+  public boolean updateMedia(Entity entity, InputStream mediaContent)
+      throws ODataApplicationException {
+    checkForMedia(entity);
+    return true;
+  }
+
+  //  note these are not tied to entities for simplicity sake
+  public InputStream readMedia(Entity entity) throws ODataApplicationException {
+    checkForMedia(entity);
+    try {
+      return new FileInputStream(new File("src/test/resources/OlingoOrangeTM.png"));
+    } catch (FileNotFoundException e) {
+      throw new ODataApplicationException("image not found", 500, Locale.getDefault());
+    }
+  }
+
+  //  note these are not tied to entities for simplicity sake
+  public boolean deleteMedia(Entity entity) throws ODataApplicationException {
+    checkForMedia(entity);
+    return true;
+  }
+
+  private void checkForMedia(Entity entity) throws ODataApplicationException {
+    EdmEntityType type = this.metadata.getEdm().getEntityType(
+        new FullQualifiedName(entity.getType()));
+    if (!type.hasStream()) {
+      throw new ODataApplicationException("No Media proeprty on the entity", 500,
+          Locale.getDefault());
+    }
+  }
+
+  public boolean deleteStream(Entity entity, EdmProperty property) {
+    // should remove stream links
+    return true;
+  }
+
+  public boolean updateStream(Entity entity, EdmProperty property, InputStream streamContent) {
+    // should add stream links
+    return true;
+  }
+}
\ No newline at end of file