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 2016/02/04 14:02:22 UTC

[5/7] olingo-odata4 git commit: [OLINGO-852] less warnings + general clean-up

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/5d7c1287/fit/src/main/java/org/apache/olingo/fit/Services.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/Services.java b/fit/src/main/java/org/apache/olingo/fit/Services.java
index 45f11a5..698938e 100644
--- a/fit/src/main/java/org/apache/olingo/fit/Services.java
+++ b/fit/src/main/java/org/apache/olingo/fit/Services.java
@@ -25,15 +25,22 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.net.URI;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.ConcurrentModificationException;
+import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.mail.Header;
 import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMultipart;
 import javax.ws.rs.BadRequestException;
@@ -51,6 +58,8 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriInfo;
@@ -59,63 +68,140 @@ import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.math.NumberUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.cxf.interceptor.InInterceptors;
+import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
 import org.apache.cxf.jaxrs.ext.multipart.Multipart;
 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
 import org.apache.olingo.client.api.data.ResWrap;
+import org.apache.olingo.client.api.serialization.ODataDeserializer;
+import org.apache.olingo.client.api.serialization.ODataSerializer;
+import org.apache.olingo.client.core.serialization.AtomSerializer;
+import org.apache.olingo.client.core.serialization.JsonDeserializer;
+import org.apache.olingo.client.core.serialization.JsonSerializer;
+import org.apache.olingo.commons.api.data.ComplexValue;
 import org.apache.olingo.commons.api.data.Entity;
 import org.apache.olingo.commons.api.data.EntityCollection;
 import org.apache.olingo.commons.api.data.Link;
 import org.apache.olingo.commons.api.data.Property;
 import org.apache.olingo.commons.api.data.ValueType;
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
 import org.apache.olingo.commons.api.format.ContentType;
 import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import org.apache.olingo.commons.core.edm.primitivetype.EdmPrimitiveTypeFactory;
+import org.apache.olingo.fit.metadata.EntityType;
 import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
 import org.apache.olingo.fit.methods.PATCH;
 import org.apache.olingo.fit.rest.ResolvingReferencesInterceptor;
 import org.apache.olingo.fit.rest.XHTTPMethodInterceptor;
+import org.apache.olingo.fit.serializer.FITAtomDeserializer;
 import org.apache.olingo.fit.utils.AbstractUtilities;
 import org.apache.olingo.fit.utils.Accept;
 import org.apache.olingo.fit.utils.Commons;
 import org.apache.olingo.fit.utils.ConstantKey;
 import org.apache.olingo.fit.utils.Constants;
 import org.apache.olingo.fit.utils.FSManager;
+import org.apache.olingo.fit.utils.JSONUtilities;
 import org.apache.olingo.fit.utils.LinkInfo;
+import org.apache.olingo.fit.utils.XMLUtilities;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 @Service
 @Path("/V40/Static.svc")
 @InInterceptors(classes = { XHTTPMethodInterceptor.class, ResolvingReferencesInterceptor.class })
