From 23789e100b8ef92ff7096969a8551b0ffb600fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= <--global> Date: Thu, 17 Jul 2014 14:18:21 +0200 Subject: [PATCH] [OLINGO-369] continue-on-error behavior implemented for both transactional and non-transactional persistence --- .../ext/proxy/api/PersistenceManager.java | 7 +- .../commons/AbstractPersistenceManager.java | 9 +- .../EntityContainerInvocationHandler.java | 5 +- ...onTransactionalPersistenceManagerImpl.java | 31 +++++-- .../TransactionalPersistenceManagerImpl.java | 61 ++++++++++--- .../org/apache/olingo/fit/V4Services.java | 35 +++++-- .../fit/proxy/v3/ContextTestITCase.java | 3 +- .../proxy/v4/APIBasicDesignTestITCase.java | 6 +- .../fit/proxy/v4/AbstractTestITCase.java | 10 +- .../olingo/fit/proxy/v4/AsyncTestITCase.java | 6 +- .../fit/proxy/v4/ContextTestITCase.java | 91 +++++++++++++++++++ .../fit/proxy/v4/EntityCreateTestITCase.java | 2 +- .../fit/proxy/v4/EntityUpdateTestITCase.java | 2 +- .../olingo/fit/proxy/v4/FilterTestITCase.java | 8 +- .../v4/OperationImportInvokeTestITCase.java | 4 +- .../fit/proxy/v4/PropertyTestITCase.java | 2 +- .../apache/olingo/fit/v4/BatchTestITCase.java | 72 ++++++++------- .../olingo/client/api/Configuration.java | 74 ++++++++------- .../ODataClientErrorException.java | 3 +- .../ODataServerErrorException.java | 3 +- .../request/batch/v4/ODataBatchRequest.java | 4 - .../olingo/client/core/ConfigurationImpl.java | 14 ++- .../header/ODataErrorResponseChecker.java | 85 +++++++++++++++++ .../request/AbstractRequest.java | 60 +++--------- .../request/batch/AbstractBatchManager.java | 23 ++--- .../batch/ODataChangesetResponseItem.java | 14 +-- .../batch/v3/ODataBatchRequestImpl.java | 19 +--- .../batch/v4/ODataBatchRequestImpl.java | 43 +++------ .../commons/api/ODataRuntimeException.java | 10 +- 29 files changed, 455 insertions(+), 251 deletions(-) create mode 100644 fit/src/test/java/org/apache/olingo/fit/proxy/v4/ContextTestITCase.java create mode 100644 lib/client-core/src/main/java/org/apache/olingo/client/core/communication/header/ODataErrorResponseChecker.java diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/PersistenceManager.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/PersistenceManager.java index 2d195bdb5..41efb313f 100644 --- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/PersistenceManager.java +++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/api/PersistenceManager.java @@ -19,6 +19,8 @@ package org.apache.olingo.ext.proxy.api; import java.io.Serializable; +import java.util.List; +import org.apache.olingo.commons.api.ODataRuntimeException; /** * Interface for container operations. @@ -27,6 +29,9 @@ public interface PersistenceManager extends Serializable { /** * Flushes all pending changes to the OData service. + * + * @return a list where n-th item is either null (if corresponding request went out successfully) or the exception + * bearing the error returned from the service. */ - void flush(); + List flush(); } diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractPersistenceManager.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractPersistenceManager.java index 50b1b5406..5d128a5b8 100644 --- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractPersistenceManager.java +++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/AbstractPersistenceManager.java @@ -25,6 +25,7 @@ import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateR 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.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.domain.CommonODataEntity; import org.apache.olingo.commons.api.domain.ODataLink; import org.apache.olingo.commons.api.domain.ODataLinkType; @@ -67,10 +68,10 @@ abstract class AbstractPersistenceManager implements PersistenceManager { this.factory = factory; } - protected abstract void doFlush(final PersistenceChanges changes, final TransactionItems items); + protected abstract List doFlush(PersistenceChanges changes, TransactionItems items); @Override - public void flush() { + public List flush() { final PersistenceChanges changes = new PersistenceChanges(); final TransactionItems items = new TransactionItems(); @@ -89,9 +90,11 @@ abstract class AbstractPersistenceManager implements PersistenceManager { processDelayedUpdates(delayedUpdates, pos, items, changes); - doFlush(changes, items); + final List result = doFlush(changes, items); factory.getContext().detachAll(); + + return result; } private ODataLink buildNavigationLink(final String name, final URI uri, final ODataLinkType type) { 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 14b313c41..fb7d361ed 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 @@ -23,8 +23,6 @@ import org.apache.olingo.ext.proxy.Service; import org.apache.olingo.ext.proxy.api.annotations.EntityContainer; import org.apache.olingo.ext.proxy.api.annotations.EntitySet; import org.apache.olingo.ext.proxy.api.annotations.Singleton; -import org.apache.olingo.ext.proxy.utils.ClassUtils; - import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -82,8 +80,7 @@ public final class EntityContainerInvocationHandler extends AbstractInvocationHa if (isSelfMethod(method, args)) { return invokeSelfMethod(method, args); } else if ("flush".equals(method.getName()) && ArrayUtils.isEmpty(args)) { - factory.getPersistenceManager().flush(); - return ClassUtils.returnVoid(); + return factory.getPersistenceManager().flush(); } else if ("operations".equals(method.getName()) && ArrayUtils.isEmpty(args)) { final Class returnType = method.getReturnType(); diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/NonTransactionalPersistenceManagerImpl.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/NonTransactionalPersistenceManagerImpl.java index 9bf14681e..a92068b36 100644 --- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/NonTransactionalPersistenceManagerImpl.java +++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/NonTransactionalPersistenceManagerImpl.java @@ -18,19 +18,21 @@ */ package org.apache.olingo.ext.proxy.commons; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import org.apache.olingo.client.api.communication.request.ODataBasicRequest; import org.apache.olingo.client.api.communication.request.ODataBatchableRequest; 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.commons.api.ODataRuntimeException; import org.apache.olingo.ext.proxy.Service; -import java.util.Map; - /** * {@link org.apache.olingo.ext.proxy.api.PersistenceManager} implementation not using OData batch requests: any - * read-write operation will be sent separately to the OData service when calling flush(); any intermediate - * error will be logged and ignored. + * read-write operation will be sent separately to the OData service when calling flush(). */ public class NonTransactionalPersistenceManagerImpl extends AbstractPersistenceManager { @@ -41,8 +43,13 @@ public class NonTransactionalPersistenceManagerImpl extends AbstractPersistenceM } @Override - protected void doFlush(final PersistenceChanges changes, final TransactionItems items) { - for (Map.Entry entry : changes.getChanges().entrySet()) { + protected List doFlush(final PersistenceChanges changes, final TransactionItems items) { + final List result = new ArrayList(); + + for (final Iterator> itor = + changes.getChanges().entrySet().iterator(); itor.hasNext();) { + + final Map.Entry entry = itor.next(); try { final ODataResponse response = ((ODataBasicRequest) entry.getKey()).execute(); @@ -53,9 +60,19 @@ public class NonTransactionalPersistenceManagerImpl extends AbstractPersistenceM entry.getValue().setEntity(((ODataEntityUpdateResponse) response).getBody()); LOG.debug("Upgrade updated object '{}'", entry.getValue()); } - } catch (Exception e) { + + result.add(null); + } catch (ODataRuntimeException e) { LOG.error("While performing {}", entry.getKey().getURI(), e); + + if (factory.getClient().getConfiguration().isContinueOnError()) { + result.add(e); + } else { + throw e; + } } } + + return result; } } diff --git a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/TransactionalPersistenceManagerImpl.java b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/TransactionalPersistenceManagerImpl.java index 3da30195a..44be98391 100644 --- a/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/TransactionalPersistenceManagerImpl.java +++ b/ext/client-proxy/src/main/java/org/apache/olingo/ext/proxy/commons/TransactionalPersistenceManagerImpl.java @@ -18,6 +18,12 @@ */ package org.apache.olingo.ext.proxy.commons; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.apache.http.ProtocolVersion; +import org.apache.http.StatusLine; import org.apache.olingo.client.api.communication.request.ODataBatchableRequest; import org.apache.olingo.client.api.communication.request.ODataRequest; import org.apache.olingo.client.api.communication.request.ODataStreamedRequest; @@ -29,12 +35,11 @@ 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.core.communication.header.ODataErrorResponseChecker; import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem; +import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.ext.proxy.Service; -import java.util.Iterator; -import java.util.Map; - /** * {@link org.apache.olingo.ext.proxy.api.PersistenceManager} implementation using OData batch requests to implement * high-level user transactions: all read-write operations will be packed in a batch request to the OData service when @@ -52,20 +57,20 @@ public class TransactionalPersistenceManagerImpl extends AbstractPersistenceMana * Transactional changes commit. */ @Override - protected void doFlush(final PersistenceChanges changes, final TransactionItems items) { + protected List doFlush(final PersistenceChanges changes, final TransactionItems items) { final CommonODataBatchRequest request = factory.getClient().getBatchRequestFactory().getBatchRequest(factory.getClient().getServiceRoot()); ((ODataRequest) request).setAccept( factory.getClient().getConfiguration().getDefaultBatchAcceptFormat().toContentTypeString()); - final BatchManager streamManager = (BatchManager) ((ODataStreamedRequest) request).payloadManager(); + final BatchManager batchManager = (BatchManager) ((ODataStreamedRequest) request).payloadManager(); - final ODataChangeset changeset = streamManager.addChangeset(); + final ODataChangeset changeset = batchManager.addChangeset(); for (Map.Entry entry : changes.getChanges().entrySet()) { changeset.addRequest(entry.getKey()); } - final ODataBatchResponse response = streamManager.getResponse(); + final ODataBatchResponse response = batchManager.getResponse(); // This should be 202 for service version <= 3.0 and 200 for service version >= 4.0 but it seems that // many service implementations are not fully compliant in this respect. @@ -73,24 +78,54 @@ public class TransactionalPersistenceManagerImpl extends AbstractPersistenceMana throw new IllegalStateException("Operation failed"); } + final List result = new ArrayList(); + if (!items.isEmpty()) { - final Iterator iter = response.getBody(); - if (!iter.hasNext()) { + final Iterator batchResItor = response.getBody(); + if (!batchResItor.hasNext()) { throw new IllegalStateException("Unexpected operation result"); } - final ODataBatchResponseItem item = iter.next(); + final ODataBatchResponseItem item = batchResItor.next(); if (!(item instanceof ODataChangesetResponseItem)) { throw new IllegalStateException("Unexpected batch response item " + item.getClass().getSimpleName()); } final ODataChangesetResponseItem chgres = (ODataChangesetResponseItem) item; - for (Integer changesetItemId : items.sortedValues()) { + for (final Iterator itor = items.sortedValues().iterator(); itor.hasNext();) { + final Integer changesetItemId = itor.next(); LOG.debug("Expected changeset item {}", changesetItemId); + final ODataResponse res = chgres.next(); if (res.getStatusCode() >= 400) { - throw new IllegalStateException("Transaction failed: " + res.getStatusMessage()); + if (factory.getClient().getConfiguration().isContinueOnError()) { + result.add(ODataErrorResponseChecker.checkResponse( + factory.getClient(), + new StatusLine() { + + @Override + public ProtocolVersion getProtocolVersion() { + return null; + } + + @Override + public int getStatusCode() { + return res.getStatusCode(); + } + + @Override + public String getReasonPhrase() { + return res.getStatusMessage(); + } + }, + res.getRawResponse(), + ((ODataRequest) request).getAccept())); + } else { + throw new IllegalStateException("Transaction failed: " + res.getStatusMessage()); + } + } else { + result.add(null); } final EntityInvocationHandler handler = items.get(changesetItemId); @@ -107,5 +142,7 @@ public class TransactionalPersistenceManagerImpl extends AbstractPersistenceMana } } response.close(); + + return result; } } diff --git a/fit/src/main/java/org/apache/olingo/fit/V4Services.java b/fit/src/main/java/org/apache/olingo/fit/V4Services.java index d41fda139..0c01f497e 100644 --- a/fit/src/main/java/org/apache/olingo/fit/V4Services.java +++ b/fit/src/main/java/org/apache/olingo/fit/V4Services.java @@ -85,6 +85,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; +import javax.ws.rs.BadRequestException; @Service @Path("/V40/Static.svc") @@ -301,7 +302,7 @@ public class V4Services extends AbstractServices { addChangesetItemIntro(chbos, lastContebtID, cboundary); res = bodyPartRequest(new MimeBodyPart(part.getInputStream()), references); - if (res == null || res.getStatus() >= 400) { + if (!continueOnError && (res == null || res.getStatus() >= 400)) { throw new Exception("Failure processing changeset"); } @@ -372,7 +373,7 @@ public class V4Services extends AbstractServices { return StringUtils.isBlank(filter) && StringUtils.isBlank(search) ? NumberUtils.isNumber(type) ? super.getEntityInternal( - uriInfo.getRequestUri().toASCIIString(), accept, "People", type, format, null, null) + uriInfo.getRequestUri().toASCIIString(), accept, "People", type, format, null, null) : super.getEntitySet(accept, "People", type) : super.getEntitySet(uriInfo, accept, "People", top, skip, format, count, filter, orderby, skiptoken); } @@ -555,7 +556,7 @@ public class V4Services extends AbstractServices { final Link link = new LinkImpl(); link.setRel("edit"); link.setHref(URI.create( - Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + Constants.get(version, ConstantKey.DEFAULT_SERVICE_URL) + "ProductDetails(ProductID=6,ProductDetailID=1)").toASCIIString()); entry.setEditLink(link); @@ -753,7 +754,7 @@ public class V4Services extends AbstractServices { return utils.getValue().createResponse( FSManager.instance(version).readFile(Constants.get(version, ConstantKey.REF) - + File.separatorChar + filename, utils.getKey()), + + File.separatorChar + filename, utils.getKey()), null, utils.getKey()); } catch (Exception e) { @@ -762,6 +763,24 @@ public class V4Services extends AbstractServices { } } + @POST + @Path("/People") + @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON}) + @Consumes({MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_OCTET_STREAM}) + public Response postPeople( + @Context final UriInfo uriInfo, + @HeaderParam("Accept") @DefaultValue(StringUtils.EMPTY) final String accept, + @HeaderParam("Content-Type") @DefaultValue(StringUtils.EMPTY) final String contentType, + @HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) final String prefer, + final String entity) { + + if ("{\"@odata.type\":\"#Microsoft.Test.OData.Services.ODataWCFService.Person\"}".equals(entity)) { + return xml.createFaultResponse(accept, new BadRequestException()); + } + + return super.postNewEntity(uriInfo, accept, contentType, prefer, "People", entity); + } + @Override public Response patchEntity( final UriInfo uriInfo, @@ -775,9 +794,9 @@ public class V4Services extends AbstractServices { final Response response = getEntityInternal(uriInfo.getRequestUri().toASCIIString(), - accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY); + accept, entitySetName, entityId, accept, StringUtils.EMPTY, StringUtils.EMPTY); return response.getStatus() >= 400 - ? postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, changes) + ? super.postNewEntity(uriInfo, accept, contentType, prefer, entitySetName, changes) : super.patchEntity(uriInfo, accept, contentType, prefer, ifMatch, entitySetName, entityId, changes); } @@ -1001,7 +1020,7 @@ public class V4Services extends AbstractServices { // 1. Fetch the contained entity to be removed final InputStream entry = FSManager.instance(version). readFile(containedPath(entityId, containedEntitySetName). - append('(').append(containedEntityId).append(')').toString(), Accept.ATOM); + append('(').append(containedEntityId).append(')').toString(), Accept.ATOM); final ResWrap container = atomDeserializer.toEntity(entry); // 2. Remove the contained entity @@ -1275,7 +1294,7 @@ public class V4Services extends AbstractServices { final ResWrap result = new ResWrap( URI.create(Constants.get(version, ConstantKey.ODATA_METADATA_PREFIX) - + "Microsoft.Test.OData.Services.ODataWCFService.Address"), + + "Microsoft.Test.OData.Services.ODataWCFService.Address"), null, entity.getProperty("address")); diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v3/ContextTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v3/ContextTestITCase.java index c7bfbca5a..82a564849 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v3/ContextTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v3/ContextTestITCase.java @@ -322,7 +322,6 @@ public class ContextTestITCase extends AbstractTestITCase { final Login login = container.getLogin().newLogin(); final EntityInvocationHandler handler = (EntityInvocationHandler) Proxy.getInvocationHandler(login); - assertTrue(service.getContext().entityContext().isAttached(handler)); try { @@ -356,7 +355,7 @@ public class ContextTestITCase extends AbstractTestITCase { @Test public void flushTest() { - Customer customer = container.getCustomer().newCustomer(); + final Customer customer = container.getCustomer().newCustomer(); customer.setCustomerId(300); customer.setName("samplename"); diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/APIBasicDesignTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/APIBasicDesignTestITCase.java index 9485ef17e..907dea6c8 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/APIBasicDesignTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/APIBasicDesignTestITCase.java @@ -48,7 +48,7 @@ import static org.junit.Assert.fail; public class APIBasicDesignTestITCase extends AbstractTestITCase { protected Service getContainerFactory() { - return containerFactory; + return service; } protected InMemoryEntities getContainer() { @@ -133,7 +133,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase { assertEquals(BigDecimal.ZERO, actual.getShelfLife()); assertEquals(2, actual.getOrderShelfLifes().size()); - containerFactory.getContext().detachAll(); + service.getContext().detachAll(); // Delete order ... container.getOrders().delete(container.getOrders().getByKey(1105)); @@ -142,7 +142,7 @@ public class APIBasicDesignTestITCase extends AbstractTestITCase { container.flush(); - containerFactory.getContext().detachAll(); + service.getContext().detachAll(); try { container.getOrders().getByKey(105).load(); fail(); diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AbstractTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AbstractTestITCase.java index 8e7c36aad..0feccab37 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AbstractTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AbstractTestITCase.java @@ -63,7 +63,7 @@ public abstract class AbstractTestITCase { protected static String testAuthServiceRootURL; - protected static Service containerFactory; + protected static Service service; protected static InMemoryEntities container; @@ -77,11 +77,11 @@ public abstract class AbstractTestITCase { testLargeModelServiceRootURL = "http://localhost:9080/stub/StaticService/V40/Static.svc/large"; testAuthServiceRootURL = "http://localhost:9080/stub/DefaultService.svc/V40/Static.svc"; - containerFactory = Service.getV4(testStaticServiceRootURL); - containerFactory.getClient().getConfiguration().setDefaultBatchAcceptFormat(ContentType.APPLICATION_OCTET_STREAM); - container = containerFactory.getEntityContainer(InMemoryEntities.class); + service = Service.getV4(testStaticServiceRootURL); + service.getClient().getConfiguration().setDefaultBatchAcceptFormat(ContentType.APPLICATION_OCTET_STREAM); + container = service.getEntityContainer(InMemoryEntities.class); assertNotNull(container); - containerFactory.getContext().detachAll(); + service.getContext().detachAll(); } protected Customer readCustomer(final InMemoryEntities container, final int id) { diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AsyncTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AsyncTestITCase.java index 4d097c1cb..d29d916b4 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AsyncTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/AsyncTestITCase.java @@ -41,7 +41,7 @@ public class AsyncTestITCase extends AbstractTestITCase { @Test public void retrieveEntitySet() throws InterruptedException, ExecutionException { final Future futureCustomers = - new AsyncCall(containerFactory.getClient().getConfiguration()) { + new AsyncCall(service.getClient().getConfiguration()) { @Override public CustomerCollection call() { @@ -69,7 +69,7 @@ public class AsyncTestITCase extends AbstractTestITCase { Person person = container.getPeople().getByKey(1); person.setFirstName(randomFirstName); - final Future futureFlush = new AsyncCall(containerFactory.getClient().getConfiguration()) { + final Future futureFlush = new AsyncCall(service.getClient().getConfiguration()) { @Override public Void call() { @@ -83,7 +83,7 @@ public class AsyncTestITCase extends AbstractTestITCase { Thread.sleep(1000L); } - new AsyncCall(containerFactory.getClient().getConfiguration()) { + new AsyncCall(service.getClient().getConfiguration()) { @Override public Person call() { diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/ContextTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/ContextTestITCase.java new file mode 100644 index 000000000..936351aac --- /dev/null +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/ContextTestITCase.java @@ -0,0 +1,91 @@ +/* + * 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.fit.proxy.v4; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Proxy; +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.List; +import java.util.TimeZone; +import org.apache.olingo.client.api.communication.ODataClientErrorException; +import org.apache.olingo.client.api.v4.EdmEnabledODataClient; +import org.apache.olingo.commons.api.ODataRuntimeException; +import org.apache.olingo.commons.api.format.ContentType; +import org.apache.olingo.ext.proxy.Service; +import org.apache.olingo.ext.proxy.commons.EntityInvocationHandler; +import org.apache.olingo.fit.proxy.v4.staticservice.microsoft.test.odata.services.odatawcfservice.InMemoryEntities; +import org.apache.olingo.fit.proxy.v4.staticservice.microsoft.test.odata.services.odatawcfservice.types.Address; +import org.apache.olingo.fit.proxy.v4.staticservice.microsoft.test.odata.services.odatawcfservice.types.Employee; +import org.apache.olingo.fit.proxy.v4.staticservice.microsoft.test.odata.services.odatawcfservice.types.Person; +import org.junit.Test; + +public class ContextTestITCase extends AbstractTestITCase { + + private void continueOnError(final Service service, final InMemoryEntities container) { + final Person person = container.getPeople().newPerson(); + + final EntityInvocationHandler handler = (EntityInvocationHandler) Proxy.getInvocationHandler(person); + assertTrue(service.getContext().entityContext().isAttached(handler)); + + final Employee employee = container.getPeople().newEmployee(); + employee.setPersonID(199); + employee.setFirstName("Fabio"); + employee.setLastName("Martelli"); + employee.setEmails(Collections.singleton("fabio.martelli@tirasa.net")); + final Calendar date = Calendar.getInstance(TimeZone.getTimeZone("GMT")); + date.clear(); + date.set(2011, 3, 4, 9, 0, 0); + employee.setDateHired(new Timestamp(date.getTimeInMillis())); + final Address homeAddress = employee.factory().newHomeAddress(); + homeAddress.setCity("Pescara"); + homeAddress.setPostalCode("65100"); + homeAddress.setStreet("viale Gabriele D'Annunzio 256"); + employee.setHomeAddress(homeAddress); + employee.setNumbers(Arrays.asList(new String[] {"3204725072", "08569930"})); + + final List result = container.flush(); + assertEquals(2, result.size()); + assertTrue(result.get(0) instanceof ODataClientErrorException); + assertNull(result.get(1)); + } + + @Test + public void transactionalContinueOnError() { + service.getClient().getConfiguration().setContinueOnError(true); + continueOnError(service, container); + service.getClient().getConfiguration().setContinueOnError(false); + } + + @Test + public void nonTransactionalContinueOnError() { + final Service _service = Service.getV4(testStaticServiceRootURL, false); + _service.getClient().getConfiguration().setDefaultBatchAcceptFormat(ContentType.APPLICATION_OCTET_STREAM); + _service.getClient().getConfiguration().setContinueOnError(true); + + final InMemoryEntities _container = _service.getEntityContainer(InMemoryEntities.class); + + continueOnError(_service, _container); + } +} diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityCreateTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityCreateTestITCase.java index 0df5dd9d0..32ae0ed78 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityCreateTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityCreateTestITCase.java @@ -62,7 +62,7 @@ import static org.junit.Assert.fail; public class EntityCreateTestITCase extends AbstractTestITCase { protected Service getContainerFactory() { - return containerFactory; + return service; } protected InMemoryEntities getContainer() { diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityUpdateTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityUpdateTestITCase.java index b320172d0..5ee7ad96c 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityUpdateTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/EntityUpdateTestITCase.java @@ -55,7 +55,7 @@ import static org.junit.Assert.assertTrue; public class EntityUpdateTestITCase extends AbstractTestITCase { protected Service getContainerFactory() { - return containerFactory; + return service; } protected InMemoryEntities getContainer() { diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/FilterTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/FilterTestITCase.java index c09de7916..04cd069fb 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/FilterTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/FilterTestITCase.java @@ -44,7 +44,7 @@ public class FilterTestITCase extends AbstractTestITCase { final People people = container.getPeople(); PersonCollection result = - people.filter(containerFactory.getClient().getFilterFactory().lt("PersonID", 3)).execute(); + people.filter(service.getClient().getFilterFactory().lt("PersonID", 3)).execute(); // 1. check that result looks as expected assertEquals(2, result.size()); @@ -77,9 +77,9 @@ public class FilterTestITCase extends AbstractTestITCase { @Test public void search() { final Search search = container.getPeople().createSearch().setSearch( - containerFactory.getClient().getSearchFactory().or( - containerFactory.getClient().getSearchFactory().literal("Bob"), - containerFactory.getClient().getSearchFactory().literal("Jill"))); + service.getClient().getSearchFactory().or( + service.getClient().getSearchFactory().literal("Bob"), + service.getClient().getSearchFactory().literal("Jill"))); final PersonCollection result = search.getResult(); assertFalse(result.isEmpty()); diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/OperationImportInvokeTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/OperationImportInvokeTestITCase.java index 4574dc8bd..0c1680156 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/OperationImportInvokeTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/OperationImportInvokeTestITCase.java @@ -51,7 +51,7 @@ public class OperationImportInvokeTestITCase extends AbstractTestITCase { @Test public void getPerson() { - final Address address = container.getPeople().newPerson().factory().newHomeAddress(); + final Address address = container.getPeople().getByKey(1).factory().newHomeAddress(); address.setStreet("1 Microsoft Way"); address.setPostalCode("98052"); address.setCity("London"); @@ -80,7 +80,7 @@ public class OperationImportInvokeTestITCase extends AbstractTestITCase { @Test public void resetBossAddress() { - final Address address = container.getPeople().newPerson().factory().newHomeAddress(); + final Address address = container.getPeople().getByKey(1).factory().newHomeAddress(); address.setStreet("Via Le Mani Dal Naso, 123"); address.setPostalCode("Tollo"); address.setCity("66010"); diff --git a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/PropertyTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/PropertyTestITCase.java index 7908fc55e..850194228 100644 --- a/fit/src/test/java/org/apache/olingo/fit/proxy/v4/PropertyTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/proxy/v4/PropertyTestITCase.java @@ -49,7 +49,7 @@ public class PropertyTestITCase extends AbstractTestITCase { fail(); } catch (IllegalStateException e) { // ignore and detach all - containerFactory.getContext().detachAll(); + service.getContext().detachAll(); } } } diff --git a/fit/src/test/java/org/apache/olingo/fit/v4/BatchTestITCase.java b/fit/src/test/java/org/apache/olingo/fit/v4/BatchTestITCase.java index 1d8e69def..ddeb39dfd 100644 --- a/fit/src/test/java/org/apache/olingo/fit/v4/BatchTestITCase.java +++ b/fit/src/test/java/org/apache/olingo/fit/v4/BatchTestITCase.java @@ -158,7 +158,7 @@ public class BatchTestITCase extends AbstractTestITCase { assertEquals(404, res.getStatusCode()); assertEquals("Not Found", res.getStatusMessage()); assertEquals(Integer.valueOf(3), Integer.valueOf( - res.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next())); + res.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next())); assertFalse(retitem.hasNext()); assertFalse(iter.hasNext()); @@ -175,10 +175,12 @@ public class BatchTestITCase extends AbstractTestITCase { } private void continueOnError(final boolean continueOnError) { + final boolean preContinueOnError = client.getConfiguration().isContinueOnError(); + client.getConfiguration().setContinueOnError(continueOnError); + // create your request final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL); request.setAccept(ACCEPT); - request.continueOnError(); final BatchManager streamManager = request.payloadManager(); @@ -187,7 +189,7 @@ public class BatchTestITCase extends AbstractTestITCase { // ------------------------------------------- // prepare URI URIBuilder targetURI = client.newURIBuilder(testStaticServiceRootURL); - targetURI.appendEntitySetSegment("UnexistinfEntitySet").appendKeySegment(1); + targetURI.appendEntitySetSegment("UnexistingEntitySet").appendKeySegment(1); // create new request ODataEntityRequest queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build()); @@ -232,6 +234,8 @@ public class BatchTestITCase extends AbstractTestITCase { assertEquals(200, res.getStatusCode()); assertEquals("OK", res.getStatusMessage()); } + + client.getConfiguration().setContinueOnError(preContinueOnError); } @Test @@ -249,7 +253,7 @@ public class BatchTestITCase extends AbstractTestITCase { // add create request final ODataEntityCreateRequest createReq = - client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), order); + client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), order); changeset.addRequest(createReq); @@ -259,8 +263,8 @@ public class BatchTestITCase extends AbstractTestITCase { // add update request: link CustomerInfo(17) to the new customer final ODataEntity customerChanges = client.getObjectFactory().newEntity(order.getTypeName()); customerChanges.addLink(client.getObjectFactory().newEntitySetNavigationLink( - "OrderDetails", - client.newURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("OrderDetails"). + "OrderDetails", + client.newURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("OrderDetails"). appendKeySegment(new HashMap() { private static final long serialVersionUID = 3109256773218160485L; @@ -271,7 +275,7 @@ public class BatchTestITCase extends AbstractTestITCase { }).build())); final ODataEntityUpdateRequest updateReq = client.getCUDRequestFactory().getEntityUpdateRequest( - URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges); + URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges); changeset.addRequest(updateReq); @@ -293,10 +297,10 @@ public class BatchTestITCase extends AbstractTestITCase { order = ((ODataEntityCreateResponse) res).getBody(); final ODataEntitySetRequest req = client.getRetrieveRequestFactory().getEntitySetRequest( - URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString() + "/OrderDetails")); + URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString() + "/OrderDetails")); assertEquals(Integer.valueOf(7), - req.execute().getBody().getEntities().get(0).getProperty("OrderID").getPrimitiveValue(). + req.execute().getBody().getEntities().get(0).getProperty("OrderID").getPrimitiveValue(). toCastValue(Integer.class)); res = chgitem.next(); @@ -305,13 +309,13 @@ public class BatchTestITCase extends AbstractTestITCase { // clean ... assertEquals(204, client.getCUDRequestFactory().getDeleteRequest( - URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString())).execute(). - getStatusCode()); + URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString())).execute(). + getStatusCode()); try { client.getRetrieveRequestFactory().getEntityRequest( - URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString())). - execute().getBody(); + URIUtils.getURI(testStaticServiceRootURL, order.getEditLink().toASCIIString())). + execute().getBody(); fail(); } catch (Exception e) { // ignore @@ -332,7 +336,7 @@ public class BatchTestITCase extends AbstractTestITCase { // prepare URI URIBuilder targetURI = client.newURIBuilder(testStaticServiceRootURL); targetURI.appendEntitySetSegment("Customers").appendKeySegment(1). - expand("Orders").select("PersonID,Orders/OrderID"); + expand("Orders").select("PersonID,Orders/OrderID"); // create new request ODataEntityRequest queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build()); @@ -348,7 +352,7 @@ public class BatchTestITCase extends AbstractTestITCase { targetURI = client.newURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Orders"); final ODataEntity original = newOrder(2000); final ODataEntityCreateRequest createReq = - client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original); + client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original); createReq.setFormat(ODataFormat.JSON); streamManager.addRequest(createReq); // ------------------------------------------- @@ -386,7 +390,7 @@ public class BatchTestITCase extends AbstractTestITCase { } @Test - @SuppressWarnings({ "unchecked" }) + @SuppressWarnings({"unchecked"}) public void batchRequest() throws EdmPrimitiveTypeException { // create your request final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL); @@ -418,15 +422,15 @@ public class BatchTestITCase extends AbstractTestITCase { final URI editLink = targetURI.build(); final ODataEntity patch = client.getObjectFactory().newEntity( - new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Customer")); + new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Customer")); patch.setEditLink(editLink); patch.getProperties().add(client.getObjectFactory().newPrimitiveProperty( - "LastName", - client.getObjectFactory().newPrimitiveValueBuilder().buildString("new last name"))); + "LastName", + client.getObjectFactory().newPrimitiveValueBuilder().buildString("new last name"))); final ODataEntityUpdateRequest changeReq = - client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patch); + client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patch); changeReq.setFormat(ODataFormat.JSON_FULL_METADATA); changeset.addRequest(changeReq); @@ -435,7 +439,7 @@ public class BatchTestITCase extends AbstractTestITCase { targetURI = client.newURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Orders"); final ODataEntity original = newOrder(1000); final ODataEntityCreateRequest createReq = - client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original); + client.getCUDRequestFactory().getEntityCreateRequest(targetURI.build(), original); createReq.setFormat(ODataFormat.JSON); changeset.addRequest(createReq); // ------------------------------------------- @@ -468,7 +472,7 @@ public class BatchTestITCase extends AbstractTestITCase { assertEquals("OK", res.getStatusMessage()); ODataEntityRequestImpl.ODataEntityResponseImpl entres = - (ODataEntityRequestImpl.ODataEntityResponseImpl) res; + (ODataEntityRequestImpl.ODataEntityResponseImpl) res; ODataEntity entity = entres.getBody(); assertEquals(1, entity.getProperty("PersonID").getPrimitiveValue().toCastValue(Integer.class), 0); @@ -513,7 +517,7 @@ public class BatchTestITCase extends AbstractTestITCase { public void async() { // create your request final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest( - URI.create(testStaticServiceRootURL + "/async/").normalize().toASCIIString()); + URI.create(testStaticServiceRootURL + "/async/").normalize().toASCIIString()); request.setAccept(ACCEPT); final AsyncBatchRequestWrapper async = client.getAsyncRequestFactory().getAsyncBatchRequestWrapper(request); @@ -556,17 +560,17 @@ public class BatchTestITCase extends AbstractTestITCase { final Iterator iter = response.getBody(); // retrieve the first item (ODataRetrieve) - ODataBatchResponseItem item = iter.next(); + final ODataBatchResponseItem item = iter.next(); assertTrue(item instanceof ODataSingleResponseItem); // The service return interim results to an asynchronously executing batch. - ODataSingleResponseItem retitem = (ODataSingleResponseItem) item; - ODataResponse res = retitem.next(); + final ODataSingleResponseItem retitem = (ODataSingleResponseItem) item; + final ODataResponse res = retitem.next(); assertTrue(res instanceof AsyncResponse); assertEquals(202, res.getStatusCode()); assertEquals("Accepted", res.getStatusMessage()); - Collection newMonitorLocation = res.getHeader(HeaderName.location); + final Collection newMonitorLocation = res.getHeader(HeaderName.location); if (newMonitorLocation != null && !newMonitorLocation.isEmpty()) { responseWrapper.forceNextMonitorCheck(URI.create(newMonitorLocation.iterator().next())); // .... now you can start again with isDone() and getODataResponse(). @@ -634,20 +638,20 @@ public class BatchTestITCase extends AbstractTestITCase { private ODataEntity newOrder(final int id) { final ODataEntity order = getClient().getObjectFactory(). - newEntity(new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Order")); + newEntity(new FullQualifiedName("Microsoft.Test.OData.Services.ODataWCFService.Order")); order.getProperties().add(getClient().getObjectFactory().newPrimitiveProperty("OrderID", - getClient().getObjectFactory().newPrimitiveValueBuilder().buildInt32(id))); + getClient().getObjectFactory().newPrimitiveValueBuilder().buildInt32(id))); order.getProperties().add(getClient().getObjectFactory().newPrimitiveProperty("OrderDate", - getClient().getObjectFactory().newPrimitiveValueBuilder(). + getClient().getObjectFactory().newPrimitiveValueBuilder(). setType(EdmPrimitiveTypeKind.DateTimeOffset).setValue(Calendar.getInstance()).build())); order.getProperties().add(getClient().getObjectFactory().newPrimitiveProperty("ShelfLife", - getClient().getObjectFactory().newPrimitiveValueBuilder(). + getClient().getObjectFactory().newPrimitiveValueBuilder(). setType(EdmPrimitiveTypeKind.Duration).setValue(new BigDecimal("0.0000002")).build())); order.getProperties().add(getClient().getObjectFactory().newCollectionProperty("OrderShelfLifes", - getClient().getObjectFactory().newCollectionValue(EdmPrimitiveTypeKind.Duration.name()).add( - getClient().getObjectFactory().newPrimitiveValueBuilder().setType(EdmPrimitiveTypeKind.Duration). - setValue(new BigDecimal("0.0000002")).build()))); + getClient().getObjectFactory().newCollectionValue(EdmPrimitiveTypeKind.Duration.name()).add( + getClient().getObjectFactory().newPrimitiveValueBuilder().setType(EdmPrimitiveTypeKind.Duration). + setValue(new BigDecimal("0.0000002")).build()))); return order; } diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/Configuration.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/Configuration.java index dc0f014b8..a9dfedd28 100644 --- a/lib/client-api/src/main/java/org/apache/olingo/client/api/Configuration.java +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/Configuration.java @@ -32,16 +32,18 @@ public interface Configuration { /** * Gets the configured default Accept header value format for a batch request. + * * @return configured default Accept header value for a batch request. */ ContentType getDefaultBatchAcceptFormat(); - + /** * Set the default Accept header value format for a batch request. + * * @param contentType default Accept header value. */ void setDefaultBatchAcceptFormat(ContentType contentType); - + /** * Gets the configured OData format for AtomPub exchanges. If this configuration parameter doesn't exist the * JSON_FULL_METADATA format will be used as default. @@ -197,52 +199,62 @@ public interface Configuration { void setKeyAsSegment(boolean value); /** - * Gets whether query URIs in request should contain fully qualified type name. - * - OData Intermediate Conformance Level: - * MUST support casting to a derived type according to [OData-URL] if derived types are present in the model. + * Gets whether query URIs in request should contain fully qualified type name. - OData Intermediate Conformance + * Level: MUST support casting to a derived type according to [OData-URL] if derived types are present in the model. *
- * Example: http://host/service/Customers/Model.VipCustomer(102) or - * http://host/service/Customers/Model.VipCustomer + * Example: http://host/service/Customers/Model.VipCustomer(102) or http://host/service/Customers/Model.VipCustomer * - * @return whether query URIs in request should contain fully qualified type name. - * segment. + * @return whether query URIs in request should contain fully qualified type name. segment. */ - boolean isAddressingDerivedTypes() ; + boolean isAddressingDerivedTypes(); + + /** + * Sets whether query URIs in request should contain fully qualified type name. - OData Intermediate Conformance + * Level: MUST support casting to a derived type according to [OData-URL] if derived types are present in the model. + *
+ * Example: http://host/service/Customers/Model.VipCustomer(102) or http://host/service/Customers/Model.VipCustomer + * + * @param value 'TRUE' to use this feature. + */ + void setAddressingDerivedTypes(boolean value); + + /** + * Checks whether operation name in request URI should be fully qualified name, which is required by OData V4 + * protocol, but some service may still choose to support shorter name. + *
+ * Example: http://host/service/Customers(2)/NS1.Model.IncreaseSalary VS + * http://host/service/Customers(2)/IncreaseSalary + * + * @return wheter operation name in request URI should be fully qualified name. segment. + */ + boolean isUseUrlOperationFQN(); /** * Sets whether operation name in request URI should be fully qualified name, which is required by OData V4 protocol, * but some service may still choose to support shorter name. *
- * Example: http://host/service/Customers(2)/NS1.Model.IncreaseSalary VS + * Example: http://host/service/Customers(2)/NS1.Model.IncreaseSalary VS * http://host/service/Customers(2)/IncreaseSalary * * @param value 'TRUE' to use this feature. - */ - void setUseUrlOperationFQN(final boolean value); - - /** - * Sets whether operation name in request URI should be fully qualified name, which is required by OData V4 protocol, - * but some service may still choose to support shorter name. - *
- * Example: http://host/service/Customers(2)/NS1.Model.IncreaseSalary VS - * http://host/service/Customers(2)/IncreaseSalary - * - * @return whether whether operation name in request URI should be fully qualified name. - * segment. */ - boolean isUseUrlOperationFQN() ; + void setUseUrlOperationFQN(boolean value); /** - * Sets whether query URIs in request should contain fully qualified type name. - * - OData Intermediate Conformance Level: - * MUST support casting to a derived type according to [OData-URL] if derived types are present in the model. - *
- * Example: http://host/service/Customers/Model.VipCustomer(102) or - * http://host/service/Customers/Model.VipCustomer + * When processing a set of requests (in batch requests, for example), checks if the execution will be aborted after + * first error encountered or not. + * + * @return whether execution of a set of requests will be aborted after first error + */ + boolean isContinueOnError(); + + /** + * When processing a set of requests (in batch requests, for example), sets if the execution will be aborted after + * first error encountered or not. * * @param value 'TRUE' to use this feature. */ - void setAddressingDerivedTypes(final boolean value); + void setContinueOnError(boolean value); /** * Retrieves request executor service. diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataClientErrorException.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataClientErrorException.java index 9284d42ec..178241a3b 100644 --- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataClientErrorException.java +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataClientErrorException.java @@ -20,6 +20,7 @@ package org.apache.olingo.client.api.communication; import org.apache.commons.lang3.StringUtils; import org.apache.http.StatusLine; +import org.apache.olingo.commons.api.ODataRuntimeException; import org.apache.olingo.commons.api.domain.ODataError; /** @@ -27,7 +28,7 @@ import org.apache.olingo.commons.api.domain.ODataError; * * @see ODataError */ -public class ODataClientErrorException extends RuntimeException { +public class ODataClientErrorException extends ODataRuntimeException { private static final long serialVersionUID = -2551523202755268162L; diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataServerErrorException.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataServerErrorException.java index 5003b3d28..a3d218586 100644 --- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataServerErrorException.java +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/ODataServerErrorException.java @@ -19,11 +19,12 @@ package org.apache.olingo.client.api.communication; import org.apache.http.StatusLine; +import org.apache.olingo.commons.api.ODataRuntimeException; /** * Represents a server error in OData. */ -public class ODataServerErrorException extends RuntimeException { +public class ODataServerErrorException extends ODataRuntimeException { private static final long serialVersionUID = -6423014532618680135L; diff --git a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/batch/v4/ODataBatchRequest.java b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/batch/v4/ODataBatchRequest.java index 6c348ad9d..0c948d564 100644 --- a/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/batch/v4/ODataBatchRequest.java +++ b/lib/client-api/src/main/java/org/apache/olingo/client/api/communication/request/batch/v4/ODataBatchRequest.java @@ -23,11 +23,7 @@ import org.apache.olingo.client.api.communication.request.batch.BatchManager; import org.apache.olingo.client.api.communication.request.batch.CommonODataBatchRequest; import org.apache.olingo.client.api.communication.response.ODataBatchResponse; -/** - * This class implements a batch request. - */ public interface ODataBatchRequest extends CommonODataBatchRequest, ODataStreamedRequest { - ODataBatchRequest continueOnError(); } diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/ConfigurationImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/ConfigurationImpl.java index a6e4bf5e4..638700e15 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/ConfigurationImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/ConfigurationImpl.java @@ -57,6 +57,8 @@ public class ConfigurationImpl implements Configuration { private static final String CHUNKING = "chunking"; + private static final String CONTINUE_ON_ERROR = "continueOnError"; + private final Map CONF = new HashMap(); private transient ExecutorService executor = Executors.newFixedThreadPool(10); @@ -105,7 +107,7 @@ public class ConfigurationImpl implements Configuration { @Override public ODataFormat getDefaultFormat() { - ODataFormat format = getDefaultPubFormat(); + final ODataFormat format = getDefaultPubFormat(); return format == ODataFormat.ATOM ? ODataFormat.XML : format; } @@ -209,6 +211,16 @@ public class ConfigurationImpl implements Configuration { setProperty(USE_OPERATION_FQN_IN_URL, value); } + @Override + public boolean isContinueOnError() { + return (Boolean) getProperty(CONTINUE_ON_ERROR, false); + } + + @Override + public void setContinueOnError(final boolean value) { + setProperty(CONTINUE_ON_ERROR, value); + } + @Override public ExecutorService getExecutor() { return executor; diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/header/ODataErrorResponseChecker.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/header/ODataErrorResponseChecker.java new file mode 100644 index 000000000..a57d035cd --- /dev/null +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/header/ODataErrorResponseChecker.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.olingo.client.core.communication.header; + +import java.io.InputStream; +import org.apache.http.StatusLine; +import org.apache.olingo.client.api.CommonODataClient; +import org.apache.olingo.client.api.communication.ODataClientErrorException; +import org.apache.olingo.client.api.communication.ODataServerErrorException; +import org.apache.olingo.commons.api.ODataRuntimeException; +import org.apache.olingo.commons.api.domain.ODataError; +import org.apache.olingo.commons.api.format.ODataFormat; +import org.apache.olingo.commons.api.serialization.ODataDeserializerException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ODataErrorResponseChecker { + + protected static final Logger LOG = LoggerFactory.getLogger(ODataErrorResponseChecker.class); + + private static ODataError getGenericError(final int code, final String errorMsg) { + final ODataError error = new ODataError(); + error.setCode(String.valueOf(code)); + error.setMessage(errorMsg); + return error; + } + + public static ODataRuntimeException checkResponse( + final CommonODataClient odataClient, final StatusLine statusLine, final InputStream entity, + final String accept) { + + ODataRuntimeException result = null; + + if (statusLine.getStatusCode() >= 400) { + if (entity == null) { + result = new ODataClientErrorException(statusLine); + } else { + final ODataFormat format = accept.contains("xml") ? ODataFormat.XML : ODataFormat.JSON; + + ODataError error; + try { + error = odataClient.getReader().readError(entity, format); + } catch (final RuntimeException e) { + LOG.warn("Error deserializing error response", e); + error = getGenericError( + statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } catch (final ODataDeserializerException e) { + LOG.warn("Error deserializing error response", e); + error = getGenericError( + statusLine.getStatusCode(), + statusLine.getReasonPhrase()); + } + + if (statusLine.getStatusCode() >= 500) { + result = new ODataServerErrorException(statusLine); + } else { + result = new ODataClientErrorException(statusLine, error); + } + } + } + + return result; + } + + private ODataErrorResponseChecker() { + // private constructor for static utility class + } +} diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java index 2b0ac7434..ea510bf32 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/AbstractRequest.java @@ -15,20 +15,14 @@ */ package org.apache.olingo.client.core.communication.request; -import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpUriRequest; import org.apache.olingo.client.api.CommonEdmEnabledODataClient; import org.apache.olingo.client.api.CommonODataClient; -import org.apache.olingo.client.api.communication.ODataClientErrorException; -import org.apache.olingo.client.api.communication.ODataServerErrorException; -import org.apache.olingo.client.api.http.HttpClientException; -import org.apache.olingo.commons.api.domain.ODataError; -import org.apache.olingo.commons.api.format.ODataFormat; -import org.apache.olingo.commons.api.serialization.ODataDeserializerException; +import org.apache.olingo.client.core.communication.header.ODataErrorResponseChecker; +import org.apache.olingo.commons.api.ODataRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - import java.io.IOException; public abstract class AbstractRequest { @@ -38,13 +32,6 @@ public abstract class AbstractRequest { */ protected static final Logger LOG = LoggerFactory.getLogger(AbstractRequest.class); - private ODataError getGenericError(final int code, final String errorMsg) { - final ODataError error = new ODataError(); - error.setCode(String.valueOf(code)); - error.setMessage(errorMsg); - return error; - } - protected void checkRequest(final CommonODataClient odataClient, final HttpUriRequest request) { // If using and Edm enabled client, checks that the cached service root matches the request URI if (odataClient instanceof CommonEdmEnabledODataClient @@ -61,39 +48,18 @@ public abstract class AbstractRequest { protected void checkResponse( final CommonODataClient odataClient, final HttpResponse response, final String accept) { - if (response.getStatusLine().getStatusCode() >= 400) { - try { - final HttpEntity httpEntity = response.getEntity(); - if (httpEntity == null) { - throw new ODataClientErrorException(response.getStatusLine()); - } else { - final ODataFormat format = accept.contains("xml") ? ODataFormat.XML : ODataFormat.JSON; - - ODataError error; - try { - error = odataClient.getReader().readError(httpEntity.getContent(), format); - } catch (final RuntimeException e) { - LOG.warn("Error deserializing error response", e); - error = getGenericError( - response.getStatusLine().getStatusCode(), - response.getStatusLine().getReasonPhrase()); - } catch (final ODataDeserializerException e) { - LOG.warn("Error deserializing error response", e); - error = getGenericError( - response.getStatusLine().getStatusCode(), - response.getStatusLine().getReasonPhrase()); - } - - if (response.getStatusLine().getStatusCode() >= 500) { - throw new ODataServerErrorException(response.getStatusLine()); - } else { - throw new ODataClientErrorException(response.getStatusLine(), error); - } - } - } catch (IOException e) { - throw new HttpClientException( - "Received '" + response.getStatusLine() + "' but could not extract error body", e); + try { + final ODataRuntimeException exception = ODataErrorResponseChecker. + checkResponse(odataClient, + response.getStatusLine(), + response.getEntity() == null ? null : response.getEntity().getContent(), + accept); + if (exception != null) { + throw exception; } + } catch (IOException e) { + throw new ODataRuntimeException( + "Received '" + response.getStatusLine() + "' but could not extract error body", e); } } } diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/AbstractBatchManager.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/AbstractBatchManager.java index 34caf15d2..dc1d000ef 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/AbstractBatchManager.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/AbstractBatchManager.java @@ -27,7 +27,6 @@ import org.apache.olingo.client.api.communication.request.batch.ODataSingleReque import org.apache.olingo.client.api.communication.response.ODataBatchResponse; import org.apache.olingo.client.core.communication.request.AbstractODataStreamManager; import org.apache.olingo.client.core.communication.request.Wrapper; - import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -36,6 +35,8 @@ import java.util.concurrent.TimeUnit; */ public abstract class AbstractBatchManager extends AbstractODataStreamManager { + protected final boolean continueOnError; + /** * Batch request current item. */ @@ -46,15 +47,12 @@ public abstract class AbstractBatchManager extends AbstractODataStreamManager> futureWrap) { + protected AbstractBatchManager(final CommonODataBatchRequest req, + final Wrapper> futureWrap, final boolean continueOnError) { + super(futureWrap); this.req = req; + this.continueOnError = continueOnError; } /** @@ -68,7 +66,7 @@ public abstract class AbstractBatchManager extends AbstractODataStreamManager= 400) { + if (current.getStatusCode() >= 400 && !continueOnError) { // found error .... breaking = true; } diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v3/ODataBatchRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v3/ODataBatchRequestImpl.java index 493e2a0a1..e406e16f9 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v3/ODataBatchRequestImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v3/ODataBatchRequestImpl.java @@ -34,15 +34,11 @@ import org.apache.olingo.client.core.communication.request.batch.AbstractBatchMa import org.apache.olingo.client.core.communication.request.batch.AbstractODataBatchRequest; import org.apache.olingo.client.core.communication.response.AbstractODataResponse; import org.apache.olingo.client.core.communication.response.batch.ODataBatchResponseManager; - import java.io.IOException; import java.net.URI; import java.util.Iterator; import java.util.concurrent.TimeUnit; -/** - * This class implements a batch request. - */ public class ODataBatchRequestImpl extends AbstractODataBatchRequest implements ODataBatchRequest, ODataStreamedRequest { @@ -60,20 +56,14 @@ public class ODataBatchRequestImpl return (BatchManager) payloadManager; } - /** - * {@inheritDoc } - */ @Override public ODataBatchRequest rawAppend(final byte[] toBeStreamed) throws IOException { getPayloadManager().getBodyStreamWriter().write(toBeStreamed); return this; } - /** - * {@inheritDoc } - */ @Override - public ODataBatchRequest rawAppend(final byte[] toBeStreamed, int off, int len) throws IOException { + public ODataBatchRequest rawAppend(final byte[] toBeStreamed, final int off, final int len) throws IOException { getPayloadManager().getBodyStreamWriter().write(toBeStreamed, off, len); return this; } @@ -84,7 +74,7 @@ public class ODataBatchRequestImpl public class BatchManagerImpl extends AbstractBatchManager implements BatchManager { public BatchManagerImpl(final ODataBatchRequest req) { - super(req, ODataBatchRequestImpl.this.futureWrapper); + super(req, ODataBatchRequestImpl.this.futureWrapper, odataClient.getConfiguration().isContinueOnError()); } @Override @@ -100,11 +90,6 @@ public class ODataBatchRequestImpl } } - /** - * This class implements a response to a batch request. - * - * @see org.apache.olingo.client.core.communication.request.ODataBatchRequest - */ protected class ODataBatchResponseImpl extends AbstractODataResponse implements ODataBatchResponse { protected ODataBatchResponseImpl(final CommonODataClient odataClient, final HttpClient httpClient, diff --git a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v4/ODataBatchRequestImpl.java b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v4/ODataBatchRequestImpl.java index e7f38994e..d0e5b145a 100644 --- a/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v4/ODataBatchRequestImpl.java +++ b/lib/client-core/src/main/java/org/apache/olingo/client/core/communication/request/batch/v4/ODataBatchRequestImpl.java @@ -18,6 +18,10 @@ */ package org.apache.olingo.client.core.communication.request.batch.v4; +import java.io.IOException; +import java.net.URI; +import java.util.Iterator; +import java.util.concurrent.TimeUnit; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.olingo.client.api.CommonODataClient; @@ -34,20 +38,10 @@ import org.apache.olingo.client.core.communication.request.batch.AbstractODataBa import org.apache.olingo.client.core.communication.response.AbstractODataResponse; import org.apache.olingo.client.core.communication.response.batch.ODataBatchResponseManager; -import java.io.IOException; -import java.net.URI; -import java.util.Iterator; -import java.util.concurrent.TimeUnit; - -/** - * This class implements a batch request. - */ public class ODataBatchRequestImpl extends AbstractODataBatchRequest implements ODataBatchRequest { - private boolean continueOnError = false; - public ODataBatchRequestImpl(final ODataClient odataClient, final URI uri) { super(odataClient, uri); setAccept(odataClient.getConfiguration().getDefaultBatchAcceptFormat().toContentTypeString()); @@ -61,18 +55,12 @@ public class ODataBatchRequestImpl return (BatchManager) payloadManager; } - /** - * {@inheritDoc } - */ @Override public ODataBatchRequest rawAppend(final byte[] toBeStreamed) throws IOException { getPayloadManager().getBodyStreamWriter().write(toBeStreamed); return this; } - /** - * {@inheritDoc } - */ @Override public ODataBatchRequest rawAppend(final byte[] toBeStreamed, int off, int len) throws IOException { getPayloadManager().getBodyStreamWriter().write(toBeStreamed, off, len); @@ -80,10 +68,12 @@ public class ODataBatchRequestImpl } @Override - public ODataBatchRequest continueOnError() { - addCustomHeader(HeaderName.prefer, new ODataPreferences(odataClient.getServiceVersion()).continueOnError()); - continueOnError = true; - return this; + protected HttpResponse doExecute() { + if (odataClient.getConfiguration().isContinueOnError()) { + addCustomHeader(HeaderName.prefer, new ODataPreferences(odataClient.getServiceVersion()).continueOnError()); + } + + return super.doExecute(); } /** @@ -92,7 +82,8 @@ public class ODataBatchRequestImpl public class BatchManagerImpl extends AbstractBatchManager implements BatchManager { public BatchManagerImpl(final ODataBatchRequest req) { - super(req, ODataBatchRequestImpl.this.futureWrapper); + super(req, ODataBatchRequestImpl.this.futureWrapper, + ODataBatchRequestImpl.this.odataClient.getConfiguration().isContinueOnError()); } @Override @@ -105,11 +96,6 @@ public class ODataBatchRequestImpl } } - /** - * This class implements a response to a batch request. - * - * @see org.apache.olingo.client.core.communication.request.ODataBatchRequest - */ protected class ODataBatchResponseImpl extends AbstractODataResponse implements ODataBatchResponse { protected ODataBatchResponseImpl( @@ -118,12 +104,9 @@ public class ODataBatchRequestImpl super(odataClient, httpClient, res); } - /** - * {@inheritDoc} - */ @Override public Iterator getBody() { - return new ODataBatchResponseManager(this, expectedResItems, continueOnError); + return new ODataBatchResponseManager(this, expectedResItems, odataClient.getConfiguration().isContinueOnError()); } @Override diff --git a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/ODataRuntimeException.java b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/ODataRuntimeException.java index 0a2c3c85d..0c8374aa6 100644 --- a/lib/commons-api/src/main/java/org/apache/olingo/commons/api/ODataRuntimeException.java +++ b/lib/commons-api/src/main/java/org/apache/olingo/commons/api/ODataRuntimeException.java @@ -20,18 +20,18 @@ package org.apache.olingo.commons.api; public class ODataRuntimeException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 5492375572049190883L; public ODataRuntimeException(final String msg) { super(msg); } - public ODataRuntimeException(final String msg, final Exception e) { - super(msg, e); + public ODataRuntimeException(final String msg, final Exception cause) { + super(msg, cause); } - public ODataRuntimeException(final Exception e) { - super(e); + public ODataRuntimeException(final Exception cause) { + super(cause); } }