[OLINGO-175, OLINGO-205, OLINGO-246] provided batch error V4 + continueOnError support

This commit is contained in:
fmartelli 2014-04-18 11:16:55 +02:00
parent e4a4f9e6eb
commit 6dfdee2ef2
11 changed files with 635 additions and 140 deletions

View File

@ -207,19 +207,25 @@ public abstract class AbstractServices {
@Path("/$batch")
@Consumes("multipart/mixed")
@Produces("application/octet-stream; boundary=" + BOUNDARY)
public Response batch(final @Multipart MultipartBody attachment) {
public Response batch(
@HeaderParam("Prefer") @DefaultValue(StringUtils.EMPTY) String prefer,
final @Multipart MultipartBody attachment) {
try {
return xml.createBatchResponse(exploreMultipart(attachment.getAllAttachments(), BOUNDARY), BOUNDARY);
final boolean continueOnError = prefer.contains("odata.continue-on-error");
return xml.createBatchResponse(
exploreMultipart(attachment.getAllAttachments(), BOUNDARY, continueOnError),
BOUNDARY);
} catch (IOException e) {
return xml.createFaultResponse(Accept.XML.toString(version), e);
}
}
private Response bodyPartRequest(final MimeBodyPart body) throws Exception {
protected Response bodyPartRequest(final MimeBodyPart body) throws Exception {
return bodyPartRequest(body, Collections.<String, String>emptyMap());
}
private Response bodyPartRequest(final MimeBodyPart body, final Map<String, String> references) throws Exception {
protected Response bodyPartRequest(final MimeBodyPart body, final Map<String, String> references) throws Exception {
@SuppressWarnings("unchecked")
final Enumeration<Header> en = (Enumeration<Header>) body.getAllHeaders();
@ -269,93 +275,29 @@ public abstract class AbstractServices {
}
}
public InputStream exploreMultipart(final List<Attachment> attachments, final String boundary) throws IOException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
protected abstract InputStream exploreMultipart(
final List<Attachment> attachments, final String boundary, final boolean continueOnError)
throws IOException;
Response res = null;
try {
for (Attachment obj : attachments) {
bos.write(("--" + boundary).getBytes());
bos.write(Constants.CRLF);
final Object content = obj.getDataHandler().getContent();
if (content instanceof MimeMultipart) {
final Map<String, String> references = new HashMap<String, String>();
final String cboundary = "changeset_" + UUID.randomUUID().toString();
bos.write(("Content-Type: multipart/mixed;boundary=" + cboundary).getBytes());
bos.write(Constants.CRLF);
bos.write(Constants.CRLF);
final ByteArrayOutputStream chbos = new ByteArrayOutputStream();
String lastContebtID = null;
try {
for (int i = 0; i < ((MimeMultipart) content).getCount(); i++) {
final MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) content).getBodyPart(i);
lastContebtID = part.getContentID();
addChangesetItemIntro(chbos, lastContebtID, cboundary);
res = bodyPartRequest(new MimeBodyPart(part.getInputStream()), references);
if (res.getStatus() >= 400) {
throw new Exception("Failure processing changeset");
}
addSingleBatchResponse(res, lastContebtID, chbos);
references.put("$" + lastContebtID, res.getHeaderString("Location"));
}
bos.write(chbos.toByteArray());
IOUtils.closeQuietly(chbos);
bos.write(("--" + cboundary + "--").getBytes());
bos.write(Constants.CRLF);
} catch (Exception e) {
LOG.warn("While processing changeset", e);
IOUtils.closeQuietly(chbos);
addChangesetItemIntro(bos, lastContebtID, cboundary);
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, "1", bos);
} else {
addSingleBatchResponse(res, lastContebtID, bos);
}
bos.write(("--" + cboundary + "--").getBytes());
bos.write(Constants.CRLF);
}
} else {
addItemIntro(bos);
res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()));
if (res.getStatus() >= 400) {
throw new Exception("Failure processing changeset");
}
addSingleBatchResponse(res, bos);
}
}
} catch (Exception e) {
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, bos);
} else {
addSingleBatchResponse(res, bos);
}
}
bos.write(("--" + boundary + "--").getBytes());
return new ByteArrayInputStream(bos.toByteArray());
protected void addItemIntro(final ByteArrayOutputStream bos) throws IOException {
addItemIntro(bos, null);
}
private void addItemIntro(final ByteArrayOutputStream bos) throws IOException {
protected void addItemIntro(final ByteArrayOutputStream bos, final String contentId) throws IOException {
bos.write("Content-Type: application/http".getBytes());
bos.write(Constants.CRLF);
bos.write("Content-Transfer-Encoding: binary".getBytes());
bos.write(Constants.CRLF);
if (StringUtils.isNotBlank(contentId)) {
bos.write(("Content-ID: " + contentId).getBytes());
bos.write(Constants.CRLF);
}
bos.write(Constants.CRLF);
}
private void addChangesetItemIntro(
protected void addChangesetItemIntro(
final ByteArrayOutputStream bos, final String contentId, final String cboundary) throws IOException {
bos.write(("--" + cboundary).getBytes());
bos.write(Constants.CRLF);
@ -364,12 +306,12 @@ public abstract class AbstractServices {
addItemIntro(bos);
}
private void addSingleBatchResponse(
protected void addSingleBatchResponse(
final Response response, final ByteArrayOutputStream bos) throws IOException {
addSingleBatchResponse(response, null, bos);
}
private void addSingleBatchResponse(
protected void addSingleBatchResponse(
final Response response, final String contentId, final ByteArrayOutputStream bos) throws IOException {
bos.write("HTTP/1.1 ".getBytes());
bos.write(String.valueOf(response.getStatusInfo().getStatusCode()).getBytes());
@ -406,12 +348,12 @@ public abstract class AbstractServices {
bos.write(Constants.CRLF);
}
private void addErrorBatchResponse(final Exception e, final ByteArrayOutputStream bos)
protected void addErrorBatchResponse(final Exception e, final ByteArrayOutputStream bos)
throws IOException {
addErrorBatchResponse(e, null, bos);
}
private void addErrorBatchResponse(final Exception e, final String contentId, final ByteArrayOutputStream bos)
protected void addErrorBatchResponse(final Exception e, final String contentId, final ByteArrayOutputStream bos)
throws IOException {
addSingleBatchResponse(xml.createFaultResponse(Accept.XML.toString(version), e), contentId, bos);
}

View File

@ -20,7 +20,9 @@ package org.apache.olingo.fit;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
@ -37,6 +39,7 @@ import static javax.ws.rs.core.Response.status;
import javax.ws.rs.core.UriInfo;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import static org.apache.olingo.fit.AbstractServices.LOG;
@ -206,4 +209,11 @@ public class V3ActionOverloading extends AbstractServices {
protected void setInlineCount(Feed feed, String count) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected InputStream exploreMultipart(
final List<Attachment> attachments, final String boundary, final boolean continueOnError)
throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -18,9 +18,16 @@
*/
package org.apache.olingo.fit;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
@ -37,6 +44,7 @@ import javax.ws.rs.core.Response;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.fit.methods.MERGE;
@ -107,6 +115,92 @@ public class V3Services extends AbstractServices {
}
}
@Override
public InputStream exploreMultipart(
final List<Attachment> attachments, final String boundary, final boolean contineOnError)
throws IOException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
Response res = null;
boolean goon = true;
for (int i = 0; i < attachments.size() && goon; i++) {
try {
final Attachment obj = attachments.get(i);
bos.write(("--" + boundary).getBytes());
bos.write(Constants.CRLF);
final Object content = obj.getDataHandler().getContent();
if (content instanceof MimeMultipart) {
final Map<String, String> references = new HashMap<String, String>();
final String cboundary = "changeset_" + UUID.randomUUID().toString();
bos.write(("Content-Type: multipart/mixed;boundary=" + cboundary).getBytes());
bos.write(Constants.CRLF);
bos.write(Constants.CRLF);
final ByteArrayOutputStream chbos = new ByteArrayOutputStream();
String lastContebtID = null;
try {
for (int j = 0; j < ((MimeMultipart) content).getCount(); j++) {
final MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) content).getBodyPart(j);
lastContebtID = part.getContentID();
addChangesetItemIntro(chbos, lastContebtID, cboundary);
res = bodyPartRequest(new MimeBodyPart(part.getInputStream()), references);
if (res.getStatus() >= 400) {
throw new Exception("Failure processing changeset");
}
addSingleBatchResponse(res, lastContebtID, chbos);
references.put("$" + lastContebtID, res.getHeaderString("Location"));
}
bos.write(chbos.toByteArray());
IOUtils.closeQuietly(chbos);
bos.write(("--" + cboundary + "--").getBytes());
bos.write(Constants.CRLF);
} catch (Exception e) {
LOG.warn("While processing changeset", e);
IOUtils.closeQuietly(chbos);
addChangesetItemIntro(bos, lastContebtID, cboundary);
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, "1", bos);
} else {
addSingleBatchResponse(res, lastContebtID, bos);
}
goon = contineOnError;
}
} else {
addItemIntro(bos);
res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()));
if (res.getStatus() >= 400) {
goon = contineOnError;
throw new Exception("Failure processing changeset");
}
addSingleBatchResponse(res, bos);
}
} catch (Exception e) {
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, bos);
} else {
addSingleBatchResponse(res, bos);
}
}
}
bos.write(("--" + boundary + "--").getBytes());
return new ByteArrayInputStream(bos.toByteArray());
}
/**
* Retrieve links sample.
*

View File

@ -19,13 +19,18 @@
package org.apache.olingo.fit;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
@ -42,25 +47,22 @@ import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.apache.olingo.commons.api.data.Container;
import org.apache.olingo.commons.api.data.Feed;
import org.apache.olingo.commons.api.data.Property;
import org.apache.olingo.commons.api.edm.constants.ODataServiceVersion;
import org.apache.olingo.commons.core.data.AtomEntryImpl;
import org.apache.olingo.commons.core.data.AtomFeedImpl;
import org.apache.olingo.commons.core.data.AtomSerializer;
import org.apache.olingo.commons.core.data.JSONEntryImpl;
import org.apache.olingo.commons.core.data.JSONFeedImpl;
import org.apache.olingo.commons.core.edm.EdmTypeInfo;
import org.apache.olingo.fit.methods.PATCH;
import org.apache.olingo.fit.serializer.FITAtomDeserializer;
import org.apache.olingo.fit.serializer.JsonFeedContainer;
import org.apache.olingo.fit.utils.AbstractUtilities;
import org.apache.olingo.fit.utils.Accept;
import org.apache.olingo.fit.utils.Commons;
import org.apache.olingo.fit.utils.ConstantKey;
import org.apache.olingo.fit.utils.Constants;
import org.apache.olingo.fit.utils.DataBinder;
import org.apache.olingo.fit.utils.FSManager;
import org.apache.olingo.fit.utils.LinkInfo;
import org.apache.olingo.fit.utils.ResolvingReferencesInterceptor;
@ -83,6 +85,91 @@ public class V4Services extends AbstractServices {
}
}
@Override
public InputStream exploreMultipart(
final List<Attachment> attachments, final String boundary, final boolean continueOnError)
throws IOException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
Response res = null;
boolean goon = true;
for (int i = 0; i < attachments.size() && goon; i++) {
try {
final Attachment obj = attachments.get(i);
bos.write(("--" + boundary).getBytes());
bos.write(Constants.CRLF);
final Object content = obj.getDataHandler().getContent();
if (content instanceof MimeMultipart) {
final ByteArrayOutputStream chbos = new ByteArrayOutputStream();
String lastContebtID = null;
try {
final Map<String, String> references = new HashMap<String, String>();
final String cboundary = "changeset_" + UUID.randomUUID().toString();
chbos.write(("Content-Type: multipart/mixed;boundary=" + cboundary).getBytes());
chbos.write(Constants.CRLF);
chbos.write(Constants.CRLF);
for (int j = 0; j < ((MimeMultipart) content).getCount(); j++) {
final MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) content).getBodyPart(j);
lastContebtID = part.getContentID();
addChangesetItemIntro(chbos, lastContebtID, cboundary);
res = bodyPartRequest(new MimeBodyPart(part.getInputStream()), references);
if (res.getStatus() >= 400) {
throw new Exception("Failure processing changeset");
}
addSingleBatchResponse(res, lastContebtID, chbos);
references.put("$" + lastContebtID, res.getHeaderString("Location"));
}
chbos.write(("--" + cboundary + "--").getBytes());
chbos.write(Constants.CRLF);
bos.write(chbos.toByteArray());
IOUtils.closeQuietly(chbos);
} catch (Exception e) {
LOG.warn("While processing changeset", e);
IOUtils.closeQuietly(chbos);
addItemIntro(bos, lastContebtID);
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, "1", bos);
} else {
addSingleBatchResponse(res, lastContebtID, bos);
}
goon = continueOnError;
}
} else {
addItemIntro(bos);
res = bodyPartRequest(new MimeBodyPart(obj.getDataHandler().getInputStream()));
if (res.getStatus() >= 400) {
goon = continueOnError;
throw new Exception("Failure processing batch item");
}
addSingleBatchResponse(res, bos);
}
} catch (Exception e) {
if (res == null || res.getStatus() < 400) {
addErrorBatchResponse(e, bos);
} else {
addSingleBatchResponse(res, bos);
}
}
}
bos.write(("--" + boundary + "--").getBytes());
return new ByteArrayInputStream(bos.toByteArray());
}
/**
* Retrieve entity reference sample.
*

View File

@ -161,7 +161,7 @@ public class ODataPreferences {
* @return preference.
*/
public String continueOnError() {
return PreferenceNames.callback.isSupportedBy(serviceVersion).toString();
return PreferenceNames.continueOnError.isSupportedBy(serviceVersion).toString();
}
/**

View File

@ -125,6 +125,19 @@ public abstract class AbstractODataBatchResponseItem implements ODataBatchRespon
return responses.values().iterator();
}
@Override
public boolean hasNext() {
if (closed) {
throw new IllegalStateException("Invalid request - the item has been closed");
}
if (expectedItemsIterator == null) {
expectedItemsIterator = responses.values().iterator();
}
return expectedItemsIterator.hasNext();
}
/**
* {@inheritDoc }
*/

