You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by il...@apache.org on 2014/04/27 07:49:17 UTC

[4/6] [OLINGO-254] Specific client (EdmEnabledODataClient) provided to work with Edm: type information is derived from contextURL, hence such client is useful in JSON with full or minimal metadata

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
new file mode 100644
index 0000000..162221a
--- /dev/null
+++ b/fit/src/main/java/org/apache/olingo/fit/utils/XMLUtilities.java
@@ -0,0 +1,1132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.fit.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+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;
+import java.util.Set;
+import javax.ws.rs.NotFoundException;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLEventWriter;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.fit.metadata.Metadata;
+import org.apache.olingo.fit.metadata.NavigationProperty;
+
+public class XMLUtilities extends AbstractUtilities {
+
+  protected static XMLInputFactory ifactory = null;
+
+  protected static XMLOutputFactory ofactory = null;
+
+  public XMLUtilities(final ODataServiceVersion version) throws Exception {
+    super(version);
+  }
+
+  @Override
+  protected Accept getDefaultFormat() {
+    return Accept.ATOM;
+  }
+
+  protected XMLEventReader getEventReader(final InputStream is) throws XMLStreamException {
+    if (ifactory == null) {
+      ifactory = XMLInputFactory.newInstance();
+    }
+    ifactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+    return ifactory.createXMLEventReader(is, "UTF-8");
+  }
+
+  protected static XMLEventWriter getEventWriter(final OutputStream os) throws XMLStreamException {
+    if (ofactory == null) {
+      ofactory = XMLOutputFactory.newInstance();
+    }
+
+    return ofactory.createXMLEventWriter(os, "UTF-8");
+  }
+
+  private void writeEvent(final XMLEvent event, final XMLEventWriter writer) {
+    if (writer != null) {
+      try {
+        writer.add(event);
+      } catch (XMLStreamException e) {
+        LOG.error("Error writing event {}", event, e);
+      }
+    }
+  }
+
+  private void skipElement(
+          final StartElement start,
+          final XMLEventReader reader,
+          final XMLEventWriter writer,
+          final boolean excludeStart)
+          throws Exception {
+
+    if (!excludeStart) {
+      writeEvent(start, writer);
+    }
+
+    int depth = 1;
+    boolean found = false;
+
+    while (reader.hasNext() && !found) {
+      final XMLEvent event = reader.nextEvent();
+
+      writeEvent(event, writer);
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+        found = depth == 0 && start.getName().equals(event.asEndElement().getName());
+      }
+    }
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected InputStream addLinks(
+          final String entitySetName, final String entitykey, final InputStream is, final Set<String> links)
+          throws Exception {
+
+    // -----------------------------------------
+    // 0. Build reader and writer
+    // -----------------------------------------
+    final XMLEventReader reader = getEventReader(is);
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+    // -----------------------------------------
+    final Map.Entry<Integer, XMLElement> entry =
+            extractElement(reader, writer, Collections.singletonList("entry"), 0, 1, 1);
+
+    writer.add(entry.getValue().getStart());
+
+    final Metadata metadata = Commons.getMetadata(version);
+    final Map<String, NavigationProperty> navigationProperties = metadata.getNavigationProperties(entitySetName);
+
+    // add for links
+    for (String link : links) {
+      final Set<Attribute> attributes = new HashSet<Attribute>();
+      attributes.add(eventFactory.createAttribute(new QName("title"), link));
+      attributes.add(eventFactory.createAttribute(new QName("href"),
+              Commons.getLinksURI(version, entitySetName, entitykey, link)));
+      attributes.add(eventFactory.createAttribute(new QName("rel"),
+              Constants.get(version, ConstantKey.ATOM_LINK_REL) + link));
+      attributes.add(eventFactory.createAttribute(new QName("type"),
+              navigationProperties.get(link).isFeed()
+              ? Constants.get(version, ConstantKey.ATOM_LINK_FEED)
+              : Constants.get(version, ConstantKey.ATOM_LINK_ENTRY)));
+
+      writer.add(eventFactory.createStartElement(
+              new QName(Constants.get(version, ConstantKey.LINK)), attributes.iterator(), null));
+      writer.add(eventFactory.createEndElement(new QName(Constants.get(version, ConstantKey.LINK)), null));
+    }
+
+    writer.add(entry.getValue().getContentReader());
+    writer.add(entry.getValue().getEnd());
+    writer.add(reader);
+    IOUtils.closeQuietly(is);
+
+    writer.flush();
+    writer.close();
+    reader.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected Set<String> retrieveAllLinkNames(final InputStream is) throws Exception {
+    final Set<String> links = new HashSet<String>();
+
+    final XMLEventReader reader = getEventReader(is);
+
+    try {
+
+      int startDepth = 0;
+
+      while (true) {
+        final Map.Entry<Integer, XMLElement> linkInfo =
+                extractElement(reader, null,
+                        Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), startDepth, 2, 2);
+
+        startDepth = linkInfo.getKey();
+
+        links.add(linkInfo.getValue().getStart().getAttributeByName(new QName("title")).getValue());
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(is);
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected NavigationLinks retrieveNavigationInfo(
+          final String entitySetName, final InputStream is)
+          throws Exception {
+
+    final NavigationLinks links = new NavigationLinks();
+
+    final XMLEventReader reader = getEventReader(is);
+
+    try {
+      final List<Map.Entry<String, String>> filter = new ArrayList<Map.Entry<String, String>>();
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=entry"));
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=feed"));
+
+      int startDepth = 0;
+
+      while (true) {
+        // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
+        final Map.Entry<Integer, XMLElement> linkInfo = extractElement(
+                reader, null, Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
+                filter, true, startDepth, 2, 2);
+        final XMLElement link = linkInfo.getValue();
+        startDepth = linkInfo.getKey();
+
+        final String title = link.getStart().getAttributeByName(new QName("title")).getValue();
+
+        final Attribute hrefAttr = link.getStart().getAttributeByName(new QName("href"));
+        final String href = hrefAttr == null ? null : hrefAttr.getValue();
+
+        try {
+          final XMLElement inlineElement =
+                  extractElement(link.getContentReader(), null,
+                          Collections.<String>singletonList(Constants.get(version, ConstantKey.INLINE)), 0, -1, -1).
+                  getValue();
+          final XMLEventReader inlineReader = inlineElement.getContentReader();
+
+          try {
+            while (true) {
+              final XMLElement entry =
+                      extractElement(inlineReader, null, Collections.<String>singletonList("entry"), 0, -1, -1).
+                      getValue();
+              links.addInlines(title, entry.toStream());
+            }
+          } catch (Exception e) {
+            // Reached the end of document
+          }
+
+          inlineReader.close();
+        } catch (Exception ignore) {
+          // inline element not found (inlines are not mondatory).
+          if (StringUtils.isNotBlank(href) && ENTITY_URI_PATTERN.matcher(href).matches()) {
+            links.addLinks(title, href.substring(href.lastIndexOf('/') + 1));
+          }
+        }
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      reader.close();
+    }
+
+    return links;
+  }
+
+  /**
+   * {@inheritDoc }
+   */
+  @Override
+  protected InputStream normalizeLinks(
+          final String entitySetName, final String entityKey, final InputStream is, final NavigationLinks links)
+          throws Exception {
+
+    // -----------------------------------------
+    // 0. Build reader and writer
+    // -----------------------------------------
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(is, bos);
+    is.close();
+
+    final ByteArrayOutputStream tmpBos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(tmpBos);
+
+    final XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 1. Normalize links
+    // -----------------------------------------
+    final Set<String> added = new HashSet<String>();
+
+    try {
+      final List<Map.Entry<String, String>> filter = new ArrayList<Map.Entry<String, String>>();
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=entry"));
+      filter.add(new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=feed"));
+
+      Map.Entry<Integer, XMLElement> linkInfo = null;
+
+      while (true) {
+        // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
+        linkInfo = extractElement(
+                reader, writer,
+                Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), filter, true,
+                linkInfo == null ? 0 : linkInfo.getKey(), 2, 2);
+        final XMLElement link = linkInfo.getValue();
+
+        final String title = link.getStart().getAttributeByName(new QName("title")).getValue();
+
+        if (!added.contains(title)) {
+          added.add(title);
+
+          final String normalizedLink = String.format(
+                  "<link href=\"%s(%s)/%s\" rel=\"%s\" title=\"%s\" type=\"%s\"/>",
+                  entitySetName,
+                  entityKey,
+                  title,
+                  link.getStart().getAttributeByName(new QName("rel")).getValue(),
+                  title,
+                  link.getStart().getAttributeByName(new QName("type")).getValue());
+
+          addAtomElement(IOUtils.toInputStream(normalizedLink, Constants.ENCODING), writer);
+        }
+      }
+    } catch (Exception ignore) {
+      // ignore
+    } finally {
+      writer.close();
+      reader.close();
+    }
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 2. Add edit link if missing
+    // -----------------------------------------
+    final InputStream content = addEditLink(
+            new ByteArrayInputStream(tmpBos.toByteArray()),
+            entitySetName,
+            Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + entitySetName + "(" + entityKey + ")");
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // 3. Add content element if missing
+    // -----------------------------------------
+    return addAtomContent(
+            content,
+            entitySetName,
+            Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + entitySetName + "(" + entityKey + ")");
+    // -----------------------------------------
+
+  }
+
+  public XMLElement getXmlElement(
+          final StartElement start,
+          final XMLEventReader reader)
+          throws Exception {
+
+    final XMLElement res = new XMLElement();
+    res.setStart(start);
+
+    final Charset encoding = Charset.forName("UTF-8");
+    final ByteArrayOutputStream content = new ByteArrayOutputStream();
+    final OutputStreamWriter writer = new OutputStreamWriter(content, encoding);
+
+    int depth = 1;
+
+    while (reader.hasNext() && depth > 0) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+      }
+
+      if (depth == 0) {
+        res.setEnd(event.asEndElement());
+      } else {
+        event.writeAsEncodedUnicode(writer);
+      }
+    }
+
+    writer.flush();
+    writer.close();
+
+    res.setContent(new ByteArrayInputStream(content.toByteArray()));
+
+    return res;
+  }
+
+  private void addAtomElement(
+          final InputStream content,
+          final XMLEventWriter writer)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(content);
+
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+    XMLEvent newLine = eventFactory.createSpace("\n");
+
+    try {
+      writer.add(newLine);
+
+      while (reader.hasNext()) {
+        final XMLEvent event = reader.nextEvent();
+
+        if (event.getEventType() != XMLStreamConstants.START_DOCUMENT
+                && event.getEventType() != XMLStreamConstants.END_DOCUMENT
+                && event.getEventType() != XMLStreamConstants.COMMENT) {
+          writer.add(event);
+        }
+      }
+      writer.add(newLine);
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(content);
+    }
+  }
+
+  @Override
+  public InputStream addEditLink(
+          final InputStream content, final String title, final String href)
+          throws Exception {
+
+    final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+    IOUtils.copy(content, copy);
+    IOUtils.closeQuietly(content);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    XMLEventWriter writer = getEventWriter(bos);
+
+    final String editLinkElement = String.format("<link rel=\"edit\" title=\"%s\" href=\"%s\" />", title, href);
+
+    try {
+      // check edit link existence
+      extractElement(reader, writer, Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
+              Collections.<Map.Entry<String, String>>singletonList(
+                      new AbstractMap.SimpleEntry<String, String>("rel", "edit")), false, 0, -1, -1);
+
+      addAtomElement(IOUtils.toInputStream(editLinkElement, Constants.ENCODING), writer);
+      writer.add(reader);
+
+    } catch (Exception e) {
+      reader.close();
+      reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+      bos = new ByteArrayOutputStream();
+      writer = getEventWriter(bos);
+
+      final XMLElement entryElement =
+              extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+
+      writer.add(entryElement.getStart());
+
+      addAtomElement(IOUtils.toInputStream(editLinkElement, Constants.ENCODING), writer);
+
+      writer.add(entryElement.getContentReader());
+      writer.add(entryElement.getEnd());
+
+      writer.add(reader);
+
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream addOperation(final InputStream content, final String name, final String metaAnchor,
+          final String href) throws Exception {
+
+    final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+    IOUtils.copy(content, copy);
+    IOUtils.closeQuietly(content);
+
+    final String action = String.format("<m:action metadata=\"%s%s\" title=\"%s\" target=\"%s\"/>",
+            Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL), metaAnchor, name, href);
+    final String newContent = new String(copy.toByteArray(), "UTF-8").replaceAll("\\<content ", action + "\\<content ");
+
+    return IOUtils.toInputStream(newContent, "UTF-8");
+  }
+
+  public InputStream addAtomContent(
+          final InputStream content, final String title, final String href)
+          throws Exception {
+
+    final ByteArrayOutputStream copy = new ByteArrayOutputStream();
+    IOUtils.copy(content, copy);
+
+    IOUtils.closeQuietly(content);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    XMLEventWriter writer = getEventWriter(bos);
+
+    try {
+      // check edit link existence
+      XMLElement contentElement =
+              extractElement(reader, writer, Collections.<String>singletonList("content"), 0, 2, 2).getValue();
+      writer.add(contentElement.getStart());
+      writer.add(contentElement.getContentReader());
+      writer.add(contentElement.getEnd());
+      writer.add(reader);
+    } catch (Exception e) {
+      reader.close();
+      reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+      bos = new ByteArrayOutputStream();
+      writer = getEventWriter(bos);
+
+      if (isMediaContent(title)) {
+        final XMLElement entryElement =
+                extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+
+        writer.add(entryElement.getStart());
+        writer.add(entryElement.getContentReader());
+
+        addAtomElement(
+                IOUtils.toInputStream(String.format("<content type=\"*/*\" src=\"%s/$value\" />", href)),
+                writer);
+
+        writer.add(entryElement.getEnd());
+      } else {
+        try {
+          final XMLElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList(
+                                  Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3).getValue();
+
+          addAtomElement(
+                  IOUtils.toInputStream("<content type=\"application/xml\">"),
+                  writer);
+
+          writer.add(entryElement.getStart());
+          writer.add(entryElement.getContentReader());
+          writer.add(entryElement.getEnd());
+
+          addAtomElement(
+                  IOUtils.toInputStream("</content>"),
+                  writer);
+        } catch (Exception nf) {
+          reader.close();
+          reader = getEventReader(new ByteArrayInputStream(copy.toByteArray()));
+
+          bos = new ByteArrayOutputStream();
+          writer = getEventWriter(bos);
+
+          final XMLElement entryElement =
+                  extractElement(reader, writer, Collections.<String>singletonList("entry"), 0, 1, 1).getValue();
+          writer.add(entryElement.getStart());
+          writer.add(entryElement.getContentReader());
+
+          addAtomElement(
+                  IOUtils.toInputStream("<content type=\"application/xml\"/>"),
+                  writer);
+
+          writer.add(entryElement.getEnd());
+        }
+      }
+
+      writer.add(reader);
+
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  public int countAllElements(final String entitySetName) throws Exception {
+    final String basePath = entitySetName + File.separatorChar;
+    int count = countFeedElements(fsManager.readFile(basePath + Constants.get(version, ConstantKey.FEED), Accept.XML),
+            "entry");
+
+    final String skipTokenDirPath = fsManager.getAbsolutePath(basePath + Constants.get(version, ConstantKey.SKIP_TOKEN),
+            null);
+
+    try {
+      final FileObject skipToken = fsManager.resolve(skipTokenDirPath);
+      final FileObject[] files = fsManager.findByExtension(skipToken, Accept.XML.getExtension().substring(1));
+
+      for (FileObject file : files) {
+        count += countFeedElements(fsManager.readFile(
+                basePath + Constants.get(version, ConstantKey.SKIP_TOKEN) + File.separatorChar
+                + file.getName().getBaseName(), null), "entry");
+      }
+    } catch (FileSystemException fse) {
+      LOG.debug("Resource path '{}' not found", skipTokenDirPath);
+    }
+
+    return count;
+  }
+
+  private int countFeedElements(final InputStream is, final String elementName) throws XMLStreamException {
+    final XMLEventReader reader = getEventReader(is);
+
+    int count = 0;
+
+    while (reader.hasNext()) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && elementName.equals(event.asStartElement().getName().getLocalPart())) {
+        count++;
+      }
+    }
+
+    reader.close();
+    return count;
+  }
+
+  public Map.Entry<Integer, XMLElement> extractElement(
+          final XMLEventReader reader, final XMLEventWriter writer, final List<String> path,
+          final int startPathPos, final int minPathPos, final int maxPathPos)
+          throws Exception {
+    return extractElement(reader, writer, path, null, false, startPathPos, minPathPos, maxPathPos);
+  }
+
+  public Map.Entry<Integer, XMLElement> extractElement(
+          final XMLEventReader reader, final XMLEventWriter writer, final List<String> path,
+          final Collection<Map.Entry<String, String>> filter,
+          final boolean filterInOr,
+          final int startPathPos, final int minPathPos, final int maxPathPos)
+          throws Exception {
+
+    StartElement start = null;
+    int searchFor = 0;
+    int depth = startPathPos;
+
+    // Current inspected element
+    String current = null;
+
+    // set defaults
+    final List<String> pathElementNames = path == null ? Collections.<String>emptyList() : path;
+    final Collection<Map.Entry<String, String>> filterAttrs =
+            filter == null ? Collections.<Map.Entry<String, String>>emptySet() : filter;
+
+    while (reader.hasNext() && start == null) {
+      final XMLEvent event = reader.nextEvent();
+
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+        depth++;
+
+        if (current != null || ((minPathPos < 0 || minPathPos <= depth) && (maxPathPos < 0 || depth <= maxPathPos))) {
+          if (pathElementNames.isEmpty()
+                  || pathElementNames.get(searchFor).trim().equals(event.asStartElement().getName().getLocalPart())) {
+
+            if (searchFor < pathElementNames.size() - 1) {
+              // path exploring not completed
+              writeEvent(event, writer);
+              current = pathElementNames.get(searchFor).trim();
+              searchFor++;
+            } else {
+
+              // path exploring completed ... evaluate filter about path element name attribute
+              boolean match = filterAttrs.isEmpty() || !filterInOr;
+
+              for (Map.Entry<String, String> filterAttr : filterAttrs) {
+                final Attribute attr = event.asStartElement().getAttributeByName(new QName(filterAttr.getKey().trim()));
+
+                if (attr == null || !filterAttr.getValue().trim().equals(attr.getValue())) {
+                  match = filterInOr ? match : false;
+                } else {
+                  match = filterInOr ? true : match;
+                }
+              }
+
+              if (match) {
+                // found searched element
+                start = event.asStartElement();
+              } else {
+                skipElement(event.asStartElement(), reader, writer, false);
+                depth--;
+              }
+            }
+          } else if (current == null) {
+            writeEvent(event, writer);
+          } else {
+            // skip element
+            skipElement(event.asStartElement(), reader, writer, false);
+            depth--;
+          }
+        } else {
+          writeEvent(event, writer);
+        }
+
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT) {
+        depth--;
+
+        writeEvent(event, writer);
+
+        if (event.asEndElement().getName().getLocalPart().equals(current)) {
+          // back step ....
+          searchFor--;
+          current = searchFor > 0 ? pathElementNames.get(searchFor - 1).trim() : null;
+        }
+      } else {
+        writeEvent(event, writer);
+      }
+    }
+
+    if (start == null) {
+      throw new NotFoundException();
+    }
+
+    return new SimpleEntry<Integer, XMLElement>(Integer.valueOf(depth - 1), getXmlElement(start, reader));
+  }
+
+  public InputStream addAtomInlinecount(
+          final InputStream feed, final int count, final Accept accept)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(feed);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    try {
+
+      final XMLElement feedElement =
+              extractElement(reader, writer, Collections.<String>singletonList("feed"), 0, 1, 1).getValue();
+
+      writer.add(feedElement.getStart());
+      addAtomElement(IOUtils.toInputStream(String.format("<m:count>%d</m:count>", count), Constants.ENCODING), writer);
+      writer.add(feedElement.getContentReader());
+      writer.add(feedElement.getEnd());
+
+      while (reader.hasNext()) {
+        writer.add(reader.nextEvent());
+      }
+
+    } finally {
+      writer.flush();
+      writer.close();
+      reader.close();
+      IOUtils.closeQuietly(feed);
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream selectEntity(final InputStream entity, final String[] propertyNames) throws Exception {
+    final XMLEventReader reader = getEventReader(entity);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final List<String> found = new ArrayList<String>(Arrays.asList(propertyNames));
+
+    boolean inProperties = false;
+    boolean writeCurrent = true;
+    Boolean writeNext = null;
+    String currentName = null;
+
+    final List<String> fieldToBeSaved = new ArrayList<String>(Arrays.asList(propertyNames));
+
+    while (reader.hasNext()) {
+      final XMLEvent event = reader.nextEvent();
+      if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && Constants.get(version, ConstantKey.LINK).equals(event.asStartElement().getName().getLocalPart())
+              && !fieldToBeSaved.contains(
+                      event.asStartElement().getAttributeByName(new QName("title")).getValue())
+              && !"edit".equals(event.asStartElement().getAttributeByName(new QName("rel")).getValue())) {
+        writeCurrent = false;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+              && Constants.get(version, ConstantKey.LINK).equals(event.asEndElement().getName().getLocalPart())) {
+        writeNext = true;
+      } else if (event.getEventType() == XMLStreamConstants.START_ELEMENT
+              && (Constants.get(version, ConstantKey.PROPERTIES)).equals(
+                      event.asStartElement().getName().getLocalPart())) {
+        writeCurrent = true;
+        writeNext = false;
+        inProperties = true;
+      } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+              && (Constants.get(version, ConstantKey.PROPERTIES)).equals(
+                      event.asEndElement().getName().getLocalPart())) {
+        writeCurrent = true;
+      } else if (inProperties) {
+        if (event.getEventType() == XMLStreamConstants.START_ELEMENT) {
+          final String elementName = event.asStartElement().getName().getLocalPart();
+
+          for (String propertyName : propertyNames) {
+            if ((Constants.get(version, ConstantKey.ATOM_PROPERTY_PREFIX) + propertyName.trim()).equals(elementName)) {
+              writeCurrent = true;
+              found.remove(propertyName);
+              currentName = propertyName;
+            }
+          }
+
+        } else if (event.getEventType() == XMLStreamConstants.END_ELEMENT
+                && StringUtils.isNotBlank(currentName)
+                && (Constants.get(version, ConstantKey.ATOM_PROPERTY_PREFIX) + currentName.trim()).equals(
+                        event.asEndElement().getName().getLocalPart())) {
+          writeNext = false;
+          currentName = null;
+        }
+
+      }
+
+      if (writeCurrent) {
+        writer.add(event);
+      }
+
+      if (writeNext != null) {
+        writeCurrent = writeNext;
+        writeNext = null;
+      }
+    }
+
+    writer.flush();
+    writer.close();
+    reader.close();
+    IOUtils.closeQuietly(entity);
+
+    // Do not raise any exception in order to support FC properties as well
+    // if (!found.isEmpty()) {
+    //     throw new Exception(String.format("Could not find a properties '%s'", found));
+    // }
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream readEntities(
+          final List<String> links, final String linkName, final String next, final boolean forceFeed)
+          throws Exception {
+
+    if (links.isEmpty()) {
+      throw new NotFoundException();
+    }
+
+    final Charset encoding = Charset.forName("UTF-8");
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final OutputStreamWriter writer = new OutputStreamWriter(bos, encoding);
+
+    writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>".toCharArray());
+
+    if (forceFeed || links.size() > 1) {
+      // build a feed
+
+      writer.write(("<feed xml:base=\"" + Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + "\" "
+              + "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\">")
+              .toCharArray());
+
+      writer.write(("<id>" + Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + "entityset(entityid)/" + linkName
+              + "</id>").toCharArray());
+
+      writer.write(("<title type=\"text\">" + linkName + "</title>").toCharArray());
+      writer.write("<updated>2014-03-03T13:40:49Z</updated>".toCharArray());
+      writer.write(("<link rel=\"self\" title=\"" + linkName + "\" href=\"" + linkName + "\" />").toCharArray());
+    }
+
+    for (String link : links) {
+      try {
+        final Map.Entry<String, String> uri = Commons.parseEntityURI(link);
+
+        final XMLElement entry =
+                extractElement(
+                        getEventReader(readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue()),
+                        null,
+                        Collections.<String>singletonList("entry"),
+                        0, 1, 1).getValue();
+
+        IOUtils.copy(entry.toStream(), writer, encoding);
+      } catch (Exception e) {
+        // log and ignore link
+        LOG.warn("Error parsing uri {}", link, e);
+      }
+    }
+
+    if (forceFeed || links.size() > 1) {
+
+      if (StringUtils.isNotBlank(next)) {
+        writer.write(String.format("<link rel=\"next\" href=\"%s\" />", next).toCharArray());
+      }
+
+      writer.write("</feed>".toCharArray());
+    }
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @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 =
+            extractElement(reader, null,
+                    Collections.<String>singletonList(Constants.get(version, ConstantKey.PROPERTIES)), 0, 2, 3);
+    reader.close();
+
+    reader = propertyElement.getValue().getContentReader();
+
+    try {
+      while (true) {
+        final XMLElement property = extractElement(reader, null, null, 0, -1, -1).getValue();
+        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 =
+                extractElement(reader, null,
+                        Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)), pos, 2, 2);
+
+        res.put("[Constants.get(version, ConstantKey.LINK)]"
+                + linkElement.getValue().getStart().getAttributeByName(new QName("title")).getValue(),
+                linkElement.getValue().toStream());
+
+        pos = linkElement.getKey();
+      }
+    } catch (Exception ignore) {
+      // end
+    }
+
+    return res;
+  }
+
+  @Override
+  protected InputStream replaceLink(
+          final InputStream toBeChanged, final String linkName, final InputStream replacement)
+          throws Exception {
+    final XMLEventReader reader = getEventReader(toBeChanged);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final XMLEventFactory eventFactory = XMLEventFactory.newInstance();
+    XMLEvent newLine = eventFactory.createSpace("\n");
+
+    try {
+      final XMLElement linkElement =
+              extractElement(reader, writer,
+                      Collections.<String>singletonList(Constants.get(version, ConstantKey.LINK)),
+                      Collections.<Map.Entry<String, String>>singletonList(
+                              new SimpleEntry<String, String>("title", linkName)), false, 0, -1, -1).getValue();
+      writer.add(linkElement.getStart());
+
+      // ------------------------------------------
+      // write inline ...
+      // ------------------------------------------
+      writer.add(newLine);
+      writer.add(eventFactory.createStartElement("m", null, "inline"));
+
+      addAtomElement(replacement, writer);
+
+      writer.add(eventFactory.createEndElement("m", null, "inline"));
+      writer.add(newLine);
+      // ------------------------------------------
+
+      writer.add(linkElement.getEnd());
+
+      writer.add(reader);
+      writer.flush();
+      writer.close();
+    } finally {
+      reader.close();
+      IOUtils.closeQuietly(toBeChanged);
+    }
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(
+          final String entitySetName, final String entityId, final String linkName)
+          throws Exception {
+
+    final LinkInfo links = readLinks(entitySetName, entityId, linkName, Accept.XML);
+    return extractLinkURIs(links.getLinks());
+  }
+
+  @Override
+  public Map.Entry<String, List<String>> extractLinkURIs(final InputStream is)
+          throws Exception {
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    IOUtils.copy(is, bos);
+    IOUtils.closeQuietly(is);
+
+    XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    final List<String> links = new ArrayList<String>();
+    try {
+      while (true) {
+        links.add(IOUtils.toString(extractElement(reader, null, Collections.<String>singletonList("uri"), 0, -1, -1).
+                getValue().getContent()));
+      }
+    } catch (Exception ignore) {
+      // End document reached ...
+    }
+    reader.close();
+
+    String next;
+
+    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));
+    try {
+      next = IOUtils.toString(extractElement(reader, null, Collections.<String>singletonList("next"), 0, -1, -1).
+              getValue().getContent());
+    } catch (Exception ignore) {
+      // next link is not mandatory
+      next = null;
+    }
+    reader.close();
+
+    return new AbstractMap.SimpleEntry<String, List<String>>(next, links);
+  }
+
+  @Override
+  public InputStream replaceProperty(
+          final InputStream src, final InputStream replacement, final List<String> path, final boolean justValue)
+          throws Exception {
+
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(Constants.get(version, ConstantKey.ATOM_PROPERTY_PREFIX) + element);
+    }
+
+    final XMLEventReader reader = getEventReader(src);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final Map.Entry<Integer, XMLElement> element = extractElement(reader, writer, pathElements, 0, 3, 4);
+
+    if (justValue) {
+      writer.add(element.getValue().getStart());
+    }
+
+    final XMLEventReader changesReader = new XMLEventReaderWrapper(replacement);
+
+    while (changesReader.hasNext()) {
+      final XMLEvent event = changesReader.nextEvent();
+      if (event.isStartElement() && event.asStartElement().getName().equals(element.getValue().getStart().getName())) {
+        writer.add(element.getValue().getStart());
+        writer.add(changesReader);
+      } else {
+        writer.add(event);
+      }
+    }
+
+    changesReader.close();
+    IOUtils.closeQuietly(replacement);
+
+    if (justValue) {
+      writer.add(element.getValue().getEnd());
+    }
+
+    writer.add(reader);
+
+    reader.close();
+    IOUtils.closeQuietly(src);
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+
+  @Override
+  public InputStream deleteProperty(final InputStream src, final List<String> path) throws Exception {
+    final List<String> pathElements = new ArrayList<String>();
+
+    for (String element : path) {
+      pathElements.add(Constants.get(version, ConstantKey.ATOM_PROPERTY_PREFIX) + element);
+    }
+
+    final XMLEventReader reader = getEventReader(src);
+
+    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    final XMLEventWriter writer = getEventWriter(bos);
+
+    final Map.Entry<Integer, XMLElement> element = extractElement(reader, writer, pathElements, 0, 3, 4);
+
+    final XMLEventReader changesReader = new XMLEventReaderWrapper(IOUtils.toInputStream(
+            String.format("<%s m:null=\"true\" />", path.get(path.size() - 1)), Constants.ENCODING));
+
+    writer.add(changesReader);
+    changesReader.close();
+
+    writer.add(reader);
+
+    reader.close();
+    IOUtils.closeQuietly(src);
+
+    writer.flush();
+    writer.close();
+
+    return new ByteArrayInputStream(bos.toByteArray());
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/XmlElement.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/XmlElement.java b/fit/src/main/java/org/apache/olingo/fit/utils/XmlElement.java
deleted file mode 100644
index 850c862..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/XmlElement.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.fit.utils;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.events.EndElement;
-import javax.xml.stream.events.StartElement;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class XmlElement {
-
-  /**
-   * Logger.
-   */
-  protected static final Logger LOG = LoggerFactory.getLogger(XmlElement.class);
-
-  private static Charset encoding = Charset.forName("UTF-8");
-
-  private StartElement start;
-
-  private EndElement end;
-
-  private ByteArrayOutputStream content = new ByteArrayOutputStream();
-
-  public StartElement getStart() {
-    return start;
-  }
-
-  public void setStart(StartElement start) {
-    this.start = start;
-  }
-
-  public EndElement getEnd() {
-    return end;
-  }
-
-  public void setEnd(EndElement end) {
-    this.end = end;
-  }
-
-  public InputStream getContent() throws XMLStreamException {
-    return new ByteArrayInputStream(content.toByteArray());
-  }
-
-  public XMLEventReader getContentReader() throws Exception {
-    return new XMLEventReaderWrapper(getContent());
-  }
-
-  public void setContent(final InputStream content) throws IOException {
-    this.content.reset();
-
-    final InputStreamReader reader = new InputStreamReader(content, encoding);
-    final OutputStreamWriter writer = new OutputStreamWriter(this.content, encoding);
-    IOUtils.copyLarge(reader, writer);
-
-    writer.flush();
-    IOUtils.closeQuietly(reader);
-    IOUtils.closeQuietly(writer);
-    IOUtils.closeQuietly(content);
-  }
-
-  public InputStream toStream() throws Exception {
-    InputStream res;
-    try {
-      final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-      final OutputStreamWriter osw = new OutputStreamWriter(bos, encoding);
-
-      getStart().writeAsEncodedUnicode(osw);
-
-      IOUtils.copy(getContent(), osw, encoding);
-
-      getEnd().writeAsEncodedUnicode(osw);
-      osw.flush();
-      osw.close();
-
-      res = new ByteArrayInputStream(bos.toByteArray());
-    } catch (Exception e) {
-      LOG.error("Error serializing elemnt", e);
-      res = null;
-    }
-    return res;
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
deleted file mode 100644
index 24b6c55..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v3/JSONUtilities.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.fit.utils.v3;
-
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-
-public class JSONUtilities extends org.apache.olingo.fit.utils.AbstractJSONUtilities {
-
-  public JSONUtilities() throws Exception {
-    super(ODataServiceVersion.V30);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
deleted file mode 100644
index 2eb0983..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v3/XMLUtilities.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.fit.utils.v3;
-
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-
-public class XMLUtilities extends org.apache.olingo.fit.utils.AbstractXMLUtilities {
-
-  public XMLUtilities() throws Exception {
-    super(ODataServiceVersion.V30);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
deleted file mode 100644
index 9b2d607..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v4/JSONUtilities.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.fit.utils.v4;
-
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-
-public class JSONUtilities extends org.apache.olingo.fit.utils.AbstractJSONUtilities {
-
-  public JSONUtilities() throws Exception {
-    super(ODataServiceVersion.V40);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/fit/src/main/java/org/apache/olingo/fit/utils/v4/XMLUtilities.java
----------------------------------------------------------------------
diff --git a/fit/src/main/java/org/apache/olingo/fit/utils/v4/XMLUtilities.java b/fit/src/main/java/org/apache/olingo/fit/utils/v4/XMLUtilities.java
deleted file mode 100644
index 86e6e3c..0000000
--- a/fit/src/main/java/org/apache/olingo/fit/utils/v4/XMLUtilities.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.fit.utils.v4;
-
-import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
-
-public class XMLUtilities extends org.apache.olingo.fit.utils.AbstractXMLUtilities {
-
-  public XMLUtilities() throws Exception {
-    super(ODataServiceVersion.V40);
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonEdmEnabledODataClient.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonEdmEnabledODataClient.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonEdmEnabledODataClient.java
new file mode 100644
index 0000000..2b5478f
--- /dev/null
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonEdmEnabledODataClient.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.client.api;
+
+import java.net.URI;
+import org.apache.olingo.client.api.communication.request.cud.CommonUpdateType;
+import org.apache.olingo.commons.api.edm.Edm;
+
+/**
+ * Client interface that caches the Edm metadata information for a given service root.
+ * <br/>
+ * Be aware that any request generated via this client instance will be performed against the given service root.
+ *
+ * @param <UT> concrete update type, depending on the protocol version
+ */
+public interface CommonEdmEnabledODataClient<UT extends CommonUpdateType> extends CommonODataClient<UT> {
+
+  String getServiceRoot();
+
+  /**
+   * Checks if the cached Edm matadata information matches the argument and, if not, updates the cache against the
+   * configured service root.
+   *
+   * @param metadataETag metadata ETag to be compared against the cache
+   * @return Edm
+   */
+  Edm getEdm(String metadataETag);
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonODataClient.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonODataClient.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonODataClient.java
index b3a4427..fccaa54 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonODataClient.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/CommonODataClient.java
@@ -36,6 +36,11 @@ import org.apache.olingo.client.api.uri.CommonURIBuilder;
 import org.apache.olingo.client.api.uri.CommonFilterFactory;
 import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
 
+/**
+ * Generic client interface (common to all supported OData protocol versions).
+ *
+ * @param <UT> concrete update type, depending on the protocol version
+ */
 public interface CommonODataClient<UT extends CommonUpdateType> {
 
   ODataServiceVersion getServiceVersion();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataRawResponse.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataRawResponse.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataRawResponse.java
index 8dd189a..a3de84f 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataRawResponse.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataRawResponse.java
@@ -18,6 +18,8 @@
  */
 package org.apache.olingo.client.api.communication.response;
 
+import org.apache.olingo.commons.api.data.ResWrap;
+
 /**
  * This interface represents a generic OData response.
  */
@@ -29,5 +31,5 @@ public interface ODataRawResponse extends ODataResponse {
    * @param reference an OData domain object class reference
    * @return response body parsed as the given reference, if available, <tt>null</tt> otherwise
    */
-  <T> T getBodyAs(final Class<T> reference);
+  <T> ResWrap<T> getBodyAs(final Class<T> reference);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataResponse.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataResponse.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataResponse.java
index 7da07ec..1538ad3 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataResponse.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/response/ODataResponse.java
@@ -19,7 +19,6 @@
 package org.apache.olingo.client.api.communication.response;
 
 import java.io.InputStream;
-import java.net.URI;
 import java.util.Collection;
 import java.util.Map;
 import org.apache.http.HttpResponse;
@@ -59,31 +58,7 @@ public interface ODataResponse {
    *
    * @return ETag header value, if provided
    */
-  String getEtag();
-
-  /**
-   * The context URL describes the content of the payload. It consists of the canonical metadata document URL and a
-   * fragment identifying the relevant portion of the metadata document.
-   * <br />
-   * Request payloads generally do not require context URLs as the type of the payload can generally be determined from
-   * the request URL.
-   * <br />
-   * For details on how the context URL is used to describe a payload, see the relevant sections in the particular
-   * format.
-   *
-   * @return context URL.
-   */
-  URI getContextURL();
-
-  /**
-   * An ETag header MAY also be returned on a metadata document request or service document request to allow the client
-   * subsequently to make a conditional request for the metadata or service document. Clients can also compare the value
-   * of the ETag header returned from a metadata document request to the metadata ETag returned in a response in order
-   * to verify the version of the metadata used to generate that response.
-   *
-   * @return metadata ETag.
-   */
-  String getMetadataETag();
+  String getETag();
 
   /**
    * Gets the content type.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/domain/ODataEntitySetIterator.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/domain/ODataEntitySetIterator.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/domain/ODataEntitySetIterator.java
index 107816f..8002845 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/domain/ODataEntitySetIterator.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/domain/ODataEntitySetIterator.java
@@ -31,16 +31,17 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.olingo.client.api.CommonODataClient;
 import org.apache.olingo.commons.api.Constants;
 import org.apache.olingo.commons.api.data.Entry;
-import org.apache.olingo.commons.api.format.ODataPubFormat;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
 import org.apache.olingo.commons.api.domain.CommonODataEntitySet;
+import org.apache.olingo.commons.api.format.ODataPubFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * OData entity set iterator class.
  * <br/>
- * <b>Please don't forget to call the <tt>close()>/<tt> method when not needed any more.</b>
+ * <b>Please don't forget to call the <tt>close()>/</tt> method when not needed any more.</b>
  *
  * @param <E> concrete ODataEntity implementation
  * @param <ES> concrete ODataEntitySet implementation
@@ -61,7 +62,7 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
 
   private final ODataPubFormat format;
 
-  private Entry cached;
+  private ResWrap<Entry> cached;
 
   private ES entitySet;
 
@@ -78,7 +79,7 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
    * @param stream source stream.
    * @param format OData format.
    */
-  public ODataEntitySetIterator(final CommonODataClient odataClient, final InputStream stream,
+  public ODataEntitySetIterator(final CommonODataClient<?> odataClient, final InputStream stream,
           final ODataPubFormat format) {
 
     this.odataClient = odataClient;
@@ -171,10 +172,10 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
     return entitySet.getNext();
   }
 
-  private Entry nextJsonEntryFromFeed(final InputStream input, final OutputStream osFeed) {
+  private ResWrap<Entry> nextJsonEntryFromFeed(final InputStream input, final OutputStream osFeed) {
     final ByteArrayOutputStream entry = new ByteArrayOutputStream();
 
-    Entry jsonEntry = null;
+    ResWrap<Entry> jsonEntry = null;
     try {
       int c;
 
@@ -209,7 +210,7 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
 
         if (c >= 0) {
           jsonEntry = odataClient.getDeserializer().toEntry(
-                  new ByteArrayInputStream(entry.toByteArray()), ODataPubFormat.JSON).getObject();
+                  new ByteArrayInputStream(entry.toByteArray()), ODataPubFormat.JSON);
         }
       } else {
         while ((c = input.read()) >= 0) {
@@ -230,10 +231,12 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
    * @param format de-serialize as AtomFeed or JSONFeed
    * @return de-serialized entity set.
    */
-  private Entry nextAtomEntryFromFeed(final InputStream input, final OutputStream osFeed, final String namespaces) {
+  private ResWrap<Entry> nextAtomEntryFromFeed(
+          final InputStream input, final OutputStream osFeed, final String namespaces) {
+
     final ByteArrayOutputStream entry = new ByteArrayOutputStream();
 
-    Entry atomEntry = null;
+    ResWrap<Entry> atomEntry = null;
 
     try {
       if (consume(input, "<entry>", osFeed, false) >= 0) {
@@ -243,7 +246,7 @@ public class ODataEntitySetIterator<ES extends CommonODataEntitySet, E extends C
 
         if (consume(input, "</entry>", entry, true) >= 0) {
           atomEntry = odataClient.getDeserializer().
-                  toEntry(new ByteArrayInputStream(entry.toByteArray()), ODataPubFormat.ATOM).getObject();
+                  toEntry(new ByteArrayInputStream(entry.toByteArray()), ODataPubFormat.ATOM);
         }
       }
     } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/ClientODataDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/ClientODataDeserializer.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/ClientODataDeserializer.java
index 24b6e60..9bca91a 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/ClientODataDeserializer.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/ClientODataDeserializer.java
@@ -21,7 +21,7 @@ package org.apache.olingo.client.api.op;
 import java.io.InputStream;
 import org.apache.olingo.client.api.data.ServiceDocument;
 import org.apache.olingo.client.api.edm.xml.XMLMetadata;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.format.ODataFormat;
 import org.apache.olingo.commons.api.op.CommonODataDeserializer;
 
@@ -36,5 +36,5 @@ public interface ClientODataDeserializer extends CommonODataDeserializer {
    * @param format OData service document format.
    * @return <tt>ServiceDocument</tt> object.
    */
-  Container<ServiceDocument> toServiceDocument(InputStream input, ODataFormat format);
+  ResWrap<ServiceDocument> toServiceDocument(InputStream input, ODataFormat format);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataBinder.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataBinder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataBinder.java
index 0d8da52..acb08ae 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataBinder.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataBinder.java
@@ -19,12 +19,12 @@
 package org.apache.olingo.client.api.op;
 
 import java.io.Serializable;
-import java.net.URI;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.data.Feed;
 import org.apache.olingo.commons.api.data.Link;
 import org.apache.olingo.commons.api.data.Property;
 import org.apache.olingo.client.api.data.ServiceDocument;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
 import org.apache.olingo.commons.api.domain.CommonODataEntitySet;
 import org.apache.olingo.commons.api.domain.ODataLink;
@@ -90,41 +90,23 @@ public interface CommonODataBinder extends Serializable {
    * Gets <tt>ODataEntitySet</tt> from the given feed resource.
    *
    * @param resource feed resource.
-   * @return <tt>ODataEntitySet</tt> object.
+   * @return {@link CommonODataEntitySet} object.
    */
-  CommonODataEntitySet getODataEntitySet(Feed resource);
-
-  /**
-   * Gets <tt>ODataEntitySet</tt> from the given feed resource.
-   *
-   * @param resource feed resource.
-   * @param defaultBaseURI default base URI.
-   * @return <tt>ODataEntitySet</tt> object.
-   */
-  CommonODataEntitySet getODataEntitySet(Feed resource, URI defaultBaseURI);
-
-  /**
-   * Gets <tt>ODataEntity</tt> from the given entry resource.
-   *
-   * @param resource entry resource.
-   * @return <tt>ODataEntity</tt> object.
-   */
-  CommonODataEntity getODataEntity(Entry resource);
+  CommonODataEntitySet getODataEntitySet(ResWrap<Feed> resource);
 
   /**
    * Gets <tt>ODataEntity</tt> from the given entry resource.
    *
    * @param resource entry resource.
-   * @param defaultBaseURI default base URI.
-   * @return <tt>ODataEntity</tt> object.
+   * @return {@link CommonODataEntity} object.
    */
-  CommonODataEntity getODataEntity(Entry resource, URI defaultBaseURI);
+  CommonODataEntity getODataEntity(ResWrap<Entry> resource);
 
   /**
    * Gets an <tt>ODataProperty</tt> from the given property resource.
    *
-   * @param property property resource.
-   * @return <tt>ODataProperty</tt> object.
+   * @param resource property resource.
+   * @return {@link CommonODataProperty} object.
    */
-  CommonODataProperty getODataProperty(Property property);
+  CommonODataProperty getODataProperty(ResWrap<Property> resource);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataReader.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataReader.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataReader.java
index 923ac23..0f1335a 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataReader.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/CommonODataReader.java
@@ -22,7 +22,7 @@ import java.io.InputStream;
 import java.io.Serializable;
 import java.util.List;
 import org.apache.olingo.client.api.edm.xml.Schema;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.domain.ODataError;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
 import org.apache.olingo.commons.api.domain.CommonODataEntitySet;
@@ -112,5 +112,5 @@ public interface CommonODataReader extends Serializable {
    * @param reference reference.
    * @return read object.
    */
-  <T> Container<T> read(InputStream src, String format, Class<T> reference);
+  <T> ResWrap<T> read(InputStream src, String format, Class<T> reference);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataBinder.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataBinder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataBinder.java
index 52cb625..92d3061 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataBinder.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataBinder.java
@@ -18,13 +18,13 @@
  */
 package org.apache.olingo.client.api.op.v3;
 
-import java.net.URI;
 import org.apache.olingo.commons.api.data.v3.LinkCollection;
 import org.apache.olingo.client.api.domain.v3.ODataLinkCollection;
 import org.apache.olingo.client.api.op.CommonODataBinder;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.data.Feed;
 import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.domain.v3.ODataEntity;
 import org.apache.olingo.commons.api.domain.v3.ODataEntitySet;
 import org.apache.olingo.commons.api.domain.v3.ODataProperty;
@@ -32,19 +32,13 @@ import org.apache.olingo.commons.api.domain.v3.ODataProperty;
 public interface ODataBinder extends CommonODataBinder {
 
   @Override
-  ODataEntitySet getODataEntitySet(Feed resource);
+  ODataEntitySet getODataEntitySet(ResWrap<Feed> resource);
 
   @Override
-  ODataEntitySet getODataEntitySet(Feed resource, URI defaultBaseURI);
+  ODataEntity getODataEntity(ResWrap<Entry> resource);
 
   @Override
-  ODataEntity getODataEntity(Entry resource);
-
-  @Override
-  ODataEntity getODataEntity(Entry resource, URI defaultBaseURI);
-
-  @Override
-  ODataProperty getODataProperty(Property property);
+  ODataProperty getODataProperty(ResWrap<Property> resource);
 
   /**
    * Gets <tt>ODataLinkCollection</tt> from the given link collection resource.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataDeserializer.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataDeserializer.java
index bc16b74..ff559a1 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataDeserializer.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v3/ODataDeserializer.java
@@ -20,7 +20,7 @@ package org.apache.olingo.client.api.op.v3;
 
 import java.io.InputStream;
 import org.apache.olingo.client.api.op.ClientODataDeserializer;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.data.v3.LinkCollection;
 import org.apache.olingo.commons.api.format.ODataFormat;
 
@@ -33,6 +33,6 @@ public interface ODataDeserializer extends ClientODataDeserializer {
    * @param format OData format.
    * @return de-serialized links.
    */
-  Container<LinkCollection> toLinkCollection(InputStream input, ODataFormat format);
+  ResWrap<LinkCollection> toLinkCollection(InputStream input, ODataFormat format);
 
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v4/ODataBinder.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v4/ODataBinder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v4/ODataBinder.java
index d2805bb..9c84a8c 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v4/ODataBinder.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/op/v4/ODataBinder.java
@@ -18,11 +18,11 @@
  */
 package org.apache.olingo.client.api.op.v4;
 
-import java.net.URI;
 import org.apache.olingo.client.api.op.CommonODataBinder;
 import org.apache.olingo.commons.api.data.Entry;
 import org.apache.olingo.commons.api.data.Feed;
 import org.apache.olingo.commons.api.data.Property;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.domain.v4.ODataEntity;
 import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
 import org.apache.olingo.commons.api.domain.v4.ODataProperty;
@@ -30,19 +30,11 @@ import org.apache.olingo.commons.api.domain.v4.ODataProperty;
 public interface ODataBinder extends CommonODataBinder {
 
   @Override
-  ODataEntitySet getODataEntitySet(Feed resource);
+  ODataEntitySet getODataEntitySet(ResWrap<Feed> resource);
 
   @Override
-  ODataEntitySet getODataEntitySet(Feed resource, URI defaultBaseURI);
+  ODataEntity getODataEntity(ResWrap<Entry> resource);
 
   @Override
-  ODataEntity getODataEntity(Entry resource);
-
-  @Override
-  ODataEntity getODataEntity(Entry resource, URI defaultBaseURI);
-
-  @Override
-  ODataProperty getODataProperty(Property property);
-
-  ODataProperty getODataProperty(Property property, URI base);
+  ODataProperty getODataProperty(ResWrap<Property> resource);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/v3/EdmEnabledODataClient.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/v3/EdmEnabledODataClient.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/v3/EdmEnabledODataClient.java
new file mode 100644
index 0000000..379c8b0
--- /dev/null
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/v3/EdmEnabledODataClient.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.client.api.v3;
+
+import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
+import org.apache.olingo.client.api.communication.request.cud.v3.UpdateType;
+
+public interface EdmEnabledODataClient extends CommonEdmEnabledODataClient<UpdateType>, ODataClient {
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-api/src/main/java/org/apache/olingo/client/api/v4/EdmEnabledODataClient.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/v4/EdmEnabledODataClient.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/v4/EdmEnabledODataClient.java
new file mode 100644
index 0000000..abd341d
--- /dev/null
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/v4/EdmEnabledODataClient.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.olingo.client.api.v4;
+
+import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
+import org.apache.olingo.client.api.communication.request.cud.v4.UpdateType;
+
+public interface EdmEnabledODataClient extends CommonEdmEnabledODataClient<UpdateType>, ODataClient {
+
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/ODataClientFactory.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/ODataClientFactory.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/ODataClientFactory.java
index e0fcd76..a9c87ed 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/ODataClientFactory.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/ODataClientFactory.java
@@ -18,16 +18,32 @@
  */
 package org.apache.olingo.client.core;
 
+import org.apache.olingo.commons.api.format.ODataPubFormat;
+
 public final class ODataClientFactory {
 
   public static org.apache.olingo.client.api.v3.ODataClient getV3() {
     return new org.apache.olingo.client.core.v3.ODataClientImpl();
   }
 
+  public static org.apache.olingo.client.api.v3.EdmEnabledODataClient getEdmEnabledV3(final String serviceRoot) {
+    final org.apache.olingo.client.api.v3.EdmEnabledODataClient instance =
+            new org.apache.olingo.client.core.v3.EdmEnabledODataClientImpl(serviceRoot);
+    instance.getConfiguration().setDefaultPubFormat(ODataPubFormat.JSON);
+    return instance;
+  }
+
   public static org.apache.olingo.client.api.v4.ODataClient getV4() {
     return new org.apache.olingo.client.core.v4.ODataClientImpl();
   }
 
+  public static org.apache.olingo.client.api.v4.EdmEnabledODataClient getEdmEnabledV4(final String serviceRoot) {
+    final org.apache.olingo.client.api.v4.EdmEnabledODataClient instance =
+            new org.apache.olingo.client.core.v4.EdmEnabledODataClientImpl(serviceRoot);
+    instance.getConfiguration().setDefaultPubFormat(ODataPubFormat.JSON);
+    return instance;
+  }
+
   private ODataClientFactory() {
     // empty constructory for static utility class
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractODataRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractODataRequest.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractODataRequest.java
index 8bc4d1c..c8185d4 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractODataRequest.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractODataRequest.java
@@ -364,6 +364,8 @@ public abstract class AbstractODataRequest<T extends Format> extends AbstractReq
    * @return HttpReponse object.
    */
   protected HttpResponse doExecute() {
+    checkRequest(odataClient, request);
+
     // Set Content-Type and Accept headers with default values, if not yet set
     if (StringUtils.isBlank(odataHeaders.getHeader(HeaderName.contentType))) {
       setContentType(getContentType());
@@ -390,7 +392,7 @@ public abstract class AbstractODataRequest<T extends Format> extends AbstractReq
       }
     }
 
-    final HttpResponse response;
+    HttpResponse response;
     try {
       response = this.httpClient.execute(this.request);
     } catch (IOException e) {
@@ -400,7 +402,7 @@ public abstract class AbstractODataRequest<T extends Format> extends AbstractReq
       throw new HttpClientException(e);
     }
 
-    checkForResponse(odataClient, response, getAccept());
+    checkResponse(odataClient, response, getAccept());
 
     return response;
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java
index 25514b8..5779e08 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java
@@ -18,6 +18,8 @@ package org.apache.olingo.client.core.communication.request;
 import java.io.IOException;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
 import org.apache.olingo.client.api.CommonODataClient;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
 import org.apache.olingo.client.api.communication.ODataServerErrorException;
@@ -50,8 +52,21 @@ public abstract class AbstractRequest {
     return error;
   }
 
-  protected <C extends CommonODataClient<?>> void checkForResponse(
-          final C odataClient, final HttpResponse response, final String accept) {
+  protected void checkRequest(final CommonODataClient<?> odataClient, final HttpUriRequest request) {
+    // If using and Edm enabled client, checks that the cached service root matches the request URI
+    if (odataClient instanceof CommonEdmEnabledODataClient
+            && !request.getURI().toASCIIString().startsWith(
+                    ((CommonEdmEnabledODataClient) odataClient).getServiceRoot())) {
+
+      throw new IllegalArgumentException(
+              String.format("The current request URI %s does not match the configured service root %s",
+                      request.getURI().toASCIIString(),
+                      ((CommonEdmEnabledODataClient) odataClient).getServiceRoot()));
+    }
+  }
+
+  protected void checkResponse(
+          final CommonODataClient<?> odataClient, final HttpResponse response, final String accept) {
 
     if (response.getStatusLine().getStatusCode() >= 500) {
       throw new ODataServerErrorException(response.getStatusLine());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
index 7f9ad36..19ed949 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityCreateRequestImpl.java
@@ -33,7 +33,7 @@ import org.apache.olingo.client.api.http.HttpMethod;
 import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.client.core.communication.request.AbstractODataBasicRequest;
 import org.apache.olingo.client.core.communication.response.AbstractODataResponse;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.data.Entry;
 
 /**
@@ -119,10 +119,10 @@ public class ODataEntityCreateRequestImpl<E extends CommonODataEntity>
     public E getBody() {
       if (entity == null) {
         try {
-          final Container<Entry> container = odataClient.getDeserializer().toEntry(getRawResponse(),
-                  ODataPubFormat.fromString(getAccept()));
-
-          entity = (E) odataClient.getBinder().getODataEntity(extractFromContainer(container));
+          final ResWrap<Entry> resource = odataClient.getDeserializer().
+                  toEntry(getRawResponse(), ODataPubFormat.fromString(getAccept()));
+          
+          entity = (E) odataClient.getBinder().getODataEntity(resource);
         } finally {
           this.close();
         }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
index 67be0d4..1134d79 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataEntityUpdateRequestImpl.java
@@ -33,7 +33,7 @@ import org.apache.olingo.client.api.http.HttpMethod;
 import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.client.core.communication.request.AbstractODataBasicRequest;
 import org.apache.olingo.client.core.communication.response.AbstractODataResponse;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.data.Entry;
 
 /**
@@ -124,10 +124,10 @@ public class ODataEntityUpdateRequestImpl<E extends CommonODataEntity>
     public E getBody() {
       if (entity == null) {
         try {
-          final Container<Entry> container = odataClient.getDeserializer().toEntry(getRawResponse(),
-                  ODataPubFormat.fromString(getAccept()));
+          final ResWrap<Entry> resource = odataClient.getDeserializer().
+                  toEntry(getRawResponse(), ODataPubFormat.fromString(getAccept()));
 
-          entity = (E) odataClient.getBinder().getODataEntity(extractFromContainer(container));
+          entity = (E) odataClient.getBinder().getODataEntity(resource);
         } finally {
           this.close();
         }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/15e7718a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataPropertyUpdateRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataPropertyUpdateRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataPropertyUpdateRequestImpl.java
index 26250dc..81e1dd4 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataPropertyUpdateRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/cud/ODataPropertyUpdateRequestImpl.java
@@ -33,7 +33,7 @@ import org.apache.olingo.client.api.http.HttpMethod;
 import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.client.core.communication.request.AbstractODataBasicRequest;
 import org.apache.olingo.client.core.communication.response.AbstractODataResponse;
-import org.apache.olingo.commons.api.data.Container;
+import org.apache.olingo.commons.api.data.ResWrap;
 import org.apache.olingo.commons.api.data.Property;
 
 /**
@@ -118,10 +118,10 @@ public class ODataPropertyUpdateRequestImpl extends AbstractODataBasicRequest<OD
     public CommonODataProperty getBody() {
       if (property == null) {
         try {
-          final Container<Property> container = odataClient.getDeserializer().toProperty(getRawResponse(),
-                  ODataFormat.fromString(getAccept()));
+          final ResWrap<Property> resource = odataClient.getDeserializer().
+                  toProperty(getRawResponse(), ODataFormat.fromString(getAccept()));
 
-          property = odataClient.getBinder().getODataProperty(extractFromContainer(container));
+          property = odataClient.getBinder().getODataProperty(resource);
         } finally {
           this.close();
         }