blob: 8557f365c94a9dd728a21bd4c9738b71eac267e2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.olingo.client.core.communication.response;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.request.batch.ODataBatchLineIterator;
import org.apache.olingo.client.api.communication.response.ODataResponse;
import org.apache.olingo.client.api.http.NoContentException;
import org.apache.olingo.client.core.ConfigurationImpl;
import org.apache.olingo.client.core.communication.util.PipedInputStream;
import org.apache.olingo.client.core.communication.util.PipedOutputStream;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchController;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchLineIteratorImpl;
import org.apache.olingo.client.core.communication.request.batch.ODataBatchUtilities;
import org.apache.olingo.commons.api.Constants;
import org.apache.olingo.commons.api.ex.ODataRuntimeException;
import org.apache.olingo.commons.api.http.HttpHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract representation of an OData response.
*/
public abstract class AbstractODataResponse implements ODataResponse {
/**
* Logger.
*/
protected static final Logger LOG = LoggerFactory.getLogger(ODataResponse.class);
protected final ODataClient odataClient;
private static final byte[] CRLF = {13, 10};
/**
* HTTP client.
*/
protected final HttpClient httpClient;
/**
* HTTP response.
*/
protected final HttpResponse res;
/**
* Response headers.
*/
protected final Map<String, Collection<String>> headers =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
/**
* Response code.
*/
protected int statusCode = -1;
/**
* Response message.
*/
protected String statusMessage = null;
/**
* Response body/payload.
*/
protected InputStream payload = null;
/**
* Initialization check.
*/
protected boolean hasBeenInitialized = false;
/**
* Batch info (if to be batched).
*/
protected ODataBatchController batchInfo = null;
private byte[] inputContent = null;
public AbstractODataResponse(
final ODataClient odataClient, final HttpClient httpclient, final HttpResponse res) {
this.odataClient = odataClient;
this.httpClient = httpclient;
this.res = res;
if (res != null) {
initFromHttpResponse(res);
}
}
@Override
public Collection<String> getHeaderNames() {
return headers.keySet();
}
@Override
public Collection<String> getHeader(final String name) {
return headers.get(name);
}
@Override
public String getETag() {
final Collection<String> etag = getHeader(HttpHeader.ETAG);
return etag == null || etag.isEmpty() ? null : etag.iterator().next();
}
@Override
public String getContentType() {
final Collection<String> contentTypes = getHeader(HttpHeader.CONTENT_TYPE);
return contentTypes == null || contentTypes.isEmpty() ? null : contentTypes.iterator().next();
}
@Override
public int getStatusCode() {
return statusCode;
}
@Override
public String getStatusMessage() {
return statusMessage;
}
@Override
public final ODataResponse initFromHttpResponse(final HttpResponse res) {
try {
this.payload = res.getEntity() == null ? null : res.getEntity().getContent();
this.inputContent = null;
} catch (final IllegalStateException | IOException e) {
HttpClientUtils.closeQuietly(res);
LOG.error("Error retrieving payload", e);
throw new ODataRuntimeException(e);
}
for (Header header : res.getAllHeaders()) {
final Collection<String> headerValues;
if (headers.containsKey(header.getName())) {
headerValues = headers.get(header.getName());
} else {
headerValues = new HashSet<>();
headers.put(header.getName(), headerValues);
}
headerValues.add(header.getValue());
}
statusCode = res.getStatusLine().getStatusCode();
statusMessage = res.getStatusLine().getReasonPhrase();
hasBeenInitialized = true;
return this;
}
@Override
public ODataResponse initFromBatch(
final Map.Entry<Integer, String> responseLine,
final Map<String, Collection<String>> headers,
final ODataBatchLineIterator batchLineIterator,
final String boundary) {
if (hasBeenInitialized) {
throw new IllegalStateException("Request already initialized");
}
this.batchInfo = new ODataBatchController(batchLineIterator, boundary);
this.statusCode = responseLine.getKey();
this.statusMessage = responseLine.getValue();
this.headers.putAll(headers);
this.hasBeenInitialized = true;
return this;
}
@Override
public ODataResponse initFromEnclosedPart(final InputStream part) {
try {
if (hasBeenInitialized) {
throw new IllegalStateException("Request already initialized");
}
final ODataBatchLineIteratorImpl batchLineIterator =
new ODataBatchLineIteratorImpl(IOUtils.lineIterator(part, Constants.UTF8));
final Map.Entry<Integer, String> partResponseLine = ODataBatchUtilities.readResponseLine(batchLineIterator);
LOG.debug("Retrieved async item response {}", partResponseLine);
this.statusCode = partResponseLine.getKey();
this.statusMessage = partResponseLine.getValue();
final Map<String, Collection<String>> partHeaders = ODataBatchUtilities.readHeaders(batchLineIterator);
LOG.debug("Retrieved async item headers {}", partHeaders);
this.headers.putAll(partHeaders);
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (batchLineIterator.hasNext()) {
bos.write(batchLineIterator.nextLine().getBytes(Constants.UTF8));
bos.write(CRLF);
}
try {
this.payload = new ByteArrayInputStream(bos.toByteArray());
} catch (Exception e) {
LOG.error("Error retrieving payload", e);
throw new IllegalStateException(e);
}
this.hasBeenInitialized = true;
return this;
} catch (IOException e) {
LOG.error("Error streaming payload response", e);
throw new IllegalStateException(e);
}
}
@Override
public void close() {
closeHttpResponse();
odataClient.getConfiguration().getHttpClientFactory().close(httpClient);
if (batchInfo != null) {
batchInfo.setValidBatch(false);
}
}
protected void closeHttpResponse() {
if(res != null && res instanceof CloseableHttpResponse) {
try {
((CloseableHttpResponse) res).close();
} catch (IOException e) {
LOG.debug("Unable to close response: {}", res, e);
}
}
}
@Override
public InputStream getRawResponse() {
InputStream inputStream = null;
if (HttpStatus.SC_NO_CONTENT == getStatusCode()) {
throw new NoContentException();
}
if (payload == null && batchInfo != null && batchInfo.isValidBatch()) {
// get input stream till the end of item
payload = new PipedInputStream(null);
try {
final PipedOutputStream os = new PipedOutputStream((PipedInputStream) payload,
ConfigurationImpl.DEFAULT_BUFFER_SIZE);
new Thread(new Runnable() {
@Override
public void run() {
try {
ODataBatchUtilities.readBatchPart(batchInfo, os, true);
} catch (Exception e) {
LOG.error("Error streaming batch item payload", e);
} finally {
IOUtils.closeQuietly(os);
}
}
}).start();
} catch (Exception e) {
LOG.error("Error streaming payload response", e);
throw new IllegalStateException(e);
}
} else if (payload != null) {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
org.apache.commons.io.IOUtils.copy(payload, byteArrayOutputStream);
if(inputContent == null){
inputContent = byteArrayOutputStream.toByteArray();
}
inputStream = new ByteArrayInputStream(inputContent);
return inputStream;
} catch (IOException e) {
HttpClientUtils.closeQuietly(res);
LOG.error("Error retrieving payload", e);
throw new ODataRuntimeException(e);
}
}
return payload;
}
}