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/05/17 16:06:40 UTC

[5/5] git commit: [OLINGO-260] contained tests

[OLINGO-260] contained tests


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

Branch: refs/heads/master
Commit: ca66d671313eabdc04c9afc2854c6ef28bfd3920
Parents: c35bc55
Author: Francesco Chicchiriccò <--global>
Authored: Sat May 17 16:06:18 2014 +0200
Committer: Francesco Chicchiriccò <--global>
Committed: Sat May 17 16:06:18 2014 +0200

----------------------------------------------------------------------
 .../ext/proxy/api/annotations/EntitySet.java    |   5 +-
 .../api/annotations/NavigationProperty.java     |   2 +
 .../commons/AbstractInvocationHandler.java      |  36 +-
 .../AbstractStructuredInvocationHandler.java    | 313 +++++++++++++++
 .../commons/AbstractTypeInvocationHandler.java  | 308 ---------------
 .../ComplexFactoryInvocationHandler.java        |  18 +-
 .../proxy/commons/ComplexInvocationHandler.java | 186 +++++++++
 .../commons/ComplexTypeInvocationHandler.java   | 186 ---------
 .../olingo/ext/proxy/commons/ContainerImpl.java |  84 ++--
 .../EntityContainerInvocationHandler.java       |   4 +-
 .../proxy/commons/EntityInvocationHandler.java  | 385 +++++++++++++++++++
 .../commons/EntitySetInvocationHandler.java     |  72 ++--
 .../commons/EntityTypeInvocationHandler.java    | 376 ------------------
 .../commons/OperationInvocationHandler.java     |   8 +-
 .../commons/SingletonInvocationHandler.java     |  12 +-
 .../ext/proxy/context/AttachedEntity.java       |   8 +-
 .../olingo/ext/proxy/context/EntityContext.java |  28 +-
 .../ext/proxy/context/EntityLinkDesc.java       |  20 +-
 .../olingo/ext/proxy/context/EntityUUID.java    |  15 +-
 .../olingo/ext/proxy/utils/CoreUtils.java       |  26 +-
 .../olingo/ext/pojogen/AbstractPOJOGenMojo.java |  29 ++
 .../olingo/ext/pojogen/AbstractUtility.java     |  39 +-
 .../ext/pojogen/NavPropertyContainsTarget.java  |  16 +-
 .../src/main/resources/containedEntitySet.vm    |  65 ++++
 .../src/main/resources/container.vm             |   1 -
 .../src/main/resources/entitySet.vm             |   2 +-
 .../src/main/resources/entityType.vm            |  18 +-
 .../src/main/resources/entityTypeKey.vm         |   2 +-
 .../src/main/resources/v30/complexType.vm       |   2 +-
 .../src/main/resources/v30/entitySet.vm         |  19 -
 .../src/main/resources/v40/complexType.vm       |   4 +-
 .../src/main/resources/v40/entitySet.vm         |  19 -
 .../MyPaymentInstruments(101902).full.json      |  16 +
 .../101/links/MyPaymentInstruments(101902).xml  |  40 ++
 .../olingo/fit/proxy/v3/ContextTestITCase.java  |  90 ++---
 .../fit/proxy/v3/EntityRetrieveTestITCase.java  |   4 +-
 .../fit/proxy/v3/EntityUpdateTestITCase.java    |   4 +-
 .../fit/proxy/v4/DerivedTypeTestITCase.java     |  13 +-
 .../fit/proxy/v4/EntityCreateTestITCase.java    |  33 ++
 .../fit/proxy/v4/EntityRetrieveTestITCase.java  |  14 +-
 .../fit/proxy/v4/EntityUpdateTestITCase.java    |  20 +-
 .../odata/services/opentypesservicev4/Row.java  |   2 +-
 .../services/opentypesservicev4/RowIndex.java   |   2 +-
 .../services/odatawcfservice/Accounts.java      |   4 +-
 .../odatawcfservice/ActiveSubscriptions.java    |  51 +++
 .../odata/services/odatawcfservice/Assets.java  |  51 +++
 .../odatawcfservice/BillingStatements.java      |  51 +++
 .../odata/services/odatawcfservice/Boss.java    |   1 -
 .../odata/services/odatawcfservice/Club.java    |  51 +++
 .../odata/services/odatawcfservice/Company.java |   1 -
 .../services/odatawcfservice/CreditRecords.java |  51 +++
 .../services/odatawcfservice/Customers.java     |   4 +-
 .../odatawcfservice/DefaultStoredPI.java        |   1 -
 .../services/odatawcfservice/Departments.java   |   4 +-
 .../services/odatawcfservice/Employees.java     |   4 +-
 .../odatawcfservice/InMemoryEntities.java       |  13 -
 .../services/odatawcfservice/LabourUnion.java   |   1 -
 .../services/odatawcfservice/MyGiftCard.java    |  51 +++
 .../odatawcfservice/MyPaymentInstruments.java   |  53 +++
 .../services/odatawcfservice/OrderDetails.java  |   4 +-
 .../odata/services/odatawcfservice/Orders.java  |   4 +-
 .../odata/services/odatawcfservice/People.java  |   4 +-
 .../odatawcfservice/ProductDetails.java         |   4 +-
 .../odatawcfservice/ProductReviews.java         |   4 +-
 .../services/odatawcfservice/Products.java      |   4 +-
 .../services/odatawcfservice/PublicCompany.java |   1 -
 .../services/odatawcfservice/StoredPIs.java     |   4 +-
 .../odatawcfservice/SubscriptionTemplates.java  |   4 +-
 .../services/odatawcfservice/VipCustomer.java   |   1 -
 .../services/odatawcfservice/package-info.java  |   1 -
 .../odatawcfservice/types/AccessLevel.java      |   1 -
 .../services/odatawcfservice/types/Account.java |  43 +--
 .../types/AccountCollection.java                |   1 -
 .../odatawcfservice/types/AccountInfo.java      |   5 +-
 .../services/odatawcfservice/types/Address.java |   7 +-
 .../services/odatawcfservice/types/Asset.java   |   7 +-
 .../odatawcfservice/types/AssetCollection.java  |   1 -
 .../services/odatawcfservice/types/Club.java    |   5 +-
 .../odatawcfservice/types/ClubCollection.java   |   1 -
 .../services/odatawcfservice/types/Color.java   |   1 -
 .../services/odatawcfservice/types/Company.java |  43 +--
 .../odatawcfservice/types/CompanyAddress.java   |   9 +-
 .../odatawcfservice/types/CompanyCategory.java  |   1 -
 .../types/CompanyCollection.java                |   1 -
 .../odatawcfservice/types/CreditCardPI.java     |  53 ++-
 .../types/CreditCardPICollection.java           |   1 -
 .../odatawcfservice/types/CreditRecord.java     |   9 +-
 .../types/CreditRecordCollection.java           |   1 -
 .../odatawcfservice/types/Customer.java         |  47 ++-
 .../types/CustomerCollection.java               |   1 -
 .../odatawcfservice/types/Department.java       |  15 +-
 .../types/DepartmentCollection.java             |   1 -
 .../odatawcfservice/types/Employee.java         |  37 +-
 .../types/EmployeeCollection.java               |   1 -
 .../odatawcfservice/types/GiftCard.java         |  11 +-
 .../types/GiftCardCollection.java               |   1 -
 .../odatawcfservice/types/HomeAddress.java      |   9 +-
 .../odatawcfservice/types/LabourUnion.java      |   5 +-
 .../types/LabourUnionCollection.java            |   1 -
 .../services/odatawcfservice/types/Order.java   |  33 +-
 .../odatawcfservice/types/OrderCollection.java  |   1 -
 .../odatawcfservice/types/OrderDetail.java      |  27 +-
 .../types/OrderDetailCollection.java            |   1 -
 .../odatawcfservice/types/OrderDetailKey.java   |   5 +-
 .../types/PaymentInstrument.java                |  33 +-
 .../types/PaymentInstrumentCollection.java      |   1 -
 .../services/odatawcfservice/types/Person.java  |  25 +-
 .../odatawcfservice/types/PersonCollection.java |   1 -
 .../services/odatawcfservice/types/Product.java |  27 +-
 .../types/ProductCollection.java                |   1 -
 .../odatawcfservice/types/ProductDetail.java    |  25 +-
 .../types/ProductDetailCollection.java          |   1 -
 .../odatawcfservice/types/ProductDetailKey.java |   5 +-
 .../odatawcfservice/types/ProductReview.java    |  13 +-
 .../types/ProductReviewCollection.java          |   1 -
 .../odatawcfservice/types/ProductReviewKey.java |   9 +-
 .../odatawcfservice/types/PublicCompany.java    |  71 ++--
 .../types/PublicCompanyCollection.java          |   1 -
 .../odatawcfservice/types/Statement.java        |   9 +-
 .../types/StatementCollection.java              |   1 -
 .../odatawcfservice/types/StoredPI.java         |   9 +-
 .../types/StoredPICollection.java               |   1 -
 .../odatawcfservice/types/Subscription.java     |  11 +-
 .../types/SubscriptionCollection.java           |   1 -
 .../odatawcfservice/types/package-info.java     |   1 -
 .../apache/olingo/fit/v4/AsyncTestITCase.java   |   2 +-
 .../olingo/fit/v4/EntityRetrieveTestITCase.java |   4 +-
 .../core/edm/primitivetype/EdmDuration.java     |   1 -
 128 files changed, 2052 insertions(+), 1561 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/EntitySet.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/EntitySet.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/EntitySet.java
