You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by mi...@apache.org on 2015/07/10 10:39:22 UTC

[4/5] olingo-odata4 git commit: [OLINGO-729] Merge branch 'master' into OLINGO-729_BatchDeSerializer

[OLINGO-729] Merge branch 'master' into OLINGO-729_BatchDeSerializer


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata4/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata4/commit/594ad4ab
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata4/tree/594ad4ab
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata4/diff/594ad4ab

Branch: refs/heads/OLINGO-729_BatchDeSerializer
Commit: 594ad4ab45b6e4d2033361d16931a66a77aa0918
Parents: 3c591da 4baaf0d
Author: Michael Bolz <mi...@sap.com>
Authored: Fri Jul 10 08:19:25 2015 +0200
Committer: Michael Bolz <mi...@sap.com>
Committed: Fri Jul 10 09:24:45 2015 +0200

----------------------------------------------------------------------
 .../apache/olingo/fit/v4/BatchTestITCase.java   |   2 -
 .../org/apache/olingo/server/api/OData.java     |   9 ++
 .../olingo/server/api/ODataHttpHandler.java     |   7 ++
 .../server/api/debug/DebugResponseHelper.java   |  38 ++++++
 .../olingo/server/api/debug/DebugSupport.java   |  49 ++++++++
 .../server/api/debug/DefaultDebugSupport.java   |  52 ++++++++
 .../server/core/ODataHttpHandlerImpl.java       |  33 ++++-
 .../apache/olingo/server/core/ODataImpl.java    |   9 ++
 .../core/debug/DebugResponseHelperImpl.java     |  33 +++++
 .../deserializer/batch/BatchLineReader.java     |  50 +++++---
 .../serializer/BatchResponseSerializer.java     |   3 +-
 .../serializer/json/ODataJsonSerializer.java    |   1 -
 .../deserializer/batch/BatchLineReaderTest.java |  92 +++++++++++---
 .../serializer/BatchResponseSerializerTest.java |  60 ++++++++-
 .../olingo/server/tecsvc/TechnicalServlet.java  |   5 +
 pom.xml                                         |   1 +
 .../myservice/mynamespace/data/Storage.java     |  92 +++++++-------
 .../mynamespace/service/DemoEdmProvider.java    |  43 +++----
 .../service/DemoEntityCollectionProcessor.java  |  47 ++++---
 .../service/DemoEntityProcessor.java            |  64 ++++------
 .../service/DemoPrimitiveProcessor.java         |  42 +++----
 .../java/myservice/mynamespace/util/Util.java   | 126 ++++++++++---------
 .../myservice/mynamespace/web/DemoServlet.java  |  17 ++-
 23 files changed, 600 insertions(+), 275 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/594ad4ab/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java
----------------------------------------------------------------------
diff --cc lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java
index d96a5f5,0000000..150d318
mode 100644,000000..100644
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReader.java
@@@ -1,275 -1,0 +1,295 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements. See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership. The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License. You may obtain a copy of the License at
 + *
 + * http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied. See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.olingo.server.core.deserializer.batch;
 +
 +import org.apache.olingo.commons.api.format.ContentType;
 +import org.apache.olingo.commons.api.http.HttpHeader;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.nio.ByteBuffer;
 +import java.nio.charset.Charset;
 +import java.util.ArrayList;
 +import java.util.List;
 +
