org.apache.olingo.ext.proxy.commons.AbstractPersistenceManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of odata-client-proxy Show documentation
Show all versions of odata-client-proxy Show documentation
Java client API for OData services: Proxy.
/*
* 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.Proxy;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.client.api.EdmEnabledODataClient;
import org.apache.olingo.client.api.communication.header.ODataPreferences;
import org.apache.olingo.client.api.communication.request.cud.ODataDeleteRequest;
import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
import org.apache.olingo.client.api.communication.request.cud.ODataReferenceAddingRequest;
import org.apache.olingo.client.api.communication.request.streamed.ODataMediaEntityUpdateRequest;
import org.apache.olingo.client.api.communication.request.streamed.ODataStreamUpdateRequest;
import org.apache.olingo.client.core.uri.URIUtils;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientLink;
import org.apache.olingo.client.api.domain.ClientLinkType;
import org.apache.olingo.ext.proxy.AbstractService;
import org.apache.olingo.ext.proxy.api.EdmStreamValue;
import org.apache.olingo.ext.proxy.api.PersistenceManager;
import org.apache.olingo.ext.proxy.api.annotations.NavigationProperty;
import org.apache.olingo.ext.proxy.context.AttachedEntity;
import org.apache.olingo.ext.proxy.context.AttachedEntityStatus;
import org.apache.olingo.ext.proxy.context.EntityLinkDesc;
import org.apache.olingo.ext.proxy.utils.ClassUtils;
import org.apache.olingo.ext.proxy.utils.CoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
abstract class AbstractPersistenceManager implements PersistenceManager {
/**
* Logger.
*/
protected static final Logger LOG = LoggerFactory.getLogger(AbstractPersistenceManager.class);
private static final long serialVersionUID = 2065240290461241515L;
protected final AbstractService> service;
AbstractPersistenceManager(final AbstractService> factory) {
this.service = factory;
}
@Override
public Future flushAsync() {
return service.getClient().getConfiguration().getExecutor().submit(new Callable() {
@Override
public Void call() throws Exception {
flush();
return ClassUtils.returnVoid();
}
});
}
protected abstract void doFlush(PersistenceChanges changes, TransactionItems items);
@Override
public void flush() {
final PersistenceChanges changes = new PersistenceChanges();
final TransactionItems items = new TransactionItems();
int pos = 0;
final List delayedUpdates = new ArrayList();
for (AttachedEntity attachedEntity : service.getContext().entityContext()) {
final AttachedEntityStatus status = attachedEntity.getStatus();
if (((status != AttachedEntityStatus.ATTACHED
&& status != AttachedEntityStatus.LINKED) || attachedEntity.getEntity().isChanged())
&& !items.contains(attachedEntity.getEntity())) {
pos++;
pos = processEntityContext(attachedEntity.getEntity(), pos, items, delayedUpdates, changes);
}
}
processDelayedUpdates(delayedUpdates, pos, items, changes);
// remove null values
items.normalize();
for (URI uri : service.getContext().entityContext().getFurtherDeletes()) {
pos++;
queueDelete(uri, null, changes);
items.put(null, pos);
}
if (!items.isEmpty()) {
doFlush(changes, items);
}
service.getContext().detachAll();
}
private ClientLink buildNavigationLink(final String name, final URI uri, final ClientLinkType type) {
ClientLink result;
switch (type) {
case ENTITY_NAVIGATION:
result = service.getClient().getObjectFactory().newEntityNavigationLink(name, uri);
break;
case ENTITY_SET_NAVIGATION:
result = service.getClient().getObjectFactory().newEntitySetNavigationLink(name, uri);
break;
default:
throw new IllegalArgumentException("Invalid link type " + type.name());
}
return result;
}
protected int processEntityContext(
final EntityInvocationHandler handler,
int pos,
final TransactionItems items,
final List delayedUpdates,
final PersistenceChanges changeset) {
int posNumber = pos;
items.put(handler, null);
final ClientEntity entity = handler.getEntity();
entity.getNavigationLinks().clear();
final AttachedEntityStatus currentStatus = service.getContext().entityContext().getStatus(handler);
LOG.debug("Process '{}({})'", handler, currentStatus);
if (AttachedEntityStatus.DELETED != currentStatus) {
entity.getProperties().clear();
CoreUtils.addProperties(service.getClient(), handler.getPropertyChanges(), entity);
entity.getAnnotations().clear();
CoreUtils.addAnnotations(service.getClient(), handler.getAnnotations(), entity);
for (Map.Entry entry : handler.getPropAnnotatableHandlers().entrySet()) {
CoreUtils.addAnnotations(service.getClient(),
entry.getValue().getAnnotations(), entity.getProperty(entry.getKey()));
}
}
for (Map.Entry property : handler.getLinkChanges().entrySet()) {
final ClientLinkType type = Collection.class.isAssignableFrom(property.getValue().getClass())
? ClientLinkType.ENTITY_SET_NAVIGATION
: ClientLinkType.ENTITY_NAVIGATION;
final Set toBeLinked = new HashSet();
for (Object proxy : type == ClientLinkType.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)) {
posNumber = processEntityContext(target, posNumber, items, delayedUpdates, changeset);
posNumber++;
}
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));
}
}
}
if (!toBeLinked.isEmpty()) {
delayedUpdates.add(new EntityLinkDesc(property.getKey().name(), handler, toBeLinked, type));
}
if (property.getValue() instanceof Proxy) {
final InvocationHandler target = Proxy.getInvocationHandler(property.getValue());
if (target instanceof EntityCollectionInvocationHandler) {
for (String ref : ((EntityCollectionInvocationHandler>) target).referenceItems) {
delayedUpdates.add(new EntityLinkDesc(property.getKey().name(), handler, ref));
}
}
}
}
for (Map.Entry entry : handler.getNavPropAnnotatableHandlers().entrySet()) {
CoreUtils.addAnnotations(service.getClient(),
entry.getValue().getAnnotations(),
entity.getNavigationLink(entry.getKey()));
}
final AttachedEntityStatus processedStatus = queue(handler, entity, changeset);
if (processedStatus != null) {
// insert into the process queue
LOG.debug("{}: Insert '{}' into the process queue", posNumber, handler);
items.put(handler, posNumber);
} else {
posNumber--;
}
if (processedStatus != AttachedEntityStatus.DELETED) {
int startingPos = posNumber;
if (handler.getEntity().isMediaEntity() && handler.isChanged()) {
// update media properties
if (!handler.getPropertyChanges().isEmpty()) {
final URI targetURI = currentStatus == AttachedEntityStatus.NEW
? URI.create("$" + startingPos)
: URIUtils.getURI(
service.getClient().getServiceRoot(), handler.getEntity().getEditLink().toASCIIString());
queueUpdate(handler, targetURI, entity, changeset);
posNumber++;
items.put(handler, posNumber);
LOG.debug("{}: Update media properties for '{}' into the process queue", posNumber, handler);
}
// update media content
if (handler.getStreamChanges() != null) {
final URI targetURI = currentStatus == AttachedEntityStatus.NEW
? URI.create("$" + startingPos + "/$value")
: URIUtils.getURI(
service.getClient().getServiceRoot(),
handler.getEntity().getEditLink().toASCIIString() + "/$value");
queueUpdateMediaEntity(handler, targetURI, handler.getStreamChanges(), changeset);
// update media info (use null key)
posNumber++;
items.put(null, posNumber);
LOG.debug("{}: Update media info for '{}' into the process queue", posNumber, handler);
}
}
for (Map.Entry streamedChanges : handler.getStreamedPropertyChanges().entrySet()) {
final URI targetURI = currentStatus == AttachedEntityStatus.NEW
? URI.create("$" + startingPos) : URIUtils.getURI(
service.getClient().getServiceRoot(),
CoreUtils.getMediaEditLink(streamedChanges.getKey(), entity).toASCIIString());
queueUpdateMediaResource(handler, targetURI, streamedChanges.getValue(), changeset);
// update media info (use null key)
posNumber++;
items.put(handler, posNumber);
LOG.debug("{}: Update media info (null key) for '{}' into the process queue", posNumber, handler);
}
}
return posNumber;
}
protected void processDelayedUpdates(
final List delayedUpdates,
int pos,
final TransactionItems items,
final PersistenceChanges changeset) {
int posNumber = pos;
for (EntityLinkDesc delayedUpdate : delayedUpdates) {
if (StringUtils.isBlank(delayedUpdate.getReference())) {
posNumber++;
items.put(delayedUpdate.getSource(), posNumber);
final ClientEntity changes =
service.getClient().getObjectFactory().newEntity(delayedUpdate.getSource().getEntity().getTypeName());
AttachedEntityStatus status = service.getContext().entityContext().getStatus(delayedUpdate.getSource());
final URI sourceURI;
if (status == AttachedEntityStatus.CHANGED) {
sourceURI = URIUtils.getURI(
service.getClient().getServiceRoot(),
delayedUpdate.getSource().getEntity().getEditLink().toASCIIString());
} else {
int sourcePos = items.get(delayedUpdate.getSource());
sourceURI = URI.create("$" + sourcePos);
}
for (EntityInvocationHandler target : delayedUpdate.getTargets()) {
status = service.getContext().entityContext().getStatus(target);
final URI targetURI;
if (status == AttachedEntityStatus.CHANGED) {
targetURI = URIUtils.getURI(
service.getClient().getServiceRoot(), target.getEntity().getEditLink().toASCIIString());
} else {
int targetPos = items.get(target);
targetURI = URI.create("$" + targetPos);
}
changes.addLink(delayedUpdate.getType() == ClientLinkType.ENTITY_NAVIGATION
? service.getClient().getObjectFactory().
newEntityNavigationLink(delayedUpdate.getSourceName(), targetURI)
: service.getClient().getObjectFactory().
newEntitySetNavigationLink(delayedUpdate.getSourceName(), targetURI));
LOG.debug("'{}' from {} to {}", delayedUpdate.getType().name(), sourceURI, targetURI);
}
queueUpdate(delayedUpdate.getSource(), sourceURI, changes, changeset);
} else {
URI sourceURI = URIUtils.getURI(
service.getClient().getServiceRoot(),
delayedUpdate.getSource().getEntity().getEditLink().toASCIIString()
+ "/" + delayedUpdate.getSourceName() + "/$ref");
if (queueUpdateLinkViaRef(
delayedUpdate.getSource(), sourceURI, URI.create(delayedUpdate.getReference()), changeset)) {
posNumber++;
items.put(delayedUpdate.getSource(), posNumber);
}
}
}
}
private AttachedEntityStatus queue(
final EntityInvocationHandler handler,
final ClientEntity entity,
final PersistenceChanges changeset) {
switch (service.getContext().entityContext().getStatus(handler)) {
case NEW:
queueCreate(handler, entity, changeset);
return AttachedEntityStatus.NEW;
case DELETED:
queueDelete(handler, entity, changeset);
return AttachedEntityStatus.DELETED;
default:
if (handler.isChanged(false)) {
queueUpdate(handler, entity, changeset);
return AttachedEntityStatus.CHANGED;
} else {
return null;
}
}
}
private void queueCreate(
final EntityInvocationHandler handler,
final ClientEntity entity,
final PersistenceChanges changeset) {
LOG.debug("Create '{}'", handler);
changeset.addChange(service.getClient().getCUDRequestFactory().
getEntityCreateRequest(handler.getEntitySetURI(), entity), handler);
}
private void queueUpdateMediaEntity(
final EntityInvocationHandler handler,
final URI uri,
final EdmStreamValue input,
final PersistenceChanges changeset) {
LOG.debug("Update media entity '{}'", uri);
final ODataMediaEntityUpdateRequest> req =
service.getClient().getCUDRequestFactory().getMediaEntityUpdateRequest(uri, input.getStream());
if (StringUtils.isNotBlank(input.getContentType())) {
req.setContentType(input.getContentType());
}
if (StringUtils.isNotBlank(handler.getETag())) {
req.setIfMatch(handler.getETag());
}
changeset.addChange(req, handler);
}
private void queueUpdateMediaResource(
final EntityInvocationHandler handler,
final URI uri,
final EdmStreamValue input,
final PersistenceChanges changeset) {
LOG.debug("Update media entity '{}'", uri);
final ODataStreamUpdateRequest req =
service.getClient().getCUDRequestFactory().getStreamUpdateRequest(uri, input.getStream());
if (StringUtils.isNotBlank(input.getContentType())) {
req.setContentType(input.getContentType());
}
if (StringUtils.isNotBlank(handler.getETag())) {
req.setIfMatch(handler.getETag());
}
changeset.addChange(req, handler);
}
private void queueUpdate(
final EntityInvocationHandler handler,
final ClientEntity changes,
final PersistenceChanges changeset) {
LOG.debug("Update '{}'", handler.getEntityURI());
final ODataEntityUpdateRequest req =
((EdmEnabledODataClient) service.getClient()).getCUDRequestFactory().
getEntityUpdateRequest(handler.getEntityURI(),
org.apache.olingo.client.api.communication.request.cud.UpdateType.PATCH, changes);
req.setPrefer(new ODataPreferences().returnContent());
if (StringUtils.isNotBlank(handler.getETag())) {
req.setIfMatch(handler.getETag());
}
changeset.addChange(req, handler);
}
private boolean queueUpdateLinkViaRef(
final EntityInvocationHandler handler,
final URI source,
final URI targetRef,
final PersistenceChanges changeset) {
LOG.debug("Update '{}'", targetRef);
URI sericeRoot = handler.getClient().newURIBuilder(handler.getClient().getServiceRoot()).build();
final ODataReferenceAddingRequest req =
((org.apache.olingo.client.api.EdmEnabledODataClient) service.getClient()).getCUDRequestFactory().
getReferenceAddingRequest(sericeRoot, source, targetRef);
req.setPrefer(new ODataPreferences().returnContent());
if (StringUtils.isNotBlank(handler.getETag())) {
req.setIfMatch(handler.getETag());
}
changeset.addChange(req, handler);
return true;
}
private void queueUpdate(
final EntityInvocationHandler handler,
final URI uri,
final ClientEntity changes,
final PersistenceChanges changeset) {
LOG.debug("Update '{}'", uri);
final ODataEntityUpdateRequest req =
((EdmEnabledODataClient) service.getClient()).getCUDRequestFactory().
getEntityUpdateRequest(uri,
org.apache.olingo.client.api.communication.request.cud.UpdateType.PATCH, changes);
req.setPrefer(new ODataPreferences().returnContent());
if (StringUtils.isNotBlank(handler.getETag())) {
req.setIfMatch(handler.getETag());
}
changeset.addChange(req, handler);
}
private void queueDelete(
final EntityInvocationHandler handler,
final ClientEntity entity,
final PersistenceChanges changeset) {
final URI deleteURI = entity.getEditLink() == null ? handler.getEntityURI() : entity.getEditLink();
changeset.addChange(buildDeleteRequest(deleteURI, handler.getETag()), handler);
}
private void queueDelete(
final URI deleteURI,
final String etag,
final PersistenceChanges changeset) {
changeset.addChange(buildDeleteRequest(deleteURI, etag), null);
}
private ODataDeleteRequest buildDeleteRequest(final URI deleteURI, final String etag) {
LOG.debug("Delete '{}'", deleteURI);
final ODataDeleteRequest req = service.getClient().getCUDRequestFactory().getDeleteRequest(deleteURI);
if (StringUtils.isNotBlank(etag)) {
req.setIfMatch(etag);
}
return req;
}
private AttachedEntityStatus resolveNavigationLink(
final NavigationProperty property, final EntityInvocationHandler handler) {
if (handler.getUUID().getEntitySetURI() == null) {
//Load key
CoreUtils.getKey(service.getClient(), handler, handler.getTypeRef(), handler.getEntity());
handler.updateUUID(CoreUtils.getTargetEntitySetURI(service.getClient(), property), handler.getTypeRef(), null);
service.getContext().entityContext().attach(handler, AttachedEntityStatus.NEW);
return AttachedEntityStatus.NEW;
} else {
// existent object
service.getContext().entityContext().attach(handler, AttachedEntityStatus.LINKED);
return AttachedEntityStatus.LINKED;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy