| /* |
| * 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.serializer; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| import org.apache.olingo.commons.api.ODataRuntimeException; |
| import org.apache.olingo.commons.api.http.HttpContentType; |
| import org.apache.olingo.commons.api.http.HttpHeader; |
| import org.apache.olingo.commons.api.http.HttpStatusCode; |
| import org.apache.olingo.server.api.ODataResponse; |
| import org.apache.olingo.server.api.batch.exception.BatchSerializerException; |
| import org.apache.olingo.server.api.batch.exception.BatchSerializerException.MessageKeys; |
| import org.apache.olingo.server.api.deserializer.batch.ODataResponsePart; |
| import org.apache.olingo.server.core.deserializer.batch.BatchParserCommon; |
| |
| public class BatchResponseSerializer { |
| private static final int BUFFER_SIZE = 4096; |
| private static final String DOUBLE_DASH = "--"; |
| private static final String COLON = ":"; |
| private static final String SP = " "; |
| private static final String CRLF = "\r\n"; |
| |
| public InputStream serialize(final List<ODataResponsePart> responses, final String boundary) |
| throws BatchSerializerException { |
| StringBuilder builder = createBody(responses, boundary); |
| |
| return new ByteArrayInputStream(builder.toString().getBytes()); |
| } |
| |
| private StringBuilder createBody(final List<ODataResponsePart> batchResponses, final String boundary) |
| throws BatchSerializerException { |
| final StringBuilder builder = new StringBuilder(); |
| |
| for (final ODataResponsePart part : batchResponses) { |
| builder.append(getDashBoundary(boundary)); |
| |
| if (part.isChangeSet()) { |
| appendChangeSet(part, builder); |
| } else { |
| appendBodyPart(part.getResponses().get(0), builder, false); |
| } |
| } |
| builder.append(getCloseDelimiter(boundary)); |
| |
| return builder; |
| } |
| |
| private void appendChangeSet(ODataResponsePart part, StringBuilder builder) throws BatchSerializerException { |
| final String changeSetBoundary = generateBoundary("changeset"); |
| |
| appendChangeSetHeader(builder, changeSetBoundary); |
| builder.append(CRLF); |
| |
| for (final ODataResponse response : part.getResponses()) { |
| builder.append(getDashBoundary(changeSetBoundary)); |
| appendBodyPart(response, builder, true); |
| } |
| |
| builder.append(getCloseDelimiter(changeSetBoundary)); |
| } |
| |
| private void appendBodyPart(ODataResponse response, StringBuilder builder, boolean isChangeSet) |
| throws BatchSerializerException { |
| byte[] body = getBody(response); |
| |
| appendBodyPartHeader(response, builder, isChangeSet); |
| builder.append(CRLF); |
| |
| appendStatusLine(response, builder); |
| appendResponseHeader(response, body.length, builder); |
| builder.append(CRLF); |
| |
| builder.append(new String(body)); |
| builder.append(CRLF); |
| } |
| |
| private byte[] getBody(final ODataResponse response) { |
| final InputStream content = response.getContent(); |
| final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| |
| if (content != null) { |
| byte[] buffer = new byte[BUFFER_SIZE]; |
| int n; |
| |
| try { |
| while ((n = content.read(buffer, 0, buffer.length)) != -1) { |
| out.write(buffer, 0, n); |
| } |
| out.flush(); |
| } catch (IOException e) { |
| throw new ODataRuntimeException(e); |
| } |
| |
| return out.toByteArray(); |
| } else { |
| return new byte[0]; |
| } |
| } |
| |
| private void appendChangeSetHeader(StringBuilder builder, final String changeSetBoundary) { |
| appendHeader(HttpHeader.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED.toString() + "; boundary=" |
| + changeSetBoundary, builder); |
| } |
| |
| private void appendHeader(String name, String value, StringBuilder builder) { |
| builder.append(name) |
| .append(COLON) |
| .append(SP) |
| .append(value) |
| .append(CRLF); |
| } |
| |
| private void appendStatusLine(ODataResponse response, StringBuilder builder) { |
| builder.append("HTTP/1.1") |
| .append(SP) |
| .append("" + response.getStatusCode()) |
| .append(SP) |
| .append(HttpStatusCode.fromStatusCode(response.getStatusCode()).toString()) |
| .append(CRLF); |
| } |
| |
| private void appendResponseHeader(ODataResponse response, int contentLength, StringBuilder builder) { |
| final Map<String, String> header = response.getHeaders(); |
| |
| for (final String key : header.keySet()) { |
| // Requests do never has a content id header |
| if (!key.equalsIgnoreCase(BatchParserCommon.HTTP_CONTENT_ID)) { |
| appendHeader(key, header.get(key), builder); |
| } |
| } |
| |
| appendHeader(HttpHeader.CONTENT_LENGTH, "" + contentLength, builder); |
| } |
| |
| private void appendBodyPartHeader(ODataResponse response, StringBuilder builder, boolean isChangeSet) |
| throws BatchSerializerException { |
| appendHeader(HttpHeader.CONTENT_TYPE, HttpContentType.APPLICATION_HTTP, builder); |
| appendHeader(BatchParserCommon.HTTP_CONTENT_TRANSFER_ENCODING, BatchParserCommon.BINARY_ENCODING, builder); |
| |
| if (isChangeSet) { |
| if (response.getHeaders().get(BatchParserCommon.HTTP_CONTENT_ID) != null) { |
| appendHeader(BatchParserCommon.HTTP_CONTENT_ID, response.getHeaders().get(BatchParserCommon.HTTP_CONTENT_ID), |
| builder); |
| } else { |
| throw new BatchSerializerException("Missing content id", MessageKeys.MISSING_CONTENT_ID); |
| } |
| } |
| } |
| |
| private String getDashBoundary(String boundary) { |
| return DOUBLE_DASH + boundary + CRLF; |
| } |
| |
| private String getCloseDelimiter(final String boundary) { |
| return DOUBLE_DASH + boundary + DOUBLE_DASH + CRLF; |
| } |
| |
| private String generateBoundary(final String value) { |
| return value + "_" + UUID.randomUUID().toString(); |
| } |
| } |