You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by sk...@apache.org on 2014/05/19 12:34:43 UTC
[45/45] 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/0ef9b0fb
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/0ef9b0fb
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/0ef9b0fb
Branch: refs/heads/olingo-266-ref
Commit: 0ef9b0fb968597d0028c73036cb92cf9de8e1902
Parents: ea4524b
Author: Francesco Chicchiriccò <--global>
Authored: Sat May 17 16:06:18 2014 +0200
Committer: Stephan Klevenz <st...@sap.com>
Committed: Mon May 19 12:33:32 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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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/0ef9b0fb/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);
+ }
+}