You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by fm...@apache.org on 2014/03/09 11:37:55 UTC

git commit: OLINGO-175 provided patch update and some refactorings

Repository: incubator-olingo-odata4
Updated Branches:
  refs/heads/master 61b7b913a -> a9f74a87e


OLINGO-175 provided patch update and some refactorings


Project: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/commit/a9f74a87
Tree: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/tree/a9f74a87
Diff: http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/diff/a9f74a87

Branch: refs/heads/master
Commit: a9f74a87e44c4a0a61ecae7e3a774a440359215d
Parents: 61b7b91
Author: fmartelli <fa...@gmail.com>
Authored: Sun Mar 9 11:37:41 2014 +0100
Committer: fmartelli <fa...@gmail.com>
Committed: Sun Mar 9 11:37:41 2014 +0100

----------------------------------------------------------------------
 .../engine/it/EntityCreateTestITCase.java       |  18 +-
 .../engine/it/EntityUpdateTestITCase.java       |   4 +-
 .../testservice/AbstractServices.java           | 116 ++++++++-----
 .../odatajclient/testservice/methods/MERGE.java |  31 ++++
 .../odatajclient/testservice/methods/PATCH.java |  32 ++++
 .../testservice/utils/AbstractUtilities.java    |  52 ++++--
 .../odatajclient/testservice/utils/Commons.java |   8 +-
 .../testservice/utils/Constants.java            |   2 +
 .../testservice/utils/JSONUtilities.java        |  42 ++++-
 .../testservice/utils/LinkInfo.java             |  54 ++++++
 .../testservice/utils/MetadataLinkInfo.java     |  97 +++++++++++
 .../utils/XMLEventReaderWrapper.java            | 144 +++++++++++++++
 .../testservice/utils/XMLUtilities.java         | 174 ++++++++++++++++++-
 .../testservice/utils/XmlElement.java           | 110 +-----------
 .../resources/v3/Product/-10/entity.full.json   |  40 ++++-
 .../main/resources/v3/Product/-10/entity.xml    |   4 +-
 .../src/main/resources/v3/Product/-10/etag.txt  |   2 +-
 17 files changed, 726 insertions(+), 204 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityCreateTestITCase.java
----------------------------------------------------------------------
diff --git a/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityCreateTestITCase.java b/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityCreateTestITCase.java
index d5edfcd..ce05ae1 100644
--- a/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityCreateTestITCase.java
+++ b/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityCreateTestITCase.java
@@ -204,7 +204,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
     public void createWithBackNavigationAsAtom() {
         final ODataPubFormat format = ODataPubFormat.ATOM;
         final ODataEntity actual = createWithBackNavigationLink(format, 9);
-        cleanAfterCreate(format, actual, true, getOldServiceRoot());
+        cleanAfterCreate(format, actual, true, getServiceRoot());
     }
 
     @Test
@@ -212,7 +212,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
         // this needs to be full, otherwise there is no mean to recognize links
         final ODataPubFormat format = ODataPubFormat.JSON_FULL_METADATA;
         final ODataEntity actual = createWithBackNavigationLink(format, 10);
-        cleanAfterCreate(format, actual, true, getOldServiceRoot());
+        cleanAfterCreate(format, actual, true, getServiceRoot());
     }
 
     @Test
@@ -379,7 +379,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
         final String sampleName = "Sample customer";
 
         ODataEntity customer = getSampleCustomerProfile(id, sampleName, false);
-        customer = createEntity(getOldServiceRoot(), format, customer, "Customer");
+        customer = createEntity(getServiceRoot(), format, customer, "Customer");
 
         ODataEntity order = client.getObjectFactory().newEntity(
                 "Microsoft.Test.OData.Services.AstoriaDefaultService.Order");
@@ -389,19 +389,19 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
                 client.getPrimitiveValueBuilder().setValue(id).setType(EdmSimpleType.Int32).build()));
 
         order.addLink(client.getObjectFactory().newEntityNavigationLink(
-                "Customer", URIUtils.getURI(getOldServiceRoot(), customer.getEditLink().toASCIIString())));
+                "Customer", URIUtils.getURI(getServiceRoot(), customer.getEditLink().toASCIIString())));
 
-        order = createEntity(getOldServiceRoot(), format, order, "Order");
+        order = createEntity(getServiceRoot(), format, order, "Order");
 
         ODataEntity changes = client.getObjectFactory().newEntity(
                 "Microsoft.Test.OData.Services.AstoriaDefaultService.Customer");
         changes.setEditLink(customer.getEditLink());
         changes.addLink(client.getObjectFactory().newFeedNavigationLink(
-                "Orders", URIUtils.getURI(getOldServiceRoot(), order.getEditLink().toASCIIString())));
+                "Orders", URIUtils.getURI(getServiceRoot(), order.getEditLink().toASCIIString())));
         update(UpdateType.PATCH, changes, format, null);
 
         final ODataEntityRequest customerreq = client.getRetrieveRequestFactory().getEntityRequest(
-                URIUtils.getURI(getOldServiceRoot(), order.getEditLink().toASCIIString() + "/Customer"));
+                URIUtils.getURI(getServiceRoot(), order.getEditLink().toASCIIString() + "/Customer"));
         customerreq.setFormat(format);
 
         customer = customerreq.execute().getBody();
@@ -410,7 +410,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
                 Integer.valueOf(id), customer.getProperty("CustomerId").getPrimitiveValue().<Integer>toCastValue());
 
         final ODataEntitySetRequest orderreq = client.getRetrieveRequestFactory().getEntitySetRequest(
-                URIUtils.getURI(getOldServiceRoot(), customer.getEditLink().toASCIIString() + "/Orders"));
+                URIUtils.getURI(getServiceRoot(), customer.getEditLink().toASCIIString() + "/Orders"));
         orderreq.setFormat(format);
 
         final ODataRetrieveResponse<ODataEntitySet> orderres = orderreq.execute();
