/**
* Copyright 2014 Opower, Inc.
* Licensed 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 com.opower.rest.client.generator.executors;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Closeables;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.FluentCaseInsensitiveStringsMap;
import com.ning.http.client.RequestBuilder;
import com.ning.http.client.Response;
import com.opower.rest.client.generator.core.BaseClientResponse;
import com.opower.rest.client.generator.core.ClientRequest;
import com.opower.rest.client.generator.core.ClientRequestFilter;
import com.opower.rest.client.generator.core.ClientResponse;
import com.opower.rest.client.generator.core.SelfExpandingBufferredInputStream;
import com.opower.rest.client.generator.util.CaseInsensitiveMap;
import javax.ws.rs.core.MultivaluedMap;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
public class AsyncHttpClientExecutor extends AbstractClientExecutor {
private final AsyncHttpClient httpClient;
public AsyncHttpClientExecutor() {
this(new AsyncHttpClientConfig.Builder().build(), ImmutableList.<ClientRequestFilter>of());
}
public AsyncHttpClientExecutor(List<ClientRequestFilter> requestFilters) {
this(new AsyncHttpClientConfig.Builder().build(), requestFilters);
}
public AsyncHttpClientExecutor(AsyncHttpClientConfig clientConfig) {
this(clientConfig, ImmutableList.<ClientRequestFilter>of());
}
public AsyncHttpClientExecutor(AsyncHttpClientConfig clientConfig, List<ClientRequestFilter> requestFilters) {
super(requestFilters);
this.httpClient = new AsyncHttpClient(clientConfig);
}
@Override
public ClientResponse execute(ClientRequest request) throws Exception {
RequestBuilder requestBuilder = new RequestBuilder(request.getHttpMethod()).setUrl(request.getUri());
requestBuilder.setFollowRedirects(request.followRedirects());
if(request.getBody() != null && !request.getFormParameters().isEmpty()) {
throw new RuntimeException("You cannot send both form parameters and an entity body");
}
commitHeaders(request, requestBuilder);
if (!request.getFormParameters().isEmpty()) {
for (Map.Entry<String, List<String>> formParam : request.getFormParameters().entrySet()) {
List<String> values = formParam.getValue();
for (String value : values) {
requestBuilder.addParameter(formParam.getKey(), value);
}
}
} else if (request.getBody() != null) {
if (request.getHttpMethod().equals("GET")) {
throw new RuntimeException("A GET request cannot have a body.");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
request.writeRequestBody(request.getHeadersAsObjects(), baos);
requestBuilder.setBody(baos.toByteArray());
}
Response rawResponse = this.httpClient.executeRequest(requestBuilder.build()).get();
BaseClientResponse response = new BaseClientResponse(new SimpleBaseClientResponseStreamFactory(rawResponse), this,
request.getErrorStatusCriteria());
response.setStatus(rawResponse.getStatusCode());
response.setHeaders(extractHeaders(rawResponse));
response.setProviders(request.getProviders());
return response;
}
public void commitHeaders(ClientRequest request, RequestBuilder requestBuilder) {
MultivaluedMap<String, String> headers = request.getHeaders();
for (Map.Entry<String, List<String>> header : headers.entrySet()) {
List<String> values = header.getValue();
for (String value : values) {
requestBuilder.addHeader(header.getKey(), value);
}
}
}
/**
* Extracts the headers from the given HttpResponse.
* @param response the HttpResponse to get the headers from
* @return A map of the headers found on the HttpResponse
*/
public static CaseInsensitiveMap<String> extractHeaders(
Response response) {
CaseInsensitiveMap<String> headers = new CaseInsensitiveMap<String>();
FluentCaseInsensitiveStringsMap headerMap = response.getHeaders();
for (String headerName : headerMap.keySet()) {
for(String headerValue : headerMap.get(headerName)) {
headers.add(headerName, headerValue);
}
}
return headers;
}
@Override
public void close() throws Exception {
}
private class SimpleBaseClientResponseStreamFactory implements BaseClientResponse.BaseClientResponseStreamFactory {
private final Response res;
private InputStream stream;
private SimpleBaseClientResponseStreamFactory(Response res) {
this.res = res;
}
public InputStream getInputStream() throws IOException {
if (stream == null) {
InputStream rawStream = this.res.getResponseBodyAsStream();
if(rawStream == null) {
return null;
}
this.stream = new SelfExpandingBufferredInputStream(rawStream);
}
return this.stream;
}
public void performReleaseConnection() {
try {
Closeables.close(this.stream, true);
} catch (IOException ex) {
Throwables.propagate(ex);
}
}
}
}