-public class Services extends AbstractServices {
+public class Services {
 
   /**
-   * CR/LF.
+   * Logger.
    */
-  protected static final byte[] CRLF = { 13, 10 };
-
-  protected static final Pattern RELENTITY_SELECT_PATTERN = Pattern.compile("^.*\\(\\$select=.*\\)$");
+  protected static final Logger LOG = LoggerFactory.getLogger(Services.class);
 
-  protected static final Pattern CROSSJOIN_PATTERN = Pattern.compile(
+  private static final Pattern REQUEST_PATTERN = Pattern.compile("(.*) (http://.*) HTTP/.*");
+  private static final Pattern BATCH_REQUEST_REF_PATTERN = Pattern.compile("(.*) ([$]\\d+)(.*) HTTP/.*");
+  private static final Pattern REF_PATTERN = Pattern.compile("([$]\\d+)");
+  private static final Pattern RELENTITY_SELECT_PATTERN = Pattern.compile("^.*\\(\\$select=.*\\)$");
+  private static final Pattern CROSSJOIN_PATTERN = Pattern.compile(
       "^\\$crossjoin\\(.*\\)\\?\\$filter=\\([a-zA-Z/]+ eq [a-zA-Z/]+\\)$");
+  protected static final String BOUNDARY = "batch_243234_25424_ef_892u748";
+  protected static final String MULTIPART_MIXED = "multipart/mixed";
+  protected static final String APPLICATION_OCTET_STREAM = "application/octet-stream";
 
   private final Map<String, String> providedAsync = new HashMap<String, String>();
 
+  protected final ODataDeserializer atomDeserializer = new FITAtomDeserializer();
+  protected final ODataDeserializer jsonDeserializer = new JsonDeserializer(true);
+  protected final ODataSerializer atomSerializer = new AtomSerializer(true);
+  protected final ODataSerializer jsonSerializer = new JsonSerializer(true, ContentType.JSON_FULL_METADATA);
+
+  protected final Metadata metadata;
+  protected final XMLUtilities xml;
+  protected final JSONUtilities json;
+
   public Services() throws IOException {
-    super(ODataServiceVersion.V40, Commons.getMetadata(ODataServiceVersion.V40));
+    this(Commons.getMetadata());
   }
 
   protected Services(final Metadata metadata) throws IOException {
-    super(ODataServiceVersion.V40, metadata);
+    this.metadata = metadata;
+    xml = new XMLUtilities(metadata);
+    json = new JSONUtilities(metadata);
+  }
+
+  /**
+   * Provide sample services.
+   *
+   * @param accept Accept header.
+   * @return OData services.
+   */
+  @GET
+  public Response getServices(@HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept) {
+    try {
+      final Accept acceptType = Accept.parse(accept);
+
+      if (acceptType == Accept.ATOM) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      return xml.createResponse(
+          null,
+          FSManager.instance().readFile(Constants.get(ConstantKey.SERVICES), acceptType),
+          null, acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Provide sample getMetadata().
+   *
+   * @return getMetadata().
+   */
+  @GET
+  @Path("/$metadata")
+  @Produces(MediaType.APPLICATION_XML)
+  public Response getMetadata() {
+    return getMetadata(Constants.get(ConstantKey.METADATA));
+  }
+
+  protected Response getMetadata(final String filename) {
+    try {
+      return xml.createResponse(null, FSManager.instance().readRes(filename, Accept.XML), null, Accept.XML);
+    } catch (Exception e) {
+      return xml.createFaultResponse(Accept.XML.toString(), e);
+    }
   }
 
   @GET
   @Path("/redirect/{name}({id})")
-  public Response conformanceRedirect(
-      @Context final UriInfo uriInfo,
-      @PathParam("name") final String name,
-      @PathParam("id") final String id) {
+  public Response conformanceRedirect(@Context final UriInfo uriInfo) {
     return Response.temporaryRedirect(
         URI.create(uriInfo.getRequestUri().toASCIIString().replace("/redirect", ""))).build();
   }
@@ -128,7 +214,7 @@ public class Services extends AbstractServices {
 
     try {
       if (CROSSJOIN_PATTERN.matcher("$crossjoin(" + elements + ")?$filter=" + filter).matches()) {
-        final InputStream feed = FSManager.instance(version).readFile("crossjoin", Accept.JSON);
+        final InputStream feed = FSManager.instance().readFile("crossjoin", Accept.JSON);
 
         return xml.createResponse(feed, null, Accept.JSON_FULLMETA);
       } else {
@@ -141,9 +227,7 @@ public class Services extends AbstractServices {
 
   @GET
   @Path("/relatedEntitySelect/{path:.*}")
-  public Response relatedEntitySelect(
-      @PathParam("path") final String path,
-      @QueryParam("$expand") final String expand) {
+  public Response relatedEntitySelect(@QueryParam("$expand") final String expand) {
 
     if (RELENTITY_SELECT_PATTERN.matcher(expand).matches()) {
       return xml.createResponse(null, null, Accept.JSON_FULLMETA);
@@ -177,7 +261,6 @@ public class Services extends AbstractServices {
   @PUT
   @Path("/People(1)/Parent")
   public Response changeSingleValuedNavigationPropertyReference(
-      @Context final UriInfo uriInfo,
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
       @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
       final String content) {
@@ -196,39 +279,95 @@ public class Services extends AbstractServices {
   }
 
   @POST
-  @Path("/async/$batch")
-  public Response async(
-      @Context final UriInfo uriInfo,
+  @Path("/$batch")
+  @Consumes(MULTIPART_MIXED)
+  @Produces(APPLICATION_OCTET_STREAM + ";boundary=" + BOUNDARY)
+  public Response batch(
       @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
       final @Multipart MultipartBody attachment) {
+    try {
+      final boolean continueOnError = prefer.contains("odata.continue-on-error");
+      return xml.createBatchResponse(
+          exploreMultipart(attachment.getAllAttachments(), BOUNDARY, continueOnError));
+    } catch (IOException e) {
+      return xml.createFaultResponse(Accept.XML.toString(), e);
+    }
+  }
+
+  // ----------------------------------------------
+  // just for non nullable property test into PropertyTestITCase
+  // ----------------------------------------------
+  @PATCH
+  @Path("/Driver('2')")
+  public Response patchDriver() {
+    return xml.createFaultResponse(Accept.JSON_FULLMETA.toString(), new Exception("Non nullable properties"));
+  }
+
+  @GET
+  @Path("/StoredPIs(1000)")
+  public Response getStoredPI(@Context final UriInfo uriInfo) {
+    final Entity entity = new Entity();
+    entity.setType("Microsoft.Test.OData.Services.ODataWCFService.StoredPI");
+    final Property id = new Property();
+    id.setType("Edm.Int32");
+    id.setName("StoredPIID");
+    id.setValue(ValueType.PRIMITIVE, 1000);
+    entity.getProperties().add(id);
+    final Link edit = new Link();
+    edit.setHref(uriInfo.getRequestUri().toASCIIString());
+    edit.setRel("edit");
+    edit.setTitle("StoredPI");
+    entity.setEditLink(edit);
+
+    final ByteArrayOutputStream content = new ByteArrayOutputStream();
+    final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING);
+    try {
+      jsonSerializer.write(writer, new ResWrap<Entity>((URI) null, null, entity));
+      return xml.createResponse(new ByteArrayInputStream(content.toByteArray()), null, Accept.JSON_FULLMETA);
+    } catch (Exception e) {
+      LOG.error("While creating StoredPI", e);
+      return xml.createFaultResponse(Accept.JSON_FULLMETA.toString(), e);
+    }
+  }
+
+  @PATCH
+  @Path("/StoredPIs(1000)")
+  public Response patchStoredPI() {
+    // just for non nullable property test into PropertyTestITCase
+    return xml.createFaultResponse(Accept.JSON_FULLMETA.toString(), new Exception("Non nullable properties"));
+  }
+
+  @POST
+  @Path("/async/$batch")
+  public Response async(@Context final UriInfo uriInfo) {
 
     try {
       final ByteArrayOutputStream bos = new ByteArrayOutputStream();
       bos.write("HTTP/1.1 200 Ok".getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write("OData-Version: 4.0".getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write(("Content-Type: " + ContentType.APPLICATION_OCTET_STREAM + ";boundary=" + BOUNDARY).getBytes());
-      bos.write(CRLF);
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
+      bos.write(Constants.CRLF);
 
       bos.write(("--" + BOUNDARY).getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write("Content-Type: application/http".getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write("Content-Transfer-Encoding: binary".getBytes());
-      bos.write(CRLF);
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
+      bos.write(Constants.CRLF);
 
       bos.write("HTTP/1.1 202 Accepted".getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write("Location: http://service-root/async-monitor".getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
       bos.write("Retry-After: 10".getBytes());
-      bos.write(CRLF);
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
+      bos.write(Constants.CRLF);
       bos.write(("--" + BOUNDARY + "--").getBytes());
-      bos.write(CRLF);
+      bos.write(Constants.CRLF);
 
       final UUID uuid = UUID.randomUUID();
       providedAsync.put(uuid.toString(), bos.toString(Constants.ENCODING.toString()));
@@ -263,11 +402,13 @@ public class Services extends AbstractServices {
           ? Constants.get(ConstantKey.ENTITY)
           : Constants.get(ConstantKey.FEED));
 
-      final InputStream feed = FSManager.instance(version).readFile(path.toString(), acceptType);
+      final InputStream feed = FSManager.instance().readFile(path.toString(), acceptType);
 
       final StringBuilder builder = new StringBuilder();
-      builder.append("HTTP/1.1 200 Ok").append(new String(CRLF));
-      builder.append("Content-Type: ").append(accept).append(new String(CRLF)).append(new String(CRLF));
+      builder.append("HTTP/1.1 200 Ok").append(new String(Constants.CRLF));
+      builder.append("Content-Type: ").append(accept)
+          .append(new String(Constants.CRLF))
+          .append(new String(Constants.CRLF));
       builder.append(IOUtils.toString(feed));
       IOUtils.closeQuietly(feed);
 
@@ -281,15 +422,80 @@ public class Services extends AbstractServices {
     }
   }
 
-  @Override
-  protected void setInlineCount(final EntityCollection entitySet, final String count) {
+  private void setInlineCount(final EntityCollection entitySet, final String count) {
     if ("true".equals(count)) {
       entitySet.setCount(entitySet.getEntities().size());
     }
   }
 
-  @Override
-  public InputStream exploreMultipart(
+  private Response bodyPartRequest(final MimeBodyPart body, final Map<String, String> references) throws Exception {
+    @SuppressWarnings("unchecked")
+    final Enumeration<Header> en = body.getAllHeaders();
+
+    Header header = en.nextElement();
+    final String request =
+        header.getName() + (StringUtils.isNotBlank(header.getValue()) ? ":" + header.getValue() : "");
+
+    final Matcher matcher = REQUEST_PATTERN.matcher(request);
+    final Matcher matcherRef = BATCH_REQUEST_REF_PATTERN.matcher(request);
+
+    final MultivaluedMap<String, String> headers = new MultivaluedHashMap<String, String>();
+
+    while (en.hasMoreElements()) {
+      header = en.nextElement();
+      headers.putSingle(header.getName(), header.getValue());
+    }
+
+    final Response res;
+    final String url;
+    final String method;
+
+    if (matcher.find()) {
+      url = matcher.group(2);
+      method = matcher.group(1);
+    } else if (matcherRef.find()) {
+      url = references.get(matcherRef.group(2)) + matcherRef.group(3);
+      method = matcherRef.group(1);
+    } else {
+      url = null;
+      method = null;
+    }
+
+    if (url == null) {
+      res = null;
+    } else {
+      final WebClient client = WebClient.create(url, "odatajclient", "odatajclient", null);
+      client.headers(headers);
+
+      if ("DELETE".equals(method)) {
+        res = client.delete();
+      } else {
+        final InputStream is = body.getDataHandler().getInputStream();
+        String content = IOUtils.toString(is);
+        IOUtils.closeQuietly(is);
+
+        final Matcher refs = REF_PATTERN.matcher(content);
+
+        while (refs.find()) {
+          content = content.replace(refs.group(1), references.get(refs.group(1)));
+        }
+
+        if ("PATCH".equals(method) || "MERGE".equals(method)) {
+          client.header("X-HTTP-METHOD", method);
+          res = client.invoke("POST", IOUtils.toInputStream(content));
+        } else {
+          res = client.invoke(method, IOUtils.toInputStream(content));
+        }
+      }
+
+      // When updating to CXF 3.0.1, uncomment the following line, see CXF-5865
+      // client.close();
+    }
+
+    return res;
+  }
+
+  private InputStream exploreMultipart(
       final List<Attachment> attachments, final String boundary, final boolean continueOnError)
       throws IOException {
 
@@ -349,9 +555,10 @@ public class Services extends AbstractServices {
             goon = continueOnError;
           }
         } else {
-          addItemIntro(bos);
+          addItemIntro(bos, null);
 
-          res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()));
+          res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()),
+              Collections.<String, String> emptyMap());
 
           if (res.getStatus() >= 400) {
             goon = continueOnError;
@@ -374,121 +581,131 @@ public class Services extends AbstractServices {
     return new ByteArrayInputStream(bos.toByteArray());
   }
 
-  @GET
-  @Path("/People/{type:.*}")
-  public Response getPeople(
-      @Context final UriInfo uriInfo,
-      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @PathParam("type") final String type,
-      @QueryParam("$top") @DefaultValue(StringUtils.EMPTY) final String top,
-      @QueryParam("$skip") @DefaultValue(StringUtils.EMPTY) final String skip,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
-      @QueryParam("$count") @DefaultValue(StringUtils.EMPTY) final String count,
-      @QueryParam("$filter") @DefaultValue(StringUtils.EMPTY) final String filter,
-      @QueryParam("$search") @DefaultValue(StringUtils.EMPTY) final String search,
-      @QueryParam("$orderby") @DefaultValue(StringUtils.EMPTY) final String orderby,
-      @QueryParam("$skiptoken") @DefaultValue(StringUtils.EMPTY) final String skiptoken) {
+  private void addItemIntro(final ByteArrayOutputStream bos, final String contentId) throws IOException {
+    bos.write("Content-Type: application/http".getBytes());
+    bos.write(Constants.CRLF);
+    bos.write("Content-Transfer-Encoding: binary".getBytes());
+    bos.write(Constants.CRLF);
 
-    return StringUtils.isBlank(filter) && StringUtils.isBlank(search)
-        ? NumberUtils.isNumber(type)
-            ? super.getEntityInternal(
-                uriInfo.getRequestUri().toASCIIString(), accept, "People", type, format, null, null)
-            : super.getEntitySet(accept, "People", type)
-        : super.getEntitySet(uriInfo, accept, "People", top, skip, format, count, filter, orderby, skiptoken, type);
+    if (StringUtils.isNotBlank(contentId)) {
+      bos.write(("Content-ID: " + contentId).getBytes());
+      bos.write(Constants.CRLF);
+    }
+
+    bos.write(Constants.CRLF);
   }
 
-  @GET
-  @Path("/Boss")
-  public Response getSingletonBoss(
-      @Context final UriInfo uriInfo,
-      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+  private void addChangesetItemIntro(
+      final ByteArrayOutputStream bos, final String contentId, final String cboundary) throws IOException {
+    bos.write(("--" + cboundary).getBytes());
+    bos.write(Constants.CRLF);
+    bos.write(("Content-ID: " + contentId).getBytes());
+    bos.write(Constants.CRLF);
+    addItemIntro(bos, null);
+  }
 
-    return getEntityInternal(
-        uriInfo.getRequestUri().toASCIIString(), accept, "Boss", StringUtils.EMPTY, format, null, null);
+  private void addSingleBatchResponse(
+      final Response response, final ByteArrayOutputStream bos) throws IOException {
+    addSingleBatchResponse(response, null, bos);
   }
 
-  @GET
-  @Path("/Company")
-  public Response getSingletonCompany(
-      @Context final UriInfo uriInfo,
-      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+  private void addSingleBatchResponse(
+      final Response response, final String contentId, final ByteArrayOutputStream bos) throws IOException {
+    bos.write("HTTP/1.1 ".getBytes());
+    bos.write(String.valueOf(response.getStatusInfo().getStatusCode()).getBytes());
+    bos.write(" ".getBytes());
+    bos.write(response.getStatusInfo().getReasonPhrase().getBytes());
+    bos.write(Constants.CRLF);
 
-    return getEntityInternal(
-        uriInfo.getRequestUri().toASCIIString(), accept, "Company", StringUtils.EMPTY, format, null, null);
+    for (Map.Entry<String, List<Object>> header : response.getHeaders().entrySet()) {
+      final StringBuilder builder = new StringBuilder();
+      for (Object value : header.getValue()) {
+        if (builder.length() > 0) {
+          builder.append(", ");
+        }
+        builder.append(value.toString());
+      }
+      builder.insert(0, ": ").insert(0, header.getKey());
+      bos.write(builder.toString().getBytes());
+      bos.write(Constants.CRLF);
+    }
+
+    if (StringUtils.isNotBlank(contentId)) {
+      bos.write(("Content-ID: " + contentId).getBytes());
+      bos.write(Constants.CRLF);
+    }
+
+    bos.write(Constants.CRLF);
+
+    final Object entity = response.getEntity();
+    if (entity != null) {
+      bos.write(IOUtils.toByteArray((InputStream) entity));
+      bos.write(Constants.CRLF);
+    }
+
+    bos.write(Constants.CRLF);
   }
 
-  @PATCH
-  @Path("/Company")
-  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
-  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
-  public Response patchSingletonCompany(
-      @Context final UriInfo uriInfo,
-      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
-      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
-      @HeaderParam("If-Match") @DefaultValue(StringUtils.EMPTY) final String ifMatch,
-      final String changes) {
+  protected void addErrorBatchResponse(final Exception e, final ByteArrayOutputStream bos)
+      throws IOException {
+    addErrorBatchResponse(e, null, bos);
+  }
 
-    return super.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, "Company", StringUtils.EMPTY, changes);
+  protected void addErrorBatchResponse(final Exception e, final String contentId, final ByteArrayOutputStream bos)
+      throws IOException {
+    addSingleBatchResponse(xml.createFaultResponse(Accept.XML.toString(), e), contentId, bos);
   }
 
+  /**
+   * Retrieve entities from the given entity set and the given type.
+   *
+   * @param accept Accept header.
+   * @param name entity set.
+   * @param type entity type.
+   * @return entity set.
+   */
   @GET
-  @Path("/Customers")
-  public Response getCustomers(
-      @Context final UriInfo uriInfo,
+  @Path("/{name}/{type:[a-zA-Z].*}")
+  public Response getEntitySet(
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
-      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
-      @QueryParam("$deltatoken") @DefaultValue(StringUtils.EMPTY) final String deltatoken) {
+      @PathParam("name") final String name,
+      @PathParam("type") final String type) {
 
     try {
-      final Accept acceptType;
-      if (StringUtils.isNotBlank(format)) {
-        acceptType = Accept.valueOf(format.toUpperCase());
-      } else {
-        acceptType = Accept.parse(accept);
+      final Accept acceptType = Accept.parse(accept);
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
       }
 
-      final InputStream output;
-      if (StringUtils.isBlank(deltatoken)) {
-        final InputStream input = (InputStream) getEntitySet(
-            uriInfo, accept, "Customers", null, null, format, null, null, null, null).getEntity();
-        final EntityCollection entitySet = xml.readEntitySet(acceptType, input);
-
-        boolean trackChanges = prefer.contains("odata.track-changes");
-        if (trackChanges) {
-          entitySet.setDeltaLink(URI.create("Customers?$deltatoken=8015"));
-        }
+      final String basePath = name + File.separatorChar;
+      final StringBuilder path = new StringBuilder(name).
+          append(File.separatorChar).append(type).
+          append(File.separatorChar);
 
-        output = xml.writeEntitySet(acceptType, new ResWrap<EntityCollection>(
-            URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + "Customers"),
-            null,
-            entitySet));
-      } else {
-        output = FSManager.instance(version).readFile("delta", acceptType);
-      }
+      path.append(metadata.getEntitySet(name).isSingleton()
+          ? Constants.get(ConstantKey.ENTITY)
+              : Constants.get(ConstantKey.FEED));
 
-      final Response response = xml.createResponse(
-          null,
-          output,
-          null,
-          acceptType);
-      if (StringUtils.isNotBlank(prefer)) {
-        response.getHeaders().put("Preference-Applied", Collections.<Object> singletonList(prefer));
-      }
-      return response;
+      final InputStream feed = FSManager.instance().readFile(path.toString(), acceptType);
+      return xml.createResponse(null, feed, Commons.getETag(basePath), acceptType);
     } catch (Exception e) {
       return xml.createFaultResponse(accept, e);
     }
   }
 
   @GET
-  @Path("/Company/Microsoft.Test.OData.Services.ODataWCFService.GetEmployeesCount{paren:[\\(\\)]*}")
-  public Response functionGetEmployeesCount(
+  @Path("/{name}/{type:[a-zA-Z].*}")
+  public Response getEntitySet(@Context final UriInfo uriInfo,
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+      @PathParam("name") final String name,
+      @QueryParam("$top") @DefaultValue(StringUtils.EMPTY) final String top,
+      @QueryParam("$skip") @DefaultValue(StringUtils.EMPTY) final String skip,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      @QueryParam("$count") @DefaultValue(StringUtils.EMPTY) final String count,
+      @QueryParam("$filter") @DefaultValue(StringUtils.EMPTY) final String filter,
+      @QueryParam("$orderby") @DefaultValue(StringUtils.EMPTY) final String orderby,
+      @QueryParam("$skiptoken") @DefaultValue(StringUtils.EMPTY) final String skiptoken,
+      @PathParam("type") final String type) {
 
     try {
       final Accept acceptType;
@@ -498,111 +715,450 @@ public class Services extends AbstractServices {
         acceptType = Accept.parse(accept);
       }
 
-      final Property property = new Property();
-      property.setType("Edm.Int32");
-      property.setValue(ValueType.PRIMITIVE, 2);
-      final ResWrap<Property> container = new ResWrap<Property>(
-          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + property.getType()), null,
-          property);
+      final String location = uriInfo.getRequestUri().toASCIIString();
+      try {
+        // search for function ...
+        final InputStream func = FSManager.instance().readFile(name, acceptType);
+        return xml.createResponse(location, func, null, acceptType);
+      } catch (NotFoundException e) {
+        if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+          throw new UnsupportedMediaTypeException("Unsupported media type");
+        }
 
-      return xml.createResponse(
-          null,
-          xml.writeProperty(acceptType, container),
-          null,
-          acceptType);
-    } catch (Exception e) {
-      return xml.createFaultResponse(accept, e);
-    }
-  }
+        // search for entitySet ...
+        final String basePath = name + File.separatorChar;
 
-  @POST
-  @Path("/Company/Microsoft.Test.OData.Services.ODataWCFService.IncreaseRevenue{paren:[\\(\\)]*}")
-  public Response actionIncreaseRevenue(
-      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
-      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
-      final String param) {
+        final StringBuilder builder = new StringBuilder();
+        builder.append(basePath);
 
-    try {
-      final Accept acceptType;
-      if (StringUtils.isNotBlank(format)) {
-        acceptType = Accept.valueOf(format.toUpperCase());
-      } else {
-        acceptType = Accept.parse(accept);
-      }
+        if (type != null) {
+          builder.append(type).append(File.separatorChar);
+        }
 
-      final Accept contentTypeValue = Accept.parse(contentType);
-      final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
+        if (StringUtils.isNotBlank(orderby)) {
+          builder.append(Constants.get(ConstantKey.ORDERBY)).append(File.separatorChar).
+          append(orderby).append(File.separatorChar);
+        }
 
-      return xml.createResponse(
-          null,
-          xml.writeProperty(acceptType, entry.getProperty("IncreaseValue")),
-          null,
-          acceptType);
+        if (StringUtils.isNotBlank(filter)) {
+          builder.append(Constants.get(ConstantKey.FILTER)).append(File.separatorChar).
+          append(filter.replaceAll("/", "."));
+        } else if (StringUtils.isNotBlank(skiptoken)) {
+          builder.append(Constants.get(ConstantKey.SKIP_TOKEN)).append(File.separatorChar).
+          append(skiptoken);
+        } else {
+          builder.append(metadata.getEntitySet(name).isSingleton()
+              ? Constants.get(ConstantKey.ENTITY)
+                  : Constants.get(ConstantKey.FEED));
+        }
+
+        final InputStream feed = FSManager.instance().readFile(builder.toString(), Accept.ATOM);
+
+        final ResWrap<EntityCollection> container = atomDeserializer.toEntitySet(feed);
+
+        setInlineCount(container.getPayload(), count);
+
+        final ByteArrayOutputStream content = new ByteArrayOutputStream();
+        final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING);
+
+        // -----------------------------------------------
+        // Evaluate $skip and $top
+        // -----------------------------------------------
+        List<Entity> entries = new ArrayList<Entity>(container.getPayload().getEntities());
+
+        if (StringUtils.isNotBlank(skip)) {
+          entries = entries.subList(Integer.valueOf(skip), entries.size());
+        }
+
+        if (StringUtils.isNotBlank(top)) {
+          entries = entries.subList(0, Integer.valueOf(top));
+        }
+
+        container.getPayload().getEntities().clear();
+        container.getPayload().getEntities().addAll(entries);
+        // -----------------------------------------------
+
+        if (acceptType == Accept.ATOM) {
+          atomSerializer.write(writer, container);
+        } else {
+          jsonSerializer.write(writer, container);
+        }
+        writer.flush();
+        writer.close();
+
+        return xml.createResponse(
+            location,
+            new ByteArrayInputStream(content.toByteArray()),
+            Commons.getETag(basePath),
+            acceptType);
+      }
     } catch (Exception e) {
       return xml.createFaultResponse(accept, e);
     }
   }
 
+  /**
+   * Retrieve entity set or function execution sample.
+   *
+   * @param accept Accept header.
+   * @param name entity set or function name.
+   * @param format format query option.
+   * @param count count query option.
+   * @param filter filter query option.
+   * @param orderby orderby query option.
+   * @param skiptoken skiptoken query option.
+   * @return entity set or function result.
+   */
   @GET
-  @Path("/Products({entityId})/Microsoft.Test.OData.Services.ODataWCFService.GetProductDetails({param:.*})")
-  public Response functionGetProductDetails(
+  @Path("/{name}")
+  public Response getEntitySet(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("name") final String name,
+      @QueryParam("$top") @DefaultValue(StringUtils.EMPTY) final String top,
+      @QueryParam("$skip") @DefaultValue(StringUtils.EMPTY) final String skip,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      @QueryParam("$count") @DefaultValue(StringUtils.EMPTY) final String count,
+      @QueryParam("$filter") @DefaultValue(StringUtils.EMPTY) final String filter,
+      @QueryParam("$orderby") @DefaultValue(StringUtils.EMPTY) final String orderby,
+      @QueryParam("$skiptoken") @DefaultValue(StringUtils.EMPTY) final String skiptoken) {
+
+    return getEntitySet(uriInfo, accept, name, top, skip, format, count, filter, orderby, skiptoken, null);
+  }
+
+  @GET
+  @Path("/Person({entityId})")
+  public Response getPerson(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    final Response internal = getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "Person", entityId, format, null, null);
+    if (internal.getStatus() == 200) {
+      InputStream entity = (InputStream) internal.getEntity();
+      try {
+        if (utils.getKey() == Accept.JSON_FULLMETA || utils.getKey() == Accept.ATOM) {
+          entity = utils.getValue().addOperation(entity, "Sack", "#DefaultContainer.Sack",
+              uriInfo.getAbsolutePath().toASCIIString()
+              + "/Microsoft.Test.OData.Services.AstoriaDefaultService.SpecialEmployee/Sack");
+        }
+
+        return utils.getValue().createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            entity,
+            internal.getHeaderString("ETag"),
+            utils.getKey());
+      } catch (Exception e) {
+        LOG.error("Error retrieving entity", e);
+        return xml.createFaultResponse(accept, e);
+      }
+    } else {
+      return internal;
+    }
+  }
+
+  @GET
+  @Path("/Product({entityId})")
+  public Response getProduct(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    final Response internal = getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "Product", entityId, format, null, null);
+    if (internal.getStatus() == 200) {
+      InputStream entity = (InputStream) internal.getEntity();
+      try {
+        if (utils.getKey() == Accept.JSON_FULLMETA || utils.getKey() == Accept.ATOM) {
+          entity = utils.getValue().addOperation(entity,
+              "ChangeProductDimensions", "#DefaultContainer.ChangeProductDimensions",
+              uriInfo.getAbsolutePath().toASCIIString() + "/ChangeProductDimensions");
+        }
+
+        return utils.getValue().createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            entity,
+            internal.getHeaderString("ETag"),
+            utils.getKey());
+      } catch (Exception e) {
+        LOG.error("Error retrieving entity", e);
+        return xml.createFaultResponse(accept, e);
+      }
+    } else {
+      return internal;
+    }
+  }
+
+  @GET
+  @Path("/ComputerDetail({entityId})")
+  public Response getComputerDetail(
+      @Context final UriInfo uriInfo,
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
       @PathParam("entityId") final String entityId,
       @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
 
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    final Response internal = getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "ComputerDetail", entityId, format, null, null);
+    if (internal.getStatus() == 200) {
+      InputStream entity = (InputStream) internal.getEntity();
+      try {
+        if (utils.getKey() == Accept.JSON_FULLMETA || utils.getKey() == Accept.ATOM) {
+          entity = utils.getValue().addOperation(entity,
+              "ResetComputerDetailsSpecifications", "#DefaultContainer.ResetComputerDetailsSpecifications",
+              uriInfo.getAbsolutePath().toASCIIString() + "/ResetComputerDetailsSpecifications");
+        }
+
+        return utils.getValue().createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            entity,
+            internal.getHeaderString("ETag"),
+            utils.getKey());
+      } catch (Exception e) {
+        LOG.error("Error retrieving entity", e);
+        return xml.createFaultResponse(accept, e);
+      }
+    } else {
+      return internal;
+    }
+  }
+
+  /**
+   * Retrieve entity sample.
+   *
+   * @param accept Accept header.
+   * @param entitySetName Entity set name.
+   * @param entityId entity id.
+   * @param format format query option.
+   * @param expand expand query option.
+   * @param select select query option.
+   * @return entity.
+   */
+  @GET
+  @Path("/{entitySetName}({entityId})")
+  public Response getEntity(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entitySetName") final String entitySetName,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      @QueryParam("$expand") @DefaultValue(StringUtils.EMPTY) final String expand,
+      @QueryParam("$select") @DefaultValue(StringUtils.EMPTY) final String select) {
+
+    return getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, entitySetName, entityId, format, expand, select);
+  }
+
+  protected Response getEntityInternal(
+      final String location,
+      final String accept,
+      final String entitySetName,
+      final String entityId,
+      final String format,
+      final String expand,
+      final String select) {
+
     try {
-      final Accept acceptType;
-      if (StringUtils.isNotBlank(format)) {
-        acceptType = Accept.valueOf(format.toUpperCase());
-      } else {
-        acceptType = Accept.parse(accept);
+      final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+      if (utils.getKey() == Accept.XML || utils.getKey() == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
       }
 
-      final Entity entry = new Entity();
-      entry.setType("Microsoft.Test.OData.Services.ODataWCFService.ProductDetail");
-      final Property productId = new Property();
-      productId.setName("ProductID");
-      productId.setType("Edm.Int32");
-      productId.setValue(ValueType.PRIMITIVE, Integer.valueOf(entityId));
-      entry.getProperties().add(productId);
-      final Property productDetailId = new Property();
-      productDetailId.setName("ProductDetailID");
-      productDetailId.setType("Edm.Int32");
-      productDetailId.setValue(ValueType.PRIMITIVE, 2);
-      entry.getProperties().add(productDetailId);
+      final Map.Entry<String, InputStream> entityInfo =
+          utils.getValue().readEntity(entitySetName, entityId, Accept.ATOM);
 
-      final Link link = new Link();
-      link.setRel("edit");
-      link.setHref(URI.create(
-          Constants.get(ConstantKey.DEFAULT_SERVICE_URL)
-              + "ProductDetails(ProductID=6,ProductDetailID=1)").toASCIIString());
-      entry.setEditLink(link);
+      final InputStream entity = entityInfo.getValue();
 
-      final EntityCollection feed = new EntityCollection();
-      feed.getEntities().add(entry);
+      ResWrap<Entity> container = atomDeserializer.toEntity(entity);
+      if (container.getContextURL() == null) {
+        container = new ResWrap<Entity>(URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX)
+            + entitySetName + Constants.get(ConstantKey.ODATA_METADATA_ENTITY_SUFFIX)),
+            container.getMetadataETag(), container.getPayload());
+      }
+      final Entity entry = container.getPayload();
 
-      final ResWrap<EntityCollection> container = new ResWrap<EntityCollection>(
-          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + "ProductDetail"), null,
-          feed);
+      if ((this instanceof KeyAsSegment)) {
+        final Link editLink = new Link();
+        editLink.setRel("edit");
+        editLink.setTitle(entitySetName);
+        editLink.setHref(Constants.get(ConstantKey.DEFAULT_SERVICE_URL) + entitySetName + "/" + entityId);
+
+        entry.setEditLink(editLink);
+      }
+
+      if (StringUtils.isNotBlank(select)) {
+        final List<String> properties = Arrays.asList(select.split(","));
+        final Set<Property> toBeRemoved = new HashSet<Property>();
+
+        for (Property property : entry.getProperties()) {
+          if (!properties.contains(property.getName())) {
+            toBeRemoved.add(property);
+          }
+        }
+
+        entry.getProperties().removeAll(toBeRemoved);
+
+        final Set<Link> linkToBeRemoved = new HashSet<Link>();
+
+        for (Link link : entry.getNavigationLinks()) {
+          if (!properties.contains(link.getTitle().replaceAll("@.*$", "")) && !properties.contains(link.getTitle())) {
+            linkToBeRemoved.add(link);
+          }
+        }
+
+        entry.getNavigationLinks().removeAll(linkToBeRemoved);
+      }
+
+      String tempExpand = expand;
+      if (StringUtils.isNotBlank(tempExpand)) {
+        tempExpand = StringUtils.substringBefore(tempExpand, "(");
+        final List<String> links = Arrays.asList(tempExpand.split(","));
+
+        final Map<Link, Link> replace = new HashMap<Link, Link>();
+
+        for (Link link : entry.getNavigationLinks()) {
+          if (links.contains(link.getTitle())) {
+            // expand link
+            final Link rep = new Link();
+            rep.setHref(link.getHref());
+            rep.setRel(link.getRel());
+            rep.setTitle(link.getTitle());
+            rep.setType(link.getType());
+            if (link.getType().equals(Constants.get(ConstantKey.ATOM_LINK_ENTRY))) {
+              // inline entry
+              final Entity inline = atomDeserializer.toEntity(
+                  xml.expandEntity(entitySetName, entityId, link.getTitle())).getPayload();
+              rep.setInlineEntity(inline);
+            } else if (link.getType().equals(Constants.get(ConstantKey.ATOM_LINK_FEED))) {
+              // inline feed
+              final EntityCollection inline = atomDeserializer.toEntitySet(
+                  xml.expandEntity(entitySetName, entityId, link.getTitle())).getPayload();
+              rep.setInlineEntitySet(inline);
+            }
+            replace.put(link, rep);
+          }
+        }
+
+        for (Map.Entry<Link, Link> link : replace.entrySet()) {
+          entry.getNavigationLinks().remove(link.getKey());
+          entry.getNavigationLinks().add(link.getValue());
+        }
+      }
 
       return xml.createResponse(
-          null,
-          xml.writeEntitySet(acceptType, container),
-          null,
-          acceptType);
+          location,
+          xml.writeEntity(utils.getKey(), container),
+          Commons.getETag(entityInfo.getKey()),
+          utils.getKey());
     } catch (Exception e) {
+      LOG.error("Error retrieving entity", e);
       return xml.createFaultResponse(accept, e);
     }
   }
 
-  @POST
-  @Path("/Products({entityId})/Microsoft.Test.OData.Services.ODataWCFService.AddAccessRight{paren:[\\(\\)]*}")
-  public Response actionAddAccessRight(
+  @GET
+  @Path("/{entitySetName}({entityId})/$value")
+  public Response getMediaEntity(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entitySetName") final String entitySetName,
+      @PathParam("entityId") final String entityId) {
+
+    try {
+      if (!accept.contains("*/*") && !accept.contains("application/octet-stream")) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final AbstractUtilities utils = getUtilities(null);
+      final Map.Entry<String, InputStream> entityInfo = utils.readMediaEntity(entitySetName, entityId);
+      return utils.createResponse(
+          uriInfo.getRequestUri().toASCIIString(),
+          entityInfo.getValue(),
+          Commons.getETag(entityInfo.getKey()),
+          null);
+
+    } catch (Exception e) {
+      LOG.error("Error retrieving entity", e);
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @GET
+  @Path("/People/{type:.*}")
+  public Response getPeople(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("type") final String type,
+      @QueryParam("$top") @DefaultValue(StringUtils.EMPTY) final String top,
+      @QueryParam("$skip") @DefaultValue(StringUtils.EMPTY) final String skip,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      @QueryParam("$count") @DefaultValue(StringUtils.EMPTY) final String count,
+      @QueryParam("$filter") @DefaultValue(StringUtils.EMPTY) final String filter,
+      @QueryParam("$search") @DefaultValue(StringUtils.EMPTY) final String search,
+      @QueryParam("$orderby") @DefaultValue(StringUtils.EMPTY) final String orderby,
+      @QueryParam("$skiptoken") @DefaultValue(StringUtils.EMPTY) final String skiptoken) {
+
+    return StringUtils.isBlank(filter) && StringUtils.isBlank(search) ?
+        NumberUtils.isNumber(type) ?
+            getEntityInternal(uriInfo.getRequestUri().toASCIIString(), accept, "People", type, format, null, null) :
+            getEntitySet(accept, "People", type) :
+        getEntitySet(uriInfo, accept, "People", top, skip, format, count, filter, orderby, skiptoken, type);
+  }
+
+  @GET
+  @Path("/Boss")
+  public Response getSingletonBoss(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    return getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "Boss", StringUtils.EMPTY, format, null, null);
+  }
+
+  @GET
+  @Path("/Company")
+  public Response getSingletonCompany(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    return getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "Company", StringUtils.EMPTY, format, null, null);
+  }
+
+  @PATCH
+  @Path("/Company")
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  public Response patchSingletonCompany(
+      @Context final UriInfo uriInfo,
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
       @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      @HeaderParam("If-Match") @DefaultValue(StringUtils.EMPTY) final String ifMatch,
+      final String changes) {
+
+    return patchEntityInternal(uriInfo, accept, contentType, prefer, ifMatch, "Company", StringUtils.EMPTY, changes);
+  }
+
+  @GET
+  @Path("/Customers")
+  public Response getCustomers(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
       @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
-      final String param) {
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      @QueryParam("$deltatoken") @DefaultValue(StringUtils.EMPTY) final String deltatoken) {
 
     try {
       final Accept acceptType;
@@ -612,236 +1168,1448 @@ public class Services extends AbstractServices {
         acceptType = Accept.parse(accept);
       }
 
-      final Accept contentTypeValue = Accept.parse(contentType);
-      final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
-
-      assert 1 == entry.getProperties().size();
-      assert entry.getProperty("accessRight") != null;
+      final InputStream output;
+      if (StringUtils.isBlank(deltatoken)) {
+        final InputStream input = (InputStream) getEntitySet(
+            uriInfo, accept, "Customers", null, null, format, null, null, null, null).getEntity();
+        final EntityCollection entitySet = xml.readEntitySet(acceptType, input);
 
-      final Property property = entry.getProperty("accessRight");
-      property.setType("Microsoft.Test.OData.Services.ODataWCFService.AccessLevel");
+        boolean trackChanges = prefer.contains("odata.track-changes");
+        if (trackChanges) {
+          entitySet.setDeltaLink(URI.create("Customers?$deltatoken=8015"));
+        }
 
-      final ResWrap<Property> result = new ResWrap<Property>(
-          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + property.getType()),
-          null, property);
+        output = xml.writeEntitySet(acceptType, new ResWrap<EntityCollection>(
+            URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + "Customers"),
+            null,
+            entitySet));
+      } else {
+        output = FSManager.instance().readFile("delta", acceptType);
+      }
 
-      return xml.createResponse(
+      final Response response = xml.createResponse(
           null,
-          xml.writeProperty(acceptType, result),
+          output,
           null,
           acceptType);
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object> singletonList(prefer));
+      }
+      return response;
     } catch (Exception e) {
       return xml.createFaultResponse(accept, e);
     }
   }
 
   @POST
-  @Path("/Customers({personId})/Microsoft.Test.OData.Services.ODataWCFService.ResetAddress{paren:[\\(\\)]*}")
-  public Response actionResetAddress(
+  @Path("/{entitySetName}")
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM })
+  public Response postNewEntity(
       @Context final UriInfo uriInfo,
       @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
-      @PathParam("personId") final String personId,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      @PathParam("entitySetName") final String entitySetName,
+      final String entity) {
+
+    try {
+      final Accept acceptType = Accept.parse(accept);
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final ResWrap<Entity> container;
+
+      final org.apache.olingo.fit.metadata.EntitySet entitySet = metadata.getEntitySet(entitySetName);
+
+      final Entity entry;
+      final String entityKey;
+      if (xml.isMediaContent(entitySetName)) {
+        entry = new Entity();
+        entry.setMediaContentType(ContentType.APPLICATION_OCTET_STREAM.toContentTypeString());
+        entry.setType(entitySet.getType());
+
+        entityKey = xml.getDefaultEntryKey(entitySetName, entry);
+
+        xml.addMediaEntityValue(entitySetName, entityKey, IOUtils.toInputStream(entity, Constants.ENCODING));
+
+        final Pair<String, EdmPrimitiveTypeKind> id = Commons.getMediaContent().get(entitySetName);
+        if (id != null) {
+          final Property prop = new Property();
+          prop.setName(id.getKey());
+          prop.setType(id.getValue().toString());
+          prop.setValue(ValueType.PRIMITIVE,
+              id.getValue() == EdmPrimitiveTypeKind.Int32
+              ? Integer.parseInt(entityKey)
+                  : id.getValue() == EdmPrimitiveTypeKind.Guid
+                  ? UUID.fromString(entityKey)
+                      : entityKey);
+          entry.getProperties().add(prop);
+        }
+
+        final Link editLink = new Link();
+        editLink.setHref(Commons.getEntityURI(entitySetName, entityKey));
+        editLink.setRel("edit");
+        editLink.setTitle(entitySetName);
+        entry.setEditLink(editLink);
+
+        entry.setMediaContentSource(URI.create(editLink.getHref() + "/$value"));
+
+        container = new ResWrap<Entity>((URI) null, null, entry);
+      } else {
+        final Accept contentTypeValue = Accept.parse(contentType);
+        if (Accept.ATOM == contentTypeValue) {
+          container = atomDeserializer.toEntity(IOUtils.toInputStream(entity, Constants.ENCODING));
+        } else {
+          container = jsonDeserializer.toEntity(IOUtils.toInputStream(entity, Constants.ENCODING));
+        }
+        entry = container.getPayload();
+        updateInlineEntities(entry);
+
+        entityKey = xml.getDefaultEntryKey(entitySetName, entry);
+      }
+
+      normalizeAtomEntry(entry, entitySetName, entityKey);
+
+      final ByteArrayOutputStream content = new ByteArrayOutputStream();
+      final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING);
+      atomSerializer.write(writer, container);
+      writer.flush();
+      writer.close();
+
+      final InputStream serialization =
+          xml.addOrReplaceEntity(entityKey, entitySetName, new ByteArrayInputStream(content.toByteArray()), entry);
+
+      ResWrap<Entity> result = atomDeserializer.toEntity(serialization);
+      result = new ResWrap<Entity>(
+          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX)
+              + entitySetName + Constants.get(ConstantKey.ODATA_METADATA_ENTITY_SUFFIX)),
+              null, result.getPayload());
+
+      final String path = Commons.getEntityBasePath(entitySetName, entityKey);
+      FSManager.instance().putInMemory(result, path + Constants.get(ConstantKey.ENTITY));
+
+      final String location;
+
+      if ((this instanceof KeyAsSegment)) {
+        location = uriInfo.getRequestUri().toASCIIString() + "/" + entityKey;
+
+        final Link editLink = new Link();
+        editLink.setRel("edit");
+        editLink.setTitle(entitySetName);
+        editLink.setHref(location);
+
+        result.getPayload().setEditLink(editLink);
+      } else {
+        location = uriInfo.getRequestUri().toASCIIString() + "(" + entityKey + ")";
+      }
+
+      final Response response;
+      if ("return-no-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(
+            location,
+            null,
+            null,
+            acceptType,
+            Response.Status.NO_CONTENT);
+      } else {
+        response = xml.createResponse(
+            location,
+            xml.writeEntity(acceptType, result),
+            null,
+            acceptType,
+            Response.Status.CREATED);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object> singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      LOG.error("While creating new entity", e);
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  private void updateInlineEntities(final Entity entity) {
+    final String type = entity.getType();
+    EntityType entityType;
+    Map<String, NavigationProperty> navProperties = Collections.emptyMap();
+    if (type != null && type.length() > 0) {
+      entityType = metadata.getEntityOrComplexType(type);
+      navProperties = entityType.getNavigationPropertyMap();
+    }
+
+    for (Property property : entity.getProperties()) {
+      if (navProperties.containsKey(property.getName())) {
+        Link alink = new Link();
+        alink.setTitle(property.getName());
+        alink.getAnnotations().addAll(property.getAnnotations());
+
+        alink.setType(navProperties.get(property.getName()).isEntitySet()
+            ? Constants.get(ConstantKey.ATOM_LINK_FEED)
+                : Constants.get(ConstantKey.ATOM_LINK_ENTRY));
+
+        alink.setRel(Constants.get(ConstantKey.ATOM_LINK_REL) + property.getName());
+
+        if (property.isCollection()) {
+          EntityCollection inline = new EntityCollection();
+          for (Object value : property.asCollection()) {
+            Entity inlineEntity = new Entity();
+            inlineEntity.setType(navProperties.get(property.getName()).getType());
+            for (Property prop : ((ComplexValue) value).getValue()) {
+              inlineEntity.getProperties().add(prop);
+            }
+            inline.getEntities().add(inlineEntity);
+          }
+          alink.setInlineEntitySet(inline);
+        } else if (property.isComplex()) {
+          Entity inline = new Entity();
+          inline.setType(navProperties.get(property.getName()).getType());
+          for (Property prop : property.asComplex().getValue()) {
+            inline.getProperties().add(prop);
+          }
+          alink.setInlineEntity(inline);
+
+        } else {
+          throw new IllegalStateException("Invalid navigation property " + property);
+        }
+        entity.getNavigationLinks().add(alink);
+      }
+    }
+  }
+
+  private void normalizeAtomEntry(final Entity entry, final String entitySetName, final String entityKey) {
+    final org.apache.olingo.fit.metadata.EntitySet entitySet = metadata.getEntitySet(entitySetName);
+    final EntityType entityType = metadata.getEntityOrComplexType(entitySet.getType());
+    for (Map.Entry<String, org.apache.olingo.fit.metadata.Property> property : entityType.getPropertyMap().entrySet()) {
+      if (entry.getProperty(property.getKey()) == null && property.getValue().isNullable()) {
+        final Property prop = new Property();
+        prop.setName(property.getKey());
+        prop.setValue(ValueType.PRIMITIVE, null);
+        entry.getProperties().add(prop);
+      }
+    }
+
+    for (Map.Entry<String, NavigationProperty> property : entityType.getNavigationPropertyMap().entrySet()) {
+      boolean found = false;
+      for (Link link : entry.getNavigationLinks()) {
+        if (link.getTitle().equals(property.getKey())) {
+          found = true;
+        }
+      }
+
+      if (!found) {
+        final Link link = new Link();
+        link.setTitle(property.getKey());
+        link.setType(property.getValue().isEntitySet()
+            ? Constants.get(ConstantKey.ATOM_LINK_FEED)
+                : Constants.get(ConstantKey.ATOM_LINK_ENTRY));
+        link.setRel(Constants.get(ConstantKey.ATOM_LINK_REL) + property.getKey());
+        link.setHref(entitySetName + "(" + entityKey + ")/" + property.getKey());
+        entry.getNavigationLinks().add(link);
+      }
+    }
+  }
+
+  @GET
+  @Path("/Company/Microsoft.Test.OData.Services.ODataWCFService.GetEmployeesCount{paren:[\\(\\)]*}")
+  public Response functionGetEmployeesCount(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept);
+      }
+
+      final Property property = new Property();
+      property.setType("Edm.Int32");
+      property.setValue(ValueType.PRIMITIVE, 2);
+      final ResWrap<Property> container = new ResWrap<Property>(
+          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + property.getType()), null,
+          property);
+
+      return xml.createResponse(
+          null,
+          xml.writeProperty(acceptType, container),
+          null,
+          acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Person({entityId})/{type:.*}/Sack")
+  public Response actionSack(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    if (utils.getKey() == Accept.XML || utils.getKey() == Accept.TEXT) {
+      throw new UnsupportedMediaTypeException("Unsupported media type");
+    }
+
+    try {
+      final Map.Entry<String, InputStream> entityInfo = xml.readEntity("Person", entityId, Accept.ATOM);
+
+      final InputStream entity = entityInfo.getValue();
+      final ResWrap<Entity> container = atomDeserializer.toEntity(entity);
+
+      container.getPayload().getProperty("Salary").setValue(ValueType.PRIMITIVE, 0);
+      container.getPayload().getProperty("Title").setValue(ValueType.PRIMITIVE, "[Sacked]");
+
+      final FSManager fsManager = FSManager.instance();
+      fsManager.putInMemory(xml.writeEntity(Accept.ATOM, container),
+          fsManager.getAbsolutePath(Commons.getEntityBasePath("Person", entityId) + Constants.get(
+              ConstantKey.ENTITY), Accept.ATOM));
+
+      return utils.getValue().createResponse(null, null, null, utils.getKey(), Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Person/{type:.*}/IncreaseSalaries")
+  public Response actionIncreaseSalaries(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("type") final String type,
+      final String body) {
+
+    final String name = "Person";
+    try {
+      final Accept acceptType = Accept.parse(accept);
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final JsonNode tree = new ObjectMapper().readTree(body);
+      if (!tree.has("n")) {
+        throw new Exception("Missing parameter: n");
+      }
+      final int n = tree.get("n").asInt();
+
+      final StringBuilder path = new StringBuilder(name).
+          append(File.separatorChar).append(type).
+          append(File.separatorChar);
+
+      path.append(metadata.getEntitySet(name).isSingleton()
+          ? Constants.get(ConstantKey.ENTITY)
+              : Constants.get(ConstantKey.FEED));
+
+      final InputStream feed = FSManager.instance().readFile(path.toString(), acceptType);
+
+      final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+      IOUtils.copy(feed, copy);
+      IOUtils.closeQuietly(feed);
+
+      String newContent = new String(copy.toByteArray(), "UTF-8");
+      final Pattern salary = Pattern.compile(acceptType == Accept.ATOM
+          ? "\\<d:Salary m:type=\"Edm.Int32\"\\>(-?\\d+)\\</d:Salary\\>"
+              : "\"Salary\":(-?\\d+),");
+      final Matcher salaryMatcher = salary.matcher(newContent);
+      while (salaryMatcher.find()) {
+        final Long newSalary = Long.valueOf(salaryMatcher.group(1)) + n;
+        newContent = newContent.
+            replaceAll("\"Salary\":" + salaryMatcher.group(1) + ",",
+                "\"Salary\":" + newSalary + ",").
+                replaceAll("\\<d:Salary m:type=\"Edm.Int32\"\\>" + salaryMatcher.group(1) + "</d:Salary\\>",
+                    "<d:Salary m:type=\"Edm.Int32\">" + newSalary + "</d:Salary>");
+      }
+
+      FSManager.instance().putInMemory(IOUtils.toInputStream(newContent, Constants.ENCODING),
+          FSManager.instance().getAbsolutePath(path.toString(), acceptType));
+
+      return xml.createResponse(null, null, null, acceptType, Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Product({entityId})/ChangeProductDimensions")
+  public Response actionChangeProductDimensions(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      final String argument) {
+
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    if (utils.getKey() == Accept.XML || utils.getKey() == Accept.TEXT) {
+      throw new UnsupportedMediaTypeException("Unsupported media type");
+    }
+
+    try {
+      final Map.Entry<String, InputStream> entityInfo = xml.readEntity("Product", entityId, Accept.ATOM);
+
+      final InputStream entity = entityInfo.getValue();
+      final ResWrap<Entity> container = atomDeserializer.toEntity(entity);
+
+      final Entity param = xml.readEntity(utils.getKey(), IOUtils.toInputStream(argument, Constants.ENCODING));
+
+      final Property property = param.getProperty("dimensions");
+      container.getPayload().getProperty("Dimensions").setValue(property.getValueType(), property.getValue());
+
+      final FSManager fsManager = FSManager.instance();
+      fsManager.putInMemory(xml.writeEntity(Accept.ATOM, container),
+          fsManager.getAbsolutePath(Commons.getEntityBasePath("Product", entityId) + Constants.get(
+              ConstantKey.ENTITY), Accept.ATOM));
+
+      return utils.getValue().createResponse(null, null, null, utils.getKey(), Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/ComputerDetail({entityId})/ResetComputerDetailsSpecifications")
+  public Response actionResetComputerDetailsSpecifications(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      final String argument) {
+
+    final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+    if (utils.getKey() == Accept.XML || utils.getKey() == Accept.TEXT) {
+      throw new UnsupportedMediaTypeException("Unsupported media type");
+    }
+
+    try {
+      final Map.Entry<String, InputStream> entityInfo = xml.readEntity("ComputerDetail", entityId, Accept.ATOM);
+
+      final InputStream entity = entityInfo.getValue();
+      final ResWrap<Entity> container = atomDeserializer.toEntity(entity);
+
+      final Entity param = xml.readEntity(utils.getKey(), IOUtils.toInputStream(argument, Constants.ENCODING));
+
+      Property property = param.getProperty("specifications");
+      container.getPayload().getProperty("SpecificationsBag").setValue(property.getValueType(), property.getValue());
+      property = param.getProperty("purchaseTime");
+      container.getPayload().getProperty("PurchaseDate").setValue(property.getValueType(), property.getValue());
+
+      final FSManager fsManager = FSManager.instance();
+      fsManager.putInMemory(xml.writeEntity(Accept.ATOM, container),
+          fsManager.getAbsolutePath(Commons.getEntityBasePath("ComputerDetail", entityId) + Constants.get(
+              ConstantKey.ENTITY), Accept.ATOM));
+
+      return utils.getValue().createResponse(null, null, null, utils.getKey(), Response.Status.NO_CONTENT);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Company/Microsoft.Test.OData.Services.ODataWCFService.IncreaseRevenue{paren:[\\(\\)]*}")
+  public Response actionIncreaseRevenue(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
       @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
       @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
       final String param) {
 
     try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept);
+      }
+
       final Accept contentTypeValue = Accept.parse(contentType);
       final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
 
-      assert 2 == entry.getProperties().size();
-      assert entry.getProperty("addresses") != null;
-      assert entry.getProperty("index") != null;
+      return xml.createResponse(
+          null,
+          xml.writeProperty(acceptType, entry.getProperty("IncreaseValue")),
+          null,
+          acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @GET
+  @Path("/Products({entityId})/Microsoft.Test.OData.Services.ODataWCFService.GetProductDetails({param:.*})")
+  public Response functionGetProductDetails(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept);
+      }
+
+      final Entity entry = new Entity();
+      entry.setType("Microsoft.Test.OData.Services.ODataWCFService.ProductDetail");
+      final Property productId = new Property();
+      productId.setName("ProductID");
+      productId.setType("Edm.Int32");
+      productId.setValue(ValueType.PRIMITIVE, Integer.valueOf(entityId));
+      entry.getProperties().add(productId);
+      final Property productDetailId = new Property();
+      productDetailId.setName("ProductDetailID");
+      productDetailId.setType("Edm.Int32");
+      productDetailId.setValue(ValueType.PRIMITIVE, 2);
+      entry.getProperties().add(productDetailId);
+
+      final Link link = new Link();
+      link.setRel("edit");
+      link.setHref(URI.create(
+          Constants.get(ConstantKey.DEFAULT_SERVICE_URL)
+              + "ProductDetails(ProductID=6,ProductDetailID=1)").toASCIIString());
+      entry.setEditLink(link);
+
+      final EntityCollection feed = new EntityCollection();
+      feed.getEntities().add(entry);
+
+      final ResWrap<EntityCollection> container = new ResWrap<EntityCollection>(
+          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + "ProductDetail"), null,
+          feed);
+
+      return xml.createResponse(
+          null,
+          xml.writeEntitySet(acceptType, container),
+          null,
+          acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Products({entityId})/Microsoft.Test.OData.Services.ODataWCFService.AddAccessRight{paren:[\\(\\)]*}")
+  public Response actionAddAccessRight(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      final String param) {
+
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept);
+      }
+
+      final Accept contentTypeValue = Accept.parse(contentType);
+      final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
+
+      assert 1 == entry.getProperties().size();
+      assert entry.getProperty("accessRight") != null;
+
+      final Property property = entry.getProperty("accessRight");
+      property.setType("Microsoft.Test.OData.Services.ODataWCFService.AccessLevel");
+
+      final ResWrap<Property> result = new ResWrap<Property>(
+          URI.create(Constants.get(ConstantKey.ODATA_METADATA_PREFIX) + property.getType()),
+          null, property);
+
+      return xml.createResponse(
+          null,
+          xml.writeProperty(acceptType, result),
+          null,
+          acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/Customers({personId})/Microsoft.Test.OData.Services.ODataWCFService.ResetAddress{paren:[\\(\\)]*}")
+  public Response actionResetAddress(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("personId") final String personId,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      final String param) {
+
+    try {
+      final Accept contentTypeValue = Accept.parse(contentType);
+      final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
+
+      assert 2 == entry.getProperties().size();
+      assert entry.getProperty("addresses") != null;
+      assert entry.getProperty("index") != null;
+
+      return getEntityInternal(
+          uriInfo.getRequestUri().toASCIIString(), accept, "Customers", personId, format, null, null);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @GET
+  @Path("/ProductDetails(ProductID={productId},ProductDetailID={productDetailId})"
+      + "/Microsoft.Test.OData.Services.ODataWCFService.GetRelatedProduct{paren:[\\(\\)]*}")
+  public Response functionGetRelatedProduct(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("productId") final String productId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    return getEntityInternal(
+        uriInfo.getRequestUri().toASCIIString(), accept, "Products", productId, format, null, null);
+  }
+
+  @POST
+  @Path("/Accounts({entityId})/Microsoft.Test.OData.Services.ODataWCFService.RefreshDefaultPI{paren:[\\(\\)]*}")
+  public Response actionRefreshDefaultPI(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format,
+      final String param) {
+
+    try {
+      final Accept contentTypeValue = Accept.parse(contentType);
+      final Entity entry = xml.readEntity(contentTypeValue, IOUtils.toInputStream(param, Constants.ENCODING));
+
+      assert 1 == entry.getProperties().size();
+      assert entry.getProperty("newDate") != null;
+
+      return functionGetDefaultPI(accept, entityId, format);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @GET
+  @Path("/Accounts({entityId})/Microsoft.Test.OData.Services.ODataWCFService.GetDefaultPI{paren:[\\(\\)]*}")
+  public Response functionGetDefaultPI(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    return getContainedEntity(accept, entityId, "MyPaymentInstruments", entityId + "901", format);
+  }
+
+  @GET
+  @Path("/Accounts({entityId})/Microsoft.Test.OData.Services.ODataWCFService.GetAccountInfo{paren:[\\(\\)]*}")
+  public Response functionGetAccountInfo(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("entityId") final String entityId,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    return getPath(accept, "Accounts", entityId, "AccountInfo", format);
+  }
+
+  @GET
+  @Path("/Accounts({entityId})/MyGiftCard/Microsoft.Test.OData.Services.ODataWCFService.GetActualAmount({param:.*})")
+  public Response functionGetActualAmount(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    try {
+      final Accept acceptType;
+      if (StringUtils.isNotBlank(format)) {
+        acceptType = Accept.valueOf(format.toUpperCase());
+      } else {
+        acceptType = Accept.parse(accept);
+      }
+
+      final Property property = new Property();
+      property.setType("Edm.Double");
+      property.setValue(ValueType.PRIMITIVE, 41.79);
+
+      final ResWrap<Property> container = new ResWrap<Property>((URI) null, null, property);
+
+      return xml.createResponse(
+          null,
+          xml.writeProperty(acceptType, container),
+          null,
+          acceptType);
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  /**
+   * Retrieve entity reference sample.
+   *
+   * @param accept Accept header.
+   * @param path path.
+   * @param format format query option.
+   * @return entity reference or feed of entity reference.
+   */
+  @GET
+  @Path("/{path:.*}/$ref")
+  public Response getEntityReference(
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @PathParam("path") final String path,
+      @QueryParam("$format") @DefaultValue(StringUtils.EMPTY) final String format) {
+
+    try {
+      final Map.Entry<Accept, AbstractUtilities> utils = getUtilities(accept, format);
+
+      if (utils.getKey() == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final String filename = Base64.encodeBase64String(path.getBytes("UTF-8"));
+
+      return utils.getValue().createResponse(
+          FSManager.instance().readFile(Constants.get(ConstantKey.REF)
+              + File.separatorChar + filename, utils.getKey()),
+          null,
+          utils.getKey());
+    } catch (Exception e) {
+      LOG.error("Error retrieving entity", e);
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @POST
+  @Path("/People")
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM })
+  public Response postPeople(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      final String entity) {
+
+    if ("{\"@odata.type\":\"#Microsoft.Test.OData.Services.ODataWCFService.Person\"}".equals(entity)) {
+      return xml.createFaultResponse(accept, new BadRequestException());
+    }
+
+    return postNewEntity(uriInfo, accept, contentType, prefer, "People", entity);
+  }
+
+  private Response patchEntityInternal(final UriInfo uriInfo,
+      final String accept, final String contentType, final String prefer, final String ifMatch,
+      final String entitySetName, final String entityId, final String changes) {
+
+    try {
+      final Accept acceptType = Accept.parse(accept);
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final Map.Entry<String, InputStream> entityInfo = xml.readEntity(entitySetName, entityId, Accept.ATOM);
+
+      final String etag = Commons.getETag(entityInfo.getKey());
+      if (StringUtils.isNotBlank(ifMatch) && !ifMatch.equals(etag)) {
+        throw new ConcurrentModificationException("Concurrent modification");
+      }
+
+      final Accept contentTypeValue = Accept.parse(contentType);
+
+      final Entity entryChanges;
+
+      if (contentTypeValue == Accept.XML || contentTypeValue == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      } else if (contentTypeValue == Accept.ATOM) {
+        entryChanges = atomDeserializer.toEntity(
+            IOUtils.toInputStream(changes, Constants.ENCODING)).getPayload();
+      } else {
+        final ResWrap<Entity> jcont = jsonDeserializer.toEntity(IOUtils.toInputStream(changes, Constants.ENCODING));
+        entryChanges = jcont.getPayload();
+      }
+
+      final ResWrap<Entity> container = atomDeserializer.toEntity(entityInfo.getValue());
+
+      for (Property property : entryChanges.getProperties()) {
+        final Property _property = container.getPayload().getProperty(property.getName());
+        if (_property == null) {
+          container.getPayload().getProperties().add(property);
+        } else {
+          _property.setValue(property.getValueType(), property.getValue());
+        }
+      }
+
+      for (Link link : entryChanges.getNavigationLinks()) {
+        container.getPayload().getNavigationLinks().add(link);
+      }
+
+      final ByteArrayOutputStream content = new ByteArrayOutputStream();
+      final OutputStreamWriter writer = new OutputStreamWriter(content, Constants.ENCODING);
+      atomSerializer.write(writer, container);
+      writer.flush();
+      writer.close();
+
+      final InputStream res = xml.addOrReplaceEntity(
+          entityId, entitySetName, new ByteArrayInputStream(content.toByteArray()), container.getPayload());
+
+      final ResWrap<Entity> cres = atomDeserializer.toEntity(res);
+
+      normalizeAtomEntry(cres.getPayload(), entitySetName, entityId);
+
+      final String path = Commons.getEntityBasePath(entitySetName, entityId);
+      FSManager.instance().putInMemory(
+          cres, path + File.separatorChar + Constants.get(ConstantKey.ENTITY));
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            xml.readEntity(entitySetName, entityId, acceptType).getValue(),
+            null, acceptType, Response.Status.OK);
+      } else {
+        res.close();
+        response = xml.createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            null,
+            null,
+            acceptType, Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object> singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @PATCH
+  @Path("/{entitySetName}({entityId})")
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  public Response patchEntity(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      @HeaderParam("If-Match") @DefaultValue(StringUtils.EMPTY) final String ifMatch,
+      @PathParam("entitySetName") final String entitySetName,
+      @PathParam("entityId") final String entityId,
+      final String changes) {
+
+    final Response response =
+        getEntityInternal(uriInfo.getRequestUri().toASCIIString(),
+            accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY);
+    return response.getStatus() >= 400 ?
+        postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, changes) :
+        patchEntityInternal(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes);
+  }
+
+  private Response replaceEntity(final UriInfo uriInfo,
+      final String accept, final String prefer,
+      final String entitySetName, final String entityId, final String entity) {
+
+    try {
+      final Accept acceptType = Accept.parse(accept);
+
+      if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+        throw new UnsupportedMediaTypeException("Unsupported media type");
+      }
+
+      final InputStream res = getUtilities(acceptType).addOrReplaceEntity(entityId, entitySetName,
+          IOUtils.toInputStream(entity, Constants.ENCODING),
+          xml.readEntity(acceptType, IOUtils.toInputStream(entity, Constants.ENCODING)));
+
+      final ResWrap<Entity> cres;
+      if (acceptType == Accept.ATOM) {
+        cres = atomDeserializer.toEntity(res);
+      } else {
+        cres = jsonDeserializer.toEntity(res);
+      }
+
+      final String path = Commons.getEntityBasePath(entitySetName, entityId);
+      FSManager.instance().putInMemory(
+          cres, path + File.separatorChar + Constants.get(ConstantKey.ENTITY));
+
+      final Response response;
+      if ("return-content".equalsIgnoreCase(prefer)) {
+        response = xml.createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            xml.readEntity(entitySetName, entityId, acceptType).getValue(),
+            null,
+            acceptType,
+            Response.Status.OK);
+      } else {
+        res.close();
+        response = xml.createResponse(
+            uriInfo.getRequestUri().toASCIIString(),
+            null,
+            null,
+            acceptType,
+            Response.Status.NO_CONTENT);
+      }
+
+      if (StringUtils.isNotBlank(prefer)) {
+        response.getHeaders().put("Preference-Applied", Collections.<Object> singletonList(prefer));
+      }
+
+      return response;
+    } catch (Exception e) {
+      return xml.createFaultResponse(accept, e);
+    }
+  }
+
+  @PUT
+  @Path("/{entitySetName}({entityId})")
+  @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  @Consumes({ MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON })
+  public Response replaceEntity(
+      @Context final UriInfo uriInfo,
+      @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept,
+      @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType,
+      @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer,
+      @PathParam("entitySetName") final String entitySetName,
+      @PathParam("entityId") final String entityId,
+      final String entity) {
+
+    try {
+      getEntityInternal(uriInfo.getRequestUri().toASCIIString(),
+          accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY);
+      return replaceEntity(uriInfo, accept, prefer, entitySetName, entityId, entity);
+    } catch (NotFoundException e) {
+      return postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, entityId);
+    }
+  }
+
+  private StringBuilder containedPath(final String entityId, final String containedEntitySetName) {
+    return new StringBuilder("Accounts").append(File.separatorChar).
+        append(entityId).append(File.separatorChar).
+        appen

<TRUNCATED>