@@ -421,7 +421,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
                 <Integer>toCastValue());
 
         final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(
-                URIUtils.getURI(getOldServiceRoot(), customer.getEditLink().toASCIIString() + "?$expand=Orders"));
+                URIUtils.getURI(getServiceRoot(), customer.getEditLink().toASCIIString() + "?$expand=Orders"));
         req.setFormat(format);
 
         customer = req.execute().getBody();

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityUpdateTestITCase.java
----------------------------------------------------------------------
diff --git a/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityUpdateTestITCase.java b/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityUpdateTestITCase.java
index 1bfd800..7984dd9 100644
--- a/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityUpdateTestITCase.java
+++ b/ODataJClient/engine/src/test/java/com/msopentech/odatajclient/engine/it/EntityUpdateTestITCase.java
@@ -74,7 +74,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
     @Test
     public void patchAsAtom() {
         final ODataPubFormat format = ODataPubFormat.ATOM;
-        final URI uri = client.getURIBuilder(getServiceRoot()).
+        final URI uri = client.getURIBuilder(getStaticServiceRoot()).
                 appendEntityTypeSegment("Product").appendKeySegment(-10).build();
         final String etag = getETag(uri);
         final ODataEntity patch = client.getObjectFactory().newEntity(TEST_PRODUCT_TYPE);
@@ -85,7 +85,7 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
     @Test
     public void patchAsJSON() {
         final ODataPubFormat format = ODataPubFormat.JSON_FULL_METADATA;
-        final URI uri = client.getURIBuilder(getServiceRoot()).
+        final URI uri = client.getURIBuilder(getStaticServiceRoot()).
                 appendEntityTypeSegment("Product").appendKeySegment(-10).build();
         final String etag = getETag(uri);
         final ODataEntity patch = client.getObjectFactory().newEntity(TEST_PRODUCT_TYPE);

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/AbstractServices.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/AbstractServices.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/AbstractServices.java
index 72140a9..726ccc8 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/AbstractServices.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/AbstractServices.java
@@ -19,7 +19,6 @@
 package com.msopentech.odatajclient.testservice;
 
 import com.msopentech.odatajclient.testservice.utils.Accept;
-import com.msopentech.odatajclient.testservice.utils.Commons;
 import com.msopentech.odatajclient.testservice.utils.XMLUtilities;
 import com.msopentech.odatajclient.testservice.utils.JSONUtilities;
 import com.msopentech.odatajclient.testservice.utils.ODataVersion;
@@ -27,6 +26,10 @@ import com.msopentech.odatajclient.testservice.utils.FSManager;
 
 import static com.msopentech.odatajclient.testservice.utils.Constants.*;
 
+import com.msopentech.odatajclient.testservice.methods.PATCH;
+import com.msopentech.odatajclient.testservice.utils.AbstractUtilities;
+import com.msopentech.odatajclient.testservice.utils.Commons;
+import com.msopentech.odatajclient.testservice.utils.LinkInfo;
 import java.io.File;
 import java.io.InputStream;
 import java.util.Collections;
@@ -60,12 +63,12 @@ public abstract class AbstractServices {
 
     protected abstract ODataVersion getVersion();
 
-    protected final XMLUtilities atom;
+    protected final XMLUtilities xml;
 
     protected final JSONUtilities json;
 
     public AbstractServices() throws Exception {
-        this.atom = new XMLUtilities(getVersion());
+        this.xml = new XMLUtilities(getVersion());
         this.json = new JSONUtilities(getVersion());
     }
 
@@ -84,10 +87,10 @@ public abstract class AbstractServices {
                 throw new UnsupportedMediaTypeException("Unsupported media type");
             }
 
-            return atom.createResponse(
+            return xml.createResponse(
                     FSManager.instance(getVersion()).readFile(SERVICES, acceptType), null, acceptType);
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -117,10 +120,36 @@ public abstract class AbstractServices {
 
     private Response getMetadata(final String filename) {
         try {
-            return atom.
+            return xml.
                     createResponse(FSManager.instance(getVersion()).readFile(filename, Accept.XML), null, Accept.XML);
         } catch (Exception e) {
-            return atom.createFaultResponse(Accept.XML.toString(), e);
+            return xml.createFaultResponse(Accept.XML.toString(), 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(
+            @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) String accept,
+            @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
+            @PathParam("entitySetName") String entitySetName,
+            @PathParam("entityId") String entityId,
+            final String changes) {
+
+        try {
+            final Accept acceptType = Accept.parse(accept, getVersion());
+
+            if (acceptType == Accept.XML || acceptType == Accept.TEXT) {
+                throw new UnsupportedMediaTypeException("Unsupported media type");
+            }
+
+            final AbstractUtilities util = acceptType == Accept.ATOM ? xml : json;
+            InputStream res = util.patchEntity(entitySetName, entityId, IOUtils.toInputStream(changes), acceptType);
+            return xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+        } catch (Exception e) {
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -144,19 +173,18 @@ public abstract class AbstractServices {
 
             final InputStream res;
             if (acceptType == Accept.ATOM) {
-                res = atom.saveSingleEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
+                res = xml.saveSingleEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
             } else {
                 res = json.saveSingleEntity(entityId, entitySetName, IOUtils.toInputStream(entity));
             }
 
             res.close();
 
-            final Response response = atom.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+            final Response response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
             response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
             return response;
         } catch (Exception e) {
-            e.printStackTrace();
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -180,21 +208,21 @@ public abstract class AbstractServices {
 
             final InputStream res;
             if (acceptType == Accept.ATOM) {
-                res = atom.createEntity(entitySetName, IOUtils.toInputStream(entity));
+                res = xml.createEntity(entitySetName, IOUtils.toInputStream(entity));
             } else {
                 res = json.createEntity(entitySetName, IOUtils.toInputStream(entity));
             }
 
             if (prefer.equalsIgnoreCase("return-no-content")) {
                 res.close();
-                Response response = atom.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
+                Response response = xml.createResponse(null, null, acceptType, Response.Status.NO_CONTENT);
                 response.getHeaders().put("Preference-Applied", Collections.<Object>singletonList(prefer));
                 return response;
             } else {
-                return atom.createResponse(res, null, acceptType, Response.Status.CREATED);
+                return xml.createResponse(res, null, acceptType, Response.Status.CREATED);
             }
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -236,7 +264,7 @@ public abstract class AbstractServices {
             try {
                 // search for function ...
                 final InputStream func = FSManager.instance(getVersion()).readFile(name, acceptType);
-                return atom.createResponse(func, null, acceptType);
+                return xml.createResponse(func, null, acceptType);
             } catch (NotFoundException e) {
                 // search for entitySet ...
                 final String basePath = name + File.separatorChar;
@@ -258,10 +286,10 @@ public abstract class AbstractServices {
 
                 InputStream feed = FSManager.instance(getVersion()).readFile(builder.toString(), acceptType);
                 if ("allpages".equals(inlinecount)) {
-                    int count = atom.countAllElements(name);
+                    int count = xml.countAllElements(name);
                     feed.close();
                     if (acceptType == Accept.ATOM) {
-                        feed = atom.addAtomInlinecount(
+                        feed = xml.addAtomInlinecount(
                                 FSManager.instance(getVersion()).readFile(builder.toString(), acceptType),
                                 count,
                                 acceptType);
@@ -273,10 +301,10 @@ public abstract class AbstractServices {
                     }
                 }
 
-                return atom.createResponse(feed, Commons.getETag(basePath, getVersion()), acceptType);
+                return xml.createResponse(feed, Commons.getETag(basePath, getVersion()), acceptType);
             }
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -309,13 +337,13 @@ public abstract class AbstractServices {
                 acceptType = Accept.parse(accept, getVersion());
             }
 
-            final Map.Entry<String, InputStream> entityInfo = atom.readEntity(entitySetName, entityId, acceptType);
+            final Map.Entry<String, InputStream> entityInfo = xml.readEntity(entitySetName, entityId, acceptType);
 
             InputStream entity = entityInfo.getValue();
 
             if (StringUtils.isNotBlank(select)) {
                 if (acceptType == Accept.ATOM) {
-                    entity = atom.selectEntity(entity, select.split(","));
+                    entity = xml.selectEntity(entity, select.split(","));
                 } else {
                     entity = json.selectEntity(entity, select.split(","));
                 }
@@ -326,7 +354,7 @@ public abstract class AbstractServices {
                     throw new UnsupportedMediaTypeException("Unsupported media type");
                 } else if (acceptType == Accept.ATOM) {
                     for (String exp : expand.split(",")) {
-                        entity = atom.expandEntity(
+                        entity = xml.expandEntity(
                                 entitySetName,
                                 entityId,
                                 entity,
@@ -343,10 +371,10 @@ public abstract class AbstractServices {
                 }
             }
 
-            return atom.createResponse(entity, Commons.getETag(entityInfo.getKey(), getVersion()), acceptType);
+            return xml.createResponse(entity, Commons.getETag(entityInfo.getKey(), getVersion()), acceptType);
         } catch (Exception e) {
             LOG.error("Error retrieving entity", e);
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -362,9 +390,9 @@ public abstract class AbstractServices {
 
             FSManager.instance(getVersion()).deleteFile(basePath + ENTITY);
 
-            return atom.createResponse(null, null, null, Response.Status.NO_CONTENT);
+            return xml.createResponse(null, null, null, Response.Status.NO_CONTENT);
         } catch (Exception e) {
-            return atom.createFaultResponse(Accept.XML.toString(), e);
+            return xml.createFaultResponse(Accept.XML.toString(), e);
         }
     }
 
@@ -402,36 +430,36 @@ public abstract class AbstractServices {
             InputStream stream;
 
             try {
-                final Map.Entry<String, List<String>> linkInfo = XMLUtilities.extractLinkURIs(
-                        atom.readLinks(entitySetName, entityId, path, Accept.XML).getValue());
+                final LinkInfo linkInfo = xml.readLinks(entitySetName, entityId, path, Accept.XML);
+                final Map.Entry<String, List<String>> links = XMLUtilities.extractLinkURIs(linkInfo.getLinks());
 
                 switch (acceptType) {
                     case JSON:
                     case JSON_FULLMETA:
                     case JSON_NOMETA:
-                        stream = json.readEntities(linkInfo.getValue(), path, linkInfo.getKey());
+                        stream = json.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed());
                         stream = json.wrapJsonEntities(stream);
                         break;
                     default:
-                        stream = atom.readEntities(linkInfo.getValue(), path, linkInfo.getKey());
+                        stream = xml.readEntities(links.getValue(), path, links.getKey(), linkInfo.isFeed());
                 }
             } catch (NotFoundException e) {
                 // if the given path is not about any link then search for property
                 LOG.info("Retrieve property {}", path);
 
                 stream = FSManager.instance(getVersion()).readFile(
-                        basePath + ENTITY, acceptType == null || acceptType == Accept.ATOM.TEXT
+                        basePath + ENTITY, acceptType == null || acceptType == Accept.TEXT
                         ? Accept.XML : acceptType);
 
                 if (searchForValue) {
-                    stream = atom.getAtomPropertyValue(stream, path.split("/"));
+                    stream = xml.getAtomPropertyValue(stream, path.split("/"));
                 } else {
                     if (acceptType == null || acceptType == Accept.XML || acceptType == Accept.ATOM) {
                         // retrieve xml
-                        stream = atom.getAtomProperty(stream, path.split("/"));
+                        stream = xml.getAtomProperty(stream, path.split("/"));
                     } else {
                         // retrieve Edm type from xml
-                        final String edmType = atom.getEdmTypeFromXML(
+                        final String edmType = xml.getEdmTypeFromXML(
                                 FSManager.instance(getVersion()).readFile(basePath + ENTITY, Accept.XML),
                                 path.split("/"));
                         // retrieve json property
@@ -444,9 +472,9 @@ public abstract class AbstractServices {
                 }
             }
 
-            return atom.createResponse(stream, Commons.getETag(basePath, getVersion()), acceptType);
+            return xml.createResponse(stream, Commons.getETag(basePath, getVersion()), acceptType);
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -480,14 +508,14 @@ public abstract class AbstractServices {
                 throw new UnsupportedMediaTypeException("Unsupported media type");
             }
 
-            final Map.Entry<String, InputStream> links = atom.readLinks(entitySetName, entityId, linkName, acceptType);
+            final LinkInfo links = xml.readLinks(entitySetName, entityId, linkName, acceptType);
 
-            return atom.createResponse(
-                    links.getValue(),
-                    links.getKey(),
+            return xml.createResponse(
+                    links.getLinks(),
+                    links.getEtag(),
                     acceptType);
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 
@@ -510,14 +538,14 @@ public abstract class AbstractServices {
                 throw new UnsupportedMediaTypeException("Unsupported type " + accept);
             }
 
-            int count = atom.countAllElements(entitySetName);
+            int count = xml.countAllElements(entitySetName);
 
             final Response.ResponseBuilder builder = Response.ok();
             builder.entity(count);
 
             return builder.build();
         } catch (Exception e) {
-            return atom.createFaultResponse(accept, e);
+            return xml.createFaultResponse(accept, e);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/MERGE.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/MERGE.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/MERGE.java
new file mode 100644
index 0000000..bdcd217
--- /dev/null
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/MERGE.java
@@ -0,0 +1,31 @@
+/**
+ * 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 com.msopentech.odatajclient.testservice.methods;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("MERGE")
+public @interface MERGE {
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/PATCH.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/PATCH.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/PATCH.java
new file mode 100644
index 0000000..6ebd38f
--- /dev/null
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/methods/PATCH.java
@@ -0,0 +1,32 @@
+/**
+ * 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 com.msopentech.odatajclient.testservice.methods;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import javax.ws.rs.HttpMethod;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("PATCH")
+public @interface PATCH {
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/AbstractUtilities.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/AbstractUtilities.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/AbstractUtilities.java
index 1fb30a8..3be3f02 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/AbstractUtilities.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/AbstractUtilities.java
@@ -34,7 +34,6 @@ import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumSet;
-import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -69,21 +68,20 @@ public abstract class AbstractUtilities {
     }
 
     private void initialize() throws Exception {
+
         if (!initialized.contains(version)) {
+            final MetadataLinkInfo metadataLinkInfo = new MetadataLinkInfo();
+            Commons.linkInfo.put(version, metadataLinkInfo);
+
             final InputStream metadata = fsManager.readFile(Constants.METADATA, Accept.XML);
             final XMLEventReader reader = XMLUtilities.getEventReader(metadata);
 
-            final Map<String, List<String>> entityLinksPerVersion = new HashMap<String, List<String>>();
-            Commons.entityLinks.put(version, entityLinksPerVersion);
-
             int initialDepth = 0;
             try {
                 while (true) {
                     Map.Entry<Integer, XmlElement> entityType =
                             XMLUtilities.getAtomElement(reader, null, "EntityType", null, initialDepth, 4, 4, false);
                     initialDepth = entityType.getKey();
-                    entityLinksPerVersion.put(entityType.getValue().getStart().
-                            getAttributeByName(new QName("Name")).getValue(), new ArrayList<String>());
 
                     final XMLEventReader entityReader = XMLUtilities.getEventReader(entityType.getValue().toStream());
                     try {
@@ -93,9 +91,10 @@ public abstract class AbstractUtilities {
                                     entityReader, null, "NavigationProperty", null, pos, 2, 2, false);
                             pos = navProperty.getKey();
 
-                            final List<String> links = entityLinksPerVersion.get(entityType.getValue().getStart().
-                                    getAttributeByName(new QName("Name")).getValue());
-                            links.add(navProperty.getValue().
+                            metadataLinkInfo.addLinkName(
+                                    entityType.getValue().getStart().
+                                    getAttributeByName(new QName("Name")).getValue(),
+                                    navProperty.getValue().
                                     getStart().getAttributeByName(new QName("Name")).getValue());
                         }
                     } catch (Exception e) {
@@ -470,7 +469,7 @@ public abstract class AbstractUtilities {
      * @param accept accept header.
      * @return a pair of ETag/links stream
      */
-    public Map.Entry<String, InputStream> readLinks(
+    public LinkInfo readLinks(
             final String entitySetName, final String entityId, final String linkName, final Accept accept)
             throws Exception {
 
@@ -478,8 +477,11 @@ public abstract class AbstractUtilities {
                 entitySetName + File.separatorChar + Commons.getEntityKey(entityId) + File.separatorChar
                 + LINKS_FILE_PATH + File.separatorChar;
 
-        return new SimpleEntry<String, InputStream>(
-                Commons.getETag(basePath, version), fsManager.readFile(basePath + linkName, accept));
+        final LinkInfo linkInfo = new LinkInfo(fsManager.readFile(basePath + linkName, accept));
+        linkInfo.setEtag(Commons.getETag(basePath, version));
+        linkInfo.setFeed(Commons.feed.contains(entitySetName + "." + linkName));
+
+        return linkInfo;
     }
 
     public Map.Entry<String, InputStream> readEntity(
@@ -503,14 +505,18 @@ public abstract class AbstractUtilities {
         // --------------------------------
         // 0. Retrieve all 'linkName' navigation link uris (NotFoundException if missing) 
         // --------------------------------
-        final Map.Entry<String, List<String>> linkInfo =
-                XMLUtilities.extractLinkURIs(readLinks(entitySetName, entityId, linkName, Accept.XML).getValue());
+        final LinkInfo linkInfo = readLinks(entitySetName, entityId, linkName, Accept.XML);
+        final Map.Entry<String, List<String>> links = XMLUtilities.extractLinkURIs(linkInfo.getLinks());
         // --------------------------------
 
         // --------------------------------
         // 1. Retrieve expanded object (entry or feed)
         // --------------------------------
-        final InputStream expanded = readEntities(linkInfo.getValue(), linkName, linkInfo.getKey());
+        final InputStream expanded = readEntities(
+                links.getValue(),
+                linkName,
+                links.getKey(),
+                linkInfo.isFeed());
         // --------------------------------
 
         // --------------------------------
@@ -520,8 +526,17 @@ public abstract class AbstractUtilities {
         // --------------------------------
     }
 
+    public InputStream patchEntity(
+            final String entitySetName, final String entityId, final InputStream changes, final Accept accept)
+            throws Exception {
+        final Map.Entry<String, InputStream> entityInfo = readEntity(entitySetName, entityId, accept);
+        final Map<String, InputStream> replacement = getChanges(changes);
+        return createEntity(entityId, entitySetName, setChanges(entityInfo.getValue(), replacement));
+    }
+
     public abstract InputStream readEntities(
-            final List<String> links, final String linkName, final String next) throws Exception;
+            final List<String> links, final String linkName, final String next, final boolean forceFeed)
+            throws Exception;
 
     protected abstract InputStream replaceLink(
             final InputStream toBeChanged, final String linkName, final InputStream replacement) throws Exception;
@@ -529,4 +544,9 @@ public abstract class AbstractUtilities {
     public abstract InputStream selectEntity(final InputStream entity, final String[] propertyNames) throws Exception;
 
     protected abstract Accept getDefaultFormat();
+
+    protected abstract Map<String, InputStream> getChanges(final InputStream src) throws Exception;
+
+    protected abstract InputStream setChanges(
+            final InputStream toBeChanged, final Map<String, InputStream> properties) throws Exception;
 }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Commons.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Commons.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Commons.java
index 450fdb9..d3badbc 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Commons.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Commons.java
@@ -56,10 +56,12 @@ public abstract class Commons {
 
     protected final static Set<String> mediaContent = new HashSet<String>();
 
+    protected final static Set<String> feed = new HashSet<String>();
+
     protected final static Map<String, String> entitySetAlias = new HashMap<String, String>();
 
-    protected final static Map<ODataVersion, Map<String, List<String>>> entityLinks =
-            new EnumMap<ODataVersion, Map<String, List<String>>>(ODataVersion.class);
+    protected final static Map<ODataVersion, MetadataLinkInfo> linkInfo =
+            new EnumMap<ODataVersion, MetadataLinkInfo>(ODataVersion.class);
 
     static {
         sequence.put("Customer", 1000);
@@ -71,6 +73,8 @@ public abstract class Commons {
 
         entitySetAlias.put("Customer.Info", "CustomerInfo");
         entitySetAlias.put("Customer.Orders", "Order");
+        feed.add("Customer.Orders");
+        feed.add("Customer.Logins");
     }
 
     public static String getEntityKey(final String entityId) {

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Constants.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Constants.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Constants.java
index b523282..893158f 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Constants.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/Constants.java
@@ -40,6 +40,8 @@ public class Constants {
 
     public final static String INLINE = ATOM_METADATA_PREFIX + INLINE_LOCAL;
 
+    public final static String CONTENT = "content";
+
     public final static String PROPERTIES = ATOM_METADATA_PREFIX + "properties";
 
     public final static String LINK = "link";

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/JSONUtilities.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/JSONUtilities.java
index e445ef4..9162067 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/JSONUtilities.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/JSONUtilities.java
@@ -30,6 +30,7 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -63,7 +64,6 @@ public class JSONUtilities extends AbstractUtilities {
         final NavigationLinks links = new NavigationLinks();
 
         final Iterator<Map.Entry<String, JsonNode>> fieldIter = srcNode.fields();
-        final List<String> linksFromMeta = Commons.entityLinks.get(version).get(entitySetName);
 
         while (fieldIter.hasNext()) {
             final Map.Entry<String, JsonNode> field = fieldIter.next();
@@ -81,7 +81,7 @@ public class JSONUtilities extends AbstractUtilities {
                 }
 
                 links.addLinks(title, hrefs);
-            } else if (linksFromMeta.contains(field.getKey())) {
+            } else if (Commons.linkInfo.get(version).exists(entitySetName, field.getKey())) {
                 links.addInlines(field.getKey(), IOUtils.toInputStream(field.getValue().toString()));
             }
         }
@@ -252,7 +252,8 @@ public class JSONUtilities extends AbstractUtilities {
     }
 
     @Override
-    public InputStream readEntities(final List<String> links, final String linkName, final String next)
+    public InputStream readEntities(
+            final List<String> links, final String linkName, final String next, final boolean forceFeed)
             throws Exception {
 
         if (links.isEmpty()) {
@@ -264,7 +265,7 @@ public class JSONUtilities extends AbstractUtilities {
 
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
-        if (links.size() > 1) {
+        if (forceFeed || links.size() > 1) {
             bos.write("[".getBytes());
         }
 
@@ -280,7 +281,7 @@ public class JSONUtilities extends AbstractUtilities {
             IOUtils.copy(entity.getValue(), bos);
         }
 
-        if (links.size() > 1) {
+        if (forceFeed || links.size() > 1) {
             bos.write("]".getBytes());
         }
 
@@ -311,4 +312,35 @@ public class JSONUtilities extends AbstractUtilities {
 
         return IOUtils.toInputStream(toBeChangedNode.toString());
     }
+
+    @Override
+    protected Map<String, InputStream> getChanges(final InputStream src) throws Exception {
+        final Map<String, InputStream> res = new HashMap<String, InputStream>();
+
+        final ObjectMapper mapper = new ObjectMapper();
+        final JsonNode srcObject = mapper.readTree(src);
+
+        final Iterator<Map.Entry<String, JsonNode>> fields = srcObject.fields();
+        while (fields.hasNext()) {
+            final Map.Entry<String, JsonNode> field = fields.next();
+            res.put(field.getKey(), IOUtils.toInputStream(field.getValue().toString()));
+        }
+
+        return res;
+    }
+
+    @Override
+    protected InputStream setChanges(
+            final InputStream toBeChanged, final Map<String, InputStream> properties) throws Exception {
+
+        final ObjectMapper mapper = new ObjectMapper();
+        final ObjectNode toBeChangedObject = (ObjectNode) mapper.readTree(toBeChanged);
+
+        for (Map.Entry<String, InputStream> property : properties.entrySet()) {
+            final JsonNode propertyNode = mapper.readTree(property.getValue());
+            toBeChangedObject.set(property.getKey(), propertyNode);
+        }
+
+        return IOUtils.toInputStream(toBeChangedObject.toString());
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/LinkInfo.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/LinkInfo.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/LinkInfo.java
new file mode 100644
index 0000000..39ea066
--- /dev/null
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/LinkInfo.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 com.msopentech.odatajclient.testservice.utils;
+
+import java.io.InputStream;
+
+public class LinkInfo {
+
+    private final InputStream links;
+
+    private boolean feed = false;
+
+    private String etag;
+
+    public LinkInfo(final InputStream links) {
+        this.links = links;
+    }
+
+    public boolean isFeed() {
+        return feed;
+    }
+
+    public void setFeed(boolean feed) {
+        this.feed = feed;
+    }
+
+    public String getEtag() {
+        return etag;
+    }
+
+    public void setEtag(final String etag) {
+        this.etag = etag;
+    }
+
+    public InputStream getLinks() {
+        return links;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/MetadataLinkInfo.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/MetadataLinkInfo.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/MetadataLinkInfo.java
new file mode 100644
index 0000000..794d480
--- /dev/null
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/MetadataLinkInfo.java
@@ -0,0 +1,97 @@
+/**
+ * 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 com.msopentech.odatajclient.testservice.utils;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class MetadataLinkInfo {
+
+    protected final static Set<String> feed = new HashSet<String>();
+
+    protected final static Map<String, String> entitySetAlias = new HashMap<String, String>();
+
+    private Map<String, EntitySet> entitySets = new HashMap<String, EntitySet>();
+
+    public void addLinkName(final String entitySetName, final String linkName) {
+        final EntitySet entitySet;
+        if (entitySets.containsKey(entitySetName)) {
+            entitySet = entitySets.get(entitySetName);
+        } else {
+            entitySet = new EntitySet(entitySetName);
+            entitySets.put(entitySetName, entitySet);
+        }
+
+        entitySet.add(linkName);
+    }
+
+    public Collection<EntitySet> getEntitySets() {
+        return entitySets.values();
+    }
+
+    public Set<String> getNavigationLinkNames(final String entitySetName) {
+        return entitySets.containsKey(entitySetName)
+                ? entitySets.get(entitySetName).getLinks() : Collections.<String>emptySet();
+    }
+
+    public boolean exists(final String entitySetName, final String linkName) {
+        return getNavigationLinkNames(entitySetName).contains(linkName);
+    }
+
+    private static class EntitySet {
+
+        private String name;
+
+        private Set<String> links;
+
+        public EntitySet(final String name) {
+            this.name = name;
+            links = new HashSet<String>();
+        }
+
+        public void add(final String linkName) {
+            links.add(linkName);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(final String name) {
+            this.name = name;
+        }
+
+        public Set<String> getLinks() {
+            return links;
+        }
+
+        public void setLinks(final Set<String> links) {
+            this.links = links;
+        }
+
+        @Override
+        public String toString() {
+            return name + ": " + links;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLEventReaderWrapper.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLEventReaderWrapper.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLEventReaderWrapper.java
new file mode 100644
index 0000000..efd2b07
--- /dev/null
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLEventReaderWrapper.java
@@ -0,0 +1,144 @@
+/**
+ * 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 com.msopentech.odatajclient.testservice.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.XMLEvent;
+import org.apache.commons.io.IOUtils;
+
+public class XMLEventReaderWrapper implements XMLEventReader {
+
+    public final static String CONTENT = "CONTENT_TAG";
+
+    public final static String CONTENT_STAG = "<" + CONTENT + ">";
+
+    public final static String CONTENT_ETAG = "</" + CONTENT + ">";
+
+    private final XMLEventReader wrapped;
+
+    private XMLEvent nextGivenEvent = null;
+
+    public XMLEventReaderWrapper(final InputStream stream) throws Exception {
+        final XMLInputFactory factory = XMLInputFactory.newInstance();
+        factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
+        factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+
+
+        this.wrapped = factory.createXMLEventReader(
+                new ByteArrayInputStream(
+                (XMLEventReaderWrapper.CONTENT_STAG
+                + IOUtils.toString(stream)
+                + XMLEventReaderWrapper.CONTENT_ETAG).getBytes()));
+
+        init(wrapped);
+    }
+
+    public XMLEventReaderWrapper(final XMLEventReader wrapped) {
+        this.wrapped = wrapped;
+        init(wrapped);
+    }
+
+    private void init(final XMLEventReader wrapped) {
+
+        try {
+            this.nextGivenEvent = this.wrapped.nextEvent();
+
+            if (this.nextGivenEvent.isStartDocument()) {
+                this.nextGivenEvent = this.wrapped.nextEvent(); // discard start document    
+            }
+
+            if (this.nextGivenEvent.isStartElement()
+                    && CONTENT.equals(this.nextGivenEvent.asStartElement().getName().getLocalPart())) {
+                this.nextGivenEvent = this.wrapped.nextEvent(); // discard content start tag
+            }
+
+        } catch (Exception ignore) {
+            // ignore
+        }
+    }
+
+    @Override
+    public XMLEvent nextEvent() throws XMLStreamException {
+        final XMLEvent event = nextGivenEvent;
+
+        if (!isValidEvent(event)) {
+            throw new IllegalStateException("No event found");
+        }
+
+        nextGivenEvent = wrapped.hasNext() ? wrapped.nextEvent() : null;
+
+        return event;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return isValidEvent(nextGivenEvent);
+    }
+
+    @Override
+    public XMLEvent peek() throws XMLStreamException {
+        return wrapped.peek();
+    }
+
+    @Override
+    public String getElementText() throws XMLStreamException {
+        return wrapped.getElementText();
+    }
+
+    @Override
+    public XMLEvent nextTag() throws XMLStreamException {
+        XMLEvent tagEvent = wrapped.nextTag();
+        if (isValidEvent(tagEvent)) {
+            return tagEvent;
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public Object getProperty(final String string) throws IllegalArgumentException {
+        return wrapped.getProperty(string);
+    }
+
+    @Override
+    public void close() throws XMLStreamException {
+        wrapped.close();
+    }
+
+    @Override
+    public Object next() {
+        return wrapped.next();
+    }
+
+    @Override
+    public void remove() {
+        wrapped.remove();
+    }
+
+    private boolean isValidEvent(final XMLEvent event) {
+        // discard content end element tag ...
+        return event != null
+                && (!event.isEndElement()
+                || !CONTENT.equals(event.asEndElement().getName().getLocalPart()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLUtilities.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLUtilities.java
index 5d92317..65164b0 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLUtilities.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XMLUtilities.java
@@ -24,12 +24,15 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.InputStream;
+import java.io.OutputStreamWriter;
 import java.io.StringWriter;
 import java.util.AbstractMap;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -247,7 +250,7 @@ public class XMLUtilities extends AbstractUtilities {
             final XMLEventReader reader,
             final XMLEventWriter discarded,
             final String name,
-            final List<Map.Entry<String, String>> filterAttrs)
+            final Collection<Map.Entry<String, String>> filterAttrs)
             throws Exception {
         return getAtomElement(reader, discarded, name, filterAttrs, 0, -1, -1, false).getValue();
     }
@@ -256,7 +259,7 @@ public class XMLUtilities extends AbstractUtilities {
             final XMLEventReader reader,
             final XMLEventWriter discarded,
             final String name,
-            final List<Map.Entry<String, String>> filterAttrs,
+            final Collection<Map.Entry<String, String>> filterAttrs,
             final int initialDepth,
             final int minDepth,
             final int maxDepth,
@@ -272,7 +275,7 @@ public class XMLUtilities extends AbstractUtilities {
             if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
                 depth++;
 
-                if (name.trim().equals(event.asStartElement().getName().getLocalPart())
+                if ((StringUtils.isBlank(name) || name.trim().equals(event.asStartElement().getName().getLocalPart()))
                         && (minDepth < 0 || minDepth <= depth) && (maxDepth < 0 || maxDepth >= depth)) {
 
                     boolean match = filterAttrs == null || filterAttrs.isEmpty() || !filterInOr;
@@ -310,8 +313,7 @@ public class XMLUtilities extends AbstractUtilities {
             throw new Exception(String.format("Could not find an element named '%s'", name));
         }
 
-        return new AbstractMap.SimpleEntry<Integer, XmlElement>(
-                Integer.valueOf(depth - 1), getAtomElement(start, reader));
+        return new SimpleEntry<Integer, XmlElement>(Integer.valueOf(depth - 1), getAtomElement(start, reader));
     }
 
     public static XmlElement getAtomElement(
@@ -789,7 +791,8 @@ public class XMLUtilities extends AbstractUtilities {
     }
 
     @Override
-    public InputStream readEntities(final List<String> links, final String linkName, final String next)
+    public InputStream readEntities(
+            final List<String> links, final String linkName, final String next, final boolean forceFeed)
             throws Exception {
 
         if (links.isEmpty()) {
@@ -798,7 +801,7 @@ public class XMLUtilities extends AbstractUtilities {
 
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
-        if (links.size() > 1) {
+        if (forceFeed || links.size() > 1) {
             // build a feed
             bos.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>".getBytes());
 
@@ -824,7 +827,7 @@ public class XMLUtilities extends AbstractUtilities {
             IOUtils.copy(entry.toStream(), bos);
         }
 
-        if (links.size() > 1) {
+        if (forceFeed || links.size() > 1) {
 
             if (StringUtils.isNotBlank(next)) {
                 bos.write(String.format("<link rel=\"next\" href=\"%s\" />", next).getBytes());
@@ -837,6 +840,161 @@ public class XMLUtilities extends AbstractUtilities {
     }
 
     @Override
+    public Map<String, InputStream> getChanges(final InputStream src) throws Exception {
+        final Map<String, InputStream> res = new HashMap<String, InputStream>();
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        IOUtils.copy(src, bos);
+        IOUtils.closeQuietly(src);
+
+        // retrieve properties ...
+        XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+        final Map.Entry<Integer, XmlElement> propertyElement =
+                getAtomElement(reader, null, PROPERTIES, null, 0, 2, 3, false);
+        reader.close();
+
+        reader = propertyElement.getValue().getContentReader();
+
+        try {
+            while (true) {
+                final XmlElement property = getAtomElement(reader, null, null);
+                res.put(property.getStart().getName().getLocalPart(), property.toStream());
+            }
+        } catch (Exception ignore) {
+            // end
+        }
+
+        reader.close();
+
+        // retrieve links ...
+        reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+        try {
+            int pos = 0;
+            while (true) {
+                final Map.Entry<Integer, XmlElement> linkElement =
+                        getAtomElement(reader, null, LINK, null, pos, 2, 2, false);
+
+                res.put("[LINK]" + linkElement.getValue().getStart().getAttributeByName(new QName("title")).getValue(),
+                        linkElement.getValue().toStream());
+
+                pos = linkElement.getKey();
+            }
+        } catch (Exception ignore) {
+            // end
+        }
+
+        return res;
+    }
+
+    @Override
+    public InputStream setChanges(
+            final InputStream toBeChanged,
+            final Map<String, InputStream> properties)
+            throws Exception {
+
+        XMLEventReader reader = getEventReader(toBeChanged);
+
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        final XMLOutputFactory xof = XMLOutputFactory.newInstance();
+        XMLEventWriter writer = xof.createXMLEventWriter(bos);
+
+        // ---------------------------------
+        // add property changes
+        // ---------------------------------
+        Map.Entry<Integer, XmlElement> propertyElement =
+                getAtomElement(reader, writer, PROPERTIES, null, 0, 2, 3, false);
+
+        writer.flush();
+
+        ByteArrayOutputStream pbos = new ByteArrayOutputStream();
+        OutputStreamWriter pwriter = new OutputStreamWriter(pbos);
+
+        final XMLEventReader propertyReader = propertyElement.getValue().getContentReader();
+
+        try {
+            while (true) {
+                final XmlElement property = getAtomElement(propertyReader, null, null);
+                final String name = property.getStart().getName().getLocalPart();
+
+                if (properties.containsKey(name)) {
+                    // replace
+                    final InputStream replacement = properties.get(name);
+                    properties.remove(property.getStart().getName().getLocalPart());
+                    pwriter.append(IOUtils.toString(replacement));
+                    IOUtils.closeQuietly(replacement);
+                } else {
+                    pwriter.append(IOUtils.toString(property.toStream()));
+                }
+            }
+        } catch (Exception ignore) {
+            // end
+        }
+
+        for (Map.Entry<String, InputStream> remains : properties.entrySet()) {
+            if (!remains.getKey().startsWith("[LINK]")) {
+                pwriter.append(IOUtils.toString(remains.getValue()));
+                IOUtils.closeQuietly(remains.getValue());
+            }
+        }
+
+        pwriter.flush();
+        pwriter.close();
+
+        writer.add(propertyElement.getValue().getStart());
+        writer.add(new XMLEventReaderWrapper(new ByteArrayInputStream(pbos.toByteArray())));
+        writer.add(propertyElement.getValue().getEnd());
+
+        IOUtils.closeQuietly(pbos);
+
+        writer.add(reader);
+        reader.close();
+        writer.flush();
+        writer.close();
+        // ---------------------------------
+
+        // ---------------------------------
+        // add navigationm changes
+        // ---------------------------------
+        reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+
+        bos.reset();
+        writer = xof.createXMLEventWriter(bos);
+
+        propertyElement = getAtomElement(reader, writer, CONTENT, null, 0, 2, 2, false);
+        writer.flush();
+
+        pbos.reset();
+        pwriter = new OutputStreamWriter(pbos);
+
+        for (Map.Entry<String, InputStream> remains : properties.entrySet()) {
+            if (remains.getKey().startsWith("[LINK]")) {
+                pwriter.append(IOUtils.toString(remains.getValue()));
+                IOUtils.closeQuietly(remains.getValue());
+            }
+        }
+
+        pwriter.flush();
+        pwriter.close();
+
+        writer.add(new XMLEventReaderWrapper(new ByteArrayInputStream(pbos.toByteArray())));
+        IOUtils.closeQuietly(pbos);
+
+        writer.add(propertyElement.getValue().getStart());
+        writer.add(propertyElement.getValue().getContentReader());
+        writer.add(propertyElement.getValue().getEnd());
+
+        writer.add(reader);
+        reader.close();
+        writer.flush();
+        writer.close();
+        // ---------------------------------
+
+        return new ByteArrayInputStream(bos.toByteArray());
+    }
+
+    @Override
     protected InputStream replaceLink(
             final InputStream toBeChanged, final String linkName, final InputStream replacement)
             throws Exception {

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XmlElement.java
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XmlElement.java b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XmlElement.java
index dc0d755..6b7220e 100644
--- a/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XmlElement.java
+++ b/ODataJClient/test-service/src/main/java/com/msopentech/odatajclient/testservice/utils/XmlElement.java
@@ -24,11 +24,9 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLInputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.events.EndElement;
 import javax.xml.stream.events.StartElement;
-import javax.xml.stream.events.XMLEvent;
 import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -40,12 +38,6 @@ public class XmlElement {
      */
     protected static final Logger LOG = LoggerFactory.getLogger(XmlElement.class);
 
-    private final static String CONTENT = "CONTENT_TAG";
-
-    private final static String CONTENT_STAG = "<" + CONTENT + ">";
-
-    private final static String CONTENT_ETAG = "</" + CONTENT + ">";
-
     private StartElement start;
 
     private EndElement end;
@@ -73,15 +65,7 @@ public class XmlElement {
     }
 
     public XMLEventReader getContentReader() throws Exception {
-        final XMLInputFactory factory = XMLInputFactory.newInstance();
-        factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
-        factory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
-
-
-        final XMLEventReader contentReader = factory.createXMLEventReader(
-                new ByteArrayInputStream((CONTENT_STAG + IOUtils.toString(getContent()) + CONTENT_ETAG).getBytes()));
-
-        return new XMLEventReaderWrapper(contentReader);
+        return new XMLEventReaderWrapper(getContent());
     }
 
     public void setContent(final InputStream content) throws IOException {
@@ -90,98 +74,6 @@ public class XmlElement {
         content.close();
     }
 
-    private class XMLEventReaderWrapper implements XMLEventReader {
-
-        private final XMLEventReader wrapped;
-
-        private XMLEvent nextGivenEvent = null;
-
-        public XMLEventReaderWrapper(XMLEventReader wrapped) {
-            this.wrapped = wrapped;
-
-            try {
-                this.nextGivenEvent = this.wrapped.nextEvent();
-
-                if (this.nextGivenEvent.isStartDocument()) {
-                    this.nextGivenEvent = this.wrapped.nextEvent(); // discard start document    
-                }
-
-                if (this.nextGivenEvent.isStartElement()
-                        && CONTENT.equals(this.nextGivenEvent.asStartElement().getName().getLocalPart())) {
-                    this.nextGivenEvent = this.wrapped.nextEvent(); // discard content start tag
-                }
-
-            } catch (Exception ignore) {
-                // ignore
-            }
-        }
-
-        @Override
-        public XMLEvent nextEvent() throws XMLStreamException {
-            final XMLEvent event = nextGivenEvent;
-
-            if (!isValidEvent(event)) {
-                throw new IllegalStateException("No event found");
-            }
-
-            nextGivenEvent = wrapped.hasNext() ? wrapped.nextEvent() : null;
-
-            return event;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return isValidEvent(nextGivenEvent);
-        }
-
-        @Override
-        public XMLEvent peek() throws XMLStreamException {
-            return wrapped.peek();
-        }
-
-        @Override
-        public String getElementText() throws XMLStreamException {
-            return wrapped.getElementText();
-        }
-
-        @Override
-        public XMLEvent nextTag() throws XMLStreamException {
-            XMLEvent tagEvent = wrapped.nextTag();
-            if (isValidEvent(tagEvent)) {
-                return tagEvent;
-            } else {
-                return null;
-            }
-        }
-
-        @Override
-        public Object getProperty(final String string) throws IllegalArgumentException {
-            return wrapped.getProperty(string);
-        }
-
-        @Override
-        public void close() throws XMLStreamException {
-            wrapped.close();
-        }
-
-        @Override
-        public Object next() {
-            return wrapped.next();
-        }
-
-        @Override
-        public void remove() {
-            wrapped.remove();
-        }
-
-        private boolean isValidEvent(final XMLEvent event) {
-            // discard content end element tag ...
-            return event != null
-                    && (!event.isEndElement()
-                    || !CONTENT.equals(event.asEndElement().getName().getLocalPart()));
-        }
-    }
-
     public InputStream toStream() throws Exception {
         InputStream res;
         try {

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.full.json
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.full.json b/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.full.json
index 5a486e8..33ed9d3 100644
--- a/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.full.json
+++ b/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.full.json
@@ -2,7 +2,7 @@
   "odata.metadata": "http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/$metadata#Product/@Element",
   "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.Product",
   "odata.id": "http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/Product(-10)",
-  "odata.etag": "W/\"'New%20BaseConcurrency(1392649542502)'\"",
+  "odata.etag": "W/\"'assrfsssfdtrmdajadchvrqehsszybuiyiu%C3%9Flhmazsuemptziruotkqcy%C3%9F%C3%9Fp'\"",
   "odata.editLink": "Product(-10)",
   "RelatedProducts@odata.navigationLinkUrl": "Product(-10)/RelatedProducts",
   "Detail@odata.navigationLinkUrl": "Product(-10)/Detail",
@@ -15,9 +15,37 @@
           },
   "Picture@odata.mediaEditLink": "Product(-10)/Picture",
   "ProductId": -10,
-  "Description": "New Description(1391427672964)",
-  "Dimensions": null,
-  "BaseConcurrency": "New BaseConcurrency(1391427673066)",
-  "ComplexConcurrency": null,
-  "NestedComplexConcurrency": null
+  "Description": "onesusjnzuzrmzhqankkugdrftiukzkzqaggsfdmtvineulehkrbpu",
+  "Dimensions":
+          {
+            "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.Dimensions",
+            "Width@odata.type": "Edm.Decimal",
+            "Width": "-79228162514264337593543950335",
+            "Height@odata.type": "Edm.Decimal",
+            "Height": "-0.492988348718789",
+            "Depth@odata.type": "Edm.Decimal",
+            "Depth": "-78702059456772700000000000000"
+          },
+  "BaseConcurrency": "assrfsssfdtrmdajadchvrqehsszybuiyiußlhmazsuemptziruotkqcyßßp",
+  "ComplexConcurrency":
+          {
+            "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.ConcurrencyInfo",
+            "Token": null,
+            "QueriedDateTime@odata.type": "Edm.DateTime",
+            "QueriedDateTime": "2013-01-10T06:27:51.1667673"
+          },
+  "NestedComplexConcurrency":
+          {
+            "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.AuditInfo",
+            "ModifiedDate@odata.type": "Edm.DateTime",
+            "ModifiedDate": "9999-12-31T23:59:59.9999999",
+            "ModifiedBy": "gsrqilravbargkknoljssfn",
+            "Concurrency":
+                    {
+                      "odata.type": "Microsoft.Test.OData.Services.AstoriaDefaultService.ConcurrencyInfo",
+                      "Token": "び欲ぜぞボゾそaチぼ縷ソ黑ミ",
+                      "QueriedDateTime@odata.type": "Edm.DateTime",
+                      "QueriedDateTime": "2011-09-01T06:31:58.336924"
+                    }
+          }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.xml
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.xml b/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.xml
index 0fed5d7..f5b6212 100644
--- a/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.xml
+++ b/ODataJClient/test-service/src/main/resources/v3/Product/-10/entity.xml
@@ -19,7 +19,7 @@
     under the License.
 
 -->
-<entry xml:base="http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:etag="W/&quot;'New%20BaseConcurrency(1392649542502)'&quot;">
+<entry xml:base="http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:etag="W/&quot;'assrfsssfdtrmdajadchvrqehsszybuiyiu%C3%9Flhmazsuemptziruotkqcy%C3%9F%C3%9Fp'&quot;">
   <id>http://localhost:${cargo.servlet.port}/StaticService/V3/Static.svc/Product(-10)</id>
   <category term="Microsoft.Test.OData.Services.AstoriaDefaultService.Product" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
   <link rel="edit" title="Product" href="Product(-10)" />
@@ -28,7 +28,7 @@
   <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Reviews" type="application/atom+xml;type=feed" title="Reviews" href="Product(-10)/Reviews" />
   <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Photos" type="application/atom+xml;type=feed" title="Photos" href="Product(-10)/Photos" />
   <title />
-  <updated>2014-02-17T14:43:42Z</updated>
+  <updated>2014-03-08T16:39:47Z</updated>
   <author>
     <name />
   </author>

http://git-wip-us.apache.org/repos/asf/incubator-olingo-odata4/blob/a9f74a87/ODataJClient/test-service/src/main/resources/v3/Product/-10/etag.txt
----------------------------------------------------------------------
diff --git a/ODataJClient/test-service/src/main/resources/v3/Product/-10/etag.txt b/ODataJClient/test-service/src/main/resources/v3/Product/-10/etag.txt
index 0af74cf..e1f788c 100644
--- a/ODataJClient/test-service/src/main/resources/v3/Product/-10/etag.txt
+++ b/ODataJClient/test-service/src/main/resources/v3/Product/-10/etag.txt
@@ -1 +1 @@
-W/"'New%20BaseConcurrency(1392649542502)'"
\ No newline at end of file
+W/"'assrfsssfdtrmdajadchvrqehsszybuiyiu%C3%9Flhmazsuemptziruotkqcy%C3%9F%C3%9Fp'"
\ No newline at end of file