++/**
++ * Read batch content and split it into lines.
++ * This class is not thread safe.
++ */
 +public class BatchLineReader {
-   private static final byte CR = '\r';
-   private static final byte LF = '\n';
++  private static final byte CR_BYTE = '\r';
++  private static final byte LF_BYTE = '\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");
-   public static final String BOUNDARY = "boundary";
-   public static final String DOUBLE_DASH = "--";
-   public static final String CRLF = "\r\n";
++  private static final String BOUNDARY = "boundary";
++  private static final String DOUBLE_DASH = "--";
++  private static final String CR = "\r";
++  private static final String LF = "\n";
++  private static final String CRLF = "\r\n";
++  // length of the Content-Type Header field including the ':'
++  // "Content-Type:" => 13
++  private static final int CONTENT_TYPE_LENGTH = 13;
++
++  private final ReadState readState = new ReadState();
 +  private Charset currentCharset = DEFAULT_CHARSET;
 +  private String currentBoundary = null;
-   private ReadState readState = new ReadState();
 +  private InputStream reader;
 +  private byte[] buffer;
 +  private int offset = 0;
 +  private int limit = 0;
 +
 +  public BatchLineReader(final InputStream reader) {
 +    this(reader, BUFFER_SIZE);
 +  }
 +
 +  public BatchLineReader(final InputStream reader, final int bufferSize) {
 +    if (bufferSize <= 0) {
 +      throw new IllegalArgumentException("Buffer size must be greater than zero.");
 +    }
 +
 +    this.reader = reader;
 +    buffer = new byte[bufferSize];
 +  }
 +
 +  public void close() throws IOException {
 +    reader.close();
 +  }
 +
 +  public List<String> toList() throws IOException {
 +    final List<String> result = new ArrayList<String>();
 +    String currentLine = readLine();
 +    if(currentLine != null) {
 +      currentBoundary = currentLine.trim();
 +      result.add(currentLine);
 +
 +      while ((currentLine = readLine()) != null) {
 +        result.add(currentLine);
 +      }
 +    }
 +    return result;
 +  }
 +
 +  public List<Line> toLineList() throws IOException {
 +    final List<Line> result = new ArrayList<Line>();
 +    String currentLine = readLine();
 +    if(currentLine != null) {
 +      currentBoundary = currentLine.trim();
 +      int counter = 1;
 +      result.add(new Line(currentLine, counter++));
 +
 +      while ((currentLine = readLine()) != null) {
 +        result.add(new Line(currentLine, counter++));
 +      }
 +    }
 +
 +    return result;
 +  }
 +
 +  int read(final byte[] byteBuffer, final int bufferOffset, final int length) throws IOException {
 +    if ((bufferOffset + length) > byteBuffer.length) {
 +      throw new IndexOutOfBoundsException("Buffer is too small");
 +    }
 +
 +    if (length < 0 || bufferOffset < 0) {
-       throw new IndexOutOfBoundsException("Offset and length must be grater than zero");
++      throw new IndexOutOfBoundsException("Offset and length must be greater than zero");
 +    }
 +
 +    // Check if buffer is filled. Return if EOF is reached
 +    // Is buffer refill required
 +    if (limit == offset || isEOF()) {
 +      fillBuffer();
 +
 +      if (isEOF()) {
 +        return EOF;
 +      }
 +    }
 +
 +    int bytesRead = 0;
 +    int bytesToRead = length;
 +    int currentOutputOffset = bufferOffset;
 +
 +    while (bytesToRead != 0) {
 +      // Is buffer refill required?
 +      if (limit == offset) {
 +        fillBuffer();
 +
 +        if (isEOF()) {
 +          bytesToRead = 0;
 +        }
 +      }
 +
 +      if (bytesToRead > 0) {
 +        int readByte = Math.min(limit - offset, bytesToRead);
 +        bytesRead += readByte;
 +        bytesToRead -= readByte;
 +
 +        for (int i = 0; i < readByte; i++) {
 +          byteBuffer[currentOutputOffset++] = buffer[offset++];
 +        }
 +      }
 +    }
 +
 +    return bytesRead;
 +  }
 +
 +  private void updateCurrentCharset(String currentLine) {
-     // TODO: mibo: Improve this method
 +    if(currentLine != null) {
 +      if(currentLine.startsWith(HttpHeader.CONTENT_TYPE)) {
-         currentLine = currentLine.substring(13, currentLine.length() - 2).trim();
-         ContentType ct = ContentType.parse(currentLine);
++        ContentType ct = parseContentType(currentLine);
 +        if (ct != null) {
 +          String charsetString = ct.getParameter(ContentType.PARAMETER_CHARSET);
 +          if (charsetString != null) {
 +            currentCharset = Charset.forName(charsetString);
 +          } else {
 +            currentCharset = DEFAULT_CHARSET;
 +          }
 +          // boundary
 +          String boundary = ct.getParameter(BOUNDARY);
 +          if (boundary != null) {
 +            currentBoundary = DOUBLE_DASH + boundary;
 +          }
 +        }
-       } else if(CRLF.equals(currentLine)) {
++      } else if(isLinebreak(currentLine)) {
 +        readState.foundLinebreak();
 +      } else if(isBoundary(currentLine)) {
 +        readState.foundBoundary();
 +      }
 +    }
 +  }
 +
++  private ContentType parseContentType(String currentLine) {
++    currentLine = currentLine.substring(CONTENT_TYPE_LENGTH, currentLine.length()).trim();
++    return ContentType.parse(currentLine);
++  }
++
++  private boolean isLinebreak(String currentLine) {
++    if(currentLine.length() > 2) {
++      return false;
++    }
++    return CR.equals(currentLine) || LF.equals(currentLine) || CRLF.equals(currentLine);
++  }
++
 +  private boolean isBoundary(String currentLine) {
 +    if((currentBoundary + CRLF).equals(currentLine)) {
 +      return true;
 +    } else if((currentBoundary + DOUBLE_DASH + CRLF).equals(currentLine)) {
 +      return true;
 +    }
 +    return false;
 +  }
 +
 +  String readLine() throws IOException {
 +    if (limit == EOF) {
 +      return null;
 +    }
 +
 +    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
 +    boolean foundLineEnd = false; // EOF will be considered as line ending
 +
 +    while (!foundLineEnd) {
 +      // Is buffer refill required?
 +      if (limit == offset) {
 +        if (fillBuffer() == EOF) {
 +          foundLineEnd = true;
 +        }
 +      }
 +
 +      if (!foundLineEnd) {
 +        byte currentChar = this.buffer[offset++];
 +        if(!buffer.hasRemaining()) {
 +          buffer.flip();
 +          ByteBuffer tmp = ByteBuffer.allocate(buffer.limit() *2);
 +          tmp.put(buffer);
 +          buffer = tmp;
 +        }
 +        buffer.put(currentChar);
 +
-         if (currentChar == LF) {
++        if (currentChar == LF_BYTE) {
 +          foundLineEnd = true;
-         } else if (currentChar == CR) {
++        } else if (currentChar == CR_BYTE) {
 +          foundLineEnd = true;
 +
 +          // Check next char. Consume \n if available
 +          // Is buffer refill required?
 +          if (limit == offset) {
 +            fillBuffer();
 +          }
 +
 +          // Check if there is at least one character
-           if (limit != EOF && this.buffer[offset] == LF) {
-             buffer.put(LF);
++          if (limit != EOF && this.buffer[offset] == LF_BYTE) {
++            buffer.put(LF_BYTE);
 +            offset++;
 +          }
 +        }
 +      }
 +    }
 +
 +    if(buffer.position() == 0) {
 +      return null;
 +    } else {
 +      String currentLine;
 +      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);
 +      }
 +      updateCurrentCharset(currentLine);
 +      return currentLine;
 +    }
 +  }
 +
 +  private boolean isEOF() {
 +    return limit == EOF;
 +  }
 +
 +  private int fillBuffer() throws IOException {
 +    limit = reader.read(buffer, 0, buffer.length);
 +    offset = 0;
 +
 +    return limit;
 +  }
 +
 +  private Charset getCurrentCharset() {
 +    return currentCharset;
 +  }
 +
 +  /**
 +   * Read state indicator (whether currently the <code>body</code> or <code>header</code> part is read).
 +   */
 +  private class ReadState {
 +    private int state = 0;
 +
 +    public void foundLinebreak() {
 +      state++;
 +    }
 +    public void foundBoundary() {
 +      state = 0;
 +    }
 +    public boolean isReadBody() {
 +      return state >= 2;
 +    }
 +
 +    @Override
 +    public String toString() {
 +      return String.valueOf(state);
 +    }
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/594ad4ab/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/BatchResponseSerializer.java
----------------------------------------------------------------------
diff --cc lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/BatchResponseSerializer.java
index 377c5e1,577f083..21474dd
--- a/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/BatchResponseSerializer.java
+++ b/lib/server-core/src/main/java/org/apache/olingo/server/core/serializer/BatchResponseSerializer.java
@@@ -22,12 -22,6 +22,11 @@@ import java.io.ByteArrayInputStream
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
- import java.io.OutputStream;
 +import java.nio.ByteBuffer;
 +import java.nio.channels.Channels;
 +import java.nio.channels.ReadableByteChannel;
 +import java.nio.channels.WritableByteChannel;
 +import java.nio.charset.Charset;
  import java.util.List;
  import java.util.Map;
  import java.util.UUID;
@@@ -165,93 -182,4 +164,93 @@@ public class BatchResponseSerializer 
    private String generateBoundary(final String value) {
      return value + "_" + UUID.randomUUID().toString();
    }
 -}
 +
 +  /**
 +   * Builder class to create the body and the header.
 +   */
 +  private class BodyBuilder {
 +    private final Charset CHARSET_ISO_8859_1 = Charset.forName("iso-8859-1");
 +    private ByteBuffer buffer = ByteBuffer.allocate(8192);
 +    private boolean isClosed = false;
 +
 +    public byte[] getContent() {
 +      isClosed = true;
 +      byte[] tmp = new byte[buffer.position()];
 +      buffer.flip();
 +      buffer.get(tmp, 0, buffer.limit());
 +      return tmp;
 +    }
 +
 +    public BodyBuilder append(String string) {
 +      byte [] b = string.getBytes(CHARSET_ISO_8859_1);
 +      put(b);
 +      return this;
 +    }
 +
 +    private void put(byte[] b) {
 +      if(isClosed) {
 +        throw new RuntimeException("BodyBuilder is closed.");
 +      }
 +      if(buffer.remaining() < b.length) {
 +        buffer.flip();
-         ByteBuffer tmp = ByteBuffer.allocate(buffer.limit() *2);
++        ByteBuffer tmp = ByteBuffer.allocate(b.length + BUFFER_SIZE);
 +        tmp.put(buffer);
 +        buffer = tmp;
 +      }
 +      buffer.put(b);
 +    }
 +
 +    public BodyBuilder append(int statusCode) {
 +      return append(String.valueOf(statusCode));
 +    }
 +
 +    public BodyBuilder append(Body body) {
 +      put(body.getContent());
 +      return this;
 +    }
 +
 +    public String toString() {
 +      return new String(buffer.array(), 0, buffer.position());
 +    }
 +  }
 +
 +  /**
 +   * Body part which is read and stored as bytes (no charset conversion).
 +   */
 +  private class Body {
 +    private final byte[] content;
 +
 +    public Body(ODataResponse response) {
 +      this.content = getBody(response);
 +    }
 +
 +    public int getLength() {
 +      return content.length;
 +    }
 +
 +    public byte[] getContent() {
 +      return content;
 +    }
 +
 +    private byte[] getBody(final ODataResponse response) {
 +      if (response == null || response.getContent() == null) {
 +        return new byte[0];
 +      }
 +
 +      try {
 +        ByteArrayOutputStream output = new ByteArrayOutputStream();
 +        ByteBuffer inBuffer = ByteBuffer.allocate(BUFFER_SIZE);
 +        ReadableByteChannel ic = Channels.newChannel(response.getContent());
 +        WritableByteChannel oc = Channels.newChannel(output);
 +        while (ic.read(inBuffer) > 0) {
 +          inBuffer.flip();
 +          oc.write(inBuffer);
 +          inBuffer.rewind();
 +        }
 +        return output.toByteArray();
 +      } catch (IOException e) {
 +        throw new ODataRuntimeException("Error on reading request content");
 +      }
 +    }
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/594ad4ab/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java
----------------------------------------------------------------------
diff --cc lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java
index 3d2507c,0000000..afb51f0
mode 100644,000000..100644
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/deserializer/batch/BatchLineReaderTest.java
@@@ -1,280 -1,0 +1,342 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements. See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership. The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License. You may obtain a copy of the License at
 + * 
 + * http://www.apache.org/licenses/LICENSE-2.0
 + * 
 + * Unless required by applicable law or agreed to in writing,
 + * software distributed under the License is distributed on an
 + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 + * KIND, either express or implied. See the License for the
 + * specific language governing permissions and limitations
 + * under the License.
 + */
 +package org.apache.olingo.server.core.deserializer.batch;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +
 +import java.io.ByteArrayInputStream;
++import java.io.ByteArrayOutputStream;
++import java.io.IOException;
++import java.io.InputStream;
++import java.nio.ByteBuffer;
++import java.nio.channels.Channels;
++import java.nio.channels.ReadableByteChannel;
++import java.nio.channels.WritableByteChannel;
++import java.nio.charset.Charset;
 +import java.util.List;
 +
++import org.apache.commons.io.IOUtils;
++import org.apache.commons.lang3.StringUtils;
++import org.apache.olingo.commons.api.ODataRuntimeException;
++import org.apache.olingo.server.api.ODataResponse;
 +import org.junit.Test;
 +
 +public class BatchLineReaderTest {
 +
 +  private static final String TEXT_COMBINED = "Test\r" +
 +      "Test2\r\n" +
 +      "Test3\n" +
 +      "Test4\r" +
 +      "\r" +
 +      "\r\n" +
 +      "\r\n" +
 +      "Test5\n" +
 +      "Test6\r\n" +
 +      "Test7\n" +
 +      "\n";
 +
 +  private static final String TEXT_EMPTY = "";
++  private static final String LF = "\n";
 +
 +  @Test
 +  public void testSimpleText() throws Exception {
 +    final String TEXT = "Test";
 +    BatchLineReader reader = create(TEXT);
 +
 +    assertEquals(TEXT, reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testNoText() throws Exception {
 +    final String TEXT = "";
 +    BatchLineReader reader = create(TEXT);
 +
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testNoBytes() throws Exception {
 +    BatchLineReader reader =
 +        new BatchLineReader(new ByteArrayInputStream(new byte[0]));
 +
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testCRLF() throws Exception {
 +    final String TEXT = "Test\r\n" +
 +        "Test2";
 +
 +    BatchLineReader reader = create(TEXT);
 +
 +    assertEquals("Test\r\n", reader.readLine());
 +    assertEquals("Test2", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testLF() throws Exception {
 +    final String TEXT = "Test\n" +
 +        "Test2";
 +
 +    BatchLineReader reader = create(TEXT);
 +
 +    assertEquals("Test\n", reader.readLine());
 +    assertEquals("Test2", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testCR() throws Exception {
 +    final String TEXT = "Test\r" +
 +        "Test2";
 +
 +    BatchLineReader reader = create(TEXT);
 +
 +    assertEquals("Test\r", reader.readLine());
 +    assertEquals("Test2", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testCombined() throws Exception {
 +    BatchLineReader reader = create(TEXT_COMBINED);
 +
 +    assertEquals("Test\r", reader.readLine());
 +    assertEquals("Test2\r\n", reader.readLine());
 +    assertEquals("Test3\n", reader.readLine());
 +    assertEquals("Test4\r", reader.readLine());
 +    assertEquals("\r", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("Test5\n", reader.readLine());
 +    assertEquals("Test6\r\n", reader.readLine());
 +    assertEquals("Test7\n", reader.readLine());
 +    assertEquals("\n", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testCombinedBufferSizeTwo() throws Exception {
 +    BatchLineReader reader = create(TEXT_COMBINED, 2);
 +
 +    assertEquals("Test\r", reader.readLine());
 +    assertEquals("Test2\r\n", reader.readLine());
 +    assertEquals("Test3\n", reader.readLine());
 +    assertEquals("Test4\r", reader.readLine());
 +    assertEquals("\r", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("Test5\n", reader.readLine());
 +    assertEquals("Test6\r\n", reader.readLine());
 +    assertEquals("Test7\n", reader.readLine());
 +    assertEquals("\n", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testCombinedBufferSizeOne() throws Exception {
 +    final String TEXT = "Test\r" +
 +        "Test2\r\n" +
 +        "Test3\n" +
 +        "Test4\r" +
 +        "\r" +
 +        "\r\n" +
 +        "\r\n" +
 +        "Test5\n" +
 +        "Test6\r\n" +
 +        "Test7\n" +
 +        "\r\n";
 +
 +    BatchLineReader reader = create(TEXT, 1);
 +
 +    assertEquals("Test\r", reader.readLine());
 +    assertEquals("Test2\r\n", reader.readLine());
 +    assertEquals("Test3\n", reader.readLine());
 +    assertEquals("Test4\r", reader.readLine());
 +    assertEquals("\r", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertEquals("Test5\n", reader.readLine());
 +    assertEquals("Test6\r\n", reader.readLine());
 +    assertEquals("Test7\n", reader.readLine());
 +    assertEquals("\r\n", reader.readLine());
 +    assertNull(reader.readLine());
 +    assertNull(reader.readLine());
 +
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testDoubleLF() throws Exception {
 +    final String TEXT = "Test\r" +
 +        "\r";
 +
 +    BatchLineReader reader = create(TEXT, 1);
 +
 +    assertEquals("Test\r", reader.readLine());
 +    assertEquals("\r", reader.readLine());
 +    reader.close();
 +  }
 +
 +  @Test
 +  public void testReadMoreBufferCapacityThanCharacterAvailable() throws Exception {
 +    final String TEXT = "Foo";
 +    byte[] buffer = new byte[20];
 +
 +    BatchLineReader reader = create(TEXT);
 +    assertEquals(3, reader.read(buffer, 0, 20));
 +    assertEquals(-1, reader.read(buffer, 0, 20));
 +    reader.close();
 +
 +    BatchLineReader readerBufferOne = create(TEXT, 1);
 +    assertEquals(3, readerBufferOne.read(buffer, 0, 20));
 +    assertEquals(-1, readerBufferOne.read(buffer, 0, 20));
 +    readerBufferOne.close();
 +  }
 +
 +  @Test
 +  public void testLineEqualsAndHashCode() {
 +    Line l1 = new Line("The first line", 1);
 +    Line l2 = new Line("The first line", 1);
 +    Line l3 = new Line("The second line", 2);
 +
 +    assertEquals(l1, l2);
 +    assertFalse(l1.equals(l3));
 +    assertTrue(l1.hashCode() != l3.hashCode());
 +  }
 +
- //  @Test(expected = IllegalArgumentException.class)
- //  public void testSkipNegative() throws Exception {
- //    BufferedReaderIncludingLineEndings reader = create("123");
- //    reader.skip(-1);
- //  }
- 
-   @Test(expected = IllegalArgumentException.class)
-   public void testFailBufferSizeZero() throws Exception {
-     BatchLineReader reader = create(TEXT_EMPTY, 0);
-     reader.close();
-   }
++  @Test
++  public void testToList() throws Exception {
++    BatchLineReader reader = create(TEXT_COMBINED);
++    List<String> stringList = reader.toList();
 +
-   @Test(expected = IllegalArgumentException.class)
-   public void testFailBufferSizeNegative() throws Exception {
-     BatchLineReader reader = create(TEXT_EMPTY, -1);
++    assertEquals(11, stringList.size());
++    assertEquals("Test\r", stringList.get(0));
++    assertEquals("Test2\r\n", stringList.get(1));
++    assertEquals("Test3\n", stringList.get(2));
++    assertEquals("Test4\r", stringList.get(3));
++    assertEquals("\r", stringList.get(4));
++    assertEquals("\r\n", stringList.get(5));
++    assertEquals("\r\n", stringList.get(6));
++    assertEquals("Test5\n", stringList.get(7));
++    assertEquals("Test6\r\n", stringList.get(8));
++    assertEquals("Test7\n", stringList.get(9));
++    assertEquals("\n", stringList.get(10));
 +    reader.close();
 +  }
 +
 +  @Test
-   public void testToList() throws Exception {
++  public void testToLineList() throws Exception {
 +    BatchLineReader reader = create(TEXT_COMBINED);
 +    List<Line> stringList = reader.toLineList();
 +
 +    assertEquals(11, stringList.size());
 +    assertEquals("Test\r", stringList.get(0).toString());
 +    assertEquals("Test2\r\n", stringList.get(1).toString());
 +    assertEquals("Test3\n", stringList.get(2).toString());
 +    assertEquals("Test4\r", stringList.get(3).toString());
 +    assertEquals("\r", stringList.get(4).toString());
 +    assertEquals("\r\n", stringList.get(5).toString());
 +    assertEquals("\r\n", stringList.get(6).toString());
 +    assertEquals("Test5\n", stringList.get(7).toString());
 +    assertEquals("Test6\r\n", stringList.get(8).toString());
 +    assertEquals("Test7\n", stringList.get(9).toString());
 +    assertEquals("\n", stringList.get(10).toString());
 +    reader.close();
 +  }
 +
++  @Test
++  public void testBatchContent() throws Exception {
++    String batchContent = getFileContent("/batchLarge.batch", "utf-8");
++    BatchLineReader reader = create(batchContent);
++
++    List<String> lines = reader.toList();
++    assertEquals(2422, lines.size());
++    assertEquals("--batch_8194-cf13-1f56\n", lines.get(0));
++    assertEquals("              <d:City m:type=\"RefScenario.c_City\">\n", lines.get(1402));
++    assertEquals("\n", lines.get(1903));
++    assertEquals("--batch_8194-cf13-1f56--", lines.get(2421));
++  }
++
++  private String getFileContent(String fileName, String charset) throws IOException {
++    byte[] content = getFileContent(fileName);
++    return new String(content, Charset.forName(charset));
++  }
++
++  private byte[] getFileContent(String fileName) throws IOException {
++    final InputStream input = ClassLoader.class.getResourceAsStream(fileName);
++    if (input == null) {
++      throw new IOException("Requested file '" + fileName + "' was not found.");
++    }
++      ByteArrayOutputStream output = new ByteArrayOutputStream();
++      ByteBuffer inBuffer = ByteBuffer.allocate(8192);
++      ReadableByteChannel ic = Channels.newChannel(input);
++      WritableByteChannel oc = Channels.newChannel(output);
++      while (ic.read(inBuffer) > 0) {
++        inBuffer.flip();
++        oc.write(inBuffer);
++        inBuffer.rewind();
++      }
++      return output.toByteArray();
++  }
++
++  @Test(expected = IllegalArgumentException.class)
++  public void testFailBufferSizeZero() throws Exception {
++    BatchLineReader reader = create(TEXT_EMPTY, 0);
++    reader.close();
++  }
++
++  @Test(expected = IllegalArgumentException.class)
++  public void testFailBufferSizeNegative() throws Exception {
++    BatchLineReader reader = create(TEXT_EMPTY, -1);
++    reader.close();
++  }
++
 +  private BatchLineReader create(final String inputString) throws Exception {
 +    return new BatchLineReader(new ByteArrayInputStream(inputString
 +        .getBytes("UTF-8")));
 +  }
 +
 +  private BatchLineReader create(final String inputString, final int bufferSize) throws Exception {
 +    return new BatchLineReader(new ByteArrayInputStream(inputString
 +        .getBytes("UTF-8")), bufferSize);
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/olingo-odata4/blob/594ad4ab/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
----------------------------------------------------------------------
diff --cc lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
index f73479b,c5d0653..9570d35
--- a/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
+++ b/lib/server-core/src/test/java/org/apache/olingo/server/core/serializer/BatchResponseSerializerTest.java
@@@ -22,11 -22,10 +22,12 @@@ import static org.junit.Assert.assertEq
  import static org.junit.Assert.assertNotNull;
  import static org.junit.Assert.assertTrue;
  
 +import java.io.ByteArrayInputStream;
  import java.io.InputStream;
 -import java.io.InputStreamReader;
 +import java.nio.charset.Charset;
  import java.util.ArrayList;
  import java.util.List;
++import java.util.Random;
  import java.util.UUID;
  
  import org.apache.commons.io.IOUtils;
@@@ -96,219 -93,7 +97,219 @@@ public class BatchResponseSerializerTes
      assertEquals(CRLF, body.get(line++));
      assertEquals(CRLF, body.get(line++));
      assertTrue(body.get(line++).contains("--changeset_"));
-     assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
 +  }
 +
 +  @Test
 +  public void testBatchResponseUmlautsUtf8() throws Exception {
 +    final List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
 +    ODataResponse response = new ODataResponse();
 +    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
 +    response.setHeader(HttpHeader.CONTENT_TYPE,
 +            ContentType.APPLICATION_JSON.toContentTypeString() + "; charset=UTF-8");
 +    response.setContent(IOUtils.toInputStream("Wälter Winter" + CRLF));
 +
 +    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
 +    responses.add(response);
 +    parts.add(new ODataResponsePart(responses, false));
 +
 +    ODataResponse changeSetResponse = new ODataResponse();
 +    changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
 +    changeSetResponse.setHeader(HttpHeader.CONTENT_ID, "1");
 +    responses = new ArrayList<ODataResponse>(1);
 +    responses.add(changeSetResponse);
 +    parts.add(new ODataResponsePart(responses, true));
 +
 +    BatchResponseSerializer serializer = new BatchResponseSerializer();
 +    final InputStream content = serializer.serialize(parts, BOUNDARY);
 +    assertNotNull(content);
 +    final BatchLineReader reader =
 +            new BatchLineReader(content);
 +    final List<String> body = reader.toList();
 +    reader.close();
 +
 +    int line = 0;
 +    assertEquals(24, body.size());
      assertTrue(body.get(line++).contains("--batch_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
 +    assertEquals("Content-Type: application/json; charset=UTF-8" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 16" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("Wälter Winter" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--batch_"));
 +    assertTrue(body.get(line++).contains("Content-Type: multipart/mixed; boundary=changeset_"));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals("Content-ID: 1" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 204 No Content" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 0" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
-     assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
 +  }
 +
 +  @Test
 +  public void testBatchResponseUmlautsUtf8BodyIsoHeader() throws Exception {
 +    final List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
 +    ODataResponse response = new ODataResponse();
 +    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
 +    response.setHeader(HttpHeader.CONTENT_TYPE,
 +        ContentType.APPLICATION_JSON.toContentTypeString() + "; charset=UTF-8");
 +    response.setContent(IOUtils.toInputStream("Wälter Winter" + CRLF));
 +
 +    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
 +    responses.add(response);
 +    parts.add(new ODataResponsePart(responses, false));
 +
 +    ODataResponse changeSetResponse = new ODataResponse();
 +    changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
 +    changeSetResponse.setHeader(HttpHeader.CONTENT_ID, "1");
 +
 +    byte[] umlauts = "äüö".getBytes(CS_ISO_8859_1);
 +    changeSetResponse.setHeader("Custom-Header", new String(umlauts, CS_ISO_8859_1));
 +    responses = new ArrayList<ODataResponse>(1);
 +    responses.add(changeSetResponse);
 +    parts.add(new ODataResponsePart(responses, true));
 +
 +    BatchResponseSerializer serializer = new BatchResponseSerializer();
 +    final InputStream content = serializer.serialize(parts, BOUNDARY);
 +    assertNotNull(content);
 +    final BatchLineReader reader =
 +        new BatchLineReader(content);
 +    final List<String> body = reader.toList();
 +    reader.close();
 +
 +    int line = 0;
 +    assertEquals(25, body.size());
 +    assertTrue(body.get(line++).contains("--batch_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
 +    assertEquals("Content-Type: application/json; charset=UTF-8" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 16" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("Wälter Winter" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--batch_"));
 +    assertTrue(body.get(line++).contains("Content-Type: multipart/mixed; boundary=changeset_"));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals("Content-ID: 1" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 204 No Content" + CRLF, body.get(line++));
 +    assertEquals("Custom-Header: äüö" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 0" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
-     assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
 +  }
 +
 +  @Test
 +  public void testBatchResponseUmlautsUtf8BodyAndHeader() throws Exception {
 +    final List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
 +    ODataResponse response = new ODataResponse();
 +    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
 +    response.setHeader(HttpHeader.CONTENT_TYPE,
 +        ContentType.APPLICATION_JSON.toContentTypeString() + "; charset=UTF-8");
 +    response.setContent(IOUtils.toInputStream("Wälter Winter" + CRLF));
 +
 +    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
 +    responses.add(response);
 +    parts.add(new ODataResponsePart(responses, false));
 +
 +    ODataResponse changeSetResponse = new ODataResponse();
 +    changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
 +    changeSetResponse.setHeader(HttpHeader.CONTENT_ID, "1");
 +
 +//    byte[] umlauts = "äüö".getBytes(CS_UTF_8);
 +//    changeSetResponse.setHeader("Custom-Header", new String(umlauts, CS_UTF_8));
 +    changeSetResponse.setHeader("Custom-Header", "äüö");
 +    responses = new ArrayList<ODataResponse>(1);
 +    responses.add(changeSetResponse);
 +    parts.add(new ODataResponsePart(responses, true));
 +
 +    BatchResponseSerializer serializer = new BatchResponseSerializer();
 +    final InputStream content = serializer.serialize(parts, BOUNDARY);
 +    assertNotNull(content);
 +    final BatchLineReader reader =
 +        new BatchLineReader(content);
 +    final List<String> body = reader.toList();
 +    reader.close();
 +
 +    assertEquals(25, body.size());
 +    // TODO: check: with latest change in BatchResponseSerializer is not possible
 +    // to set header values with UTF-8 (only iso-8859-1)
 +//    assertEquals("Custom-Header: äüö" + CRLF, body.get(19));
 +    assertEquals("Custom-Header: äüö" + CRLF, body.get(19));
 +  }
 +
 +  @Test
 +  public void testBatchResponseUmlautsIso() throws Exception {
 +    final List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
 +    ODataResponse response = new ODataResponse();
 +    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
 +    response.setHeader(HttpHeader.CONTENT_TYPE,
 +            ContentType.APPLICATION_JSON.toContentTypeString() + "; charset=iso-8859-1");
 +    byte[] payload = ("Wälter Winter" + CRLF).getBytes("iso-8859-1");
 +    response.setContent(new ByteArrayInputStream(payload));
 +
 +    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
 +    responses.add(response);
 +    parts.add(new ODataResponsePart(responses, false));
 +
 +    ODataResponse changeSetResponse = new ODataResponse();
 +    changeSetResponse.setStatusCode(HttpStatusCode.NO_CONTENT.getStatusCode());
 +    changeSetResponse.setHeader(HttpHeader.CONTENT_ID, "1");
 +    responses = new ArrayList<ODataResponse>(1);
 +    responses.add(changeSetResponse);
 +    parts.add(new ODataResponsePart(responses, true));
 +
 +    BatchResponseSerializer serializer = new BatchResponseSerializer();
 +    final InputStream content = serializer.serialize(parts, BOUNDARY);
 +    assertNotNull(content);
 +    final BatchLineReader reader =
 +            new BatchLineReader(content);
 +    final List<String> body = reader.toList();
 +    reader.close();
 +
 +    int line = 0;
 +    assertEquals(24, body.size());
 +    assertTrue(body.get(line++).contains("--batch_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
 +    assertEquals("Content-Type: application/json; charset=iso-8859-1" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 15" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("Wälter Winter" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--batch_"));
 +    assertTrue(body.get(line++).contains("Content-Type: multipart/mixed; boundary=changeset_"));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
 +    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
 +    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
 +    assertEquals("Content-ID: 1" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals("HTTP/1.1 204 No Content" + CRLF, body.get(line++));
 +    assertEquals("Content-Length: 0" + CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertEquals(CRLF, body.get(line++));
 +    assertTrue(body.get(line++).contains("--changeset_"));
-     assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
    }
  
    @Test
@@@ -362,7 -147,7 +363,7 @@@
      assertEquals(CRLF, body.get(line++));
      assertEquals(CRLF, body.get(line++));
      assertTrue(body.get(line++).contains("--changeset_"));
--    assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
    }
  
    @Test
@@@ -397,7 -182,7 +398,54 @@@
      assertEquals("Content-Length: 13" + CRLF, body.get(line++));
      assertEquals(CRLF, body.get(line++));
      assertEquals("Walter Winter" + CRLF, body.get(line++));
++    assertTrue(body.get(line).contains("--batch_"));
++  }
++
++  @Test
++  public void testResponseVeryLargeHeader() throws Exception {
++    List<ODataResponsePart> parts = new ArrayList<ODataResponsePart>();
++    ODataResponse response = new ODataResponse();
++    response.setStatusCode(HttpStatusCode.OK.getStatusCode());
++    response.setHeader(HttpHeader.CONTENT_TYPE, "application/json");
++    final String chValue = generateTestData(20000);
++    response.setHeader("Custom-Header", chValue);
++    response.setContent(IOUtils.toInputStream("Walter Winter"));
++
++    List<ODataResponse> responses = new ArrayList<ODataResponse>(1);
++    responses.add(response);
++    parts.add(new ODataResponsePart(responses, false));
++
++    final BatchResponseSerializer serializer = new BatchResponseSerializer();
++    final InputStream content = serializer.serialize(parts, BOUNDARY);
++
++    assertNotNull(content);
++    final BatchLineReader reader =
++            new BatchLineReader(content);
++    final List<String> body = reader.toList();
++    reader.close();
++
++    int line = 0;
++    assertEquals(11, body.size());
      assertTrue(body.get(line++).contains("--batch_"));
++    assertEquals("Content-Type: application/http" + CRLF, body.get(line++));
++    assertEquals("Content-Transfer-Encoding: binary" + CRLF, body.get(line++));
++    assertEquals(CRLF, body.get(line++));
++    assertEquals("HTTP/1.1 200 OK" + CRLF, body.get(line++));
++    assertEquals("Custom-Header: " + chValue + CRLF, body.get(line++));
++    assertEquals("Content-Type: application/json" + CRLF, body.get(line++));
++    assertEquals("Content-Length: 13" + CRLF, body.get(line++));
++    assertEquals(CRLF, body.get(line++));
++    assertEquals("Walter Winter" + CRLF, body.get(line++));
++    assertTrue(body.get(line).contains("--batch_"));
++  }
++
++  private String generateTestData(int amount) {
++    StringBuilder sb = new StringBuilder();
++    Random r = new Random();
++    for (int j = 0; j < amount; j++) {
++      sb.append((char)(65 + r.nextInt(25)));
++    }
++    return sb.toString();
    }
  
    @Test
@@@ -436,6 -221,6 +484,6 @@@
      assertEquals(CRLF, body.get(line++));
      assertEquals(CRLF, body.get(line++));
      assertTrue(body.get(line++).contains("--changeset_"));
--    assertTrue(body.get(line++).contains("--batch_"));
++    assertTrue(body.get(line).contains("--batch_"));
    }
  }