View File

@ -23,6 +23,8 @@ import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.olingo.client.api.ODataBatchConstants;
import org.apache.olingo.client.api.communication.response.ODataResponse;
import static org.apache.olingo.client.core.communication.request.batch.AbstractODataBatchResponseItem.LOG;
import org.apache.olingo.client.core.communication.response.batch.ODataBatchErrorResponse;
/**
* Changeset wrapper for the corresponding batch item.
@ -34,6 +36,8 @@ public class ODataChangesetResponseItem extends AbstractODataBatchResponseItem {
*/
private ODataResponse current = null;
private boolean unexpected = false;
/**
* Constructor.
*/
@ -41,20 +45,8 @@ public class ODataChangesetResponseItem extends AbstractODataBatchResponseItem {
super(true);
}
/**
* {@inheritDoc }
*/
@Override
public boolean hasNext() {
if (closed) {
throw new IllegalStateException("Invalid request - the item has been closed");
}
if (expectedItemsIterator == null) {
expectedItemsIterator = responses.values().iterator();
}
return expectedItemsIterator.hasNext();
public void setUnexpected() {
this.unexpected = true;
}
/**
@ -62,7 +54,6 @@ public class ODataChangesetResponseItem extends AbstractODataBatchResponseItem {
*/
@Override
public ODataResponse next() {
if (current != null) {
current.close();
}
@ -71,6 +62,14 @@ public class ODataChangesetResponseItem extends AbstractODataBatchResponseItem {
throw new IllegalStateException("Invalid request - the item has been closed");
}
if (unexpected) {
return nextUnexpected();
} else {
return nextExpected();
}
}
private ODataResponse nextExpected() {
if (hasNext()) {
// consume item for condition above (like a counter ...)
expectedItemsIterator.next();
@ -119,6 +118,21 @@ public class ODataChangesetResponseItem extends AbstractODataBatchResponseItem {
return current;
}
private ODataResponse nextUnexpected() {
final Map.Entry<Integer, String> responseLine = ODataBatchUtilities.readResponseLine(batchLineIterator);
LOG.debug("Retrieved item response {}", responseLine);
if (responseLine.getKey() >= 400) {
// generate error response
final Map<String, Collection<String>> headers = ODataBatchUtilities.readHeaders(batchLineIterator);
LOG.debug("Retrieved item headers {}", headers);
return new ODataBatchErrorResponse(responseLine, headers, batchLineIterator, boundary);
}
throw new IllegalStateException("Expected item not found");
}
/**
* Unsupported operation.
*/

View File

@ -22,6 +22,8 @@ import java.util.Collection;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.olingo.client.api.communication.response.ODataResponse;
import static org.apache.olingo.client.core.communication.request.batch.AbstractODataBatchResponseItem.LOG;
import org.apache.olingo.client.core.communication.response.batch.ODataBatchErrorResponse;
/**
* Retrieve response wrapper for the corresponding batch item.
@ -37,22 +39,6 @@ public class ODataRetrieveResponseItem extends AbstractODataBatchResponseItem {
super(false);
}
/**
* {@inheritDoc }
*/
@Override
public boolean hasNext() {
if (closed) {
throw new IllegalStateException("Invalid request - the item has been closed");
}
if (expectedItemsIterator == null) {
expectedItemsIterator = responses.values().iterator();
}
return expectedItemsIterator.hasNext();
}
/**
* {@inheritDoc }
*/
@ -62,17 +48,25 @@ public class ODataRetrieveResponseItem extends AbstractODataBatchResponseItem {
throw new IllegalStateException("Invalid request - the item has been closed");
}
if (!hasNext()) {
throw new NoSuchElementException("No item found");
}
final Map.Entry<Integer, String> responseLine = ODataBatchUtilities.readResponseLine(batchLineIterator);
LOG.debug("Retrieved item response {}", responseLine);
final Map<String, Collection<String>> headers = ODataBatchUtilities.readHeaders(batchLineIterator);
LOG.debug("Retrieved item headers {}", headers);
return expectedItemsIterator.next().initFromBatch(responseLine, headers, batchLineIterator, boundary);
final ODataResponse res;
if (responseLine.getKey() >= 400) {
// generate error response
res = new ODataBatchErrorResponse(responseLine, headers, batchLineIterator, boundary);
} else {
if (!hasNext()) {
throw new NoSuchElementException("No item found");
}
res = expectedItemsIterator.next().initFromBatch(responseLine, headers, batchLineIterator, boundary);
}
return res;
}
/**

View File

@ -0,0 +1,261 @@
/*
* 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.response.batch;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.olingo.client.api.communication.header.HeaderName;
import org.apache.olingo.client.api.communication.request.batch.ODataBatchLineIterator;
import org.apache.olingo.client.api.communication.response.ODataResponse;
import org.apache.olingo.client.api.http.NoContentException;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchController;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchUtilities;
import org.slf4j.LoggerFactory;
/**
* Abstract representation of an OData response.
*/
public class ODataBatchErrorResponse implements ODataResponse {
/**
* Logger.
*/
protected static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ODataResponse.class);
/**
* HTTP client.
*/
protected final HttpClient client;
/**
* HTTP response.
*/
protected final HttpResponse res;
/**
* Response headers.
*/
protected final Map<String, Collection<String>> headers =
new TreeMap<String, Collection<String>>(String.CASE_INSENSITIVE_ORDER);
/**
* Response code.
*/
private int statusCode = -1;
/**
* Response message.
*/
private String statusMessage = null;
/**
* Response body/payload.
*/
private InputStream payload = null;
/**
* Initialization check.
*/
private boolean hasBeenInitialized = false;
/**
* Batch info (if to be batched).
*/
private ODataBatchController batchInfo = null;
/**
* Constructor.
*/
public ODataBatchErrorResponse(
final Map.Entry<Integer, String> responseLine,
final Map<String, Collection<String>> headers,
final ODataBatchLineIterator batchLineIterator,
final String boundary) {
client = null;
res = null;
if (hasBeenInitialized) {
throw new IllegalStateException("Request already initialized");
}
this.hasBeenInitialized = true;
this.batchInfo = new ODataBatchController(batchLineIterator, boundary);
this.statusCode = responseLine.getKey();
this.statusMessage = responseLine.getValue();
this.headers.putAll(headers);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getHeaderNames() {
return headers.keySet();
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getHeader(final String name) {
return headers.get(name);
}
/**
* {@inheritDoc}
*/
@Override
public Collection<String> getHeader(final HeaderName name) {
return headers.get(name.toString());
}
/**
* {@inheritDoc}
*/
@Override
public String getContentType() {
final Collection<String> contentTypes = getHeader(HeaderName.contentType);
return contentTypes == null || contentTypes.isEmpty()
? null
: contentTypes.iterator().next();
}
/**
* {@inheritDoc}
*/
@Override
public int getStatusCode() {
return statusCode;
}
/**
* {@inheritDoc}
*/
@Override
public String getStatusMessage() {
return statusMessage;
}
/**
* {@inheritDoc}
*/
@Override
public ODataResponse initFromBatch(
final Map.Entry<Integer, String> responseLine,
final Map<String, Collection<String>> headers,
final ODataBatchLineIterator batchLineIterator,
final String boundary) {
if (hasBeenInitialized) {
throw new IllegalStateException("Request already initialized");
}
this.hasBeenInitialized = true;
this.batchInfo = new ODataBatchController(batchLineIterator, boundary);
this.statusCode = responseLine.getKey();
this.statusMessage = responseLine.getValue();
this.headers.putAll(headers);
return this;
}
/**
* {@inheritDoc }
*/
@Override
public void close() {
if (client == null) {
IOUtils.closeQuietly(payload);
} else {
this.client.getConnectionManager().shutdown();
}
if (batchInfo != null) {
batchInfo.setValidBatch(false);
}
}
/**
* {@inheritDoc}
*/
@Override
public InputStream getRawResponse() {
if (HttpStatus.SC_NO_CONTENT == getStatusCode()) {
throw new NoContentException();
}
if (payload == null && batchInfo.isValidBatch()) {
// get input stream till the end of item
payload = new PipedInputStream();
try {
final PipedOutputStream os = new PipedOutputStream((PipedInputStream) payload);
new Thread(new Runnable() {
@Override
public void run() {
try {
ODataBatchUtilities.readBatchPart(batchInfo, os, true);
} catch (Exception e) {
LOG.error("Error streaming batch item payload", e);
} finally {
IOUtils.closeQuietly(os);
}
}
}).start();
} catch (IOException e) {
LOG.error("Error streaming payload response", e);
throw new IllegalStateException(e);
}
}
return payload;
}
@Override
public String getEtag() {
return null;
}
@Override
public URI getContextURL() {
return null;
}
@Override
public String getMetadataETag() {
return null;
}
}

View File

@ -32,6 +32,7 @@ import org.apache.olingo.client.api.communication.request.batch.ODataBatchRespon
import org.apache.olingo.client.api.communication.response.ODataBatchResponse;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchLineIteratorImpl;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchUtilities;
import org.apache.olingo.client.core.communication.request.batch.ODataChangesetResponseItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -121,18 +122,19 @@ public class ODataBatchResponseManager implements Iterator<ODataBatchResponseIte
current.initFromBatch(
batchLineIterator,
ODataBatchUtilities.getBoundaryFromHeader(
nextItemHeaders.get(HeaderName.contentType.toString())));
ODataBatchUtilities.getBoundaryFromHeader(nextItemHeaders.get(HeaderName.contentType.toString())));
break;
case RETRIEVE:
if (current.isChangeset()) {
throw new IllegalStateException("Unexpected batch item");
// Maybe V4 error item
((ODataChangesetResponseItem) current).setUnexpected();
}
current.initFromBatch(
batchLineIterator,
batchBoundary);
break;
default:
throw new IllegalStateException("Expected item not found");

View File

@ -32,6 +32,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpResponse;
import org.apache.olingo.client.api.ODataBatchConstants;
import org.apache.olingo.client.api.communication.header.HeaderName;
import org.apache.olingo.client.api.communication.header.ODataPreferences;
import org.apache.olingo.client.api.communication.request.ODataStreamManager;
import org.apache.olingo.client.api.communication.request.batch.BatchStreamManager;
import org.apache.olingo.client.api.communication.request.batch.ODataBatchRequest;
@ -138,15 +140,91 @@ public class BatchTestITCase extends AbstractTestITCase {
assertEquals(200, response.getStatusCode());
assertEquals("OK", response.getStatusMessage());
final Iterator<ODataBatchResponseItem> iter = response.getBody();
final ODataChangesetResponseItem chgResponseItem = (ODataChangesetResponseItem) iter.next();
// retrieve the first item (ODataRetrieve)
ODataBatchResponseItem item = response.getBody().next();
final ODataResponse res = chgResponseItem.next();
ODataChangesetResponseItem retitem = (ODataChangesetResponseItem) item;
ODataResponse res = retitem.next();
assertEquals(404, res.getStatusCode());
assertEquals("Not Found", res.getStatusMessage());
assertEquals(Integer.valueOf(3), Integer.valueOf(
res.getHeader(ODataBatchConstants.CHANGESET_CONTENT_ID_NAME).iterator().next()));
assertFalse(chgResponseItem.hasNext());
}
@Test
public void continueOnError() {
continueOnError(true);
}
@Test
public void doNotContinueOnError() {
continueOnError(false);
}
private void continueOnError(final boolean continueOnError) {
// create your request
final ODataBatchRequest request = client.getBatchRequestFactory().getBatchRequest(testStaticServiceRootURL);
if (continueOnError) {
request.addCustomHeader(HeaderName.prefer, new ODataPreferences(client.getServiceVersion()).continueOnError());
}
final BatchStreamManager streamManager = request.execute();
// -------------------------------------------
// Add retrieve item
// -------------------------------------------
ODataRetrieve retrieve = streamManager.addRetrieve();
// prepare URI
URIBuilder targetURI = client.getURIBuilder(testStaticServiceRootURL);
targetURI.appendEntitySetSegment("UnexistinfEntitySet").appendKeySegment(1);
// create new request
ODataEntityRequest<ODataEntity> queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
queryReq.setFormat(ODataPubFormat.ATOM);
retrieve.setRequest(queryReq);
// -------------------------------------------
// -------------------------------------------
// Add retrieve item
// -------------------------------------------
retrieve = streamManager.addRetrieve();
// prepare URI
targetURI = client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("Customers").appendKeySegment(1);
// create new request
queryReq = client.getRetrieveRequestFactory().getEntityRequest(targetURI.build());
retrieve.setRequest(queryReq);
// -------------------------------------------
final ODataBatchResponse response = streamManager.getResponse();
assertEquals(200, response.getStatusCode());
assertEquals("OK", response.getStatusMessage());
final Iterator<ODataBatchResponseItem> iter = response.getBody();
// retrieve the first item (ODataRetrieve)
ODataBatchResponseItem item = iter.next();
assertTrue(item instanceof ODataRetrieveResponseItem);
ODataRetrieveResponseItem retitem = (ODataRetrieveResponseItem) item;
ODataResponse res = retitem.next();
assertEquals(404, res.getStatusCode());
assertEquals("Not Found", res.getStatusMessage());
if (continueOnError) {
item = iter.next();
assertTrue(item instanceof ODataRetrieveResponseItem);
retitem = (ODataRetrieveResponseItem) item;
res = retitem.next();
assertTrue(res instanceof ODataEntityResponseImpl);
assertEquals(200, res.getStatusCode());
assertEquals("OK", res.getStatusMessage());
}
}
@Test
@ -176,13 +254,13 @@ public class BatchTestITCase extends AbstractTestITCase {
"OrderDetails",
client.getURIBuilder(testStaticServiceRootURL).appendEntitySetSegment("OrderDetails").
appendKeySegment(new HashMap<String, Object>() {
private static final long serialVersionUID = 3109256773218160485L;
private static final long serialVersionUID = 3109256773218160485L;
{
put("OrderID", 7);
put("ProductID", 5);
}
}).build()));
{
put("OrderID", 7);
put("ProductID", 5);
}
}).build()));
final ODataEntityUpdateRequest<ODataEntity> updateReq = client.getCUDRequestFactory().getEntityUpdateRequest(
URI.create("$" + createRequestRef), UpdateType.PATCH, customerChanges);