[OLINGO-365] fix for link update via reference ID

This commit is contained in:
fmartelli 2014-07-28 22:06:03 +02:00
parent 67cc0c4406
commit a5e983c9b8
4 changed files with 128 additions and 42 deletions

View File

@ -56,6 +56,8 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
protected Collection<String> referenceItems;
protected Collection<T> newest;
protected final URI baseURI;
protected CommonURIBuilder<?> uri;
@ -67,6 +69,8 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
private final Map<Class<? extends AbstractTerm>, Object> annotationsByTerm =
new HashMap<Class<? extends AbstractTerm>, Object>();
private boolean changed = false;
public AbstractCollectionInvocationHandler(
final AbstractService<?> service,
final Collection<T> items,
@ -78,6 +82,7 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
this.itemRef = itemRef;
this.items = items;
this.referenceItems = new ArrayList<String>();
this.newest = new ArrayList<T>();
this.uri = uri;
this.baseURI = this.uri == null ? null : this.uri.build();
}
@ -176,6 +181,8 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
service.getContext().entityContext().attachNew(handler);
}
}
changed = true;
newest.add(element);
return items.add(element);
}
@ -191,6 +198,7 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
return false;
}
changed = true;
return referenceItems.add(id.toASCIIString());
}
@ -249,6 +257,8 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
@Override
public boolean addAll(final Collection<? extends T> collection) {
changed = true;
newest.addAll(collection);
return items.addAll(collection);
}
@ -325,4 +335,8 @@ public abstract class AbstractCollectionInvocationHandler<T extends Serializable
this.uri = this.baseURI == null ? null : getClient().newURIBuilder(baseURI.toASCIIString());
this.nextPageURI = null;
}
public boolean isChanged() {
return changed;
}
}

View File

