Repository: olingo-odata2 Updated Branches: refs/heads/master 474d8f3e7 -> 0bce4ab78
[OLINGO-1093] Support Binary Content in Batch Contributed by Vasanth, Ramya in OLINGO-1093 Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/0bce4ab7 Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/0bce4ab7 Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/0bce4ab7 Branch: refs/heads/master Commit: 0bce4ab78054d83408782e2855d17b2dfadbc6c9 Parents: 474d8f3 Author: Christian Amend <[email protected]> Authored: Tue Apr 25 10:14:34 2017 +0200 Committer: Christian Amend <[email protected]> Committed: Tue Apr 25 10:14:34 2017 +0200 ---------------------------------------------------------------------- .../api/client/batch/BatchChangeSetPart.java | 12 +- .../core/batch/BatchChangeSetPartImpl.java | 18 +- .../olingo/odata2/core/batch/BatchHelper.java | 71 ++++++- .../odata2/core/batch/BatchResponseWriter.java | 5 +- .../odata2/core/batch/v2/BatchLineReader.java | 13 +- .../odata2/core/batch/v2/BatchParserCommon.java | 12 +- .../batch/v2/BatchRequestTransformator.java | 2 +- .../odata2/core/batch/BatchRequestTest.java | 48 +++++ .../odata2/core/batch/BatchResponseTest.java | 77 +++++++ .../core/batch/v2/BatchLineReaderTest.java | 53 +++++ .../src/test/resources/Employee_1.png | Bin 0 -> 8429 bytes .../apache/olingo/odata2/fit/ref/BatchTest.java | 204 +++++++++++++++++++ .../odata-fit/src/test/resources/Employee_1.png | Bin 0 -> 8429 bytes .../src/test/resources/simpleGet.batch | 8 + .../src/test/resources/simpleGet1.batch | 8 + .../ref/processor/ScenarioDataSource.java | 2 + .../olingo/odata2/ref/processor/Util.java | 26 +++ 17 files changed, 536 insertions(+), 23 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java index ecabf9b..93f86d5 100644 --- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java +++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchChangeSetPart.java @@ -30,7 +30,7 @@ public abstract class BatchChangeSetPart { public abstract Map<String, String> getHeaders(); - public abstract String getBody(); + public abstract Object getBody(); public abstract byte[] getBodyAsBytes(); @@ -55,6 +55,14 @@ public abstract class BatchChangeSetPart { public static BatchChangeSetPartBuilder body(final String body) { return newBuilder().body(body); } + + /** + * @param body a change request body + * @return a new builder object + */ + public static BatchChangeSetPartBuilder body(final byte[] body) { + return newBuilder().body(body); + } /** * @param uri should not be null @@ -100,6 +108,8 @@ public abstract class BatchChangeSetPart { public abstract BatchChangeSetPartBuilder headers(Map<String, String> headers); public abstract BatchChangeSetPartBuilder body(String body); + + public abstract BatchChangeSetPartBuilder body(byte[] body); public abstract BatchChangeSetPartBuilder uri(String uri); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java index b1d83ab..2b5f44f 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchChangeSetPartImpl.java @@ -28,7 +28,7 @@ import java.util.Map; public class BatchChangeSetPartImpl extends BatchChangeSetPart { private String method; private Map<String, String> headers = new HashMap<String, String>(); - private String body; + private Object body; private String uri; public String contentId; private static final String CHANGE_METHODS = "(PUT|POST|DELETE|MERGE|PATCH)"; @@ -40,7 +40,7 @@ public class BatchChangeSetPartImpl extends BatchChangeSetPart { @Override public String getBody() { - return body; + return body.toString(); } @Override @@ -49,7 +49,11 @@ public class BatchChangeSetPartImpl extends BatchChangeSetPart { return new byte[0]; } Charset charset = getCharset(); - return body.getBytes(charset); + if (body instanceof byte[]) { + return (byte[]) body; //NOSONAR + } else { + return body.toString().getBytes(charset); + } } private Charset getCharset() { @@ -74,7 +78,7 @@ public class BatchChangeSetPartImpl extends BatchChangeSetPart { public class BatchChangeSetRequestBuilderImpl extends BatchChangeSetPartBuilder { private String method; private Map<String, String> headers = new HashMap<String, String>(); - private String body; + private Object body; private String uri; private String contentId; @@ -102,6 +106,12 @@ public class BatchChangeSetPartImpl extends BatchChangeSetPart { this.body = body; return this; } + + @Override + public BatchChangeSetPartBuilder body(byte[] body) { + this.body = body; + return this; + } @Override public BatchChangeSetPartBuilder uri(final String uri) { http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java index 835f47b..4889a42 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchHelper.java @@ -40,13 +40,15 @@ import java.util.UUID; public class BatchHelper { public static final String BINARY_ENCODING = "binary"; - public static final String DEFAULT_ENCODING = "utf-8"; + public static final String UTF8_ENCODING = "UTF-8"; + public static final String ISO_ENCODING = "ISO-8859-1"; + public static String DEFAULT_ENCODING = "UTF-8"; public static final String HTTP_CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding"; public static final String HTTP_CONTENT_ID = "Content-Id"; public static final String MIME_HEADER_CONTENT_ID = "MimeHeader-ContentId"; public static final String REQUEST_HEADER_CONTENT_ID = "RequestHeader-ContentId"; - public static final Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_ENCODING); + public static Charset DEFAULT_CHARSET = Charset.forName(DEFAULT_ENCODING); protected static String generateBoundary(final String value) { return value + "_" + UUID.randomUUID().toString(); @@ -72,11 +74,21 @@ public class BatchHelper { return getCharset(contentType); } - public static Charset extractCharset(String contentType) { - if(contentType == null) { - return DEFAULT_CHARSET; + public static Charset extractCharset(ContentType contentType) { + if (contentType != null) { + final String charsetValue = contentType.getParameters().get(ContentType.PARAMETER_CHARSET); + if (charsetValue == null) { + if (contentType.isCompatible(ContentType.APPLICATION_JSON) || contentType.getSubtype().contains("xml")) { + setDefaultValues(UTF8_ENCODING); + return Charset.forName(UTF8_ENCODING); + } + } else { + setDefaultValues(charsetValue); + return Charset.forName(charsetValue); + } } - return getCharset(contentType); + setDefaultValues(ISO_ENCODING); + return Charset.forName(ISO_ENCODING); } private static Charset getCharset(String contentType) { @@ -84,10 +96,23 @@ public class BatchHelper { if(ct != null) { String charsetString = ct.getParameters().get(ContentType.PARAMETER_CHARSET); if (charsetString != null && Charset.isSupported(charsetString)) { + setDefaultValues(charsetString); return Charset.forName(charsetString); + } else { + if (ct.isCompatible(ContentType.APPLICATION_JSON) || ct.getSubtype().contains("xml")) { + setDefaultValues(UTF8_ENCODING); + return Charset.forName(UTF8_ENCODING); + } } } - return DEFAULT_CHARSET; + setDefaultValues(ISO_ENCODING); + return Charset.forName(ISO_ENCODING); + } + + private static void setDefaultValues(String contentType) { + DEFAULT_CHARSET = Charset.forName(contentType); + DEFAULT_ENCODING = contentType; + } /** @@ -151,6 +176,33 @@ public class BatchHelper { public String toString() { return new String(buffer.array(), 0, buffer.position()); } + + /** + * Fetch the calibrated length in case of binary data. + * Since after applying the charset the content length changes. + * If the previously generated length is sent back then the batch response + * body is seen truncated + * @param batchResponseBody + * @return + */ + public int calculateLength(Object batchResponseBody) { + if (batchResponseBody != null) { + if (batchResponseBody instanceof String) { + if (DEFAULT_ENCODING.equalsIgnoreCase(ISO_ENCODING)) { + try { + return ((String) batchResponseBody).getBytes(UTF8_ENCODING).length; + } catch (UnsupportedEncodingException e) { + throw new ODataRuntimeException(e); + } + } else { + return getLength(); + } + } else { + return getLength(); + } + } + return getLength(); + } } /** @@ -202,6 +254,7 @@ public class BatchHelper { return EMPTY_BYTES; } else if(entity instanceof InputStream) { try { + extractCharset(ContentType.parse(response.getHeader("Content-Type"))); ByteArrayOutputStream output = new ByteArrayOutputStream(); ByteBuffer inBuffer = ByteBuffer.allocate(BUFFER_SIZE); ReadableByteChannel ic = Channels.newChannel((InputStream) entity); @@ -215,7 +268,11 @@ public class BatchHelper { } catch (IOException e) { throw new ODataRuntimeException("Error on reading request content"); } + } else if (entity instanceof byte[]) { + setDefaultValues(ISO_ENCODING); + return (byte[]) entity; } else if(entity instanceof String) { + setDefaultValues(UTF8_ENCODING); return ((String) entity).getBytes(DEFAULT_CHARSET); } else { throw new ODataRuntimeException("Error on reading request content for entity type:" + entity.getClass()); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java index 0ef6f0e..9dc986c 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchResponseWriter.java @@ -58,14 +58,17 @@ public class BatchResponseWriter { String boundary = BatchHelper.generateBoundary("batch"); appendResponsePart(batchResponseParts, boundary); final Object batchResponseBody; + int length = 0; if(writeEntityAsInputStream) { batchResponseBody = writer.getContentAsStream(); + length = writer.calculateLength(batchResponseBody); } else { batchResponseBody = writer.getContentAsString(BatchHelper.DEFAULT_CHARSET); + length = writer.calculateLength(batchResponseBody); } return ODataResponse.entity(batchResponseBody).status(HttpStatusCodes.ACCEPTED) .header(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + "; boundary=" + boundary) - .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(writer.getLength())) + .header(HttpHeaders.CONTENT_LENGTH, String.valueOf(length)) .build(); } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReader.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReader.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReader.java index 4075764..11cb947 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReader.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReader.java @@ -33,9 +33,10 @@ public class BatchLineReader { private static final byte LF = '\n'; private static final int EOF = -1; private static final int BUFFER_SIZE = 8192; - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - private static final Charset CS_ISO_8859_1 = Charset.forName("iso-8859-1"); + private static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1"); + private static final String UTF8_CHARSET = "UTF-8"; private static final String CONTENT_TYPE = "content-type"; + private static final String XML_SUBTYPE = "xml"; public static final String BOUNDARY = "boundary"; public static final String DOUBLE_DASH = "--"; public static final String CRLF = "\r\n"; @@ -104,7 +105,11 @@ public class BatchLineReader { if (charsetString != null) { currentCharset = Charset.forName(charsetString); } else { - currentCharset = DEFAULT_CHARSET; + if (ct.isCompatible(ContentType.APPLICATION_JSON) || ct.getSubtype().contains(XML_SUBTYPE)) { + currentCharset = Charset.forName(UTF8_CHARSET); + } else { + currentCharset = DEFAULT_CHARSET; + } } // boundary String boundary = ct.getParameters().get(BOUNDARY); @@ -182,7 +187,7 @@ public class BatchLineReader { if(readState.isReadBody()) { currentLine = new String(buffer.array(), 0, buffer.position(), getCurrentCharset()); } else { - currentLine = new String(buffer.array(), 0, buffer.position(), CS_ISO_8859_1); + currentLine = new String(buffer.array(), 0, buffer.position(), DEFAULT_CHARSET); } updateCurrentCharset(currentLine); return currentLine; http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java index 76b44ce..7f5cc12 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java @@ -36,6 +36,7 @@ import org.apache.olingo.odata2.api.commons.HttpContentType; import org.apache.olingo.odata2.api.commons.HttpHeaders; import org.apache.olingo.odata2.core.batch.AcceptParser; import org.apache.olingo.odata2.core.batch.BatchHelper; +import org.apache.olingo.odata2.core.commons.ContentType; import org.apache.olingo.odata2.core.commons.Decoder; public class BatchParserCommon { @@ -97,21 +98,22 @@ public class BatchParserCommon { * Otherwise the whole content is written into the InputStream. * * @param contentType content type value - * @param body content which is written into the InputStream + * @param operation which is written into the InputStream * @param contentLength if it is a positive value the content is trimmed to according length. * Otherwise the whole content is written into the InputStream. * @return Content of BatchQueryOperation as InputStream in according charset and length * @throws BatchException if something goes wrong */ - public static InputStream convertToInputStream(final String contentType, final List<Line> body, + public static InputStream convertToInputStream(final String contentType, final BatchQueryOperation operation, final int contentLength) throws BatchException { - Charset charset = BatchHelper.extractCharset(contentType); + Charset charset = BatchHelper.extractCharset(ContentType.parse( + contentType)); final String message; if(contentLength <= -1) { - message = lineListToString(body); + message = lineListToString(operation.getBody()); } else { - message = trimLineListToLength(body, contentLength); + message = trimLineListToLength(operation.getBody(), contentLength); } return new ByteArrayInputStream(message.getBytes(charset)); } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java index bf3cc82..5a2b85b 100644 --- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java +++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java @@ -128,7 +128,7 @@ public class BatchRequestTransformator implements BatchTransformator { } else { int contentLength = BatchTransformatorCommon.getContentLength(headers); String contentType = headers.getHeader(HttpHeaders.CONTENT_TYPE); - return BatchParserCommon.convertToInputStream(contentType, operation.getBody(), contentLength); + return BatchParserCommon.convertToInputStream(contentType, operation, contentLength); } } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java index 77e344e..0908149 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java @@ -21,6 +21,7 @@ package org.apache.olingo.odata2.core.batch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertArrayEquals; import java.io.IOException; import java.io.InputStream; @@ -321,5 +322,52 @@ public class BatchRequestTest { public void testBatchChangeSetPartWithInvalidMethod() throws BatchException, IOException { BatchChangeSetPart.method(GET).uri("Employees('2')").build(); + } + + @Test + public void testBatchChangeSetRawBytes() throws IOException, BatchException { + List<BatchPart> batch = new ArrayList<BatchPart>(); + Map<String, String> headers = new HashMap<String, String>(); + headers.put("content-type", "application/octect-stream"); + byte[] data = getRawBytes(); + BatchChangeSetPart request = BatchChangeSetPart.method(PUT) + .uri("Employees('2')/$value") + .body(data) + .headers(headers) + .build(); + BatchChangeSet changeSet = BatchChangeSet.newBuilder().build(); + changeSet.add(request); + batch.add(changeSet); + + BatchRequestWriter writer = new BatchRequestWriter(); + InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY); + assertNotNull(batchRequest); + + StringHelper.Stream batchRequestStream = StringHelper.toStream(batchRequest); + String requestBody = batchRequestStream.asString("ISO-8859-1"); + checkMimeHeaders(requestBody); + checkHeaders(headers, requestBody); + + assertTrue(requestBody.contains("--batch_")); + assertTrue(requestBody.contains("--changeset_")); + assertTrue(requestBody.contains("PUT Employees('2')/$value HTTP/1.1")); + + String contentType = "multipart/mixed; boundary=" + BOUNDARY; + BatchParser parser = new BatchParser(contentType, parseProperties, true); + List<BatchRequestPart> parseResult = parser.parseBatchRequest(batchRequestStream.asStream()); + assertEquals(1, parseResult.size()); + InputStream in = parseResult.get(0).getRequests().get(0).getBody(); + StringHelper.Stream parsedReqStream = StringHelper.toStream(in); + String parsedReqData = parsedReqStream.asString("ISO-8859-1"); + assertArrayEquals(data, parsedReqData.getBytes("ISO-8859-1")); + } + + private byte[] getRawBytes() { + byte[] data = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + // binary content, not a valid UTF-8 representation of a string + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + data[i - Byte.MIN_VALUE] = (byte) i; + } + return data; } } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java index 4076a25..91411f0 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchResponseTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertArrayEquals; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -32,8 +33,11 @@ import org.apache.olingo.odata2.api.batch.BatchException; import org.apache.olingo.odata2.api.batch.BatchResponsePart; import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse; import org.apache.olingo.odata2.api.commons.HttpStatusCodes; +import org.apache.olingo.odata2.api.exception.ODataException; import org.apache.olingo.odata2.api.processor.ODataResponse; +import org.apache.olingo.odata2.core.batch.v2.BatchLineReader; import org.apache.olingo.odata2.core.batch.v2.BatchParser; +import org.apache.olingo.odata2.core.batch.v2.Line; import org.apache.olingo.odata2.testutil.helper.StringHelper; import org.junit.Test; @@ -142,4 +146,77 @@ public class BatchResponseTest { assertEquals(2, result.size()); assertEquals("Failing content:\n" + content.asString(), 19, content.linesCount()); } + + @Test + public void testBatchResponseRawBytes() throws BatchException, IOException { + List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>(); + byte[] data = getRawBytes(); + ODataResponse response = ODataResponse.entity(data) + .status(HttpStatusCodes.OK) + .contentHeader("application/octect-stream;charset=iso-8859-1") + .build(); + List<ODataResponse> responses = new ArrayList<ODataResponse>(1); + responses.add(response); + parts.add(BatchResponsePart.responses(responses).changeSet(false).build()); + + BatchResponseWriter writer = new BatchResponseWriter(); + ODataResponse batchResponse = writer.writeResponse(parts); + + assertEquals(202, batchResponse.getStatus().getStatusCode()); + assertNotNull(batchResponse.getEntity()); + String body = (String) batchResponse.getEntity(); + + assertTrue(body.contains("--batch")); + assertTrue(body.contains("HTTP/1.1 200 OK")); + assertTrue(body.contains("Content-Type: application/http")); + assertTrue(body.contains("Content-Transfer-Encoding: binary")); + + String contentHeader = batchResponse.getContentHeader(); + BatchParser parser = new BatchParser(contentHeader, true); + List<BatchSingleResponse> result = parser.parseBatchResponse(new ByteArrayInputStream(body.getBytes("iso-8859-1"))); + assertEquals(1, result.size()); + assertArrayEquals(data, result.get(0).getBody().getBytes("ISO-8859-1")); + } + + @Test + public void testBatchResponseRawBytesAsStream() throws IOException, ODataException { + List<BatchResponsePart> parts = new ArrayList<BatchResponsePart>(); + byte[] data = getRawBytes(); + ODataResponse response = ODataResponse.entity(data) + .status(HttpStatusCodes.OK) + .contentHeader("application/octect-stream;charset=iso-8859-1") + .build(); + List<ODataResponse> responses = new ArrayList<ODataResponse>(1); + responses.add(response); + parts.add(BatchResponsePart.responses(responses).changeSet(false).build()); + + BatchResponseWriter writer = new BatchResponseWriter(true); + ODataResponse batchResponse = writer.writeResponse(parts); + + assertEquals(202, batchResponse.getStatus().getStatusCode()); + assertNotNull(batchResponse.getEntity()); + BatchLineReader reader = + new BatchLineReader(batchResponse.getEntityAsStream()); + List<Line> lines = reader.toLineList(); + reader.close(); + StringBuilder builder = new StringBuilder(); + for (Line line : lines) { + builder.append(line); + } + String contentHeader = batchResponse.getContentHeader(); + BatchParser parser = new BatchParser(contentHeader, true); + List<BatchSingleResponse> result = parser.parseBatchResponse( + new ByteArrayInputStream(builder.toString().getBytes("iso-8859-1"))); + assertEquals(1, result.size()); + assertArrayEquals(data, result.get(0).getBody().getBytes("ISO-8859-1")); + } + + private byte[] getRawBytes() { + byte[] data = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + // binary content, not a valid UTF-8 representation of a string + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + data[i - Byte.MIN_VALUE] = (byte) i; + } + return data; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReaderTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReaderTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReaderTest.java index 351c693..f97b013 100644 --- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReaderTest.java +++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/v2/BatchLineReaderTest.java @@ -19,10 +19,16 @@ package org.apache.olingo.odata2.core.batch.v2; import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; import java.util.List; import org.apache.olingo.odata2.core.batch.v2.BatchLineReader; @@ -270,4 +276,51 @@ public class BatchLineReaderTest { throws UnsupportedEncodingException { return new BatchLineReader(new ByteArrayInputStream(inputString.getBytes("UTF-8")), bufferSize); } + + @Test + public void rawBytes() throws Exception { + byte[] content = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + // binary content, not a valid UTF-8 representation of a string + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + content[i - Byte.MIN_VALUE] = (byte) i; + } + BatchLineReader reader = new BatchLineReader(new ByteArrayInputStream(content)); + final String contentString = reader.readLine() // initial part up to '\n' + + reader.readLine() // second part from '\n' to '\r' + + reader.readLine(); // the rest + assertArrayEquals(content, contentString.getBytes(Charset.forName("ISO-8859-1"))); + assertNull(reader.readLine()); + reader.close(); + } + + @Test + public void imageTest() throws Exception { + byte[] data = getImageData("/Employee_1.png"); + BatchLineReader reader = new BatchLineReader(new ByteArrayInputStream(data)); + final List<Line> contentString = reader.toLineList(); + String finalContent = ""; + for (Line content : contentString) { + finalContent += content.toString(); + } + + assertArrayEquals(data, finalContent.getBytes(Charset.forName("ISO-8859-1"))); + reader.close(); + } + + private byte[] getImageData(String imageUrl) throws IOException { + byte[] data = null; + try { + InputStream in = this.getClass().getResourceAsStream(imageUrl); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + int b = 0; + while ((b = in.read()) != -1) { + stream.write(b); + } + + data = stream.toByteArray(); + } catch (IOException e) { + throw e; + } + return data; + } } http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-core/src/test/resources/Employee_1.png ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-core/src/test/resources/Employee_1.png b/odata2-lib/odata-core/src/test/resources/Employee_1.png new file mode 100644 index 0000000..f817682 Binary files /dev/null and b/odata2-lib/odata-core/src/test/resources/Employee_1.png differ http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/BatchTest.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/BatchTest.java b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/BatchTest.java index 80a2be6..d11e38e 100644 --- a/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/BatchTest.java +++ b/odata2-lib/odata-fit/src/test/java/org/apache/olingo/odata2/fit/ref/BatchTest.java @@ -22,10 +22,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertArrayEquals; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import junit.framework.Assert; @@ -33,7 +41,16 @@ import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; +import org.apache.olingo.odata2.api.client.batch.BatchChangeSet; +import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart; +import org.apache.olingo.odata2.api.client.batch.BatchPart; +import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse; +import org.apache.olingo.odata2.api.commons.HttpHeaders; +import org.apache.olingo.odata2.api.ep.EntityProvider; +import org.apache.olingo.odata2.core.batch.BatchRequestWriter; +import org.apache.olingo.odata2.ref.processor.Util; import org.apache.olingo.odata2.testutil.helper.StringHelper; import org.apache.olingo.odata2.testutil.server.ServletType; import org.junit.Test; @@ -44,6 +61,10 @@ import org.junit.Test; */ public class BatchTest extends AbstractRefTest { + private static final String PUT = "PUT"; + private static final String POST = "POST"; + private static final String BOUNDARY = "batch_123"; + public BatchTest(final ServletType servletType) { super(servletType); } @@ -181,4 +202,187 @@ public class BatchTest extends AbstractRefTest { assertEquals(202, response.getStatusLine().getStatusCode()); return response; } + + /** + * @param method + * @param data + * @param contentType + * @return + */ + private InputStream createBatchRequest(String method, byte[] data, String contentType) { + List<BatchPart> batch = new ArrayList<BatchPart>(); + Map<String, String> headers = new HashMap<String, String>(); + + BatchChangeSetPart request = null; + if (method.equalsIgnoreCase(PUT)) { + headers.put("content-type", contentType); + request = BatchChangeSetPart.method(PUT) + .uri("Employees('2')/$value") + .body(data) + .headers(headers) + .contentId("1") + .build(); + } else if (method.equalsIgnoreCase(POST)) { + headers.put("content-type", contentType); + request = BatchChangeSetPart.method(POST) + .uri("Employees") + .body(data) + .headers(headers) + .contentId("1") + .build(); + } + + BatchChangeSet changeSet = BatchChangeSet.newBuilder().build(); + changeSet.add(request); + batch.add(changeSet); + + BatchRequestWriter writer = new BatchRequestWriter(); + InputStream batchRequest = writer.writeBatchRequest(batch, BOUNDARY); + + return batchRequest; + } + + @Test + public void testBatchWithChangesetWithRawBytesInPutOperation() throws Exception { + InputStream requestPayload = createBatchRequestWithRawBytes(PUT); + final HttpPost put = new HttpPost(URI.create(getEndpoint().toString() + "$batch")); + put.setHeader("Content-Type", "multipart/mixed;boundary=" + BOUNDARY); + HttpEntity entity = new InputStreamEntity(requestPayload, -1); + put.setEntity(entity); + HttpResponse response = getHttpClient().execute(put); + byte[] actualData = Util.getInstance().getBinaryContent(); + byte[] expectedData = rawBytes(); + // Comparing data stored in the data source and the data sent in the request + assertArrayEquals(actualData, expectedData); + + assertNotNull(response); + assertEquals(202, response.getStatusLine().getStatusCode()); + String responseBody = StringHelper.inputStreamToStringCRLFLineBreaks(response.getEntity().getContent()); + assertTrue(responseBody.contains("204 No Content")); + + HttpResponse resp = execute("/simpleGet.batch", BOUNDARY); + InputStream in = resp.getEntity().getContent(); + StringHelper.Stream batchRequestStream = StringHelper.toStream(in); + String requestBody = batchRequestStream.asString(); + + String contentType = resp.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue(); + List<BatchSingleResponse> responses = EntityProvider.parseBatchResponse( + new ByteArrayInputStream(requestBody.getBytes("iso-8859-1")), contentType); + for (BatchSingleResponse batchResp : responses) { + assertEquals("200", batchResp.getStatusCode()); + assertEquals("OK", batchResp.getStatusInfo()); + assertArrayEquals(batchResp.getBody().getBytes("iso-8859-1"), actualData); + } + } + + @Test + public void testBatchWithChangesetWithRawBytesInPOSTOperation() throws Exception { + InputStream requestPayload = createBatchRequestWithRawBytes(POST); + final HttpPost put = new HttpPost(URI.create(getEndpoint().toString() + "$batch")); + put.setHeader("Content-Type", "multipart/mixed;boundary=" + BOUNDARY); + HttpEntity entity = new InputStreamEntity(requestPayload, -1); + put.setEntity(entity); + HttpResponse response = getHttpClient().execute(put); + byte[] actualData = Util.getInstance().getBinaryContent(); + byte[] expectedData = rawBytes(); + // Comparing data stored in the data source and the data sent in the request + assertArrayEquals(actualData, expectedData); + + assertNotNull(response); + assertEquals(202, response.getStatusLine().getStatusCode()); + String responseBody = StringHelper.inputStreamToStringCRLFLineBreaks(response.getEntity().getContent()); + assertTrue(responseBody.contains("201 Created")); + + HttpResponse resp = execute("/simpleGet1.batch", BOUNDARY); + InputStream in = resp.getEntity().getContent(); + StringHelper.Stream batchRequestStream = StringHelper.toStream(in); + String requestBody = batchRequestStream.asString(); + + String contentType = resp.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue(); + List<BatchSingleResponse> responses = EntityProvider.parseBatchResponse( + new ByteArrayInputStream(requestBody.getBytes("iso-8859-1")), contentType); + for (BatchSingleResponse batchResp : responses) { + assertEquals("200", batchResp.getStatusCode()); + assertEquals("OK", batchResp.getStatusInfo()); + assertArrayEquals(batchResp.getBody().getBytes("iso-8859-1"), expectedData); + } + } + + @Test + public void testBatchWithChangesetWithImageObjectInPutOperation() throws Exception { + InputStream requestPayload = createBatchRequestWithImage("/Employee_1.png", PUT); + + final HttpPost put = new HttpPost(URI.create(getEndpoint().toString() + "$batch")); + put.setHeader("Content-Type", "multipart/mixed;boundary=" + BOUNDARY); + HttpEntity entity = new InputStreamEntity(requestPayload, -1); + put.setEntity(entity); + HttpResponse response = getHttpClient().execute(put); + byte[] actualData = Util.getInstance().getBinaryContent(); + byte[] expectedData = getImageData("/Employee_1.png"); + // Comparing data stored in the data source and the data sent in the request + assertArrayEquals(actualData, expectedData); + + assertNotNull(response); + assertEquals(202, response.getStatusLine().getStatusCode()); + String responseBody = StringHelper.inputStreamToStringCRLFLineBreaks(response.getEntity().getContent()); + assertTrue(responseBody.contains("204 No Content")); + + HttpResponse resp = execute("/simpleGet.batch", BOUNDARY); + InputStream in = resp.getEntity().getContent(); + StringHelper.Stream batchRequestStream = StringHelper.toStream(in); + String requestBody = batchRequestStream.asString(); + + String contentType = resp.getFirstHeader(HttpHeaders.CONTENT_TYPE).getValue(); + List<BatchSingleResponse> responses = EntityProvider.parseBatchResponse( + new ByteArrayInputStream(requestBody.getBytes("iso-8859-1")), contentType); + for (BatchSingleResponse batchResp : responses) { + assertEquals("200", batchResp.getStatusCode()); + assertEquals("OK", batchResp.getStatusInfo()); + assertArrayEquals(batchResp.getBody().getBytes("iso-8859-1"), actualData); + } + } + + private InputStream createBatchRequestWithImage(String imageUrl, String method) throws IOException { + byte[] data = getImageData(imageUrl); + return createBatchRequest(method, data, "image/jpeg"); + } + + private InputStream createBatchRequestWithRawBytes(String method) { + byte[] data = rawBytes(); + return createBatchRequest(method, data, "application/octect-stream"); + } + + /** + * @return + */ + private byte[] rawBytes() { + byte[] data = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; + // binary content, not a valid UTF-8 representation of a string + for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) { + data[i - Byte.MIN_VALUE] = (byte) i; + } + return data; + } + + /** + * @param imageUrl + * @return + * @throws IOException + */ + private byte[] getImageData(String imageUrl) throws IOException { + byte[] data = null; + try { + InputStream in = this.getClass().getResourceAsStream(imageUrl); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + int b = 0; + while ((b = in.read()) != -1) { + stream.write(b); + } + + data = stream.toByteArray(); + } catch (IOException e) { + throw new IOException(e); + } + return data; + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-fit/src/test/resources/Employee_1.png ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-fit/src/test/resources/Employee_1.png b/odata2-lib/odata-fit/src/test/resources/Employee_1.png new file mode 100644 index 0000000..f817682 Binary files /dev/null and b/odata2-lib/odata-fit/src/test/resources/Employee_1.png differ http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-fit/src/test/resources/simpleGet.batch ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-fit/src/test/resources/simpleGet.batch b/odata2-lib/odata-fit/src/test/resources/simpleGet.batch new file mode 100644 index 0000000..4ae2fce --- /dev/null +++ b/odata2-lib/odata-fit/src/test/resources/simpleGet.batch @@ -0,0 +1,8 @@ +--batch_123 +Content-Type: application/http +Content-Transfer-Encoding:binary + +GET Employees('2')/$value HTTP/1.1 + + +--batch_123-- \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-fit/src/test/resources/simpleGet1.batch ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-fit/src/test/resources/simpleGet1.batch b/odata2-lib/odata-fit/src/test/resources/simpleGet1.batch new file mode 100644 index 0000000..21febba --- /dev/null +++ b/odata2-lib/odata-fit/src/test/resources/simpleGet1.batch @@ -0,0 +1,8 @@ +--batch_123 +Content-Type: application/http +Content-Transfer-Encoding:binary + +GET Employees('7')/$value HTTP/1.1 + + +--batch_123-- \ No newline at end of file http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java index d2f55aa..ff8813a 100644 --- a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java +++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/ScenarioDataSource.java @@ -366,6 +366,8 @@ public class ScenarioDataSource { final Employee employee = (Employee) mediaLinkEntryData; employee.setImage(binaryData.getData()); employee.setImageType(binaryData.getMimeType()); + //Storing the binary data to be used for comparison in the tests + Util.getInstance().setBinaryContent(employee.getImage()); } else if (ENTITYSET_2_1.equals(entitySet.getName())) { final Photo photo = (Photo) mediaLinkEntryData; photo.setImage(binaryData.getData()); http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/0bce4ab7/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/Util.java ---------------------------------------------------------------------- diff --git a/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/Util.java b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/Util.java new file mode 100644 index 0000000..9bbdb2e --- /dev/null +++ b/odata2-lib/odata-ref/src/main/java/org/apache/olingo/odata2/ref/processor/Util.java @@ -0,0 +1,26 @@ +package org.apache.olingo.odata2.ref.processor; + + +public class Util { + +private static final Util instance = new Util(); + + private byte[] binaryContent = null; + + public static Util getInstance() { + return instance; + } + /** + * @return the binaryContent + */ + public byte[] getBinaryContent() { + return binaryContent; + } + + /** + * @param binaryContent the binaryContent to set + */ + public void setBinaryContent(byte[] binaryContent) { + this.binaryContent = binaryContent; + } +} \ No newline at end of file
