You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/11/18 16:12:13 UTC
[12/22] olingo-odata4 git commit: Batch IT test case
Batch IT test case
Signed-off-by: Christian Amend <ch...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/4ff5fb9c
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/4ff5fb9c
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/4ff5fb9c
Branch: refs/heads/master
Commit: 4ff5fb9c8ce045e87657dec69f76db52001f1508
Parents: 5f4eb03
Author: Christian Holzer <c....@sap.com>
Authored: Mon Nov 10 17:30:18 2014 +0100
Committer: Christian Amend <ch...@apache.org>
Committed: Thu Nov 13 17:11:00 2014 +0100
----------------------------------------------------------------------
.../fit/tecsvc/client/BatchClientITCase.java | 459 +++++++++++++++++++
.../server/api/batch/ODataResponsePart.java | 52 ++-
.../server/api/processor/BatchProcessor.java | 3 +-
.../server/api/processor/DefaultProcessor.java | 93 +++-
.../core/batch/handler/BatchPartHandler.java | 7 +-
.../batch/handler/ODataResponsePartImpl.java | 44 --
.../core/batch/parser/BatchParserCommon.java | 8 -
.../core/batch/writer/BatchResponseWriter.java | 2 +-
.../batch/writer/ODataResponsePartImpl.java | 44 --
.../core/batch/BatchRequestParserTest.java | 28 +-
.../batch/handler/MockedBatchHandlerTest.java | 4 +-
.../batch/writer/BatchResponseWriterTest.java | 8 +-
12 files changed, 627 insertions(+), 125 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
----------------------------------------------------------------------
diff --git a/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
new file mode 100644
index 0000000..f2e1ab6
--- /dev/null
+++ b/fit/src/test/java/org/apache/olingo/fit/tecsvc/client/BatchClientITCase.java
@@ -0,0 +1,459 @@
+/*
+ * 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.tecsvc.client;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.math.BigDecimal;
+import java.net.URI;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.olingo.client.api.ODataBatchConstants;
+import org.apache.olingo.client.api.communication.request.batch.BatchManager;
+import org.apache.olingo.client.api.communication.request.batch.ODataBatchResponseItem;
+import org.apache.olingo.client.api.communication.request.batch.ODataChangeset;
+import org.apache.olingo.client.api.communication.request.batch.v4.ODataBatchRequest;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityCreateRequest;
+import org.apache.olingo.client.api.communication.request.cud.ODataEntityUpdateRequest;
+import org.apache.olingo.client.api.communication.request.cud.v4.UpdateType;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntityRequest;
+import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetRequest;
+import org.apache.olingo.client.api.communication.response.ODataBatchResponse;
+import org.apache.olingo.client.api.communication.response.ODataEntityCreateResponse;
+import org.apache.olingo.client.api.communication.response.ODataEntityUpdateResponse;
+import org.apache.olingo.client.api.communication.response.ODataResponse;
+import org.apache.olingo.client.api.uri.v4.URIBuilder;
+import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem;
+import org.apache.olingo.client.core.uri.URIUtils;
+import org.apache.olingo.commons.api.domain.v4.ODataEntity;
+import org.apache.olingo.commons.api.domain.v4.ODataEntitySet;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeException;
+import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind;
+import org.apache.olingo.commons.api.edm.FullQualifiedName;
+import org.apache.olingo.commons.api.format.ContentType;
+import org.apache.olingo.commons.api.format.ODataFormat;
+import org.apache.olingo.commons.api.http.HttpStatusCode;
+import org.apache.olingo.fit.tecsvc.TecSvcConst;
+import org.apache.olingo.fit.v4.AbstractTestITCase;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class BatchClientITCase extends AbstractTestITCase {
+ private final static String ACCEPT = ContentType.APPLICATION_OCTET_STREAM.toContentTypeString();
+ private static final String SERVICE_URI = TecSvcConst.BASE_URI;
+
+ @Before
+ public void setup() {
+ client.getConfiguration().setContinueOnError(false);
+ }
+
+ @Test
+ public void emptyBatchRequest() {
+ // create your request
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+
+ final BatchManager payload = request.payloadManager();
+ final ODataBatchResponse response = payload.getResponse();
+
+ assertEquals(202, response.getStatusCode());
+ assertEquals("Accepted", response.getStatusMessage());
+
+ final Iterator<ODataBatchResponseItem> iter = response.getBody();
+ assertFalse(iter.hasNext());
+ }
+
+ @Test
+ public void getBatchRequest() {
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+
+ final BatchManager payload = request.payloadManager();
+
+ // create new request
+ appendGetRequest(payload, "ESAllPrim", 32767);
+
+ // Fetch result
+ final ODataBatchResponse response = payload.getResponse();
+
+ assertEquals(202, response.getStatusCode());
+ assertEquals("Accepted", response.getStatusMessage());
+
+ final Iterator<ODataBatchResponseItem> iter = response.getBody();
+ assertTrue(iter.hasNext());
+
+ ODataBatchResponseItem item = iter.next();
+ assertFalse(item.isChangeset());
+
+ ODataResponse oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResonse.getStatusCode());
+ assertEquals(1, oDataResonse.getHeader("OData-Version").size());
+ assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
+ assertEquals(1, oDataResonse.getHeader("Content-Length").size());
+ assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+ assertEquals("application/json;odata.metadata=minimal", oDataResonse.getContentType());
+ }
+
+ @Test
+ public void testErrorWithoutContinueOnErrorPreferHeader() {
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+
+ final BatchManager payload = request.payloadManager();
+
+ appendGetRequest(payload, "ESAllPrim", 32767); // Without error
+ appendGetRequest(payload, "ESAllPrim", 42); // Error ( Key does not exist )
+ appendGetRequest(payload, "ESAllPrim", 0); // Without error
+
+ // Fetch result
+ final ODataBatchResponse response = payload.getResponse();
+ assertEquals(202, response.getStatusCode());
+
+ final Iterator<ODataBatchResponseItem> iter = response.getBody();
+
+ // Check first get request
+ assertTrue(iter.hasNext());
+ ODataBatchResponseItem item = iter.next();
+ assertFalse(item.isChangeset());
+
+ ODataResponse oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResonse.getStatusCode());
+ assertEquals(1, oDataResonse.getHeader("OData-Version").size());
+ assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
+ assertEquals(1, oDataResonse.getHeader("Content-Length").size());
+ assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+ assertEquals("application/json;odata.metadata=minimal", oDataResonse.getContentType());
+
+ // Check second get request
+ assertTrue(iter.hasNext());
+ item = iter.next();
+ assertFalse(item.isChangeset());
+
+ oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), oDataResonse.getStatusCode());
+
+ // Check if third request is available
+ assertFalse(iter.hasNext());
+ }
+
+ @Test
+ public void testErrorWithContinueOnErrorPreferHeader() {
+ client.getConfiguration().setContinueOnError(true);
+
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+
+ final BatchManager payload = request.payloadManager();
+
+ appendGetRequest(payload, "ESAllPrim", 32767); // Without error
+ appendGetRequest(payload, "ESAllPrim", 42); // Error ( Key does not exist )
+ appendGetRequest(payload, "ESAllPrim", 0); // Without error
+
+ // Fetch result
+ final ODataBatchResponse response = payload.getResponse();
+ assertEquals(202, response.getStatusCode());
+
+ final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();
+
+ // Check first get request
+ assertTrue(bodyIterator.hasNext());
+ ODataBatchResponseItem item = bodyIterator.next();
+ assertFalse(item.isChangeset());
+
+ ODataResponse oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResonse.getStatusCode());
+ assertEquals(1, oDataResonse.getHeader("OData-Version").size());
+ assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
+ assertEquals(1, oDataResonse.getHeader("Content-Length").size());
+ assertEquals("538", oDataResonse.getHeader("Content-Length").toArray()[0]);
+ assertEquals("application/json;odata.metadata=minimal", oDataResonse.getContentType());
+
+ // Check second get request
+ assertTrue(bodyIterator.hasNext());
+ item = bodyIterator.next();
+ assertFalse(item.isChangeset());
+
+ oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.NOT_FOUND.getStatusCode(), oDataResonse.getStatusCode());
+
+ // Check if third request is available
+ assertTrue(bodyIterator.hasNext());
+ item = bodyIterator.next();
+ assertFalse(item.isChangeset());
+
+ oDataResonse = item.next();
+ assertNotNull(oDataResonse);
+ assertEquals(HttpStatusCode.OK.getStatusCode(), oDataResonse.getStatusCode());
+ assertEquals(1, oDataResonse.getHeader("OData-Version").size());
+ assertEquals("4.0", oDataResonse.getHeader("OData-Version").toArray()[0]);
+ assertEquals(1, oDataResonse.getHeader("Content-Length").size());
+ assertEquals("446", oDataResonse.getHeader("Content-Length").toArray()[0]);
+ assertEquals("application/json;odata.metadata=minimal", oDataResonse.getContentType());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ @Ignore("Not implemented")
+ public void changesetWithReferences() throws EdmPrimitiveTypeException {
+ // create your request
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+ final BatchManager streamManager = request.payloadManager();
+
+ final ODataChangeset changeset = streamManager.addChangeset();
+ ODataEntity esAllPrim = newESAllPrim((short) 23);
+
+ final URIBuilder uriBuilder = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim");
+
+ // add create request
+ final ODataEntityCreateRequest<ODataEntity> createReq =
+ client.getCUDRequestFactory().getEntityCreateRequest(uriBuilder.build(), esAllPrim);
+
+ changeset.addRequest(createReq);
+
+ // retrieve request reference
+ int createRequestRef = changeset.getLastContentId();
+
+ // add update request
+ final ODataEntity customerChanges = client.getObjectFactory().newEntity(esAllPrim.getTypeName());
+ customerChanges.addLink(client.getObjectFactory().newEntitySetNavigationLink(
+ "NavPropertyETTwoPrimMany",
+ client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("NavPropertyETTwoPrimMany").
+ appendKeySegment(new HashMap<String, Object>() {
+ private static final long serialVersionUID = 3109256773218160485L;
+
+ {
+ put("PropertyInt16", 4242);
+ put("PropertyString", "Test");
+ }
+ }).build()));
+
+ final ODataEntityUpdateRequest<ODataEntity> updateReq = client.getCUDRequestFactory().getEntityUpdateRequest(
+ URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges);
+
+ changeset.addRequest(updateReq);
+
+ final ODataBatchResponse response = streamManager.getResponse();
+ assertEquals(200, response.getStatusCode());
+ assertEquals("OK", response.getStatusMessage());
+
+ // verify response payload ...
+ final Iterator<ODataBatchResponseItem> iter = response.getBody();
+
+ final ODataBatchResponseItem item = iter.next();
+ assertTrue(item instanceof ODataChangesetResponseItem);
+
+ final ODataChangesetResponseItem chgitem = (ODataChangesetResponseItem) item;
+
+ ODataResponse res = chgitem.next();
+ assertEquals(201, res.getStatusCode());
+ assertTrue(res instanceof ODataEntityCreateResponse);
+
+ esAllPrim = ((ODataEntityCreateResponse<ODataEntity>) res).getBody();
+ final ODataEntitySetRequest<ODataEntitySet> req = client.getRetrieveRequestFactory().getEntitySetRequest(
+ URIUtils.getURI(SERVICE_URI, esAllPrim.getEditLink().toASCIIString() + "/NavPropertyETTwoPrimMany"));
+
+ assertEquals(Integer.valueOf(4242),
+ req.execute().getBody().getEntities().get(0).getProperty("PropertyInt16").getPrimitiveValue().
+ toCastValue(Integer.class));
+
+ res = chgitem.next();
+ assertEquals(204, res.getStatusCode());
+ assertTrue(res instanceof ODataEntityUpdateResponse);
+
+ // clean ...
+ assertEquals(204, client.getCUDRequestFactory().getDeleteRequest(
+ URIUtils.getURI(SERVICE_URI, esAllPrim.getEditLink().toASCIIString())).execute().
+ getStatusCode());
+
+ try {
+ client.getRetrieveRequestFactory().getEntityRequest(
+ URIUtils.getURI(SERVICE_URI, esAllPrim.getEditLink().toASCIIString())).
+ execute().getBody();
+ fail("Entity not deleted");
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ private ODataEntity newESAllPrim(short id) {
+ final ODataEntity entity = getClient().getObjectFactory().
+ newEntity(new FullQualifiedName("olingo.odata.test1.ESAllPrim"));
+
+ entity.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyInt16",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildInt16(id)));
+
+ entity.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyDouble",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildDouble(3.1415)));
+
+ return entity;
+ }
+
+ //TODO If write support is implemented, remove ignore tag
+ @Test
+ @Ignore("Not implemented")
+ public void changesetBatchRequest() {
+ final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(SERVICE_URI);
+ request.setAccept(ACCEPT);
+
+ final BatchManager payload = request.payloadManager();
+ // -----------------------------
+ // - Append get request
+ // -----------------------------
+ appendGetRequest(payload, "ESAllPrim", 32767); // Without error
+
+ // -----------------------------
+ // - Append change set
+ // -----------------------------
+ final ODataChangeset changeset = payload.addChangeset();
+
+ // ------------------------
+ // POST request (Insert)
+ URIBuilder targetURI =
+ client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim");
+ URI editLink = targetURI.build();
+
+ ODataEntity post = client.getObjectFactory().newEntity(
+ new FullQualifiedName("olingo.odata.test1.ESAllPrim"));
+
+ post.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyInt16",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildInt16((short) 15)));
+
+ post.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyDouble",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildDouble(3.1415)));
+
+ final ODataEntityCreateRequest<ODataEntity> createRequest =
+ client.getCUDRequestFactory().getEntityCreateRequest(editLink, post);
+ createRequest.setFormat(ODataFormat.JSON_FULL_METADATA);
+ createRequest.setContentType("1");
+
+ changeset.addRequest(createRequest);
+
+ // ------------------------
+ // Patch request (Update)
+ targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(0);
+ editLink = targetURI.build();
+
+ ODataEntity patch = client.getObjectFactory().newEntity(new FullQualifiedName("olingo.odata.test1.ESAllPrim"));
+ patch.setEditLink(editLink);
+
+ patch.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyDouble",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildDouble(3.1415)));
+
+ ODataEntityUpdateRequest<ODataEntity> changeReq =
+ client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patch);
+ changeReq.setFormat(ODataFormat.JSON_FULL_METADATA);
+ changeReq.setContentType("2");
+ changeset.addRequest(changeReq);
+
+ // ------------------------
+ // Patch request (Upsert)
+ targetURI = client.newURIBuilder(SERVICE_URI).appendEntitySetSegment("ESAllPrim").appendKeySegment(35);
+ editLink = targetURI.build();
+
+ patch = client.getObjectFactory().newEntity(new FullQualifiedName("olingo.odata.test1.ESAllPrim"));
+ patch.setEditLink(editLink);
+
+ patch.getProperties().add(client.getObjectFactory().newPrimitiveProperty(
+ "PropertyDouble",
+ client.getObjectFactory().newPrimitiveValueBuilder().buildDouble(3.1415)));
+
+ changeReq = client.getCUDRequestFactory().getEntityUpdateRequest(UpdateType.PATCH, patch);
+ changeReq.setFormat(ODataFormat.JSON_FULL_METADATA);
+ changeReq.setContentType("3");
+ changeset.addRequest(changeReq);
+
+ // -----------------------------
+ // - Append get request
+ // -----------------------------
+ appendGetRequest(payload, "ESAllPrim", 32767); // Without error
+
+ // -----------------------------
+ // - Fetch result
+ // -----------------------------
+ final ODataBatchResponse response = payload.getResponse();
+ assertEquals(202, response.getStatusCode());
+ final Iterator<ODataBatchResponseItem> bodyIterator = response.getBody();
+
+ // Check first get request
+ assertTrue(bodyIterator.hasNext());
+ ODataBatchResponseItem item = bodyIterator.next();
+ assertFalse(item.isChangeset());
+
+ // Check change set
+ assertTrue(bodyIterator.hasNext());
+ item = bodyIterator.next();
+ assertTrue(item.isChangeset());
+
+ for (int i = 0; i < 3; i++) {
+ assertTrue(item.hasNext());
+ assertTrue(item instanceof ODataChangesetResponseItem);
+ ODataChangesetResponseItem changeSetResponseItem = (ODataChangesetResponseItem) item.next();
+ assertNotNull(changeSetResponseItem);
+
+ ODataResponse chgRequest = changeSetResponseItem.next();
+ final String contentId = chgRequest.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next();
+
+ if (contentId == "1") {
+ // Insert
+ assertEquals(HttpStatusCode.CREATED.getStatusCode(), chgRequest.getStatusCode());
+ } else if (contentId == "2") {
+ // Update
+ assertEquals(HttpStatusCode.OK.getStatusCode(), chgRequest.getStatusCode());
+ } else if (contentId == "3") {
+ // Upsert
+ assertEquals(HttpStatusCode.CREATED.getStatusCode(), chgRequest.getStatusCode());
+ } else {
+ fail("Unkonwn content id " + contentId);
+ }
+ }
+ assertFalse(item.hasNext());
+
+ // Check second get request
+ assertTrue(bodyIterator.hasNext());
+ item = bodyIterator.next();
+ assertFalse(item.isChangeset());
+ }
+
+ private void appendGetRequest(final BatchManager manager, final String segment, final Object key) {
+ URIBuilder targetURI = client.newURIBuilder(SERVICE_URI);
+ targetURI.appendEntitySetSegment(segment).appendKeySegment(key);
+
+ ODataEntityRequest<ODataEntity> queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
+ queryReq.setFormat(ODataFormat.JSON);
+ manager.addRequest(queryReq);
+ }
+}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/ODataResponsePart.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/ODataResponsePart.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/ODataResponsePart.java
index 8dd7480..c9a914a 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/ODataResponsePart.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/batch/ODataResponsePart.java
@@ -18,25 +18,63 @@
*/
package org.apache.olingo.server.api.batch;
+import java.util.Arrays;
import java.util.List;
import org.apache.olingo.server.api.ODataResponse;
-public interface ODataResponsePart {
+public class ODataResponsePart {
+ private List<ODataResponse> responses;
+ private boolean isChangeSet;
+
/**
- * Returns a collection of ODataResponses.
- * Each collections contains at least one {@link ODataResponse}.
+ * Creates a new ODataResponsePart.
*
- * If this instance represents a change set, there are may many ODataResponses
+ * An ODataResponsePart represents a collections of ODataResponses.
+ * A list of ODataResponseParts can be combined by the BatchSerializer to a single
+ * OData batch response.
*
- * @return a list of {@link ODataResponse}
+ * @param responses A list of {@link ODataResponse}
+ * @param isChangeSet True this ODataResponsePart represents a change set, otherwise false
+ */
+ public ODataResponsePart(List<ODataResponse> responses, boolean isChangeSet) {
+ this.responses = responses;
+ this.isChangeSet = isChangeSet;
+ }
+
+ /**
+ * Creates a new ODataResponsePart.
+ *
+ * An ODataResponsePart represents a collections of ODataResponses.
+ * A list of ODataResponseParts can be combined by the BatchSerializer to a single
+ * OData batch response.
+ *
+ * @param responses A single {@link ODataResponse}
+ * @param isChangeSet True this ODataResponsePart represents a change set, otherwise false
*/
- public List<ODataResponse> getResponses();
+ public ODataResponsePart(ODataResponse response, boolean isChangeSet) {
+ this.responses = Arrays.asList(new ODataResponse[] { response });
+ this.isChangeSet = isChangeSet;
+ }
/**
* Returns true if the current instance represents a change set.
*
* @return true or false
*/
- public boolean isChangeSet();
+ public List<ODataResponse> getResponses() {
+ return responses;
+ }
+
+ /**
+ * Returns a collection of ODataResponses.
+ * Each collections contains at least one {@link ODataResponse}.
+ *
+ * If this instance represents a change set, there are may many ODataResponses
+ *
+ * @return a list of {@link ODataResponse}
+ */
+ public boolean isChangeSet() {
+ return isChangeSet;
+ }
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java
index a0a7135..843fa28 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/BatchProcessor.java
@@ -24,10 +24,11 @@ import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.batch.BatchOperation;
import org.apache.olingo.server.api.batch.BatchRequestPart;
+import org.apache.olingo.server.api.batch.ODataResponsePart;
public interface BatchProcessor extends Processor {
void executeBatch(BatchOperation operation, ODataRequest request, ODataResponse response);
- List<ODataResponse> executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
+ ODataResponsePart executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
BatchRequestPart requestPart);
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
----------------------------------------------------------------------
diff --git a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
index 473c904..25778e3 100644
--- a/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
+++ b/lib/server-api/src/main/java/org/apache/olingo/server/api/processor/DefaultProcessor.java
@@ -19,7 +19,11 @@
package org.apache.olingo.server.api.processor;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.olingo.commons.api.ODataRuntimeException;
import org.apache.olingo.commons.api.format.ContentType;
import org.apache.olingo.commons.api.format.ODataFormat;
import org.apache.olingo.commons.api.http.HttpHeader;
@@ -30,6 +34,10 @@ import org.apache.olingo.server.api.ODataRequest;
import org.apache.olingo.server.api.ODataResponse;
import org.apache.olingo.server.api.ODataServerError;
import org.apache.olingo.server.api.ServiceMetadata;
+import org.apache.olingo.server.api.batch.BatchException;
+import org.apache.olingo.server.api.batch.BatchOperation;
+import org.apache.olingo.server.api.batch.BatchRequestPart;
+import org.apache.olingo.server.api.batch.ODataResponsePart;
import org.apache.olingo.server.api.serializer.ODataSerializer;
import org.apache.olingo.server.api.serializer.SerializerException;
import org.apache.olingo.server.api.uri.UriInfo;
@@ -42,8 +50,12 @@ import org.apache.olingo.server.api.uri.UriInfo;
* <p>This implementation is registered in the ODataHandler by default.
* The default can be replaced by re-registering a custom implementation.</p>
*/
-public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProcessor, ExceptionProcessor {
-
+public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProcessor, ExceptionProcessor,
+ BatchProcessor {
+
+ private static final String PREFERENCE_CONTINUE_ON_ERROR = "odata.continue-on-error";
+ private static final String PREFER_HEADER = "Prefer";
+
private OData odata;
private ServiceMetadata serviceMetadata;
@@ -89,4 +101,81 @@ public class DefaultProcessor implements MetadataProcessor, ServiceDocumentProce
response.setHeader(HttpHeader.CONTENT_TYPE, ContentType.APPLICATION_JSON.toContentTypeString());
}
}
+
+ @Override
+ public void executeBatch(BatchOperation operation, ODataRequest request, ODataResponse response) {
+ boolean continueOnError = shouldContinueOnError(request);
+
+ try {
+ final List<BatchRequestPart> parts = operation.parseBatchRequest(request.getBody());
+ final List<ODataResponsePart> responseParts = new ArrayList<ODataResponsePart>();
+
+ for (BatchRequestPart part : parts) {
+ final ODataResponsePart responsePart = operation.handleBatchRequest(part);
+ responseParts.add(responsePart); // Also add failed responses
+
+ if (responsePart.getResponses().get(0).getStatusCode() >= 400
+ && !continueOnError) {
+
+ // Perform some additions actions
+ // ...
+
+ break; // Stop processing, but serialize all recent requests
+ }
+ }
+
+ operation.writeResponseParts(responseParts, response); // Serialize responses
+ } catch (BatchException e) {
+ throw new ODataRuntimeException(e);
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ private boolean shouldContinueOnError(ODataRequest request) {
+ final List<String> preferValues = request.getHeaders(PREFER_HEADER);
+
+ if (preferValues != null) {
+ for (final String preference : preferValues) {
+ if (PREFERENCE_CONTINUE_ON_ERROR.equals(preference)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public ODataResponsePart executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
+ BatchRequestPart requestPart) {
+ List<ODataResponse> responses = new ArrayList<ODataResponse>();
+
+ for (ODataRequest request : requests) {
+ try {
+ final ODataResponse oDataResponse = operation.handleODataRequest(request, requestPart);
+
+ if (oDataResponse.getStatusCode() < 400) {
+ responses.add(oDataResponse);
+ } else {
+ // Rollback
+ // ...
+
+ // OData Version 4.0 Part 1: Protocol Plus Errata 01
+ // 11.7.4 Responding to a Batch Request
+ //
+ // When a request within a change set fails, the change set response is not represented using
+ // the multipart/mixed media type. Instead, a single response, using the application/http media type
+ // and a Content-Transfer-Encoding header with a value of binary, is returned that applies to all requests
+ // in the change set and MUST be formatted according to the Error Handling defined
+ // for the particular response format.
+
+ return new ODataResponsePart(oDataResponse, false);
+ }
+ } catch (BatchException e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ return new ODataResponsePart(responses, true);
+ }
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java
index 5bec30b..23ba106 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/BatchPartHandler.java
@@ -129,17 +129,14 @@ public class BatchPartHandler {
final List<ODataResponse> responses = new ArrayList<ODataResponse>();
responses.add(handleODataRequest(request.getRequests().get(0), request));
- return new ODataResponsePartImpl(responses, false);
+ return new ODataResponsePart(responses, false);
}
}
private ODataResponsePart handleChangeSet(BatchRequestPart request) throws BatchException {
- final List<ODataResponse> responses = new ArrayList<ODataResponse>();
final BatchChangeSetSorter sorter = new BatchChangeSetSorter(request.getRequests());
-
- responses.addAll(batchProcessor.executeChangeSet(batchOperation, sorter.getOrderdRequests(), request));
- return new ODataResponsePartImpl(responses, true);
+ return batchProcessor.executeChangeSet(batchOperation, sorter.getOrderdRequests(), request);
}
private static class UriMapping {
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/ODataResponsePartImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/ODataResponsePartImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/ODataResponsePartImpl.java
deleted file mode 100644
index da52a37..0000000
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/handler/ODataResponsePartImpl.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.server.core.batch.handler;
-
-import java.util.List;
-
-import org.apache.olingo.server.api.ODataResponse;
-import org.apache.olingo.server.api.batch.ODataResponsePart;
-
-public class ODataResponsePartImpl implements ODataResponsePart {
- private List<ODataResponse> responses;
- private boolean isChangeSet;
-
- public ODataResponsePartImpl(List<ODataResponse> responses, boolean isChangeSet) {
- this.responses = responses;
- this.isChangeSet = isChangeSet;
- }
-
- @Override
- public List<ODataResponse> getResponses() {
- return responses;
- }
-
- @Override
- public boolean isChangeSet() {
- return isChangeSet;
- }
-}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchParserCommon.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchParserCommon.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchParserCommon.java
index 43a09b6..b87f8ef 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchParserCommon.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/parser/BatchParserCommon.java
@@ -123,9 +123,6 @@ public class BatchParserCommon {
// Remove preamble
if (messageParts.size() > 0) {
messageParts.remove(0);
- } else {
- throw new BatchException("Missing boundary delimiter", BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER, ""
- + lineNumer);
}
if (!isEndReached) {
@@ -133,11 +130,6 @@ public class BatchParserCommon {
"" + lineNumer);
}
- if (messageParts.size() == 0) {
- throw new BatchException("No boundary delimiter found",
- BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER, boundary, "" + lineNumer);
- }
-
return messageParts;
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
index 812ea8b..0c13cb9 100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriter.java
@@ -149,7 +149,7 @@ public class BatchResponseWriter {
final Map<String, String> header = response.getHeaders();
for (final String key : header.keySet()) {
- // Requests do never have content id header
+ // Requests do never has a content id header
if (!key.equalsIgnoreCase(BatchParserCommon.HTTP_CONTENT_ID)) {
appendHeader(key, header.get(key), writer);
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java b/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
deleted file mode 100644
index a9711c9..0000000
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/batch/writer/ODataResponsePartImpl.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.olingo.server.core.batch.writer;
-
-import java.util.List;
-
-import org.apache.olingo.server.api.ODataResponse;
-import org.apache.olingo.server.api.batch.ODataResponsePart;
-
-public class ODataResponsePartImpl implements ODataResponsePart {
- private final List<ODataResponse> responses;
- private final boolean isChangeSet;
-
- public ODataResponsePartImpl(final List<ODataResponse> responses, final boolean isChangeSet) {
- this.responses = responses;
- this.isChangeSet = isChangeSet;
- }
-
- @Override
- public List<ODataResponse> getResponses() {
- return responses;
- }
-
- @Override
- public boolean isChangeSet() {
- return isChangeSet;
- }
-}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
index c9778e3..88fdf08 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/BatchRequestParserTest.java
@@ -274,7 +274,8 @@ public class BatchRequestParserTest {
+ GET_REQUEST
+ "--batch_8194-cf13-1f56--";
- parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ final List<BatchRequestPart> parts = parse(batch);
+ assertEquals(0, parts.size());
}
@Test
@@ -450,14 +451,23 @@ public class BatchRequestParserTest {
+ "POST Employees('1')/EmployeeName HTTP/1.1" + CRLF
+ CRLF;
- parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CLOSE_DELIMITER);
}
-
+
+ @Test
+ public void testEmptyRequest() throws BatchException, UnsupportedEncodingException {
+ final String batch = ""
+ + "--batch_8194-cf13-1f56--";
+
+ final List<BatchRequestPart> parts = parse(batch);
+ assertEquals(0, parts.size());
+ }
+
@Test
public void testBadRequest() throws UnsupportedEncodingException {
final String batch = "This is a bad request. There is no syntax and also no semantic";
- parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_CLOSE_DELIMITER);
}
@Test
@@ -496,7 +506,7 @@ public class BatchRequestParserTest {
}
@Test
- public void testInvalidChangeSetBoundary() throws UnsupportedEncodingException {
+ public void testInvalidChangeSetBoundary() throws UnsupportedEncodingException, BatchException {
final String batch = "--batch_8194-cf13-1f56" + CRLF
+ "Content-Type: multipart/mixed;boundary=changeset_f980-1cb6-94dd" + CRLF
+ CRLF
@@ -511,7 +521,12 @@ public class BatchRequestParserTest {
+ CRLF
+ "--batch_8194-cf13-1f56--";
- parseInvalidBatchBody(batch, BatchException.MessageKeys.MISSING_BOUNDARY_DELIMITER);
+ final List<BatchRequestPart> parts = parse(batch);
+ assertEquals(1, parts.size());
+
+ final BatchRequestPart part = parts.get(0);
+ assertTrue(part.isChangeSet());
+ assertEquals(0, part.getRequests().size());
}
@Test
@@ -1261,7 +1276,6 @@ public class BatchRequestParserTest {
final List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
assertNotNull(batchRequestParts);
- assertFalse(batchRequestParts.isEmpty());
return batchRequestParts;
}
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java
index e1864e2..7a85ffa 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/handler/MockedBatchHandlerTest.java
@@ -535,7 +535,7 @@ public class MockedBatchHandlerTest {
public void init(OData odata, ServiceMetadata serviceMetadata) {}
@Override
- public List<ODataResponse> executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
+ public ODataResponsePart executeChangeSet(BatchOperation operation, List<ODataRequest> requests,
BatchRequestPart requestPart) {
List<ODataResponse> responses = new ArrayList<ODataResponse>();
@@ -557,7 +557,7 @@ public class MockedBatchHandlerTest {
}
}
- return responses;
+ return new ODataResponsePart(responses, true);
}
@Override
http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/4ff5fb9c/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriterTest.java
----------------------------------------------------------------------
diff --git a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriterTest.java b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriterTest.java
index b7169b9..ea05bfb 100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriterTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/batch/writer/BatchResponseWriterTest.java
@@ -50,14 +50,14 @@ public class BatchResponseWriterTest {
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
responses.add(response);
- parts.add(new ODataResponsePartImpl(responses, false));
+ parts.add(new ODataResponsePart(responses, false));
ODataResponse changeSetResponse = new ODataResponse();
changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
changeSetResponse.setHeader(BatchParserCommon.HTTP_CONTENT_ID, "1");
responses = new ArrayList<ODataResponse>(1);
responses.add(changeSetResponse);
- parts.add(new ODataResponsePartImpl(responses, true));
+ parts.add(new ODataResponsePart(responses, true));
BatchResponseWriter writer = new BatchResponseWriter();
ODataResponse batchResponse = new ODataResponse();
@@ -109,7 +109,7 @@ public class BatchResponseWriterTest {
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
responses.add(response);
- parts.add(new ODataResponsePartImpl(responses, false));
+ parts.add(new ODataResponsePart(responses, false));
ODataResponse batchResponse = new ODataResponse();
new BatchResponseWriter().toODataResponse(parts, batchResponse);
@@ -144,7 +144,7 @@ public class BatchResponseWriterTest {
List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
responses.add(response);
- parts.add(new ODataResponsePartImpl(responses, true));
+ parts.add(new ODataResponsePart(responses, true));
BatchResponseWriter writer = new BatchResponseWriter();
ODataResponse batchResponse = new ODataResponse();