@ -174,51 +174,13 @@ abstract class AbstractPersistenceManager implements PersistenceManager {
: ODataLinkType.ENTITY_NAVIGATION;
final Set<EntityInvocationHandler> toBeLinked = new HashSet<EntityInvocationHandler>();
for (Object proxy : type == ODataLinkType.ENTITY_SET_NAVIGATION
? (Collection<?>) property.getValue() : Collections.singleton(property.getValue())) {
final EntityInvocationHandler target = (EntityInvocationHandler) Proxy.getInvocationHandler(proxy);
final AttachedEntityStatus status;
if (!service.getContext().entityContext().isAttached(target)) {
status = resolveNavigationLink(property.getKey(), target);
} else {
status = service.getContext().entityContext().getStatus(target);
}
LOG.debug("Found link to '{}({})'", target, status);
final URI editLink = target.getEntity().getEditLink();
if ((status == AttachedEntityStatus.ATTACHED || status == AttachedEntityStatus.LINKED) && !target.isChanged()) {
LOG.debug("Add link to '{}'", target);
entity.addLink(buildNavigationLink(
property.getKey().name(),
URIUtils.getURI(service.getClient().getServiceRoot(), editLink.toASCIIString()), type));
} else {
if (!items.contains(target)) {
pos = processEntityContext(target, pos, items, delayedUpdates, changeset);
pos++;
}
final Integer targetPos = items.get(target);
if (targetPos == null) {
// schedule update for the current object
LOG.debug("Schedule '{}' from '{}' to '{}'", type.name(), handler, target);
toBeLinked.add(target);
} else if (status == AttachedEntityStatus.CHANGED) {
LOG.debug("Changed: '{}' from '{}' to (${}) '{}'", type.name(), handler, targetPos, target);
entity.addLink(buildNavigationLink(
property.getKey().name(),
URIUtils.getURI(service.getClient().getServiceRoot(), editLink.toASCIIString()), type));
} else {
// 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));
}
}
toBeLinked.addAll(processLinkChanges(
handler, target, property.getKey(), type, pos, items, delayedUpdates, changeset));
}
if (!toBeLinked.isEmpty()) {
@ -236,6 +198,41 @@ abstract class AbstractPersistenceManager implements PersistenceManager {
}
}
// Required by linking provided on existent object. Say:
// container.getCustomers().getByKey(1).getOrders().add(order)
// Required by linking provided via entity reference ID. Say:
// container.getCustomers().getByKey(1).getOrders().addRef(order)
for (Map.Entry<NavigationProperty, Object> property : handler.linkCache.entrySet()) {
if (property.getValue() instanceof Proxy) {
final InvocationHandler target = Proxy.getInvocationHandler(property.getValue());
if (target instanceof EntityCollectionInvocationHandler
&& ((EntityCollectionInvocationHandler) target).isChanged()) {
final ODataLinkType type = Collection.class.isAssignableFrom(property.getValue().getClass())
? ODataLinkType.ENTITY_SET_NAVIGATION
: ODataLinkType.ENTITY_NAVIGATION;
final Set<EntityInvocationHandler> toBeLinked = new HashSet<EntityInvocationHandler>();
for (Object proxy : ((EntityCollectionInvocationHandler) target).newest) {
final EntityInvocationHandler targetEntity = (EntityInvocationHandler) Proxy.getInvocationHandler(proxy);
toBeLinked.addAll(processLinkChanges(
handler, targetEntity, property.getKey(), type, pos, items, delayedUpdates, changeset));
}
if (!toBeLinked.isEmpty()) {
delayedUpdates.add(new EntityLinkDesc(property.getKey().name(), handler, toBeLinked, type));
}
for (String ref : ((EntityCollectionInvocationHandler<?>) target).referenceItems) {
delayedUpdates.add(new EntityLinkDesc(property.getKey().name(), handler, ref));
}
}
}
}
if (entity instanceof ODataEntity) {
for (Map.Entry<String, AnnotatableInvocationHandler> entry : handler.getNavPropAnnotatableHandlers().entrySet()) {
@ -305,6 +302,62 @@ abstract class AbstractPersistenceManager implements PersistenceManager {
return pos;
}
protected Set<EntityInvocationHandler> processLinkChanges(
final EntityInvocationHandler source,
final EntityInvocationHandler target,
final NavigationProperty property,
final ODataLinkType type,
int pos,
final TransactionItems items,
final List<EntityLinkDesc> delayedUpdates,
final PersistenceChanges changeset) {
final Set<EntityInvocationHandler> toBeLinked = new HashSet<EntityInvocationHandler>();
final AttachedEntityStatus status;
if (!service.getContext().entityContext().isAttached(target)) {
status = resolveNavigationLink(property, target);
} else {
status = service.getContext().entityContext().getStatus(target);
}
LOG.debug("Found link to '{}({})'", target, status);
final URI editLink = target.getEntity().getEditLink();
if ((status == AttachedEntityStatus.ATTACHED || status == AttachedEntityStatus.LINKED) && !target.isChanged()) {
LOG.debug("Add link to '{}'", target);
source.getEntity().addLink(buildNavigationLink(
property.name(),
URIUtils.getURI(service.getClient().getServiceRoot(), editLink.toASCIIString()), type));
} else {
if (!items.contains(target)) {
pos = processEntityContext(target, pos, items, delayedUpdates, changeset);
pos++;
}
final Integer targetPos = items.get(target);
if (targetPos == null) {
// schedule update for the current object
LOG.debug("Schedule '{}' from '{}' to '{}'", type.name(), source, target);
toBeLinked.add(target);
} else if (status == AttachedEntityStatus.CHANGED) {
LOG.debug("Changed: '{}' from '{}' to (${}) '{}'", type.name(), source, targetPos, target);
source.getEntity().addLink(buildNavigationLink(
property.name(),
URIUtils.getURI(service.getClient().getServiceRoot(), editLink.toASCIIString()), type));
} else {
// create the link for the current object
LOG.debug("'{}' from '{}' to (${}) '{}'", type.name(), source, targetPos, target);
source.getEntity().addLink(
buildNavigationLink(property.name(), URI.create("$" + targetPos), type));
}
}
return toBeLinked;
}
protected void processDelayedUpdates(
final List<EntityLinkDesc> delayedUpdates,
int pos,

View File

@ -294,10 +294,21 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
return isChanged(true);
}
public boolean isChanged(final boolean includeMedia) {
public boolean isChanged(final boolean deep) {
boolean linkedChanges = false;
for (Map.Entry<NavigationProperty, Object> link : linkCache.entrySet()) {
final InvocationHandler handler = Proxy.getInvocationHandler(link.getValue());
if (handler instanceof EntityInvocationHandler) {
linkedChanges = linkedChanges || ((EntityInvocationHandler) handler).isChanged();
} else if (handler instanceof EntityCollectionInvocationHandler) {
linkedChanges = linkedChanges || ((EntityCollectionInvocationHandler) handler).isChanged();
}
}
return this.linkChanges.hashCode() != this.linksTag
|| this.propertyChanges.hashCode() != this.propertiesTag
|| (includeMedia && (this.stream != null
|| (deep && (linkedChanges
|| this.stream != null
|| !this.streamedPropertyChanges.isEmpty()));
}
@ -355,6 +366,7 @@ public class EntityInvocationHandler extends AbstractStructuredInvocationHandler
if (navPropValue != null) {
cacheLink(property, navPropValue);
attach();
}
return navPropValue;

View File

@ -115,6 +115,13 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase {
container.getCustomers().getByKey(1).setOrders(orders);
container.flush();
}
@Test
public void addViaReference2() {
final Order order = container.getOrders().getByKey(8).load();
container.getCustomers().getByKey(1).getOrders().addRef(order);
container.flush();
}
@Test
public void readAndCheckForPrimitive() {