[OLINGO-175, OLINGO-205, OLINGO-246] provided batch error V4 + continueOnError support
This commit is contained in:
parent
e4a4f9e6eb
commit
6dfdee2ef2
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -161,7 +161,7 @@ public class ODataPreferences {
|
|||
* @return preference.
|
||||
*/
|
||||
public String continueOnError() {
|
||||
return PreferenceNames.callback.isSupportedBy(serviceVersion).toString();
|
||||
return PreferenceNames.continueOnError.isSupportedBy(serviceVersion).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 }
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue