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/26 16:23:28 UTC

[2/4] [OLINGO-175, OLINGO-205] provided (v4) context url and entity reference retrieving + it tests

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataHeaderValues.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataHeaderValues.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataHeaderValues.java
deleted file mode 100644
index 37c4698..0000000
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataHeaderValues.java
+++ /dev/null
@@ -1,45 +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.client.api.communication.header;
-
-/**
- * Constant header values class.
- */
-public class ODataHeaderValues {
-
-  /**
-   * <code>Prefer</code> header, return content.
-   *
-   * @see ODataHeaders.HeaderName#prefer
-   */
-  public static final String preferReturnContent = "return-content";
-
-  /**
-   * <code>Prefer</code> header, return no content.
-   *
-   * @see ODataHeaders.HeaderName#prefer
-   */
-  public static final String preferReturnNoContent = "return-no-content";
-
-  /**
-   * @see ODataHeaders.HeaderName#dataServiceUrlConventions
-   */
-  public static final String keyAsSegment = "KeyAsSegment";
-
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataPreferences.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataPreferences.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataPreferences.java
new file mode 100644
index 0000000..6afa950
--- /dev/null
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/header/ODataPreferences.java
@@ -0,0 +1,424 @@
+/*
+ * 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.communication.header;
+
+import java.util.Arrays;
+import java.util.List;
+import org.apache.olingo.commons.api.ODataRuntimeException;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+
+/**
+ * Values of the Prefer header.
+ */
+public class ODataPreferences {
+
+  final ODataServiceVersion serviceVersion;
+
+  public ODataPreferences(final ODataServiceVersion serviceVersion) {
+    this.serviceVersion = serviceVersion;
+  }
+
+  /**
+   * <code>Prefer</code> header, return content.
+   *
+   * @see HeaderName#prefer
+   */
+  public String returnContent() {
+    return PreferenceNames.returnContent.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * <code>Prefer</code> header, return no content.
+   *
+   * @see HeaderName#prefer
+   */
+  public String returnNoContent() {
+    return PreferenceNames.returnNoContent.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * @see HeaderName#dataServiceUrlConventions
+   */
+  public String keyAsSegment() {
+    return PreferenceNames.keyAsSegment.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * The odata.allow-entityreferences preference indicates that the service is allowed to return entity references in
+   * place of entities that have previously been returned, with at least the properties requested, in the same response
+   * (for example, when serializing the expanded results of many-to-many relationships). The service MUST NOT return
+   * entity references in place of requested entities if odata.allow-entityreferences has not been specified in the
+   * request, unless explicitly defined by other rules in this document. The syntax of the odata.allow-entityreferences
+   * preference is specified in [OData-ABNF].
+   * <br />
+   * In the case the service applies the odata.allow-entityreferences preference it MUST include a Preference-Applied
+   * response header containing the odata.allow-entityreferences preference to indicate that entity references MAY be
+   * returned in place of entities that have previously been returned.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String allowEntityReferences() {
+    return PreferenceNames.allowEntityReferences.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * For scenarios in which links returned by the service are used by the client to poll for additional information, the
+   * client can specify the odata.callback preference to request that the service notify the client when data is
+   * available.
+   * <br />
+   * The odata.callback preference can be specified:
+   * <ul>
+   * <li>when requesting asynchronous processing of a request with the respond-async preference, or</li>
+   * <li>on a GET request to a delta link.</li>
+   * </ul>
+   * <br />
+   * The odata.callback preference MUST include the parameter url whose value is the URL of a callback endpoint to be
+   * invoked by the OData service when data is available. The syntax of the odata.callback preference is specified in
+   * [OData-ABNF]. For HTTP based callbacks, the OData service executes an HTTP GET request against the specified URL.
+   * <br />
+   * Services that support odata.callback SHOULD support notifying the client through HTTP. Services can advertise
+   * callback support using the Capabilities.CallbackSupport annotation term defined in [OData-VocCap].
+   * <br />
+   * If the service applies the odata.callback preference it MUST include the odata.callback preference in the
+   * Preference-Applied response header.
+   * <br />
+   * When the odata.callback preference is applied to asynchronous requests, the OData service invokes the callback
+   * endpoint once it has finished processing the request. The status monitor resource, returned in the Location header
+   * of the previously returned 202 Accepted response, can then be used to retrieve the results of the asynchronously
+   * executed request.
+   * <br />
+   * When the odata.callback preference is specified on a GET request to a delta link and there are no changes
+   * available, the OData service returns a 202 Accepted response with a Location header specifying the delta link to be
+   * used to check for future updates. The OData service then invokes the specified callback endpoint once new changes
+   * become available.
+   * <br />
+   * Combining respond-async, odata.callback and odata.track-changes preferences on a GET request to a delta-link might
+   * influence the response in a couple of ways.
+   * <ul>
+   * <li>If the service processes the request synchronously, and no updates are available, then the response is the same
+   * as if the respond-async hadn’t been specified and results in a response as described above.</li>
+   * <li>If the service processes the request asynchronously, then it responds with a 202 Accepted response specifying
+   * the URL to the status monitor resource as it would have with any other asynchronous request. Once the service has
+   * finished processing the asynchronous request to the delta link resource, if changes are available it invokes the
+   * specified callback endpoint. If no changes are available, the service SHOULD wait to notify the client until
+   * changes are available. Once notified, the client uses the status monitor resource from the Location header of the
+   * previously returned 202 Accepted response to retrieve the results. In case no updates were available after
+   * processing the initial request, the result will contain no updates and the client can use the delta-link contained
+   * in the result to retrieve the updates that have since become available.</li>
+   * </ul>
+   * <br />
+   * If the consumer specifies the same URL as callback endpoint in multiple requests, the service MAY collate them into
+   * a single notification once additional data is available for any of the requests. However, the consumer MUST be
+   * prepared to deal with receiving up to as many notifications as it requested.
+   * <br /><br />
+   * Example: using a HTTP callback endpoint to receive notification
+   * <br /><br />
+   * Prefer: odata.callback; url="http://myserver/notfication/token/12345"
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String callback(final String url) {
+    return PreferenceNames.callback.isSupportedBy(serviceVersion).toString() + ";url=\"" + url + "\"";
+  }
+
+  /**
+   * The odata.continue-on-error preference on a batch request is used to request that, upon encountering a request
+   * within the batch that returns an error, the service return the error for that request and continue processing
+   * additional requests within the batch. The syntax of the odata.continue-on-error preference is specified in
+   * [OData-ABNF].
+   * <br />
+   * If not specified, upon encountering an error the service MUST return the error within the batch and stop processing
+   * additional requests within the batch.
+   * <br />
+   * A service MAY specify the support for the odata.continue-on-error preference using an annotation with term
+   * Capabilities.BatchContinueOnErrorSupported, see [OData-VocCap].
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String continueOnError() {
+    return PreferenceNames.callback.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * The odata.include-annotations preference in a request for data or metadata is used to specify the set of
+   * annotations the client requests to be included, where applicable, in the response.
+   * <br/>
+   * The value of the odata.include-annotations preference is a comma-separated list of namespaces or namespace
+   * qualified term names to include or exclude, with "*" representing all. The full syntax of the
+   * odata.include-annotations preference is defined in [OData-ABNF].
+   * <br/>
+   * The most specific identifier always takes precedence. If the same identifier value is requested to both be excluded
+   * and included the behavior is undefined; the service MAY return or omit the specified vocabulary but MUST NOT raise
+   * an exception.
+   * <br/><br/>
+   * Example 1: a Prefer header requesting all annotations within a metadata document to be returned
+   * <br/><br/>
+   * Prefer: odata.include-annotations="*"
+   * <br/><br/>
+   * Example 2: a Prefer header requesting that no annotations are returned
+   * <br/><br/>
+   * Prefer: odata.include-annotations="-*"
+   * <br/><br/>
+   * Example 3: a Prefer header requesting that all annotations defined under the "display" namespace (recursively) be
+   * returned
+   * <br/><br/>
+   * Prefer: odata.include-annotations="display.*"
+   * <br/><br/>
+   * Example 4: a Prefer header requesting that the annotation with the term name subject within the display namespace
+   * be returned if applied
+   * <br/><br/>
+   * Prefer: odata.include-annotations="display.subject"
+   * <br/><br/>
+   * The odata.include-annotations preference is only a hint to the service. The service MAY ignore the preference and
+   * is free to decide whether or not to return annotations not specified in the odata.include-annotations preference.
+   * <br/>
+   * In the case that the client has specified the odata.include-annotations preference in the request, the service
+   * SHOULD include a Preference-Applied response header containing the odata.include-annotations preference to specify
+   * the annotations actually included, where applicable, in the response. This value may differ from the annotations
+   * requested in the Prefer header of the request.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String includeAnnotations(final String value) {
+    return PreferenceNames.includeAnnotations.isSupportedBy(serviceVersion).toString() + "=" + value;
+  }
+
+  /**
+   * The odata.maxpagesize preference is used to request that each collection within the response contain no more than
+   * the number of items specified as the positive integer value of this preference. The syntax of the odata.maxpagesize
+   * preference is specified in [OData-ABNF].
+   * <br/><br/>
+   * Example: a request for customers and their orders would result in a response containing one collection with
+   * customer entities and for every customer a separate collection with order entities. The client could specify
+   * <br/>
+   * odata.maxpagesize=50
+   * <br/>in order to request that each page of results contain a maximum of 50 customers, each with a maximum of 50
+   * orders.
+   * <br/><br/>
+   * If a collection within the result contains more than the specified odata.maxpagesize, the collection SHOULD be a
+   * partial set of the results with a next link to the next page of results. The client MAY specify a different value
+   * for this preference with every request following a next link.
+   * <br/>
+   * In the example given above, the result page should include a next link for the customer collection, if there are
+   * more than 50 customers, and additional next links for all returned orders collections with more than 50 entities.
+   * <br/>
+   * If the client has specified the odata.maxpagesize preference in the request, and the service limits the number of
+   * items in collections within the response through server-driven paging, the service MAY include a Preference-Applied
+   * response header containing the odata.maxpagesize preference and the maximum page size applied. This value may
+   * differ from the value requested by the client.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String maxPageSize(final int size) {
+    return PreferenceNames.maxPageSize.isSupportedBy(serviceVersion).toString() + "=" + size;
+  }
+
+  /**
+   * The odata.track-changes preference is used to request that the service return a delta link that can subsequently be
+   * used to obtain changes (deltas) to this result. The syntax of the odata.track-changes preference is specified in
+   * [OData-ABNF].
+   * <br />
+   * For paged results, the preference MUST be specified on the initial request. Services MUST ignore the
+   * odata.track-changes preference if applied to the next link.
+   * <br />
+   * The delta link MUST NOT be returned prior to the final page of results.
+   * <br />
+   * The service includes a Preference-Applied response header in the first page of the response containing the
+   * odata.track-changes preference to signal that changes are being tracked.
+   * <br />
+   * A service MAY specify the support for the odata.track-changes preference using an annotation with term
+   * Capabilities.ChangeTrackingSupport, see [OData-VocCap].
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String trackChanges() {
+    return PreferenceNames.trackChanges.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * The respond-async preference, as defined in [HTTP-Prefer], allows clients to request that the service process the
+   * request asynchronously.
+   * <br/>
+   * If the client has specified respond-async in the request, the service MAY process the request asynchronously and
+   * return a 202 Accepted response.
+   * <br/>
+   * The respond-async preference MAY be used for batch requests, but the service MUST ignore the respond-async
+   * preference for individual requests within a batch request.
+   * <br/>
+   * In the case that the service applies the respond-async preference it MUST include a Preference-Applied response
+   * header containing the respond-async preference.
+   * <br/>
+   * A service MAY specify the support for the respond-async preference using an annotation with term
+   * Capabilities.AsynchronousRequestsSupported, see [OData-VocCap].
+   * <br/><br/>
+   * Example: a service receiving the following header might choose to respond
+   * <ul>
+   * <li>asynchronously if the synchronous processing of the request will take longer than 10 seconds</li>
+   * <li>synchronously after 5 seconds</li>
+   * <li>asynchronously (ignoring the wait preference)</li>
+   * <li>synchronously after 15 seconds (ignoring respond-async preference and the wait preference)</li>
+   * </ul>
+   * <br/>
+   * Prefer: respond-async, wait=10
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String respondAsync() {
+    return PreferenceNames.respondAsync.isSupportedBy(serviceVersion).toString();
+  }
+
+  /**
+   * The wait preference, as defined in [HTTP-Prefer], is used to establish an upper bound on the length of time, in
+   * seconds, the client is prepared to wait for the service to process the request synchronously once it has been
+   * received.
+   * <br/>
+   * If the respond-async preference is also specified, the client requests that the service respond asynchronously
+   * after the specified length of time.
+   * <br/>
+   * If the respond-async preference has not been specified, the service MAY interpret the wait as a request to timeout
+   * after the specified period of time.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String wait(final int value) {
+    return PreferenceNames.wait.isSupportedBy(serviceVersion).toString() + "=" + value;
+  }
+
+  /**
+   * The return=representation and return=minimal preferences are defined in [HTTP-Prefer],
+   * <br/>
+   * In OData, return=representation or return=minimal is defined for use with a POST, PUT, or PATCH Data Modification
+   * Request other than to a stream property, or to an Action Request. Specifying a preference of return=representation
+   * or return=minimal in a GET or DELETE request, or any request to a stream property, SHOULD return a 4xx Client
+   * Error.
+   * <br/>
+   * A preference of return=representation or return=minimal is allowed on an individual Data Modification Request or
+   * Action Request within a batch, subject to the same restrictions, but SHOULD return a 4xx Client Error if specified
+   * on the batch request itself.
+   * <br/>
+   * A preference of return=minimal requests that the service invoke the request but does not return content in the
+   * response. The service MAY apply this preference by returning 204 No Content in which case it MAY include a
+   * Preference-Applied response header containing the return=minimal preference.
+   * <br/>
+   * A preference of return=representation requests that the service invokes the request and returns the modified
+   * entity. The service MAY apply this preference by returning the successfully modified resource in the body of the
+   * response, formatted according to the rules specified for the requested format. In this case the service MAY include
+   * a Preference-Applied response header containing the return=representation preference.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String returnMinimal() {
+    return PreferenceNames.odataReturn.isSupportedBy(serviceVersion).toString() + "=minimal";
+  }
+
+  /**
+   * The return=representation and return=minimal preferences are defined in [HTTP-Prefer],
+   * <br/>
+   * In OData, return=representation or return=minimal is defined for use with a POST, PUT, or PATCH Data Modification
+   * Request other than to a stream property, or to an Action Request. Specifying a preference of return=representation
+   * or return=minimal in a GET or DELETE request, or any request to a stream property, SHOULD return a 4xx Client
+   * Error.
+   * <br/>
+   * A preference of return=representation or return=minimal is allowed on an individual Data Modification Request or
+   * Action Request within a batch, subject to the same restrictions, but SHOULD return a 4xx Client Error if specified
+   * on the batch request itself.
+   * <br/>
+   * A preference of return=minimal requests that the service invoke the request but does not return content in the
+   * response. The service MAY apply this preference by returning 204 No Content in which case it MAY include a
+   * Preference-Applied response header containing the return=minimal preference.
+   * <br/>
+   * A preference of return=representation requests that the service invokes the request and returns the modified
+   * entity. The service MAY apply this preference by returning the successfully modified resource in the body of the
+   * response, formatted according to the rules specified for the requested format. In this case the service MAY include
+   * a Preference-Applied response header containing the return=representation preference.
+   * <br/><br/>
+   * Supported by OData version 4.0 only.
+   *
+   * @see HeaderName#prefer
+   * @return preference.
+   */
+  public String returnRepresentation() {
+    return PreferenceNames.odataReturn.isSupportedBy(serviceVersion).toString() + "=representation";
+  }
+
+  private static enum PreferenceNames {
+
+    returnContent("return-content", Arrays.asList(ODataServiceVersion.V30, ODataServiceVersion.V40)),
+    returnNoContent("return-no-content", Arrays.asList(ODataServiceVersion.V30, ODataServiceVersion.V40)),
+    keyAsSegment("KeyAsSegment", Arrays.asList(ODataServiceVersion.V30, ODataServiceVersion.V40)),
+    allowEntityReferences("odata.allow-entityreferences", Arrays.asList(ODataServiceVersion.V40)),
+    callback("odata.callback", Arrays.asList(ODataServiceVersion.V40)),
+    continueOnError("odata.continue-on-error", Arrays.asList(ODataServiceVersion.V40)),
+    includeAnnotations("odata.include-annotations", Arrays.asList(ODataServiceVersion.V40)),
+    maxPageSize("odata.maxpagesize", Arrays.asList(ODataServiceVersion.V40)),
+    trackChanges("odata.track-changes", Arrays.asList(ODataServiceVersion.V40)),
+    respondAsync("respond-async", Arrays.asList(ODataServiceVersion.V40)),
+    wait("wait", Arrays.asList(ODataServiceVersion.V40)),
+    odataReturn("return", Arrays.asList(ODataServiceVersion.V40));
+
+    private final String preferenceName;
+
+    private final List<ODataServiceVersion> supportedVersions;
+
+    private PreferenceNames(final String preferenceName, final List<ODataServiceVersion> supportedVersions) {
+      this.preferenceName = preferenceName;
+      this.supportedVersions = supportedVersions;
+    }
+
+    final PreferenceNames isSupportedBy(final ODataServiceVersion serviceVersion) {
+      if (!supportedVersions.contains(serviceVersion)) {
+        throw new ODataRuntimeException("Unsupported header " + this.toString());
+      }
+
+      return this;
+    }
+
+    @Override
+    public String toString() {
+      return preferenceName;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/ODataRequest.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/ODataRequest.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/ODataRequest.java
index 8ea098c..e9a2c90 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/ODataRequest.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/ODataRequest.java
@@ -21,16 +21,20 @@ package org.apache.olingo.client.api.communication.request;
 import java.io.InputStream;
 import java.net.URI;
 import java.util.Collection;
+import org.apache.olingo.client.api.communication.header.HeaderName;
 import org.apache.olingo.client.api.http.HttpMethod;
 
 /**
  * Abstract representation of an OData request. Get instance by using factories.
  *
- * @see CUDRequestFactory
- * @see RetrieveRequestFactory
- * @see BatchRequestFactory
- * @see InvokeRequestFactory
- * @see StreamedRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.cud.v3.CUDRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.cud.v4.CUDRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.batch.v3.BatchRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.batch.v4.BatchRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.invoke.v3.InvokeRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.invoke.v4.InvokeRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.streamed.v3.StreamedRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.streamed.v4.StreamedRequestFactory
  */
 public interface ODataRequest {
 
@@ -176,6 +180,16 @@ public interface ODataRequest {
   ODataRequest addCustomHeader(final String name, final String value);
 
   /**
+   * Adds a custom OData request header. The method fails in case of the header name is not supported by the current
+   * working version.
+   *
+   * @param name header name.
+   * @param value header value.
+   * @return current object
+   */
+  ODataRequest addCustomHeader(final HeaderName name, final String value);
+
+  /**
    * Gets byte array representation of the full request header.
    *
    * @return full request header.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/SegmentType.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/SegmentType.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/SegmentType.java
index a3e5a15..5da7f09 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/SegmentType.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/SegmentType.java
@@ -34,6 +34,7 @@ public enum SegmentType {
   NAVIGATION,
   DERIVED_ENTITY_TYPE,
   VALUE("$value"),
+  COUNT("$count"),
   BOUND_OPERATION,
   UNBOUND_OPERATION,
   METADATA("$metadata"),

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v3/URIBuilder.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v3/URIBuilder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v3/URIBuilder.java
index 72a0c03..d81edbf 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v3/URIBuilder.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v3/URIBuilder.java
@@ -42,8 +42,7 @@ public interface URIBuilder extends CommonURIBuilder<URIBuilder> {
    *
    * @param inlineCount value
    * @return current URIBuilder instance
-   * @see QueryOption#INLINECOUNT
+   * @see org.apache.olingo.client.api.uri.QueryOption#INLINECOUNT
    */
   URIBuilder inlineCount(InlineCount inlineCount);
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v4/URIBuilder.java
----------------------------------------------------------------------
diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v4/URIBuilder.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v4/URIBuilder.java
index 5fc8a1d..7aed3c0 100644
--- a/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v4/URIBuilder.java
+++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/uri/v4/URIBuilder.java
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.client.api.uri.v4;
 
+import java.util.Map;
 import org.apache.olingo.client.api.uri.CommonURIBuilder;
 
 public interface URIBuilder extends CommonURIBuilder<URIBuilder> {
@@ -65,7 +66,7 @@ public interface URIBuilder extends CommonURIBuilder<URIBuilder> {
    *
    * @param idValue opaque token.
    * @return current URIBuilder instance
-   * @see QueryOption#ID
+   * @see org.apache.olingo.client.api.uri.QueryOption#ID
    */
   URIBuilder id(String idValue);
 
@@ -74,7 +75,7 @@ public interface URIBuilder extends CommonURIBuilder<URIBuilder> {
    *
    * @param value true or false
    * @return current URIBuilder instance
-   * @see QueryOption#COUNT
+   * @see org.apache.olingo.client.api.uri.QueryOption#COUNT
    */
   URIBuilder count(boolean value);
 
@@ -83,7 +84,19 @@ public interface URIBuilder extends CommonURIBuilder<URIBuilder> {
    *
    * @param expression search expression
    * @return current URIBuilder instance
-   * @see QueryOption#SEARCH
+   * @see org.apache.olingo.client.api.uri.QueryOption#SEARCH
    */
   URIBuilder search(String expression);
+
+  /**
+   * The set of expanded entities can be refined through the application of expand options, expressed as a
+   * semicolon-separated list of system query options, enclosed in parentheses, see [OData-URL].
+   *
+   * @param expandItem item to be expanded.
+   * @param options System query options. Allowed query options are: $filter, $select, $orderby, $skip, $top, $count,
+   * $search, $expand, and $levels.
+   * @return current URIBuilder instance.
+   * @see org.apache.olingo.client.api.uri.QueryOption#EXPAND
+   */
+  URIBuilder expandWithOptions(String expandItem, Map<String, Object> options);
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/ODataRequestImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/ODataRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/ODataRequestImpl.java
index 3e6c1bb..0a8c495 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/ODataRequestImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/ODataRequestImpl.java
@@ -38,14 +38,10 @@ import org.apache.olingo.client.api.v3.Configuration;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
 import org.apache.olingo.client.api.communication.ODataServerErrorException;
 import org.apache.olingo.client.api.communication.header.HeaderName;
-import org.apache.olingo.client.api.communication.header.ODataHeaderValues;
 import org.apache.olingo.client.api.communication.header.ODataHeaders;
+import org.apache.olingo.client.api.communication.header.ODataPreferences;
 import org.apache.olingo.client.api.communication.request.ODataRequest;
 import org.apache.olingo.client.api.communication.request.ODataStreamer;
-import org.apache.olingo.client.api.communication.request.batch.v3.BatchRequestFactory;
-import org.apache.olingo.client.api.communication.request.cud.v3.CUDRequestFactory;
-import org.apache.olingo.client.api.communication.request.invoke.v3.InvokeRequestFactory;
-import org.apache.olingo.client.api.communication.request.streamed.v3.StreamedRequestFactory;
 import org.apache.olingo.client.api.communication.response.ODataResponse;
 import org.apache.olingo.commons.api.format.Format;
 import org.apache.olingo.client.api.http.HttpClientException;
@@ -66,10 +62,14 @@ import org.slf4j.LoggerFactory;
  *
  * @param <T> Accepted content-type formats by the request in object.
  *
- * @see CUDRequestFactory
- * @see BatchRequestFactory
- * @see InvokeRequestFactory
- * @see StreamedRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.cud.v3.CUDRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.cud.v4.CUDRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.batch.v3.BatchRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.batch.v4.BatchRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.invoke.v3.InvokeRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.invoke.v4.InvokeRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.streamed.v3.StreamedRequestFactory
+ * @see org.apache.olingo.client.api.communication.request.streamed.v4.StreamedRequestFactory
  */
 public class ODataRequestImpl<T extends Format> implements ODataRequest {
 
@@ -254,6 +254,15 @@ public class ODataRequestImpl<T extends Format> implements ODataRequest {
    * {@inheritDoc}
    */
   @Override
+  public ODataRequest addCustomHeader(final HeaderName name, final String value) {
+    odataHeaders.setHeader(name, value);
+    return this;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
   public String getAccept() {
     final String acceptHead = odataHeaders.getHeader(HeaderName.accept);
     return StringUtils.isBlank(acceptHead) ? getDefaultFormat().toString(odataClient.getServiceVersion()) : acceptHead;
@@ -384,7 +393,8 @@ public class ODataRequestImpl<T extends Format> implements ODataRequest {
     if (odataClient.getServiceVersion() == ODataServiceVersion.V30
             && ((Configuration) odataClient.getConfiguration()).isKeyAsSegment()) {
       addCustomHeader(
-              HeaderName.dataServiceUrlConventions.toString(), ODataHeaderValues.keyAsSegment);
+              HeaderName.dataServiceUrlConventions.toString(),
+              new ODataPreferences(odataClient.getServiceVersion()).keyAsSegment());
     }
 
     // Add all available headers

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONServiceDocumentDeserializer.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONServiceDocumentDeserializer.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONServiceDocumentDeserializer.java
index 143aed8..8594f90 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONServiceDocumentDeserializer.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/data/JSONServiceDocumentDeserializer.java
@@ -57,7 +57,7 @@ public class JSONServiceDocumentDeserializer extends ODataJacksonDeserializer<Ab
               setMetadataContext(tree.get(Constants.JSON_CONTEXT).textValue());
     }
 
-    for (final Iterator<JsonNode> itor = tree.get(Constants.JSON_VALUE).elements(); itor.hasNext();) {
+    for (final Iterator<JsonNode> itor = tree.get(Constants.VALUE).elements(); itor.hasNext();) {
       final JsonNode node = itor.next();
 
       final ServiceDocumentItemImpl item = new ServiceDocumentItemImpl();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/op/AbstractODataBinder.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/op/AbstractODataBinder.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/op/AbstractODataBinder.java
index 44e7ad4..bdc3536 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/op/AbstractODataBinder.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/op/AbstractODataBinder.java
@@ -87,21 +87,22 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
   }
 
   @Override
-  public Feed getFeed(final ODataEntitySet feed, final Class<? extends Feed> reference) {
-    final Feed feedResource = ResourceFactory.newFeed(reference);
+  public Feed getFeed(final ODataEntitySet entitySet, final Class<? extends Feed> reference) {
+    final Feed feed = ResourceFactory.newFeed(reference);
 
-    feedResource.setCount(feed.getCount());
+    feed.setContextURL(entitySet.getContextURL());
+    feed.setCount(entitySet.getCount());
 
-    final URI next = feed.getNext();
+    final URI next = entitySet.getNext();
     if (next != null) {
-      feedResource.setNext(next);
+      feed.setNext(next);
     }
 
-    for (ODataEntity entity : feed.getEntities()) {
-      feedResource.getEntries().add(getEntry(entity, ResourceFactory.entryClassForFeed(reference)));
+    for (ODataEntity entity : entitySet.getEntities()) {
+      feed.getEntries().add(getEntry(entity, ResourceFactory.entryClassForFeed(reference)));
     }
 
-    return feedResource;
+    return feed;
   }
 
   @Override
@@ -112,6 +113,9 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
   @Override
   public Entry getEntry(final ODataEntity entity, final Class<? extends Entry> reference, final boolean setType) {
     final Entry entry = ResourceFactory.newEntry(reference);
+
+    entry.setContextURL(entity.getContextURL());
+    entry.setId(entity.getReference());
     entry.setType(entity.getName());
 
     // -------------------------------------------------------------
@@ -269,13 +273,15 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
     }
 
     final URI base = defaultBaseURI == null ? resource.getBaseURI() : defaultBaseURI;
-    
+
     final URI next = resource.getNext();
 
     final ODataEntitySet entitySet = next == null
             ? client.getObjectFactory().newEntitySet()
             : client.getObjectFactory().newEntitySet(URIUtils.getURI(base, next.toASCIIString()));
 
+    entitySet.setContextURL(resource.getContextURL());
+
     if (resource.getCount() != null) {
       entitySet.setCount(resource.getCount());
     }
@@ -283,7 +289,7 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
     for (Entry entryResource : resource.getEntries()) {
       entitySet.addEntity(getODataEntity(entryResource));
     }
-    
+
     return entitySet;
   }
 
@@ -306,16 +312,19 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
     final ODataEntity entity = resource.getSelfLink() == null
             ? client.getObjectFactory().newEntity(resource.getType())
             : client.getObjectFactory().newEntity(resource.getType(),
-                    URIUtils.getURI(base, resource.getSelfLink().getHref()));
+            URIUtils.getURI(base, resource.getSelfLink().getHref()));
+
+    entity.setContextURL(resource.getContextURL());
+    entity.setReference(resource.getId());
 
     if (StringUtils.isNotBlank(resource.getETag())) {
       entity.setETag(resource.getETag());
     }
-    
+
     if (resource.getEditLink() != null) {
       entity.setEditLink(URIUtils.getURI(base, resource.getEditLink().getHref()));
     }
-    
+
     for (Link link : resource.getAssociationLinks()) {
       entity.addLink(client.getObjectFactory().newAssociationLink(link.getTitle(), base, link.getHref()));
     }
@@ -327,16 +336,16 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
       if (inlineEntry == null && inlineFeed == null) {
         entity.addLink(
                 client.getObjectFactory().newEntityNavigationLink(link.getTitle(), base, link.getHref()));
-      } else if (inlineFeed == null) {
+      } else if (inlineEntry != null) {
         entity.addLink(client.getObjectFactory().newInlineEntity(
                 link.getTitle(), base, link.getHref(),
                 getODataEntity(inlineEntry,
-                        inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
+                inlineEntry.getBaseURI() == null ? base : inlineEntry.getBaseURI())));
       } else {
         entity.addLink(client.getObjectFactory().newInlineEntitySet(
                 link.getTitle(), base, link.getHref(),
                 getODataEntitySet(inlineFeed,
-                        inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
+                inlineFeed.getBaseURI() == null ? base : inlineFeed.getBaseURI())));
       }
     }
 
@@ -374,16 +383,16 @@ public abstract class AbstractODataBinder implements CommonODataBinder {
       value = client.getPrimitiveValueBuilder().
               setText(resource.getValue().asSimple().get()).
               setType(resource.getType() == null
-                      ? null
-                      : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), resource.getType())).build();
+              ? null
+              : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), resource.getType())).build();
     } else if (resource.getValue().isGeospatial()) {
       value = client.getGeospatialValueBuilder().
               setValue(resource.getValue().asGeospatial().get()).
               setType(resource.getType() == null
-                      || EdmPrimitiveTypeKind.Geography.getFullQualifiedName().toString().equals(resource.getType())
-                      || EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().toString().equals(resource.getType())
-                      ? null
-                      : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), resource.getType())).build();
+              || EdmPrimitiveTypeKind.Geography.getFullQualifiedName().toString().equals(resource.getType())
+              || EdmPrimitiveTypeKind.Geometry.getFullQualifiedName().toString().equals(resource.getType())
+              ? null
+              : EdmPrimitiveTypeKind.valueOfFQN(client.getServiceVersion(), resource.getType())).build();
     } else if (resource.getValue().isComplex()) {
       value = new ODataComplexValue(resource.getType());
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/AbstractURIBuilder.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/AbstractURIBuilder.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/AbstractURIBuilder.java
index 2beecb3..732789c 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/AbstractURIBuilder.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/AbstractURIBuilder.java
@@ -21,6 +21,7 @@ package org.apache.olingo.client.core.uri;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -60,9 +61,7 @@ public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> impleme
     public String getValue() {
       return value;
     }
-
   }
-
   protected final List<Segment> segments = new ArrayList<Segment>();
 
   /**
@@ -111,17 +110,11 @@ public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> impleme
 
   @Override
   public UB appendKeySegment(final Map<String, Object> segmentValues) {
-    if (segmentValues == null || segmentValues.isEmpty()) {
+    final String key = buildMultiKeySegment(segmentValues, true);
+    if (StringUtils.isEmpty(key)) {
       segments.add(new Segment(SegmentType.KEY, noKeysWrapper()));
     } else {
-      final StringBuilder keyBuilder = new StringBuilder().append('(');
-      for (Map.Entry<String, Object> entry : segmentValues.entrySet()) {
-        keyBuilder.append(entry.getKey()).append('=').append(URIUtils.escape(entry.getValue()));
-        keyBuilder.append(',');
-      }
-      keyBuilder.deleteCharAt(keyBuilder.length() - 1).append(')');
-
-      segments.add(new Segment(SegmentType.KEY, keyBuilder.toString()));
+      segments.add(new Segment(SegmentType.KEY, key));
     }
 
     return getThis();
@@ -179,7 +172,14 @@ public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> impleme
 
   @Override
   public UB expand(final String... expandItems) {
-    return addQueryOption(QueryOption.EXPAND, StringUtils.join(expandItems, ","));
+    final List<String> values = new ArrayList<String>();
+    if (queryOptions.containsKey(QueryOption.EXPAND.toString())) {
+      values.add(queryOptions.get(QueryOption.EXPAND.toString()));
+    }
+
+    values.addAll(Arrays.asList(expandItems));
+
+    return addQueryOption(QueryOption.EXPAND, StringUtils.join(values, ","));
   }
 
   @Override
@@ -199,7 +199,14 @@ public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> impleme
 
   @Override
   public UB select(final String... selectItems) {
-    return addQueryOption(QueryOption.SELECT, StringUtils.join(selectItems, ","));
+    final List<String> values = new ArrayList<String>();
+    if (queryOptions.containsKey(QueryOption.SELECT.toString())) {
+      values.add(queryOptions.get(QueryOption.SELECT.toString()));
+    }
+
+    values.addAll(Arrays.asList(selectItems));
+
+    return addQueryOption(QueryOption.SELECT, StringUtils.join(values, ","));
   }
 
   @Override
@@ -271,4 +278,19 @@ public abstract class AbstractURIBuilder<UB extends CommonURIBuilder<?>> impleme
     return build().toASCIIString();
   }
 
+  protected String buildMultiKeySegment(final Map<String, Object> segmentValues, final boolean escape) {
+    if (segmentValues == null || segmentValues.isEmpty()) {
+      return StringUtils.EMPTY;
+    } else {
+      final StringBuilder keyBuilder = new StringBuilder().append('(');
+      for (Map.Entry<String, Object> entry : segmentValues.entrySet()) {
+        keyBuilder.append(entry.getKey()).append('=').append(
+                escape ? URIUtils.escape(entry.getValue()) : entry.getValue());
+        keyBuilder.append(',');
+      }
+      keyBuilder.deleteCharAt(keyBuilder.length() - 1).append(')');
+
+      return keyBuilder.toString();
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/v4/URIBuilderImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/v4/URIBuilderImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/v4/URIBuilderImpl.java
index ae4284d..fa66f02 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/v4/URIBuilderImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/uri/v4/URIBuilderImpl.java
@@ -18,6 +18,7 @@
  */
 package org.apache.olingo.client.core.uri.v4;
 
+import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.olingo.client.api.uri.QueryOption;
 import org.apache.olingo.client.api.uri.SegmentType;
@@ -79,11 +80,6 @@ public class URIBuilderImpl extends AbstractURIBuilder<URIBuilder> implements UR
   }
 
   @Override
-  public URIBuilder count(final boolean value) {
-    return addQueryOption(QueryOption.COUNT, Boolean.toString(value));
-  }
-
-  @Override
   public URIBuilder appendAllSegment() {
     segments.add(new Segment(SegmentType.ALL, SegmentType.ALL.getValue()));
     return getThis();
@@ -99,4 +95,13 @@ public class URIBuilderImpl extends AbstractURIBuilder<URIBuilder> implements UR
     return addQueryOption(QueryOption.SEARCH, expression);
   }
 
+  @Override
+  public URIBuilder count(final boolean value) {
+    return addQueryOption(QueryOption.COUNT, Boolean.toString(value));
+  }
+
+  @Override
+  public URIBuilder expandWithOptions(final String expandItem, final Map<String, Object> options) {
+    return expand(expandItem + buildMultiKeySegment(options, false));
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/main/java/org/apache/olingo/client/core/v4/ODataClientImpl.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/v4/ODataClientImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/v4/ODataClientImpl.java
index bb12f1a..5a31e2c 100644
--- a/lib/client-core/src/main/java/org/apache/olingo/client/core/v4/ODataClientImpl.java
+++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/v4/ODataClientImpl.java
@@ -82,8 +82,8 @@ public class ODataClientImpl extends AbstractODataClient implements ODataClient
   @Override
   public ODataHeaders getVersionHeaders() {
     final ODataHeadersImpl odataHeaders = new ODataHeadersImpl();
-    odataHeaders.setHeader(HeaderName.maxDataServiceVersion, ODataServiceVersion.V40.toString());
-    odataHeaders.setHeader(HeaderName.dataServiceVersion, ODataServiceVersion.V40.toString());
+    odataHeaders.setHeader(HeaderName.odataMaxVersion, ODataServiceVersion.V40.toString());
+    odataHeaders.setHeader(HeaderName.odataVersion, ODataServiceVersion.V40.toString());
     return odataHeaders;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityCreateTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityCreateTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityCreateTestITCase.java
index 8146830..1c331e5 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityCreateTestITCase.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityCreateTestITCase.java
@@ -30,7 +30,7 @@ import java.util.LinkedHashMap;
 import java.util.Set;
 import org.apache.http.entity.ContentType;
 import org.apache.olingo.client.api.communication.header.HeaderName;
-import org.apache.olingo.client.api.communication.header.ODataHeaderValues;
+import org.apache.olingo.client.api.communication.header.ODataPreferences;
 import org.apache.olingo.client.api.communication.request.UpdateType;
 import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
 import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
@@ -229,11 +229,11 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
 
     final ODataEntityCreateRequest createReq = client.getCUDRequestFactory().getEntityCreateRequest(
             client.getURIBuilder(getServiceRoot()).appendEntitySetSegment("Customer").build(), original);
-    createReq.setPrefer(ODataHeaderValues.preferReturnNoContent);
+    createReq.setPrefer(new ODataPreferences(client.getServiceVersion()).returnNoContent());
 
     final ODataEntityCreateResponse createRes = createReq.execute();
     assertEquals(204, createRes.getStatusCode());
-    assertEquals(ODataHeaderValues.preferReturnNoContent,
+    assertEquals(new ODataPreferences(client.getServiceVersion()).returnNoContent(),
             createRes.getHeader(HeaderName.preferenceApplied).iterator().next());
 
     try {
@@ -260,7 +260,7 @@ public class EntityCreateTestITCase extends AbstractTestITCase {
             client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), original);
     createReq.setFormat(ODataPubFormat.JSON_FULL_METADATA);
     createReq.setContentType(ContentType.APPLICATION_ATOM_XML.getMimeType());
-    createReq.setPrefer(ODataHeaderValues.preferReturnContent);
+    createReq.setPrefer(new ODataPreferences(client.getServiceVersion()).returnContent());
 
     try {
       final ODataEntityCreateResponse createRes = createReq.execute();

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityUpdateTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityUpdateTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityUpdateTestITCase.java
index 129f086..8a9f93f 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityUpdateTestITCase.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/EntityUpdateTestITCase.java
@@ -26,7 +26,7 @@ import java.net.URI;
 import java.util.LinkedHashMap;
 import org.apache.olingo.client.api.communication.ODataClientErrorException;
 import org.apache.olingo.client.api.communication.header.HeaderName;
-import org.apache.olingo.client.api.communication.header.ODataHeaderValues;
+import org.apache.olingo.client.api.communication.header.ODataPreferences;
 import org.apache.olingo.client.api.communication.request.UpdateType;
 import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
 import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
@@ -210,11 +210,11 @@ public class EntityUpdateTestITCase extends AbstractTestITCase {
   @Test
   public void updateReturnContent() throws EdmPrimitiveTypeException {
     final ODataEntityUpdateRequest req = buildMultiKeyUpdateReq(client.getConfiguration().getDefaultPubFormat());
-    req.setPrefer(ODataHeaderValues.preferReturnContent);
+    req.setPrefer(new ODataPreferences(client.getServiceVersion()).returnContent());
 
     final ODataEntityUpdateResponse res = req.execute();
     assertEquals(200, res.getStatusCode());
-    assertEquals(ODataHeaderValues.preferReturnContent,
+    assertEquals(new ODataPreferences(client.getServiceVersion()).returnContent(),
             res.getHeader(HeaderName.preferenceApplied).iterator().next());
     assertNotNull(res.getBody());
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/PropertyValueTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/PropertyValueTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/PropertyValueTestITCase.java
index b8dd0a8..3660cad 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/PropertyValueTestITCase.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v3/PropertyValueTestITCase.java
@@ -100,11 +100,11 @@ public class PropertyValueTestITCase extends AbstractTestITCase {
     req.setAccept("application/json");
     ODataRetrieveResponse<ODataEntity> res = req.execute();
     assertEquals(200, res.getStatusCode());
-    ODataEntity entitySet = res.getBody();
-    assertNotNull(entitySet);
+    ODataEntity entity = res.getBody();
+    assertNotNull(entity);
     assertEquals("fi653p3+MklA/LdoBlhWgnMTUUEo8tEgtbMXnF0a3CUNL9BZxXpSRiD9ebTnmNR0zWPjJ"
             + "VIDx4tdmCnq55XrJh+RW9aI/b34wAogK3kcORw=",
-            entitySet.getProperties().get(0).getValue().toString());
+            entity.getProperties().get(0).getValue().toString());
   }
 
   @Test(expected = ODataClientErrorException.class)

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java
new file mode 100644
index 0000000..a96384f
--- /dev/null
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntityRetrieveTestITCase.java
@@ -0,0 +1,280 @@
+/*
+ * 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.core.it.v4;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataRawRequest;
+import org.apache.olingo.client.api.communication.response.ODataRawResponse;
+import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.commons.api.domain.ODataEntity;
+import org.apache.olingo.commons.api.domain.ODataEntitySet;
+import org.apache.olingo.commons.api.domain.ODataInlineEntity;
+import org.apache.olingo.commons.api.domain.ODataInlineEntitySet;
+import org.apache.olingo.commons.api.domain.ODataLink;
+import org.apache.olingo.commons.api.domain.ODataProperty;
+import org.apache.olingo.commons.api.format.ODataPubFormat;
+import org.apache.olingo.client.api.uri.CommonURIBuilder;
+import org.apache.olingo.commons.core.op.ResourceFactory;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * This is the unit test class to check entity retrieve operations.
+ */
+public class EntityRetrieveTestITCase extends AbstractTestITCase {
+
+  protected String getServiceRoot() {
+    return testStaticServiceRootURL;
+  }
+
+  private void withInlineEntry(final ODataPubFormat format) {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).
+            appendEntitySetSegment("Customers").appendKeySegment(1).expand("Company");
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(format);
+
+    final ODataRetrieveResponse<ODataEntity> res = req.execute();
+    final ODataEntity entity = res.getBody();
+
+    assertNotNull(entity);
+    assertEquals("#Microsoft.Test.OData.Services.ODataWCFService.Customer", entity.getName());
+    assertEquals(getServiceRoot() + "/Customers(PersonID=1)", entity.getEditLink().toASCIIString());
+
+    assertEquals(3, entity.getNavigationLinks().size());
+    assertTrue(entity.getAssociationLinks().isEmpty());
+
+    boolean found = false;
+
+    for (ODataLink link : entity.getNavigationLinks()) {
+      if (link instanceof ODataInlineEntity) {
+        final ODataEntity inline = ((ODataInlineEntity) link).getEntity();
+        assertNotNull(inline);
+
+        debugEntry(client.getBinder().getEntry(
+                inline, ResourceFactory.entryClassForFormat(format == ODataPubFormat.ATOM)), "Just read");
+
+        final List<ODataProperty> properties = inline.getProperties();
+        assertEquals(5, properties.size());
+
+        assertTrue(properties.get(0).getName().equals("CompanyID")
+                || properties.get(1).getName().equals("CompanyID")
+                || properties.get(2).getName().equals("CompanyID")
+                || properties.get(3).getName().equals("CompanyID")
+                || properties.get(4).getName().equals("CompanyID"));
+        assertTrue(properties.get(0).getValue().toString().equals("0")
+                || properties.get(1).getValue().toString().equals("0")
+                || properties.get(2).getValue().toString().equals("0")
+                || properties.get(3).getValue().toString().equals("0")
+                || properties.get(4).getValue().toString().equals("0"));
+
+        found = true;
+      }
+    }
+
+    assertTrue(found);
+  }
+
+  @Test
+  public void withInlineEntryFromAtom() {
+    withInlineEntry(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void withInlineEntryFromJSON() {
+    // this needs to be full, otherwise there is no mean to recognize links
+    withInlineEntry(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  private void withInlineFeed(final ODataPubFormat format) {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).
+            appendEntitySetSegment("Customers").appendKeySegment(1).expand("Orders");
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(format);
+
+    final ODataRetrieveResponse<ODataEntity> res = req.execute();
+    final ODataEntity entity = res.getBody();
+    assertNotNull(entity);
+
+    boolean found = false;
+
+    for (ODataLink link : entity.getNavigationLinks()) {
+      if (link instanceof ODataInlineEntitySet) {
+        final ODataEntitySet inline = ((ODataInlineEntitySet) link).getEntitySet();
+        assertNotNull(inline);
+
+        debugFeed(client.getBinder().getFeed(inline, ResourceFactory.feedClassForFormat(
+                format == ODataPubFormat.ATOM)), "Just read");
+
+        found = true;
+      }
+    }
+
+    assertTrue(found);
+  }
+
+  @Test
+  public void withInlineFeedFromAtom() {
+    withInlineFeed(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void withInlineFeedFromJSON() {
+    // this needs to be full, otherwise there is no mean to recognize links
+    withInlineFeed(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  private void rawRequest(final ODataPubFormat format) {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).
+            appendEntitySetSegment("People").appendKeySegment(5);
+
+    final ODataRawRequest req = client.getRetrieveRequestFactory().getRawRequest(uriBuilder.build());
+    req.setFormat(format.toString(client.getServiceVersion()));
+
+    final ODataRawResponse res = req.execute();
+    assertNotNull(res);
+
+    final ODataEntitySet entitySet = res.getBodyAs(ODataEntitySet.class);
+    assertNull(entitySet);
+
+    final ODataEntity entity = res.getBodyAs(ODataEntity.class);
+    assertTrue(entity.getReference().endsWith("/StaticService/V40/Static.svc/People(5)"));
+  }
+
+  @Test
+  public void rawRequestAsAtom() {
+    rawRequest(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void rawRequestAsJSON() {
+    // this needs to be full, otherwise actions will not be provided
+    rawRequest(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  private void multiKey(final ODataPubFormat format) throws EdmPrimitiveTypeException {
+    final LinkedHashMap<String, Object> multiKey = new LinkedHashMap<String, Object>();
+    multiKey.put("ProductID", "6");
+    multiKey.put("ProductDetailID", 1);
+
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).
+            appendEntitySetSegment("ProductDetails").appendKeySegment(multiKey);
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(format);
+
+    final ODataRetrieveResponse<ODataEntity> res = req.execute();
+    final ODataEntity entity = res.getBody();
+    assertNotNull(entity);
+    assertEquals(Integer.valueOf(1),
+            entity.getProperty("ProductDetailID").getPrimitiveValue().toCastValue(Integer.class));
+  }
+
+  @Test
+  public void multiKeyAsAtom() throws EdmPrimitiveTypeException {
+    multiKey(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void multiKeyAsJSON() throws EdmPrimitiveTypeException {
+    multiKey(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  @Test
+  public void checkForETagAsATOM() {
+    checkForETag(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void checkForETagAsJSON() {
+    checkForETag(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  private void checkForETag(final ODataPubFormat format) {
+    final CommonURIBuilder<?> uriBuilder =
+            client.getURIBuilder(getServiceRoot()).appendEntitySetSegment("Orders").appendKeySegment(8);
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(format);
+
+    final ODataRetrieveResponse<ODataEntity> res = req.execute();
+    assertEquals(200, res.getStatusCode());
+
+    final String etag = res.getEtag();
+    assertTrue(StringUtils.isNotBlank(etag));
+
+    final ODataEntity order = res.getBody();
+    assertEquals(etag, order.getETag());
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  @Ignore
+  public void issue99() {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).appendEntitySetSegment("Car");
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(ODataPubFormat.JSON);
+
+    // this statement should cause an IllegalArgumentException bearing JsonParseException
+    // since we are attempting to parse an EntitySet as if it was an Entity
+    req.execute().getBody();
+  }
+
+  @Test
+  public void retrieveEntityReferenceAsAtom() {
+    retrieveEntityReference(ODataPubFormat.ATOM);
+  }
+
+  @Test
+  @Ignore
+  public void retrieveEntityReferenceAsJSON() {
+    retrieveEntityReference(ODataPubFormat.JSON_FULL_METADATA);
+  }
+
+  private void retrieveEntityReference(final ODataPubFormat format) {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(getServiceRoot()).
+            appendEntitySetSegment("Orders").appendKeySegment(8).appendNavigationSegment("CustomerForOrder").
+            appendRefSegment();
+
+    final ODataEntityRequest req = client.getRetrieveRequestFactory().getEntityRequest(uriBuilder.build());
+    req.setFormat(format);
+
+    final ODataRetrieveResponse<ODataEntity> res = req.execute();
+    assertNotNull(res);
+
+    final ODataEntity entity = res.getBody();
+    assertNotNull(entity);
+    assertTrue(entity.getReference().endsWith("/StaticService/V40/Static.svc/Customers(PersonID=1)"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntitySetTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntitySetTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntitySetTestITCase.java
index 8455ecd..473e721 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntitySetTestITCase.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/EntitySetTestITCase.java
@@ -104,6 +104,8 @@ public class EntitySetTestITCase extends AbstractTestITCase {
 
     assertNotNull(feed);
 
+    assertTrue(feed.getContextURL().toASCIIString().endsWith("$metadata#People"));
+
     debugFeed(client.getBinder().getFeed(feed, ResourceFactory.feedClassForFormat(
             ODataPubFormat.ATOM == format)), "Just retrieved feed");
 
@@ -151,5 +153,6 @@ public class EntitySetTestITCase extends AbstractTestITCase {
 
     final ODataEntitySet entitySet = res.getBodyAs(ODataEntitySet.class);
     assertNotNull(entitySet);
+    assertTrue(entitySet.getContextURL().toASCIIString().endsWith("$metadata#People"));
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/PropertyValueTestITCase.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/PropertyValueTestITCase.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/PropertyValueTestITCase.java
new file mode 100644
index 0000000..3a8b9e9
--- /dev/null
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/it/v4/PropertyValueTestITCase.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 org.apache.olingo.client.core.it.v4;
+
+import static org.junit.Assert.*;
+import java.io.IOException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.communication.ODataClientErrorException;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataPropertyRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataValueRequest;
+import org.apache.olingo.commons.api.format.ODataValueFormat;
+import org.apache.olingo.client.api.uri.CommonURIBuilder;
+import org.apache.olingo.commons.api.domain.ODataPrimitiveValue;
+import org.apache.olingo.commons.api.domain.ODataProperty;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.format.ODataPubFormat;
+import org.junit.Test;
+
+public class PropertyValueTestITCase extends AbstractTestITCase {
+
+  @Test
+  public void retrieveIntPropertyValueTest() throws EdmPrimitiveTypeException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PersonID").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    assertEquals("5", req.execute().getBody().toString());
+  }
+
+  @Test
+  public void retrieveBooleanPropertyValueTest() throws EdmPrimitiveTypeException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("IsRegistered").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    assertEquals("true", req.execute().getBody().toString());
+  }
+
+  @Test
+  public void retrieveStringPropertyValueTest() throws EdmPrimitiveTypeException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("FirstName").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    assertEquals("Peter", req.execute().getBody().toString());
+  }
+
+  @Test
+  public void retrieveDatePropertyValueTest() {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("Orders").appendKeySegment(8).appendPropertySegment("OrderDate").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    final ODataPrimitiveValue property = req.execute().getBody();
+    assertEquals("2011-03-04T16:03:57Z", property.toString());
+  }
+
+  @Test
+  public void retrieveDecimalPropertyValueTest() throws EdmPrimitiveTypeException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("Height").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    final ODataPrimitiveValue property = req.execute().getBody();
+    assertEquals("179", property.toString());
+  }
+
+  @Test
+  public void retrieveBinaryPropertyValueTest() throws IOException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PDC").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    final ODataPrimitiveValue property = req.execute().getBody();
+    assertEquals("fi653p3+MklA/LdoBlhWgnMTUUEo8tEgtbMXnF0a3CUNL9BZxXpSRiD9ebTnmNR0zWPjJ"
+            + "VIDx4tdmCnq55XrJh+RW9aI/b34wAogK3kcORw=", property.toString());
+  }
+
+  @Test(expected = ODataClientErrorException.class)
+  public void retrieveBinaryPropertyValueTestWithAtom() throws IOException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PDC").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setAccept(ODataPubFormat.ATOM.toString(ODataServiceVersion.V40));
+    req.execute().getBody();
+  }
+
+  @Test(expected = ODataClientErrorException.class)
+  public void retrieveBinaryPropertyValueTestWithXML() throws IOException {
+    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("PDC").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setAccept(ODataFormat.XML.toString());
+    req.execute().getBody();
+  }
+
+  @Test
+  public void retrieveCollectionPropertyValueTest() {
+    CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("Numbers");
+    final ODataPropertyRequest req = client.getRetrieveRequestFactory().getPropertyRequest(uriBuilder.build());
+    req.setFormat(ODataFormat.XML);
+    final ODataProperty property = req.execute().getBody();
+    assertTrue(property.getValue().isCollection());
+    assertEquals("555-555-5555", property.getCollectionValue().iterator().next().asPrimitive().toString());
+  }
+
+  @Test
+  public void retrieveNullPropertyValueTest() {
+    CommonURIBuilder<?> uriBuilder = client.getURIBuilder(testStaticServiceRootURL).
+            appendEntitySetSegment("People").appendKeySegment(5).appendPropertySegment("MiddleName").
+            appendValueSegment();
+    final ODataValueRequest req = client.getRetrieveRequestFactory().getValueRequest(uriBuilder.build());
+    req.setFormat(ODataValueFormat.TEXT);
+    final ODataPrimitiveValue property = req.execute().getBody();
+    assertTrue(StringUtils.isBlank(property.toString()));
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/v3/URIBuilderTest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/v3/URIBuilderTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/v3/URIBuilderTest.java
index 1838cf8..98fce0a 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/v3/URIBuilderTest.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/v3/URIBuilderTest.java
@@ -104,7 +104,7 @@ public class URIBuilderTest extends AbstractTest {
   public void unboundAction() throws URISyntaxException {
     final URIBuilder uriBuilder = getClient().getURIBuilder(SERVICE_ROOT).
             appendOperationCallSegment("ProductsByCategoryId",
-                    Collections.<String, Object>singletonMap("categoryId", 2));
+            Collections.<String, Object>singletonMap("categoryId", 2));
 
     assertEquals(new org.apache.http.client.utils.URIBuilder(
             SERVICE_ROOT + "/ProductsByCategoryId(categoryId=2)").build(), uriBuilder.build());
@@ -128,4 +128,22 @@ public class URIBuilderTest extends AbstractTest {
     assertEquals(new org.apache.http.client.utils.URIBuilder(
             SERVICE_ROOT + "/Customers/Model/Namespace.VipCustomer(1)").build(), uriBuilder.build());
   }
+
+  @Test
+  public void expandMoreThenOnce() throws URISyntaxException {
+    URI uri = getClient().getURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(5).
+            expand("Orders", "Customers").expand("Info").build();
+
+    assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)").
+            addParameter("$expand", "Orders,Customers,Info").build(), uri);
+  }
+
+  @Test
+  public void selectMoreThenOnce() throws URISyntaxException {
+    URI uri = getClient().getURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Customers").appendKeySegment(5).
+            select("Name", "Surname").expand("Info").select("Gender").build();
+
+    assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Customers(5)").
+            addParameter("$select", "Name,Surname,Gender").addParameter("$expand", "Info").build(), uri);
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/URIBuilderTest.java
----------------------------------------------------------------------
diff --git a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/URIBuilderTest.java b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/URIBuilderTest.java
index 868b363..56ea4f7 100644
--- a/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/URIBuilderTest.java
+++ b/lib/client-core/src/test/java/org/apache/olingo/client/core/v4/URIBuilderTest.java
@@ -20,6 +20,7 @@ package org.apache.olingo.client.core.v4;
 
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.util.LinkedHashMap;
 
 import org.apache.olingo.client.api.v4.ODataClient;
 import org.apache.olingo.client.api.uri.v4.URIBuilder;
@@ -39,6 +40,22 @@ public class URIBuilderTest extends AbstractTest {
   }
 
   @Test
+  public void expandWithOptions() throws URISyntaxException {
+    URI uri = getClient().getURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").appendKeySegment(5).
+            expandWithOptions("ProductDetails", new LinkedHashMap<String, Object>() {
+      private static final long serialVersionUID = 3109256773218160485L;
+
+      {
+        put("$expand", "ProductInfo");
+        put("$select", "Price");
+      }
+    }).expand("Orders", "Customers").build();
+
+    assertEquals(new org.apache.http.client.utils.URIBuilder(SERVICE_ROOT + "/Products(5)").
+            addParameter("$expand", "ProductDetails($expand=ProductInfo,$select=Price),Orders,Customers").build(), uri);
+  }
+
+  @Test
   public void count() throws URISyntaxException {
     URI uri = getClient().getURIBuilder(SERVICE_ROOT).appendEntitySetSegment("Products").count().build();
 
@@ -132,5 +149,4 @@ public class URIBuilderTest extends AbstractTest {
     assertEquals(new org.apache.http.client.utils.URIBuilder(
             SERVICE_ROOT + "/Products").addParameter("$search", "blue OR green").build(), uriBuilder.build());
   }
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
index b4c048f..3b5b9cc 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/Constants.java
@@ -72,6 +72,8 @@ public interface Constants {
 
   public static final QName QNAME_ATTR_XML_BASE = new QName(XMLConstants.XML_NS_URI, ATTR_XML_BASE);
 
+  public static final String CONTEXT = "context";
+
   public static final String ATTR_REL = "rel";
 
   public static final String ATTR_TITLE = "title";
@@ -173,7 +175,7 @@ public interface Constants {
 
   public final static String JSON_NULL = "odata.null";
 
-  public final static String JSON_VALUE = "value";
+  public final static String VALUE = "value";
 
   public final static String JSON_URL = "url";
 
@@ -192,6 +194,12 @@ public interface Constants {
   // Atom stuff
   public final static String ATOM_ELEM_ENTRY = "entry";
 
+  public final static String ATOM_ELEM_ENTRY_REF = "ref";
+
+  public final static String ATOM_ELEM_ENTRY_REF_ID = "id";
+
+  public final static QName QNAME_ATOM_ELEM_ENTRY_REF_ID = new QName(ATOM_ELEM_ENTRY_REF_ID);
+
   public static final QName QNAME_ATOM_ELEM_ENTRY = new QName(NS_ATOM, ATOM_ELEM_ENTRY);
 
   public final static String ATOM_ELEM_FEED = "feed";

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Entry.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Entry.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Entry.java
index ea73b7d..5ac0dbd 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Entry.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Entry.java
@@ -39,6 +39,21 @@ public interface Entry {
   URI getBaseURI();
 
   /**
+   * 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.
+   *
+   * @return context URL.
+   */
+  URI getContextURL();
+
+  /**
+   * Set context URL.
+   *
+   * @param contextURL context URL.
+   */
+  void setContextURL(final URI contextURL);
+
+  /**
    * Gets entry type.
    *
    * @return entry type.
@@ -53,13 +68,20 @@ public interface Entry {
   void setType(String type);
 
   /**
-   * Gest entry ID.
+   * Gets entry ID.
    *
    * @return entry ID.
    */
   String getId();
 
   /**
+   * Sets entry ID.
+   *
+   * @param id entry ID.
+   */
+  void setId(String id);
+
+  /**
    * Gets entry self link.
    *
    * @return self link.

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Feed.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Feed.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Feed.java
index 4e98ac6..45b28d9 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Feed.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Feed.java
@@ -31,6 +31,21 @@ public interface Feed {
   URI getBaseURI();
 
   /**
+   * 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.
+   *
+   * @return context URL.
+   */
+  URI getContextURL();
+
+  /**
+   * Set context URL.
+   *
+   * @param contextURL context URL.
+   */
+  void setContextURL(final URI contextURL);
+
+  /**
    * Sets number of entries.
    *
    * @param count number of entries
@@ -71,5 +86,4 @@ public interface Feed {
    * @param next next link.
    */
   void setNext(URI next);
-
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
index feb8f71..da34c16 100644
--- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/data/Property.java
@@ -18,8 +18,18 @@
  */
 package org.apache.olingo.commons.api.data;
 
+import java.net.URI;
+
 public interface Property {
 
+  /**
+   * 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.
+   *
+   * @return context URL.
+   */
+  URI getContextURL();
+  
   String getName();
 
   void setName(String name);

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/428277c2/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/AbstractODataPayload.java
----------------------------------------------------------------------
diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/AbstractODataPayload.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/AbstractODataPayload.java
new file mode 100644
index 0000000..9bc177c
--- /dev/null
+++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/domain/AbstractODataPayload.java
@@ -0,0 +1,52 @@
+/*
+ * 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.commons.api.domain;
+
+import java.net.URI;
+
+/**
+ * OData entity.
+ */
+public abstract class AbstractODataPayload extends ODataItem {
+
+  private static final long serialVersionUID = -8234709365887433612L;
+
+  /**
+   * Context URL.
+   */
+  private URI contextURL;
+
+  public AbstractODataPayload(final String name) {
+    super(name);
+  }
+
+  /**
+   * 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.
+   *
+   * @return context URL.
+   */
+  public URI getContextURL() {
+    return contextURL;
+  }
+
+  public void setContextURL(final URI contextURL) {
+    this.contextURL = contextURL;
+  }
+}