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 93f86d5bc..5c7690803 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
@@ -18,10 +18,10 @@
******************************************************************************/
package org.apache.olingo.odata2.api.client.batch;
-import java.util.Map;
-
import org.apache.olingo.odata2.api.rt.RuntimeDelegate;
+import java.util.Map;
+
/**
* A BatchChangeSetPart
*
BatchChangeSetPart represents a change request within a Change Set
@@ -30,9 +30,7 @@ public abstract class BatchChangeSetPart {
public abstract Map getHeaders();
- public abstract Object getBody();
-
- public abstract byte[] getBodyAsBytes();
+ public abstract BatchInputResource getBatchInputResource();
public abstract String getUri();
@@ -64,6 +62,14 @@ public static BatchChangeSetPartBuilder body(final byte[] body) {
return newBuilder().body(body);
}
+ /**
+ * @param body a change request body
+ * @return a new builder object
+ */
+ public static BatchChangeSetPartBuilder body(final BatchInputResource body) {
+ return newBuilder().body(body);
+ }
+
/**
* @param uri should not be null
* @return a new builder object
@@ -111,6 +117,8 @@ private static BatchChangeSetPartBuilder newInstance() {
public abstract BatchChangeSetPartBuilder body(byte[] body);
+ public abstract BatchChangeSetPartBuilder body(BatchInputResource inputStream);
+
public abstract BatchChangeSetPartBuilder uri(String uri);
public abstract BatchChangeSetPartBuilder method(String method);
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchInputResource.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchInputResource.java
new file mode 100644
index 000000000..04bdab495
--- /dev/null
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/client/batch/BatchInputResource.java
@@ -0,0 +1,31 @@
+package org.apache.olingo.odata2.api.client.batch;
+
+import java.io.*;
+
+public class BatchInputResource implements Closeable {
+
+ private final InputStream inputStream;
+ private final int size;
+
+ public BatchInputResource(InputStream inputStream, int size) {
+ this.inputStream = inputStream;
+ this.size = size;
+ }
+
+ public InputStream getInputStream() {
+ return inputStream;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+
+}
+
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 be88a0bd1..c0ef0d104 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
@@ -19,7 +19,9 @@
package org.apache.olingo.odata2.core.batch;
import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+import org.apache.olingo.odata2.api.client.batch.BatchInputResource;
+import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
@@ -28,7 +30,7 @@
public class BatchChangeSetPartImpl extends BatchChangeSetPart {
private String method;
private Map headers = new HashMap();
- private Object body;
+ private BatchInputResource batchInputResource;
private String uri;
private String cntId;
private static final String CHANGE_METHODS = "(PUT|POST|DELETE|MERGE|PATCH)";
@@ -39,21 +41,8 @@ public Map getHeaders() {
}
@Override
- public String getBody() {
- return body.toString();
- }
-
- @Override
- public byte[] getBodyAsBytes() {
- if(body == null) {
- return new byte[0];
- }
- Charset charset = getCharset();
- if (body instanceof byte[]) {
- return (byte[]) body; //NOSONAR
- } else {
- return body.toString().getBytes(charset);
- }
+ public BatchInputResource getBatchInputResource() {
+ return batchInputResource;
}
private Charset getCharset() {
@@ -78,9 +67,10 @@ public String getContentId() {
public class BatchChangeSetRequestBuilderImpl extends BatchChangeSetPartBuilder {
private String method;
private Map headers = new HashMap();
- private Object body;
+ private BatchInputResource batchInputResource;
private String uri;
private String contentId;
+ private String stringBody;
@Override
public BatchChangeSetPart build() {
@@ -89,7 +79,8 @@ public BatchChangeSetPart build() {
}
BatchChangeSetPartImpl.this.method = method;
BatchChangeSetPartImpl.this.headers = headers;
- BatchChangeSetPartImpl.this.body = body;
+ BatchChangeSetPartImpl.this.batchInputResource = batchInputResource != null ?
+ batchInputResource : stringBodyToBatchInputResource();
BatchChangeSetPartImpl.this.uri = uri;
BatchChangeSetPartImpl.this.cntId = contentId;
return BatchChangeSetPartImpl.this;
@@ -103,13 +94,24 @@ public BatchChangeSetPartBuilder headers(final Map headers) {
@Override
public BatchChangeSetPartBuilder body(final String body) {
- this.body = body;
+ this.stringBody = body;
return this;
}
+
+ private BatchInputResource stringBodyToBatchInputResource() {
+ byte[] bytes = stringBody.getBytes(getCharset());
+ return new BatchInputResource(new ByteArrayInputStream(bytes), bytes.length);
+ }
@Override
public BatchChangeSetPartBuilder body(byte[] body) {
- this.body = body;
+ this.batchInputResource = new BatchInputResource(new ByteArrayInputStream(body), body.length);
+ return this;
+ }
+
+ @Override
+ public BatchChangeSetPartBuilder body(BatchInputResource batchInputStream) {
+ this.batchInputResource = batchInputStream;
return this;
}
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 3d2b78f1e..b67921d84 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
@@ -19,16 +19,13 @@
package org.apache.olingo.odata2.core.batch;
import org.apache.olingo.odata2.api.client.batch.BatchChangeSetPart;
+import org.apache.olingo.odata2.api.client.batch.BatchInputResource;
import org.apache.olingo.odata2.api.commons.HttpHeaders;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import org.apache.olingo.odata2.core.commons.ContentType;
import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
+import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
@@ -119,29 +116,61 @@ private static void setDefaultValues(String contentType) {
* Builder class to create the body and the header.
*/
static class BodyBuilder {
+
+ private static final String OLINGO_TMPDIR_PROPERTY = "olingo.tmpdir";
+
public static final int DEFAULT_SIZE = 8192;
+ private static final int THRESHOLD = DEFAULT_SIZE * 8;
private final Charset CHARSET_ISO_8859_1 = Charset.forName("iso-8859-1");
+
private ByteBuffer buffer = ByteBuffer.allocate(DEFAULT_SIZE);
- private boolean isClosed = false;
+ private File fileBuffer = null;
- public byte[] getContent() {
- isClosed = true;
- byte[] tmp = new byte[buffer.position()];
- buffer.flip();
- buffer.get(tmp, 0, buffer.limit());
- return tmp;
- }
+ private boolean isClosed = false;
public InputStream getContentAsStream() {
- return new ByteArrayInputStream(getContent());
+ try {
+ return fileBuffer != null ?
+ new DeleteOnCloseFileInputStream(fileBuffer) : new ByteArrayInputStream(getBufferContent());
+ } catch (IOException exception) {
+ throw new ODataRuntimeException(exception);
+ }
}
public String getContentAsString(Charset charset) {
- return new String(getContent(), charset);
+ if (fileBuffer != null) {
+ InputStreamReader reader = null;
+ try {
+ reader = new InputStreamReader(new DeleteOnCloseFileInputStream(fileBuffer), charset);
+
+ StringBuilder sb = new StringBuilder();
+ char[] buffer = new char[DEFAULT_SIZE];
+ int bytesCount;
+ while ((bytesCount = reader.read(buffer)) != -1) {
+ sb.append(buffer, 0, bytesCount);
+ }
+ reader.close();
+
+ return sb.toString();
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+
+ return new String(getBufferContent(), charset);
}
public int getLength() {
- return (buffer.limit() > buffer.position() ? buffer.limit(): buffer.position());
+ if (fileBuffer == null) {
+ return (Math.max(buffer.limit(), buffer.position()));
+ }
+ return (int) fileBuffer.length();
}
public BodyBuilder append(String string) {
@@ -151,17 +180,97 @@ public BodyBuilder append(String string) {
}
private void put(byte[] b) {
+ put(new BatchInputResource(new ByteArrayInputStream(b), b.length));
+ }
+
+ private void put(BatchInputResource resource) {
if(isClosed) {
throw new RuntimeException("BodyBuilder is closed.");
}
- if(buffer.remaining() < b.length) {
- buffer.flip();
- int newSize = (buffer.limit() * 2) + b.length;
- ByteBuffer tmp = ByteBuffer.allocate(newSize);
- tmp.put(buffer);
- buffer = tmp;
+
+ if (fileBuffer == null) {
+ if (buffer.remaining() < resource.size()) {
+ int newSize = (buffer.limit() * 2) + resource.size();
+ if (newSize > THRESHOLD) {
+ fileBuffer = createTempFile();
+ writeToFileBuffer(new ByteArrayInputStream(buffer.array(), 0, buffer.position()),
+ resource.getInputStream());
+ buffer = null;
+ } else {
+ buffer.flip();
+ ByteBuffer tmp = ByteBuffer.allocate(newSize);
+ tmp.put(buffer);
+ buffer = tmp;
+ writeToByteBuffer(resource.getInputStream());
+ }
+ } else {
+ writeToByteBuffer(resource.getInputStream());
+ }
+ } else {
+ writeToFileBuffer(resource.getInputStream());
}
- buffer.put(b);
+ }
+
+ File createTempFile() {
+ String tempDir = System.getProperty(OLINGO_TMPDIR_PROPERTY);
+ if (tempDir == null) {
+ tempDir = System.getProperty("java.io.tmpdir");
+ }
+
+ try {
+ return File.createTempFile("odata", "olingo", new File(tempDir));
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ private void writeToByteBuffer(InputStream inputStream) {
+ int bytesCount;
+ try {
+ for (byte[] sbuf = new byte[DEFAULT_SIZE]; (bytesCount = inputStream.read(sbuf)) != -1;) {
+ buffer.put(sbuf, 0, bytesCount);
+ }
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ private void writeToFileBuffer(InputStream... inputStreams) {
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(fileBuffer, true);
+ for (InputStream inputStream : inputStreams) {
+ copyStream(inputStream, fos);
+ }
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+
+ private void copyStream(InputStream inputStream, FileOutputStream outputStream) {
+ try {
+ int bytesRead;
+ for (byte[] sbuf = new byte[DEFAULT_SIZE]; (bytesRead = inputStream.read(sbuf)) != -1; ) {
+ outputStream.write(sbuf, 0, bytesRead);
+ }
+ outputStream.flush();
+ } catch (IOException e) {
+ throw new ODataRuntimeException(e);
+ }
+ }
+
+ private byte[] getBufferContent() {
+ isClosed = true;
+ byte[] tmp = new byte[buffer.position()];
+ buffer.flip();
+ buffer.get(tmp, 0, buffer.limit());
+ return tmp;
}
public BodyBuilder append(int statusCode) {
@@ -169,7 +278,7 @@ public BodyBuilder append(int statusCode) {
}
public BodyBuilder append(Body body) {
- put(body.getContent());
+ put(body.getBatchInputResource());
return this;
}
@@ -211,39 +320,33 @@ public int calculateLength(Object batchResponseBody) {
static class Body {
private static final int BUFFER_SIZE = 8192;
public static final byte[] EMPTY_BYTES = new byte[0];
- private final byte[] content;
+ private final BatchInputResource batchInputResource;
public Body(BatchChangeSetPart response) {
- this.content = getBody(response);
+ this.batchInputResource = response.getBatchInputResource();
}
public Body(ODataResponse response) {
- this.content = getBody(response);
+ byte[] content = getBody(response);
+ this.batchInputResource = new BatchInputResource(
+ new ByteArrayInputStream(content), content.length);
}
public Body() {
- this.content = EMPTY_BYTES;
+ this.batchInputResource = new BatchInputResource(new ByteArrayInputStream(EMPTY_BYTES), 0);
setDefaultValues(ISO_ENCODING);
}
public int getLength() {
- return content.length;
+ return batchInputResource.size();
}
- public byte[] getContent() {
- return content;
+ public BatchInputResource getBatchInputResource() {
+ return batchInputResource;
}
public boolean isEmpty() {
- return content.length == 0;
- }
-
- private byte[] getBody(final BatchChangeSetPart response) {
- if (response == null || response.getBodyAsBytes() == null) {
- return EMPTY_BYTES;
- }
-
- return response.getBodyAsBytes();
+ return batchInputResource.size() == 0;
}
private byte[] getBody(final ODataResponse response) {
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
index c050a1d20..e3def7e3a 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/BatchRequestWriter.java
@@ -18,11 +18,6 @@
******************************************************************************/
package org.apache.olingo.odata2.core.batch;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Map;
-
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;
@@ -30,6 +25,10 @@
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Map;
+
public class BatchRequestWriter {
private static final String REG_EX_BOUNDARY =
"([a-zA-Z0-9_\\-\\.'\\+]{1,70})|\"([a-zA-Z0-9_\\-\\.'\\+\\s\\" +
@@ -41,7 +40,7 @@ public class BatchRequestWriter {
public static final String BOUNDARY_PREAMBLE = "changeset";
public static final String HTTP_1_1 = "HTTP/1.1";
private String batchBoundary;
- private BatchHelper.BodyBuilder writer = new BatchHelper.BodyBuilder();
+ private final BatchHelper.BodyBuilder writer = new BatchHelper.BodyBuilder();
public InputStream writeBatchRequest(final List batchParts, final String boundary) {
if (boundary.matches(REG_EX_BOUNDARY)) {
@@ -60,12 +59,9 @@ public InputStream writeBatchRequest(final List batchParts, final Str
}
writer.append("--").append(boundary).append("--");
- InputStream batchRequestBody;
- batchRequestBody = new ByteArrayInputStream(writer.getContent());
- return batchRequestBody;
+ return writer.getContentAsStream();
}
-
private void appendChangeSet(final BatchChangeSet batchChangeSet) {
String boundary = BatchHelper.generateBoundary(BOUNDARY_PREAMBLE);
while (boundary.equals(batchBoundary) || !boundary.matches(REG_EX_BOUNDARY)) {
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/DeleteOnCloseFileInputStream.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/DeleteOnCloseFileInputStream.java
new file mode 100644
index 000000000..1bdcffc1b
--- /dev/null
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/DeleteOnCloseFileInputStream.java
@@ -0,0 +1,27 @@
+package org.apache.olingo.odata2.core.batch;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+public class DeleteOnCloseFileInputStream extends FileInputStream {
+
+ private File file;
+
+ public DeleteOnCloseFileInputStream(File file) throws FileNotFoundException{
+ super(file);
+ this.file = file;
+ }
+
+ public void close() throws IOException {
+ try {
+ super.close();
+ } finally {
+ if(file != null) {
+ file.delete();
+ file = null;
+ }
+ }
+ }
+}