index 0a94df4..8da4baa 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/EntitySet.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/EntitySet.java
@@ -25,8 +25,7 @@ import java.lang.annotation.Target;
 
 /**
  * Give entity set a name. If interface extending EntitySet is not annotated with this, the effective name will be
- * class'
- * <tt>getSimpleName()</tt>.
+ * class'<tt>getSimpleName()</tt>.
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
@@ -34,5 +33,5 @@ public @interface EntitySet {
 
   String name();
 
-  boolean includeInServiceDocument() default true;
+  boolean contained() default false;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/NavigationProperty.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/NavigationProperty.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/NavigationProperty.java
index 88379bc..fd51b04 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/NavigationProperty.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/annotations/NavigationProperty.java
@@ -39,4 +39,6 @@ public @interface NavigationProperty {
   String targetContainer();
 
   String targetEntitySet();
+
+  boolean containsTarget() default false;
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
index 5181b48..e3a2bc9 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractInvocationHandler.java
@@ -87,7 +87,7 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
   }
 
   @SuppressWarnings({"unchecked", "rawtypes"})
-  protected Object getEntityCollection(
+  protected Object getEntityCollectionProxy(
           final Class<?> typeRef,
           final Class<?> typeCollectionRef,
           final String entityContainerName,
@@ -99,7 +99,7 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
 
     for (CommonODataEntity entityFromSet : entitySet.getEntities()) {
       items.add(getEntityProxy(
-              entityFromSet, entityContainerName, null, typeRef, checkInTheContext));
+              entityFromSet.getEditLink(), entityFromSet, entityContainerName, null, typeRef, checkInTheContext));
     }
 
     return Proxy.newProxyInstance(
@@ -108,27 +108,38 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
             new EntityCollectionInvocationHandler(containerHandler, items, typeRef, uri));
   }
 
-  protected <T> T getEntityProxy(
+  protected Object getEntitySetProxy(
+          final Class<?> typeRef,
+          final URI uri) {
+
+    return Proxy.newProxyInstance(
+            Thread.currentThread().getContextClassLoader(),
+            new Class<?>[] {typeRef},
+            EntitySetInvocationHandler.getInstance(typeRef, containerHandler, uri));
+  }
+
+  protected Object getEntityProxy(
+          final URI entityURI,
           final CommonODataEntity entity,
           final String entityContainerName,
-          final String entitySetName,
+          final URI entitySetURI,
           final Class<?> type,
           final boolean checkInTheContext) {
 
-    return getEntityProxy(entity, entityContainerName, entitySetName, type, null, checkInTheContext);
+    return getEntityProxy(entityURI, entity, entityContainerName, entitySetURI, type, null, checkInTheContext);
   }
 
-  @SuppressWarnings({"unchecked"})
-  protected <T> T getEntityProxy(
+  protected Object getEntityProxy(
+          final URI entityURI,
           final CommonODataEntity entity,
           final String entityContainerName,
-          final String entitySetName,
+          final URI entitySetURI,
           final Class<?> type,
           final String eTag,
           final boolean checkInTheContext) {
 
-    EntityTypeInvocationHandler handler =
-            EntityTypeInvocationHandler.getInstance(entity, entitySetName, type, containerHandler);
+    EntityInvocationHandler handler =
+            EntityInvocationHandler.getInstance(entityURI, entity, entitySetURI, type, containerHandler);
 
     if (StringUtils.isNotBlank(eTag)) {
       // override ETag into the wrapped object.
@@ -139,7 +150,7 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
       handler = EntityContainerFactory.getContext().entityContext().getEntity(handler.getUUID());
     }
 
-    return (T) Proxy.newProxyInstance(
+    return Proxy.newProxyInstance(
             Thread.currentThread().getContextClassLoader(),
             new Class<?>[] {type},
             handler);
@@ -196,7 +207,7 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
       if (edmType.isCollection()) {
         final ParameterizedType collType = (ParameterizedType) method.getReturnType().getGenericInterfaces()[0];
         final Class<?> collItemType = (Class<?>) collType.getActualTypeArguments()[0];
-        return getEntityCollection(
+        return getEntityCollectionProxy(
                 collItemType,
                 method.getReturnType(),
                 null,
@@ -205,6 +216,7 @@ abstract class AbstractInvocationHandler implements InvocationHandler {
                 false);
       } else {
         return getEntityProxy(
+                ((CommonODataEntity) result).getEditLink(),
                 (CommonODataEntity) result,
                 null,
                 null,

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
new file mode 100644
index 0000000..2e6103e
--- /dev/null
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractStructuredInvocationHandler.java
@@ -0,0 +1,313 @@
+/*
+ * 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.ext.proxy.commons;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
+import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
+import org.apache.olingo.client.core.uri.URIUtils;
+import org.apache.olingo.commons.api.domain.CommonODataEntity;
+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.ODataLinked;
+import org.apache.olingo.ext.proxy.EntityContainerFactory;
+import org.apache.olingo.ext.proxy.api.AbstractEntityCollection;
+import org.apache.olingo.ext.proxy.api.AbstractEntitySet;
+import org.apache.olingo.ext.proxy.api.annotations.EntityType;
+import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
+import org.apache.olingo.ext.proxy.api.annotations.Property;
+import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
+import org.apache.olingo.ext.proxy.context.EntityContext;
+import org.apache.olingo.ext.proxy.utils.ClassUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractStructuredInvocationHandler extends AbstractInvocationHandler {
+
+  private static final long serialVersionUID = 2629912294765040037L;
+
+  /**
+   * Logger.
+   */
+  protected static final Logger LOG = LoggerFactory.getLogger(AbstractStructuredInvocationHandler.class);
+
+  protected final Class<?> typeRef;
+
+  protected final EntityContext entityContext = EntityContainerFactory.getContext().entityContext();
+
+  protected EntityInvocationHandler entityHandler;
+
+  protected Object internal;
+
+  protected AbstractStructuredInvocationHandler(
+          final CommonEdmEnabledODataClient<?> client,
+          final Class<?> typeRef,
+          final Object internal,
+          final EntityContainerInvocationHandler containerHandler) {
+
+    super(client, containerHandler);
+    this.internal = internal;
+    this.typeRef = typeRef;
+    this.entityHandler = EntityInvocationHandler.class.cast(this);
+  }
+
+  protected AbstractStructuredInvocationHandler(
+          final CommonEdmEnabledODataClient<?> client,
+          final Class<?> typeRef,
+          final Object internal,
+          final EntityInvocationHandler entityHandler) {
+
+    super(client, entityHandler == null ? null : entityHandler.containerHandler);
+    this.internal = internal;
+    this.typeRef = typeRef;
+    this.entityHandler = entityHandler;
+  }
+
+  public EntityInvocationHandler getEntityHandler() {
+    return entityHandler;
+  }
+
+  public void setEntityHandler(EntityInvocationHandler entityHandler) {
+    this.entityHandler = entityHandler;
+  }
+
+  public Class<?> getTypeRef() {
+    return typeRef;
+  }
+
+  @Override
+  public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
+    if (isSelfMethod(method, args)) {
+      return invokeSelfMethod(method, args);
+    } else if ("operations".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
+      final Class<?> returnType = method.getReturnType();
+
+      return Proxy.newProxyInstance(
+              Thread.currentThread().getContextClassLoader(),
+              new Class<?>[] {returnType},
+              OperationInvocationHandler.getInstance(entityHandler));
+    } else if ("factory".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
+      final Class<?> returnType = method.getReturnType();
+
+      return Proxy.newProxyInstance(
+              Thread.currentThread().getContextClassLoader(),
+              new Class<?>[] {returnType},
+              ComplexFactoryInvocationHandler.getInstance(entityHandler, this));
+    } else if (method.getName().startsWith("get")) {
+      // Assumption: for each getter will always exist a setter and viceversa.
+      // get method annotation and check if it exists as expected
+      final Object res;
+      final Method getter = typeRef.getMethod(method.getName());
+
+      final Property property = ClassUtils.getAnnotation(Property.class, getter);
+      if (property == null) {
+        final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
+        if (navProp == null) {
+          throw new UnsupportedOperationException("Unsupported method " + method.getName());
+        } else {
+          // if the getter refers to a navigation property ... navigate and follow link if necessary
+          res = getNavigationPropertyValue(navProp, getter);
+        }
+      } else {
+        // if the getter refers to a property .... get property from wrapped entity
+        res = getPropertyValue(property, getter.getGenericReturnType());
+      }
+
+      // attach the current handler
+      attach();
+
+      return res;
+    } else if (method.getName().startsWith("set")) {
+      // get the corresponding getter method (see assumption above)
+      final String getterName = method.getName().replaceFirst("set", "get");
+      final Method getter = typeRef.getMethod(getterName);
+
+      final Property property = ClassUtils.getAnnotation(Property.class, getter);
+      if (property == null) {
+        final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
+        if (navProp == null) {
+          throw new UnsupportedOperationException("Unsupported method " + method.getName());
+        } else {
+          // if the getter refers to a navigation property ... 
+          if (ArrayUtils.isEmpty(args) || args.length != 1) {
+            throw new IllegalArgumentException("Invalid argument");
+          }
+
+          setNavigationPropertyValue(navProp, args[0]);
+        }
+      } else {
+        setPropertyValue(property, args[0]);
+      }
+
+      return ClassUtils.returnVoid();
+    } else {
+      throw new NoSuchMethodException(method.getName());
+    }
+  }
+
+  protected void attach() {
+    if (entityHandler != null && !entityContext.isAttached(entityHandler)) {
+      entityContext.attach(entityHandler, AttachedEntityStatus.ATTACHED);
+    }
+  }
+
+  protected void attach(final AttachedEntityStatus status) {
+    attach(status, true);
+  }
+
+  protected void attach(final AttachedEntityStatus status, final boolean override) {
+    if (entityContext.isAttached(entityHandler)) {
+      if (override) {
+        entityContext.setStatus(entityHandler, status);
+      }
+    } else {
+      entityContext.attach(entityHandler, status);
+    }
+  }
+
+  protected abstract Object getNavigationPropertyValue(final NavigationProperty property, final Method getter);
+
+  protected Object retrieveNavigationProperty(
+          final NavigationProperty property, final Method getter, final String serviceRoot) {
+
+    final Class<?> type = getter.getReturnType();
+    final Class<?> collItemType;
+    if (AbstractEntityCollection.class.isAssignableFrom(type)) {
+      final Type[] entityCollectionParams =
+              ((ParameterizedType) type.getGenericInterfaces()[0]).getActualTypeArguments();
+      collItemType = (Class<?>) entityCollectionParams[0];
+    } else {
+      collItemType = type;
+    }
+
+    final Object navPropValue;
+
+    final ODataLink link = ((ODataLinked) internal).getNavigationLink(property.name());
+    if (link instanceof ODataInlineEntity) {
+      // return entity
+      navPropValue = getEntityProxy(
+              null,
+              ((ODataInlineEntity) link).getEntity(),
+              property.targetContainer(),
+              client.getURIBuilder(serviceRoot).appendEntitySetSegment(property.targetEntitySet()).build(),
+              type,
+              false);
+    } else if (link instanceof ODataInlineEntitySet) {
+      // return entity set
+      navPropValue = getEntityCollectionProxy(
+              collItemType,
+              type,
+              property.targetContainer(),
+              ((ODataInlineEntitySet) link).getEntitySet(),
+              link.getLink(),
+              false);
+    } else {
+      // navigate
+      final URI uri = URIUtils.getURI(containerHandler.getFactory().getServiceRoot(), link.getLink().toASCIIString());
+      if (AbstractEntityCollection.class.isAssignableFrom(type)) {
+        navPropValue = getEntityCollectionProxy(
+                collItemType,
+                type,
+                property.targetContainer(),
+                client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute().getBody(),
+                uri,
+                true);
+      } else if (AbstractEntitySet.class.isAssignableFrom(type)) {
+        navPropValue = getEntitySetProxy(type, uri);
+      } else {
+        final ODataRetrieveResponse<CommonODataEntity> res =
+                client.getRetrieveRequestFactory().getEntityRequest(uri).execute();
+
+        navPropValue = getEntityProxy(
+                uri,
+                res.getBody(),
+                property.targetContainer(),
+                client.getURIBuilder(serviceRoot).appendEntitySetSegment(property.targetEntitySet()).build(),
+                type,
+                res.getETag(),
+                true);
+      }
+    }
+
+    return navPropValue;
+  }
+
+  protected abstract Object getPropertyValue(final String name, final Type type);
+
+  private Object getPropertyValue(final Property property, final Type type) {
+    return getPropertyValue(property.name(), type);
+  }
+
+  public void addAdditionalProperty(final String name, final Object value) {
+    addPropertyChanges(name, value);
+    attach(AttachedEntityStatus.CHANGED);
+  }
+
+  public Object getAdditionalProperty(final String name) {
+    return getPropertyValue(name, null);
+  }
+
+  public abstract Collection<String> getAdditionalPropertyNames();
+
+  private void setNavigationPropertyValue(final NavigationProperty property, final Object value) {
+    // 1) attach source entity
+    if (!entityContext.isAttached(entityHandler)) {
+      entityContext.attach(entityHandler, AttachedEntityStatus.CHANGED);
+    }
+
+    // 2) attach the target entity handlers
+    for (Object link : AbstractEntityCollection.class.isAssignableFrom(value.getClass())
+            ? (AbstractEntityCollection) value : Collections.singleton(value)) {
+
+      final InvocationHandler etih = Proxy.getInvocationHandler(link);
+      if (!(etih instanceof EntityInvocationHandler)) {
+        throw new IllegalArgumentException("Invalid argument type");
+      }
+
+      final EntityInvocationHandler linkedHandler = (EntityInvocationHandler) etih;
+      if (!linkedHandler.getTypeRef().isAnnotationPresent(EntityType.class)) {
+        throw new IllegalArgumentException("Invalid argument type " + linkedHandler.getTypeRef().getSimpleName());
+      }
+
+      if (!entityContext.isAttached(linkedHandler)) {
+        entityContext.attach(linkedHandler, AttachedEntityStatus.LINKED);
+      }
+    }
+
+    // 3) add links
+    addLinkChanges(property, value);
+  }
+
+  protected abstract void setPropertyValue(final Property property, final Object value);
+
+  protected abstract void addPropertyChanges(final String name, final Object value);
+
+  protected abstract void addLinkChanges(final NavigationProperty navProp, final Object value);
+
+  public abstract boolean isChanged();
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractTypeInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractTypeInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractTypeInvocationHandler.java
deleted file mode 100644
index 169ddfe..0000000
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractTypeInvocationHandler.java
+++ /dev/null
@@ -1,308 +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.ext.proxy.commons;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
-import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
-import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
-import org.apache.olingo.client.core.uri.URIUtils;
-import org.apache.olingo.commons.api.domain.CommonODataEntity;
-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.ODataLinked;
-import org.apache.olingo.ext.proxy.EntityContainerFactory;
-import org.apache.olingo.ext.proxy.api.AbstractEntityCollection;
-import org.apache.olingo.ext.proxy.api.annotations.EntityType;
-import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
-import org.apache.olingo.ext.proxy.api.annotations.Property;
-import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
-import org.apache.olingo.ext.proxy.context.EntityContext;
-import org.apache.olingo.ext.proxy.utils.ClassUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractTypeInvocationHandler extends AbstractInvocationHandler {
-
-  private static final long serialVersionUID = 2629912294765040037L;
-
-  /**
-   * Logger.
-   */
-  protected static final Logger LOG = LoggerFactory.getLogger(AbstractTypeInvocationHandler.class);
-
-  protected final Class<?> typeRef;
-
-  protected final EntityContext entityContext = EntityContainerFactory.getContext().entityContext();
-
-  protected EntityTypeInvocationHandler entityHandler;
-
-  protected Object internal;
-
-  protected AbstractTypeInvocationHandler(
-          final CommonEdmEnabledODataClient<?> client,
-          final Class<?> typeRef,
-          final Object internal,
-          final EntityContainerInvocationHandler containerHandler) {
-
-    super(client, containerHandler);
-    this.internal = internal;
-    this.typeRef = typeRef;
-    this.entityHandler = EntityTypeInvocationHandler.class.cast(this);
-  }
-
-  protected AbstractTypeInvocationHandler(
-          final CommonEdmEnabledODataClient<?> client,
-          final Class<?> typeRef,
-          final Object internal,
-          final EntityTypeInvocationHandler entityHandler) {
-
-    super(client, entityHandler == null ? null : entityHandler.containerHandler);
-    this.internal = internal;
-    this.typeRef = typeRef;
-    this.entityHandler = entityHandler;
-  }
-
-  public EntityTypeInvocationHandler getEntityHandler() {
-    return entityHandler;
-  }
-
-  public void setEntityHandler(EntityTypeInvocationHandler entityHandler) {
-    this.entityHandler = entityHandler;
-  }
-
-  public Class<?> getTypeRef() {
-    return typeRef;
-  }
-
-  @Override
-  public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
-    if (isSelfMethod(method, args)) {
-      return invokeSelfMethod(method, args);
-    } else if ("operations".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
-      final Class<?> returnType = method.getReturnType();
-
-      return Proxy.newProxyInstance(
-              Thread.currentThread().getContextClassLoader(),
-              new Class<?>[] {returnType},
-              OperationInvocationHandler.getInstance(entityHandler));
-    } else if ("factory".equals(method.getName()) && ArrayUtils.isEmpty(args)) {
-      final Class<?> returnType = method.getReturnType();
-
-      return Proxy.newProxyInstance(
-              Thread.currentThread().getContextClassLoader(),
-              new Class<?>[] {returnType},
-              ComplexFactoryInvocationHandler.getInstance(entityHandler, this));
-    } else if (method.getName().startsWith("get")) {
-      // Assumption: for each getter will always exist a setter and viceversa.
-      // get method annotation and check if it exists as expected
-      final Object res;
-      final Method getter = typeRef.getMethod(method.getName());
-
-      final Property property = ClassUtils.getAnnotation(Property.class, getter);
-      if (property == null) {
-        final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
-        if (navProp == null) {
-          throw new UnsupportedOperationException("Unsupported method " + method.getName());
-        } else {
-          // if the getter refers to a navigation property ... navigate and follow link if necessary
-          res = getNavigationPropertyValue(navProp, getter);
-        }
-      } else {
-        // if the getter refers to a property .... get property from wrapped entity
-        res = getPropertyValue(property, getter.getGenericReturnType());
-      }
-
-      // attach the current handler
-      attach();
-
-      return res;
-    } else if (method.getName().startsWith("set")) {
-      // get the corresponding getter method (see assumption above)
-      final String getterName = method.getName().replaceFirst("set", "get");
-      final Method getter = typeRef.getMethod(getterName);
-
-      final Property property = ClassUtils.getAnnotation(Property.class, getter);
-      if (property == null) {
-        final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
-        if (navProp == null) {
-          throw new UnsupportedOperationException("Unsupported method " + method.getName());
-        } else {
-          // if the getter refers to a navigation property ... 
-          if (ArrayUtils.isEmpty(args) || args.length != 1) {
-            throw new IllegalArgumentException("Invalid argument");
-          }
-
-          setNavigationPropertyValue(navProp, args[0]);
-        }
-      } else {
-        setPropertyValue(property, args[0]);
-      }
-
-      return ClassUtils.returnVoid();
-    } else {
-      throw new NoSuchMethodException(method.getName());
-    }
-  }
-
-  protected void attach() {
-    if (entityHandler != null && !entityContext.isAttached(entityHandler)) {
-      entityContext.attach(entityHandler, AttachedEntityStatus.ATTACHED);
-    }
-  }
-
-  protected void attach(final AttachedEntityStatus status) {
-    attach(status, true);
-  }
-
-  protected void attach(final AttachedEntityStatus status, final boolean override) {
-    if (entityContext.isAttached(entityHandler)) {
-      if (override) {
-        entityContext.setStatus(entityHandler, status);
-      }
-    } else {
-      entityContext.attach(entityHandler, status);
-    }
-  }
-
-  protected abstract Object getNavigationPropertyValue(final NavigationProperty property, final Method getter);
-
-  protected Object retriveNavigationProperty(final NavigationProperty property, final Method getter) {
-    final Class<?> type = getter.getReturnType();
-    final Class<?> collItemType;
-    if (AbstractEntityCollection.class.isAssignableFrom(type)) {
-      final Type[] entityCollectionParams =
-              ((ParameterizedType) type.getGenericInterfaces()[0]).getActualTypeArguments();
-      collItemType = (Class<?>) entityCollectionParams[0];
-    } else {
-      collItemType = type;
-    }
-
-    final Object navPropValue;
-
-    final ODataLink link = ((ODataLinked) internal).getNavigationLink(property.name());
-    if (link instanceof ODataInlineEntity) {
-      // return entity
-      navPropValue = getEntityProxy(
-              ((ODataInlineEntity) link).getEntity(),
-              property.targetContainer(),
-              property.targetEntitySet(),
-              type,
-              false);
-    } else if (link instanceof ODataInlineEntitySet) {
-      // return entity set
-      navPropValue = getEntityCollection(
-              collItemType,
-              type,
-              property.targetContainer(),
-              ((ODataInlineEntitySet) link).getEntitySet(),
-              link.getLink(),
-              false);
-    } else {
-      // navigate
-      final URI uri = URIUtils.getURI(
-              containerHandler.getFactory().getServiceRoot(), link.getLink().toASCIIString());
-
-      if (AbstractEntityCollection.class.isAssignableFrom(type)) {
-        navPropValue = getEntityCollection(
-                collItemType,
-                type,
-                property.targetContainer(),
-                client.getRetrieveRequestFactory().getEntitySetRequest(uri).execute().getBody(),
-                uri,
-                true);
-      } else {
-        final ODataRetrieveResponse<CommonODataEntity> res =
-                client.getRetrieveRequestFactory().getEntityRequest(uri).execute();
-
-        navPropValue = getEntityProxy(
-                res.getBody(),
-                property.targetContainer(),
-                property.targetEntitySet(),
-                type,
-                res.getETag(),
-                true);
-      }
-    }
-
-    return navPropValue;
-  }
-
-  protected abstract Object getPropertyValue(final String name, final Type type);
-
-  private Object getPropertyValue(final Property property, final Type type) {
-    return getPropertyValue(property.name(), type);
-  }
-
-  public void addAdditionalProperty(final String name, final Object value) {
-    addPropertyChanges(name, value);
-    attach(AttachedEntityStatus.CHANGED);
-  }
-
-  public Object getAdditionalProperty(final String name) {
-    return getPropertyValue(name, null);
-  }
-
-  public abstract Collection<String> getAdditionalPropertyNames();
-
-  private void setNavigationPropertyValue(final NavigationProperty property, final Object value) {
-    // 1) attach source entity
-    if (!entityContext.isAttached(entityHandler)) {
-      entityContext.attach(entityHandler, AttachedEntityStatus.CHANGED);
-    }
-
-    // 2) attach the target entity handlers
-    for (Object link : AbstractEntityCollection.class.isAssignableFrom(value.getClass())
-            ? (AbstractEntityCollection) value : Collections.singleton(value)) {
-
-      final InvocationHandler etih = Proxy.getInvocationHandler(link);
-      if (!(etih instanceof EntityTypeInvocationHandler)) {
-        throw new IllegalArgumentException("Invalid argument type");
-      }
-
-      final EntityTypeInvocationHandler linkedHandler = (EntityTypeInvocationHandler) etih;
-      if (!linkedHandler.getTypeRef().isAnnotationPresent(EntityType.class)) {
-        throw new IllegalArgumentException("Invalid argument type " + linkedHandler.getTypeRef().getSimpleName());
-      }
-
-      if (!entityContext.isAttached(linkedHandler)) {
-        entityContext.attach(linkedHandler, AttachedEntityStatus.LINKED);
-      }
-    }
-
-    // 3) add links
-    addLinkChanges(property, value);
-  }
-
-  protected abstract void setPropertyValue(final Property property, final Object value);
-
-  protected abstract void addPropertyChanges(final String name, final Object value);
-
-  protected abstract void addLinkChanges(final NavigationProperty navProp, final Object value);
-
-  public abstract boolean isChanged();
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexFactoryInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexFactoryInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexFactoryInvocationHandler.java
index 2f833c6..74ea465 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexFactoryInvocationHandler.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexFactoryInvocationHandler.java
@@ -29,22 +29,22 @@ class ComplexFactoryInvocationHandler extends AbstractInvocationHandler implemen
 
   private static final long serialVersionUID = 2629912294765040027L;
 
-  private final EntityTypeInvocationHandler entityHandler;
+  private final EntityInvocationHandler entityHandler;
 
-  private final AbstractTypeInvocationHandler invokerHandler;
+  private final AbstractStructuredInvocationHandler invokerHandler;
 
   static ComplexFactoryInvocationHandler getInstance(
           final CommonEdmEnabledODataClient<?> client,
           final EntityContainerInvocationHandler containerHandler,
-          final EntityTypeInvocationHandler entityHandler,
-          final AbstractTypeInvocationHandler targetHandler) {
+          final EntityInvocationHandler entityHandler,
+          final AbstractStructuredInvocationHandler targetHandler) {
 
     return new ComplexFactoryInvocationHandler(client, containerHandler, entityHandler, targetHandler);
   }
 
   static ComplexFactoryInvocationHandler getInstance(
-          final EntityTypeInvocationHandler entityHandler,
-          final AbstractTypeInvocationHandler targetHandler) {
+          final EntityInvocationHandler entityHandler,
+          final AbstractStructuredInvocationHandler targetHandler) {
     return new ComplexFactoryInvocationHandler(
             entityHandler == null ? null : entityHandler.containerHandler.client,
             targetHandler == null
@@ -56,8 +56,8 @@ class ComplexFactoryInvocationHandler extends AbstractInvocationHandler implemen
   private ComplexFactoryInvocationHandler(
           final CommonEdmEnabledODataClient<?> client,
           final EntityContainerInvocationHandler containerHandler,
-          final EntityTypeInvocationHandler entityHandler,
-          final AbstractTypeInvocationHandler targetHandler) {
+          final EntityInvocationHandler entityHandler,
+          final AbstractStructuredInvocationHandler targetHandler) {
 
     super(client, containerHandler);
     this.invokerHandler = targetHandler;
@@ -78,7 +78,7 @@ class ComplexFactoryInvocationHandler extends AbstractInvocationHandler implemen
       return Proxy.newProxyInstance(
               Thread.currentThread().getContextClassLoader(),
               new Class<?>[] {method.getReturnType()},
-              ComplexTypeInvocationHandler.getInstance(client, property.name(), method.getReturnType(), entityHandler));
+              ComplexInvocationHandler.getInstance(client, property.name(), method.getReturnType(), entityHandler));
     } else {
       throw new NoSuchMethodException(method.getName());
     }

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexInvocationHandler.java
new file mode 100644
index 0000000..72d42e8
--- /dev/null
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexInvocationHandler.java
@@ -0,0 +1,186 @@
+/*
+ * 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.ext.proxy.commons;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
+import org.apache.olingo.commons.api.domain.CommonODataProperty;
+import org.apache.olingo.commons.api.domain.ODataComplexValue;
+import org.apache.olingo.commons.api.domain.ODataLinked;
+import org.apache.olingo.commons.api.edm.EdmElement;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.core.edm.EdmTypeInfo;
+import org.apache.olingo.ext.proxy.api.annotations.ComplexType;
+import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
+import org.apache.olingo.ext.proxy.api.annotations.Property;
+import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
+import org.apache.olingo.ext.proxy.utils.ClassUtils;
+import org.apache.olingo.ext.proxy.utils.CoreUtils;
+
+public class ComplexInvocationHandler extends AbstractStructuredInvocationHandler {
+
+  private static final long serialVersionUID = 2629912294765040037L;
+
+  public static ComplexInvocationHandler getInstance(
+          final CommonEdmEnabledODataClient<?> client,
+          final String propertyName,
+          final Class<?> reference,
+          final EntityInvocationHandler handler) {
+
+    final Class<?> complexTypeRef;
+    if (Collection.class.isAssignableFrom(reference)) {
+      complexTypeRef = ClassUtils.extractTypeArg(reference);
+    } else {
+      complexTypeRef = reference;
+    }
+
+    final ComplexType annotation = complexTypeRef.getAnnotation(ComplexType.class);
+    if (annotation == null) {
+      throw new IllegalArgumentException("Invalid complex type " + complexTypeRef);
+    }
+
+    final FullQualifiedName typeName =
+            new FullQualifiedName(ClassUtils.getNamespace(complexTypeRef), annotation.name());
+
+    final ODataComplexValue<? extends CommonODataProperty> complex =
+            client.getObjectFactory().newComplexValue(typeName.toString());
+
+    return (ComplexInvocationHandler) ComplexInvocationHandler.getInstance(
+            client, complex, complexTypeRef, handler);
+  }
+
+  public static ComplexInvocationHandler getInstance(
+          final CommonEdmEnabledODataClient<?> client,
+          final ODataComplexValue<?> complex,
+          final Class<?> typeRef,
+          final EntityInvocationHandler handler) {
+
+    return new ComplexInvocationHandler(client, complex, typeRef, handler);
+  }
+
+  public ComplexInvocationHandler(
+          final CommonEdmEnabledODataClient<?> client,
+          final ODataComplexValue<?> complex,
+          final Class<?> typeRef,
+          final EntityInvocationHandler handler) {
+
+    super(client, typeRef, complex, handler);
+  }
+
+  @SuppressWarnings("unchecked")
+  public ODataComplexValue<CommonODataProperty> getComplex() {
+    return (ODataComplexValue<CommonODataProperty>) this.internal;
+  }
+
+  @Override
+  protected Object getPropertyValue(final String name, final Type type) {
+    try {
+      return CoreUtils.getValueFromProperty(client, getComplex().get(name), type, entityHandler);
+    } catch (Exception e) {
+      throw new IllegalArgumentException("Error getting value for property '" + name + "'", e);
+    }
+  }
+
+  @Override
+  public Collection<String> getAdditionalPropertyNames() {
+    final Set<String> res = new HashSet<String>();
+    final Set<String> propertyNames = new HashSet<String>();
+    for (Method method : typeRef.getMethods()) {
+      final Annotation ann = method.getAnnotation(Property.class);
+      if (ann != null) {
+        final String property = ((Property) ann).name();
+        propertyNames.add(property);
+      }
+    }
+
+    for (final Iterator<? extends CommonODataProperty> itor = getComplex().iterator(); itor.hasNext();) {
+      final CommonODataProperty property = itor.next();
+      if (!propertyNames.contains(property.getName())) {
+        res.add(property.getName());
+      }
+    }
+
+    return res;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected void setPropertyValue(final Property property, final Object value) {
+    final FullQualifiedName fqn =
+            new FullQualifiedName(ClassUtils.getNamespace(typeRef), typeRef.getAnnotation(ComplexType.class).name());
+
+    final EdmElement edmProperty = client.getCachedEdm().getComplexType(fqn).getProperty(property.name());
+
+    final Object toBeAdded;
+
+    if (value == null) {
+      toBeAdded = null;
+    } else if (Collection.class.isAssignableFrom(value.getClass())) {
+      toBeAdded = new ArrayList<Object>();
+      for (Object obj : (Collection) value) {
+        ((Collection) toBeAdded).add(obj instanceof Proxy ? Proxy.getInvocationHandler(obj) : obj);
+      }
+    } else if (value instanceof Proxy) {
+      toBeAdded = Proxy.getInvocationHandler(value);
+    } else {
+      toBeAdded = value;
+    }
+
+    final EdmTypeInfo type = new EdmTypeInfo.Builder().setEdm(client.getCachedEdm()).setTypeExpression(
+            edmProperty.isCollection() ? "Collection(" + property.type() + ")" : property.type()).build();
+
+    client.getBinder().add(getComplex(), CoreUtils.getODataProperty(client, property.name(), type, toBeAdded));
+
+    if (entityHandler != null && !entityContext.isAttached(entityHandler)) {
+      entityContext.attach(entityHandler, AttachedEntityStatus.CHANGED);
+    }
+  }
+
+  @Override
+  protected Object getNavigationPropertyValue(final NavigationProperty property, final Method getter) {
+    if (!(internal instanceof ODataLinked)) {
+      throw new UnsupportedOperationException("Internal object is not navigable");
+    }
+
+    return retrieveNavigationProperty(property, getter, containerHandler.getFactory().getServiceRoot());
+  }
+
+  @Override
+  protected void addPropertyChanges(final String name, final Object value) {
+    // do nothing ....
+  }
+
+  @Override
+  protected void addLinkChanges(final NavigationProperty navProp, final Object value) {
+    // do nothing ....
+  }
+
+  @Override
+  public boolean isChanged() {
+    return entityHandler == null ? false : entityHandler.isChanged();
+  }
+}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexTypeInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexTypeInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexTypeInvocationHandler.java
deleted file mode 100644
index 2175e8c..0000000
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ComplexTypeInvocationHandler.java
+++ /dev/null
@@ -1,186 +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.ext.proxy.commons;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-import org.apache.olingo.client.api.CommonEdmEnabledODataClient;
-import org.apache.olingo.commons.api.domain.CommonODataProperty;
-import org.apache.olingo.commons.api.domain.ODataComplexValue;
-import org.apache.olingo.commons.api.domain.ODataLinked;
-import org.apache.olingo.commons.api.edm.EdmElement;
-import org.apache.olingo.commons.api.edm.FullQualifiedName;
-import org.apache.olingo.commons.core.edm.EdmTypeInfo;
-import org.apache.olingo.ext.proxy.api.annotations.ComplexType;
-import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
-import org.apache.olingo.ext.proxy.api.annotations.Property;
-import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
-import org.apache.olingo.ext.proxy.utils.ClassUtils;
-import org.apache.olingo.ext.proxy.utils.CoreUtils;
-
-public class ComplexTypeInvocationHandler extends AbstractTypeInvocationHandler {
-
-  private static final long serialVersionUID = 2629912294765040037L;
-
-  public static ComplexTypeInvocationHandler getInstance(
-          final CommonEdmEnabledODataClient<?> client,
-          final String propertyName,
-          final Class<?> reference,
-          final EntityTypeInvocationHandler handler) {
-
-    final Class<?> complexTypeRef;
-    if (Collection.class.isAssignableFrom(reference)) {
-      complexTypeRef = ClassUtils.extractTypeArg(reference);
-    } else {
-      complexTypeRef = reference;
-    }
-
-    final ComplexType annotation = complexTypeRef.getAnnotation(ComplexType.class);
-    if (annotation == null) {
-      throw new IllegalArgumentException("Invalid complex type " + complexTypeRef);
-    }
-
-    final FullQualifiedName typeName =
-            new FullQualifiedName(ClassUtils.getNamespace(complexTypeRef), annotation.name());
-
-    final ODataComplexValue<? extends CommonODataProperty> complex =
-            client.getObjectFactory().newComplexValue(typeName.toString());
-
-    return (ComplexTypeInvocationHandler) ComplexTypeInvocationHandler.getInstance(
-            client, complex, complexTypeRef, handler);
-  }
-
-  public static ComplexTypeInvocationHandler getInstance(
-          final CommonEdmEnabledODataClient<?> client,
-          final ODataComplexValue<?> complex,
-          final Class<?> typeRef,
-          final EntityTypeInvocationHandler handler) {
-
-    return new ComplexTypeInvocationHandler(client, complex, typeRef, handler);
-  }
-
-  public ComplexTypeInvocationHandler(
-          final CommonEdmEnabledODataClient<?> client,
-          final ODataComplexValue<?> complex,
-          final Class<?> typeRef,
-          final EntityTypeInvocationHandler handler) {
-
-    super(client, typeRef, complex, handler);
-  }
-
-  @SuppressWarnings("unchecked")
-  public ODataComplexValue<CommonODataProperty> getComplex() {
-    return (ODataComplexValue<CommonODataProperty>) this.internal;
-  }
-
-  @Override
-  protected Object getPropertyValue(final String name, final Type type) {
-    try {
-      return CoreUtils.getValueFromProperty(client, getComplex().get(name), type, entityHandler);
-    } catch (Exception e) {
-      throw new IllegalArgumentException("Error getting value for property '" + name + "'", e);
-    }
-  }
-
-  @Override
-  public Collection<String> getAdditionalPropertyNames() {
-    final Set<String> res = new HashSet<String>();
-    final Set<String> propertyNames = new HashSet<String>();
-    for (Method method : typeRef.getMethods()) {
-      final Annotation ann = method.getAnnotation(Property.class);
-      if (ann != null) {
-        final String property = ((Property) ann).name();
-        propertyNames.add(property);
-      }
-    }
-
-    for (final Iterator<? extends CommonODataProperty> itor = getComplex().iterator(); itor.hasNext();) {
-      final CommonODataProperty property = itor.next();
-      if (!propertyNames.contains(property.getName())) {
-        res.add(property.getName());
-      }
-    }
-
-    return res;
-  }
-
-  @Override
-  @SuppressWarnings("unchecked")
-  protected void setPropertyValue(final Property property, final Object value) {
-    final FullQualifiedName fqn =
-            new FullQualifiedName(ClassUtils.getNamespace(typeRef), typeRef.getAnnotation(ComplexType.class).name());
-
-    final EdmElement edmProperty = client.getCachedEdm().getComplexType(fqn).getProperty(property.name());
-
-    final Object toBeAdded;
-
-    if (value == null) {
-      toBeAdded = null;
-    } else if (Collection.class.isAssignableFrom(value.getClass())) {
-      toBeAdded = new ArrayList<Object>();
-      for (Object obj : (Collection) value) {
-        ((Collection) toBeAdded).add(obj instanceof Proxy ? Proxy.getInvocationHandler(obj) : obj);
-      }
-    } else if (value instanceof Proxy) {
-      toBeAdded = Proxy.getInvocationHandler(value);
-    } else {
-      toBeAdded = value;
-    }
-
-    final EdmTypeInfo type = new EdmTypeInfo.Builder().setEdm(client.getCachedEdm()).setTypeExpression(
-            edmProperty.isCollection() ? "Collection(" + property.type() + ")" : property.type()).build();
-
-    client.getBinder().add(getComplex(), CoreUtils.getODataProperty(client, property.name(), type, toBeAdded));
-
-    if (entityHandler != null && !entityContext.isAttached(entityHandler)) {
-      entityContext.attach(entityHandler, AttachedEntityStatus.CHANGED);
-    }
-  }
-
-  @Override
-  protected Object getNavigationPropertyValue(final NavigationProperty property, final Method getter) {
-    if (!(internal instanceof ODataLinked)) {
-      throw new UnsupportedOperationException("Internal object is not navigable");
-    }
-
-    return retriveNavigationProperty(property, getter);
-  }
-
-  @Override
-  protected void addPropertyChanges(final String name, final Object value) {
-    // do nothing ....
-  }
-
-  @Override
-  protected void addLinkChanges(final NavigationProperty navProp, final Object value) {
-    // do nothing ....
-  }
-
-  @Override
-  public boolean isChanged() {
-    return entityHandler == null ? false : entityHandler.isChanged();
-  }
-}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ContainerImpl.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ContainerImpl.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ContainerImpl.java
index ab824ec..90a4d75 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ContainerImpl.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/ContainerImpl.java
@@ -46,7 +46,6 @@ import org.apache.olingo.client.api.communication.response.ODataBatchResponse;
 import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
 import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse;
 import org.apache.olingo.client.api.communication.response.ODataResponse;
-import org.apache.olingo.client.api.uri.CommonURIBuilder;
 import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem;
 import org.apache.olingo.client.core.uri.URIUtils;
 import org.apache.olingo.commons.api.domain.CommonODataEntity;
@@ -75,9 +74,9 @@ class ContainerImpl implements Container {
 
   private final CommonEdmEnabledODataClient<?> client;
 
-  private final EntityContainerFactory factory;
+  private final EntityContainerFactory<?> factory;
 
-  ContainerImpl(final CommonEdmEnabledODataClient<?> client, final EntityContainerFactory factory) {
+  ContainerImpl(final CommonEdmEnabledODataClient<?> client, final EntityContainerFactory<?> factory) {
     this.client = client;
     this.factory = factory;
   }
@@ -88,7 +87,7 @@ class ContainerImpl implements Container {
   @Override
   public void flush() {
     final CommonODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(client.getServiceRoot());
-    ((ODataRequest)request).setAccept(client.getConfiguration().getDefaultBatchAcceptFormat());
+    ((ODataRequest) request).setAccept(client.getConfiguration().getDefaultBatchAcceptFormat());
 
     final BatchStreamManager streamManager = (BatchStreamManager) ((ODataStreamedRequest) request).execute();
 
@@ -138,13 +137,13 @@ class ContainerImpl implements Container {
           throw new IllegalStateException("Transaction failed: " + res.getStatusMessage());
         }
 
-        final EntityTypeInvocationHandler handler = items.get(changesetItemId);
+        final EntityInvocationHandler handler = items.get(changesetItemId);
 
         if (handler != null) {
-          if (res instanceof ODataEntityCreateResponse) {
+          if (res instanceof ODataEntityCreateResponse && res.getStatusCode() == 201) {
             handler.setEntity(((ODataEntityCreateResponse) res).getBody());
             LOG.debug("Upgrade created object '{}'", handler);
-          } else if (res instanceof ODataEntityUpdateResponse) {
+          } else if (res instanceof ODataEntityUpdateResponse && res.getStatusCode() == 200) {
             handler.setEntity(((ODataEntityUpdateResponse) res).getBody());
             LOG.debug("Upgrade updated object '{}'", handler);
           }
@@ -156,7 +155,7 @@ class ContainerImpl implements Container {
   }
 
   private void batch(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final CommonODataEntity entity,
           final ODataChangeset changeset) {
 
@@ -181,19 +180,17 @@ class ContainerImpl implements Container {
   }
 
   private void batchCreate(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final CommonODataEntity entity,
           final ODataChangeset changeset) {
 
     LOG.debug("Create '{}'", handler);
 
-    final CommonURIBuilder<?> uriBuilder = client.getURIBuilder(factory.getServiceRoot()).
-            appendEntitySetSegment(handler.getEntitySetName());
-    changeset.addRequest(client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), entity));
+    changeset.addRequest(client.getCUDRequestFactory().getEntityCreateRequest(handler.getEntitySetURI(), entity));
   }
 
   private void batchUpdateMediaEntity(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final URI uri,
           final InputStream input,
           final ODataChangeset changeset) {
@@ -215,7 +212,7 @@ class ContainerImpl implements Container {
   }
 
   private void batchUpdateMediaResource(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final URI uri,
           final InputStream input,
           final ODataChangeset changeset) {
@@ -232,18 +229,20 @@ class ContainerImpl implements Container {
   }
 
   private void batchUpdate(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final CommonODataEntity changes,
           final ODataChangeset changeset) {
 
-    LOG.debug("Update '{}'", changes.getEditLink());
+    LOG.debug("Update '{}'", handler.getEntityURI());
 
     final ODataEntityUpdateRequest<CommonODataEntity> req =
             client.getServiceVersion().compareTo(ODataServiceVersion.V30) <= 0
             ? ((org.apache.olingo.client.api.v3.EdmEnabledODataClient) client).getCUDRequestFactory().
-            getEntityUpdateRequest(org.apache.olingo.client.api.communication.request.cud.v3.UpdateType.PATCH, changes)
+            getEntityUpdateRequest(handler.getEntityURI(),
+                    org.apache.olingo.client.api.communication.request.cud.v3.UpdateType.PATCH, changes)
             : ((org.apache.olingo.client.api.v4.EdmEnabledODataClient) client).getCUDRequestFactory().
-            getEntityUpdateRequest(org.apache.olingo.client.api.communication.request.cud.v4.UpdateType.PATCH, changes);
+            getEntityUpdateRequest(handler.getEntityURI(),
+                    org.apache.olingo.client.api.communication.request.cud.v4.UpdateType.PATCH, changes);
 
     req.setPrefer(new ODataPreferences(client.getServiceVersion()).returnContent());
 
@@ -255,7 +254,7 @@ class ContainerImpl implements Container {
   }
 
   private void batchUpdate(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final URI uri,
           final CommonODataEntity changes,
           final ODataChangeset changeset) {
@@ -265,11 +264,11 @@ class ContainerImpl implements Container {
     final ODataEntityUpdateRequest<CommonODataEntity> req =
             client.getServiceVersion().compareTo(ODataServiceVersion.V30) <= 0
             ? ((org.apache.olingo.client.api.v3.EdmEnabledODataClient) client).getCUDRequestFactory().
-            getEntityUpdateRequest(
-            uri, org.apache.olingo.client.api.communication.request.cud.v3.UpdateType.PATCH, changes)
+            getEntityUpdateRequest(uri,
+                    org.apache.olingo.client.api.communication.request.cud.v3.UpdateType.PATCH, changes)
             : ((org.apache.olingo.client.api.v4.EdmEnabledODataClient) client).getCUDRequestFactory().
-            getEntityUpdateRequest(
-            uri, org.apache.olingo.client.api.communication.request.cud.v4.UpdateType.PATCH, changes);
+            getEntityUpdateRequest(uri,
+                    org.apache.olingo.client.api.communication.request.cud.v4.UpdateType.PATCH, changes);
 
     req.setPrefer(new ODataPreferences(client.getServiceVersion()).returnContent());
 
@@ -281,14 +280,14 @@ class ContainerImpl implements Container {
   }
 
   private void batchDelete(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           final CommonODataEntity entity,
           final ODataChangeset changeset) {
 
-    LOG.debug("Delete '{}'", entity.getEditLink());
+    final URI deleteURI = handler.getEntityURI() == null ? entity.getEditLink() : handler.getEntityURI();
+    LOG.debug("Delete '{}'", deleteURI);
 
-    final ODataDeleteRequest req = client.getCUDRequestFactory().getDeleteRequest(URIUtils.getURI(
-            factory.getServiceRoot(), entity.getEditLink().toASCIIString()));
+    final ODataDeleteRequest req = client.getCUDRequestFactory().getDeleteRequest(deleteURI);
 
     if (StringUtils.isNotBlank(handler.getETag())) {
       req.setIfMatch(handler.getETag());
@@ -298,7 +297,7 @@ class ContainerImpl implements Container {
   }
 
   private int processEntityContext(
-          final EntityTypeInvocationHandler handler,
+          final EntityInvocationHandler handler,
           int pos,
           final TransactionItems items,
           final List<EntityLinkDesc> delayedUpdates,
@@ -323,14 +322,14 @@ class ContainerImpl implements Container {
               ? ODataLinkType.ENTITY_SET_NAVIGATION
               : ODataLinkType.ENTITY_NAVIGATION;
 
-      final Set<EntityTypeInvocationHandler> toBeLinked = new HashSet<EntityTypeInvocationHandler>();
+      final Set<EntityInvocationHandler> toBeLinked = new HashSet<EntityInvocationHandler>();
       final String serviceRoot = factory.getServiceRoot();
 
       for (Object proxy : type == ODataLinkType.ENTITY_SET_NAVIGATION
               ? (Collection) property.getValue() : Collections.singleton(property.getValue())) {
 
-        final EntityTypeInvocationHandler target =
-                (EntityTypeInvocationHandler) Proxy.getInvocationHandler(proxy);
+        final EntityInvocationHandler target =
+                (EntityInvocationHandler) Proxy.getInvocationHandler(proxy);
 
         final AttachedEntityStatus status =
                 EntityContainerFactory.getContext().entityContext().getStatus(target);
@@ -360,8 +359,7 @@ class ContainerImpl implements Container {
             // create the link for the current object
             LOG.debug("'{}' from '{}' to (${}) '{}'", type.name(), handler, targetPos, target);
 
-            entity.addLink(
-                    buildNavigationLink(property.getKey().name(), URI.create("$" + targetPos), type));
+            entity.addLink(buildNavigationLink(property.getKey().name(), URI.create("$" + targetPos), type));
           }
         }
       }
@@ -396,7 +394,7 @@ class ContainerImpl implements Container {
         final URI targetURI = currentStatus == AttachedEntityStatus.NEW
                 ? URI.create("$" + startingPos + "/$value")
                 : URIUtils.getURI(
-                factory.getServiceRoot(), handler.getEntity().getEditLink().toASCIIString() + "/$value");
+                        factory.getServiceRoot(), handler.getEntity().getEditLink().toASCIIString() + "/$value");
 
         batchUpdateMediaEntity(handler, targetURI, handler.getStreamChanges(), changeset);
 
@@ -409,8 +407,8 @@ class ContainerImpl implements Container {
     for (Map.Entry<String, InputStream> streamedChanges : handler.getStreamedPropertyChanges().entrySet()) {
       final URI targetURI = currentStatus == AttachedEntityStatus.NEW
               ? URI.create("$" + startingPos) : URIUtils.getURI(
-              factory.getServiceRoot(),
-              CoreUtils.getMediaEditLink(streamedChanges.getKey(), entity).toASCIIString());
+                      factory.getServiceRoot(),
+                      CoreUtils.getMediaEditLink(streamedChanges.getKey(), entity).toASCIIString());
 
       batchUpdateMediaResource(handler, targetURI, streamedChanges.getValue(), changeset);
 
@@ -467,7 +465,7 @@ class ContainerImpl implements Container {
         sourceURI = URI.create("$" + sourcePos);
       }
 
-      for (EntityTypeInvocationHandler target : delayedUpdate.getTargets()) {
+      for (EntityInvocationHandler target : delayedUpdate.getTargets()) {
         status = EntityContainerFactory.getContext().entityContext().getStatus(target);
 
         final URI targetURI;
@@ -492,11 +490,11 @@ class ContainerImpl implements Container {
 
   private class TransactionItems {
 
-    private final List<EntityTypeInvocationHandler> keys = new ArrayList<EntityTypeInvocationHandler>();
+    private final List<EntityInvocationHandler> keys = new ArrayList<EntityInvocationHandler>();
 
     private final List<Integer> values = new ArrayList<Integer>();
 
-    public EntityTypeInvocationHandler get(final Integer value) {
+    public EntityInvocationHandler get(final Integer value) {
       if (value != null && values.contains(value)) {
         return keys.get(values.indexOf(value));
       } else {
@@ -504,7 +502,7 @@ class ContainerImpl implements Container {
       }
     }
 
-    public Integer get(final EntityTypeInvocationHandler key) {
+    public Integer get(final EntityInvocationHandler key) {
       if (key != null && keys.contains(key)) {
         return values.get(keys.indexOf(key));
       } else {
@@ -512,14 +510,14 @@ class ContainerImpl implements Container {
       }
     }
 
-    public void remove(final EntityTypeInvocationHandler key) {
+    public void remove(final EntityInvocationHandler key) {
       if (keys.contains(key)) {
         values.remove(keys.indexOf(key));
         keys.remove(key);
       }
     }
 
-    public void put(final EntityTypeInvocationHandler key, final Integer value) {
+    public void put(final EntityInvocationHandler key, final Integer value) {
       // replace just in case of null current value; otherwise add the new entry
       if (key != null && keys.contains(key) && values.get(keys.indexOf(key)) == null) {
         remove(key);
@@ -534,7 +532,7 @@ class ContainerImpl implements Container {
       return sortedValues;
     }
 
-    public boolean contains(final EntityTypeInvocationHandler key) {
+    public boolean contains(final EntityInvocationHandler key) {
       return keys.contains(key);
     }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityContainerInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityContainerInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityContainerInvocationHandler.java
index 299a842..17f2f5c 100644
--- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityContainerInvocationHandler.java
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityContainerInvocationHandler.java
@@ -113,13 +113,13 @@ public final class EntityContainerInvocationHandler extends AbstractInvocationHa
           return Proxy.newProxyInstance(
                   Thread.currentThread().getContextClassLoader(),
                   new Class<?>[] {returnType},
-                  SingletonInvocationHandler.getInstance(returnType, this));
+                  SingletonInvocationHandler.getInstance(returnType, this, singleton.name()));
         }
       } else {
         return Proxy.newProxyInstance(
                 Thread.currentThread().getContextClassLoader(),
                 new Class<?>[] {returnType},
-                EntitySetInvocationHandler.getInstance(returnType, this));
+                EntitySetInvocationHandler.getInstance(returnType, this, entitySet.name()));
       }
 
       throw new NoSuchMethodException(method.getName());

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/ca66d671/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityInvocationHandler.java
----------------------------------------------------------------------
diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityInvocationHandler.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityInvocationHandler.java
new file mode 100644
index 0000000..361c140
--- /dev/null
+++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/EntityInvocationHandler.java
@@ -0,0 +1,385 @@
+/*
+ * 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.ext.proxy.commons;
+
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.net.URI;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataMediaRequest;
+import org.apache.olingo.client.core.uri.URIUtils;
+import org.apache.olingo.commons.api.domain.CommonODataEntity;
+import org.apache.olingo.commons.api.domain.CommonODataProperty;
+import org.apache.olingo.commons.api.domain.ODataLinked;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.format.ODataMediaFormat;
+import org.apache.olingo.ext.proxy.api.annotations.EntityType;
+import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
+import org.apache.olingo.ext.proxy.api.annotations.Property;
+import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
+import org.apache.olingo.ext.proxy.context.EntityUUID;
+import org.apache.olingo.ext.proxy.utils.CoreUtils;
+
+public class EntityInvocationHandler extends AbstractStructuredInvocationHandler {
+
+  private static final long serialVersionUID = 2629912294765040037L;
+
+  private final URI entityURI;
+
+  protected Map<String, Object> propertyChanges = new HashMap<String, Object>();
+
+  protected Map<NavigationProperty, Object> linkChanges = new HashMap<NavigationProperty, Object>();
+
+  protected int propertiesTag = 0;
+
+  protected int linksTag = 0;
+
+  private Map<String, InputStream> streamedPropertyChanges = new HashMap<String, InputStream>();
+
+  private InputStream stream;
+
+  private EntityUUID uuid;
+
+  static EntityInvocationHandler getInstance(
+          final URI entityURI,
+          final CommonODataEntity entity,
+          final EntitySetInvocationHandler<?, ?, ?> entitySet,
+          final Class<?> typeRef) {
+
+    return getInstance(
+            entityURI,
+            entity,
+            entitySet.getEntitySetURI(),
+            typeRef,
+            entitySet.containerHandler);
+  }
+
+  static EntityInvocationHandler getInstance(
+          final URI entityURI,
+          final CommonODataEntity entity,
+          final URI entitySetURI,
+          final Class<?> typeRef,
+          final EntityContainerInvocationHandler containerHandler) {
+
+    return new EntityInvocationHandler(entityURI, entity, entitySetURI, typeRef, containerHandler);
+  }
+
+  private EntityInvocationHandler(
+          final URI entityURI,
+          final CommonODataEntity entity,
+          final URI entitySetURI,
+          final Class<?> typeRef,
+          final EntityContainerInvocationHandler containerHandler) {
+
+    super(containerHandler.getClient(), typeRef, (ODataLinked) entity, containerHandler);
+
+    this.entityURI = entityURI;
+    this.internal = entity;
+    getEntity().setMediaEntity(typeRef.getAnnotation(EntityType.class).hasStream());
+
+    this.uuid = new EntityUUID(
+            containerHandler.getEntityContainerName(),
+            entitySetURI,
+            typeRef,
+            CoreUtils.getKey(client, typeRef, entity));
+  }
+
+  public void setEntity(final CommonODataEntity entity) {
+    this.internal = entity;
+    getEntity().setMediaEntity(typeRef.getAnnotation(EntityType.class).hasStream());
+
+    this.uuid = new EntityUUID(
+            getUUID().getContainerName(),
+            getUUID().getEntitySetURI(),
+            getUUID().getType(),
+            CoreUtils.getKey(client, typeRef, entity));
+
+    this.propertyChanges.clear();
+    this.linkChanges.clear();
+    this.streamedPropertyChanges.clear();
+    this.propertiesTag = 0;
+    this.linksTag = 0;
+  }
+
+  public EntityUUID getUUID() {
+    return uuid;
+  }
+
+  public String getEntityContainerName() {
+    return uuid.getContainerName();
+  }
+
+  public URI getEntitySetURI() {
+    return uuid.getEntitySetURI();
+  }
+
+  public final CommonODataEntity getEntity() {
+    return (CommonODataEntity) internal;
+  }
+
+  public URI getEntityURI() {
+    return entityURI;
+  }
+
+  /**
+   * Gets the current ETag defined into the wrapped entity.
+   *
+   * @return
+   */
+  public String getETag() {
+    return getEntity().getETag();
+  }
+
+  /**
+   * Overrides ETag value defined into the wrapped entity.
+   *
+   * @param eTag ETag.
+   */
+  public void setETag(final String eTag) {
+    getEntity().setETag(eTag);
+  }
+
+  public Map<String, Object> getPropertyChanges() {
+    return propertyChanges;
+  }
+
+  public Map<NavigationProperty, Object> getLinkChanges() {
+    return linkChanges;
+  }
+
+  private void updatePropertiesTag(final int checkpoint) {
+    if (checkpoint == propertiesTag) {
+      propertiesTag = propertyChanges.hashCode();
+    }
+  }
+
+  private void updateLinksTag(final int checkpoint) {
+    if (checkpoint == linksTag) {
+      linksTag = linkChanges.hashCode();
+    }
+  }
+
+  @Override
+  protected Object getPropertyValue(final String name, final Type type) {
+    try {
+      final CommonODataProperty property = getEntity().getProperty(name);
+
+      Object res;
+      if (propertyChanges.containsKey(name)) {
+        res = propertyChanges.get(name);
+      } else {
+        res = CoreUtils.getValueFromProperty(client, property, type, this);
+
+        if (res != null) {
+          addPropertyChanges(name, res);
+        }
+      }
+
+      return res;
+    } catch (Exception e) {
+      throw new IllegalArgumentException("Error getting value for property '" + name + "'", e);
+    }
+  }
+
+  @Override
+  public Collection<String> getAdditionalPropertyNames() {
+    final Set<String> res = new HashSet<String>(propertyChanges.keySet());
+    final Set<String> propertyNames = new HashSet<String>();
+    for (Method method : typeRef.getMethods()) {
+      final Annotation ann = method.getAnnotation(Property.class);
+      if (ann != null) {
+        final String property = ((Property) ann).name();
+        propertyNames.add(property);
+
+        // maybe someone could add a normal attribute to the additional set
+        res.remove(property);
+      }
+    }
+
+    for (CommonODataProperty property : getEntity().getProperties()) {
+      if (!propertyNames.contains(property.getName())) {
+        res.add(property.getName());
+      }
+    }
+
+    return res;
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected void setPropertyValue(final Property property, final Object value) {
+    if (property.type().equalsIgnoreCase(EdmPrimitiveTypeKind.Stream.toString())) {
+      setStreamedProperty(property, (InputStream) value);
+    } else {
+      addPropertyChanges(property.name(), value);
+
+      if (value != null) {
+        final Collection<?> coll;
+        if (Collection.class.isAssignableFrom(value.getClass())) {
+          coll = Collection.class.cast(value);
+        } else {
+          coll = Collections.singleton(value);
+        }
+
+        for (Object item : coll) {
+          if (item instanceof Proxy) {
+            final InvocationHandler handler = Proxy.getInvocationHandler(item);
+            if ((handler instanceof ComplexInvocationHandler)
+                    && ((ComplexInvocationHandler) handler).getEntityHandler() == null) {
+              ((ComplexInvocationHandler) handler).setEntityHandler(this);
+            }
+          }
+        }
+      }
+    }
+
+    attach(AttachedEntityStatus.CHANGED);
+  }
+
+  @Override
+  public boolean isChanged() {
+    return this.linkChanges.hashCode() != this.linksTag
+            || this.propertyChanges.hashCode() != this.propertiesTag
+            || this.stream != null
+            || !this.streamedPropertyChanges.isEmpty();
+  }
+
+  public void setStream(final InputStream stream) {
+    if (typeRef.getAnnotation(EntityType.class).hasStream()) {
+      IOUtils.closeQuietly(this.stream);
+      this.stream = stream;
+      attach(AttachedEntityStatus.CHANGED);
+    }
+  }
+
+  public InputStream getStreamChanges() {
+    return this.stream;
+  }
+
+  public Map<String, InputStream> getStreamedPropertyChanges() {
+    return streamedPropertyChanges;
+  }
+
+  public InputStream getStream() {
+    final URI contentSource = getEntity().getMediaContentSource();
+
+    if (this.stream == null
+            && typeRef.getAnnotation(EntityType.class).hasStream()
+            && contentSource != null) {
+
+      final String contentType =
+              StringUtils.isBlank(getEntity().getMediaContentType()) ? "*/*" : getEntity().getMediaContentType();
+
+      final ODataMediaRequest retrieveReq = client.getRetrieveRequestFactory().getMediaRequest(contentSource);
+      retrieveReq.setFormat(ODataMediaFormat.fromFormat(contentType));
+
+      this.stream = retrieveReq.execute().getBody();
+    }
+
+    return this.stream;
+  }
+
+  public Object getStreamedProperty(final Property property) {
+    InputStream res = streamedPropertyChanges.get(property.name());
+
+    try {
+      if (res == null) {
+        final URI link = URIUtils.getURI(
+                containerHandler.getFactory().getServiceRoot(),
+                CoreUtils.getMediaEditLink(property.name(), getEntity()).toASCIIString());
+
+        final ODataMediaRequest req = client.getRetrieveRequestFactory().getMediaRequest(link);
+        res = req.execute().getBody();
+
+      }
+    } catch (Exception e) {
+      res = null;
+    }
+
+    return res;
+
+  }
+
+  private void setStreamedProperty(final Property property, final InputStream input) {
+    final Object obj = propertyChanges.get(property.name());
+    if (obj instanceof InputStream) {
+      IOUtils.closeQuietly((InputStream) obj);
+    }
+
+    streamedPropertyChanges.put(property.name(), input);
+  }
+
+  @Override
+  protected Object getNavigationPropertyValue(final NavigationProperty property, final Method getter) {
+    final Object navPropValue;
+
+    if (linkChanges.containsKey(property)) {
+      navPropValue = linkChanges.get(property);
+    } else {
+      navPropValue = retrieveNavigationProperty(property, getter, containerHandler.getFactory().getServiceRoot());
+    }
+
+    if (navPropValue != null) {
+      addLinkChanges(property, navPropValue);
+    }
+
+    return navPropValue;
+  }
+
+  @Override
+  protected void addPropertyChanges(final String name, final Object value) {
+    final int checkpoint = propertyChanges.hashCode();
+    propertyChanges.put(name, value);
+    updatePropertiesTag(checkpoint);
+  }
+
+  @Override
+  protected void addLinkChanges(final NavigationProperty navProp, final Object value) {
+    final int checkpoint = linkChanges.hashCode();
+    linkChanges.put(navProp, value);
+    updateLinksTag(checkpoint);
+  }
+
+  @Override
+  public String toString() {
+    return uuid.toString();
+  }
+
+  @Override
+  public int hashCode() {
+    return uuid.hashCode();
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    return obj instanceof EntityInvocationHandler
+            && ((EntityInvocationHandler) obj).getUUID().equals(uuid);
+  }
+}