| /** |
| * Licensed to jclouds, Inc. (jclouds) under one or more |
| * contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. jclouds 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.jclouds.http.ning; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.common.base.Throwables.propagate; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.List; |
| import java.util.Map.Entry; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| |
| import javax.inject.Singleton; |
| import javax.ws.rs.core.HttpHeaders; |
| |
| import org.jclouds.crypto.CryptoStreams; |
| import org.jclouds.http.HttpCommand; |
| import org.jclouds.http.HttpCommandExecutorService; |
| import org.jclouds.http.HttpRequest; |
| import org.jclouds.http.HttpRequestFilter; |
| import org.jclouds.http.HttpResponse; |
| import org.jclouds.http.handlers.DelegatingErrorHandler; |
| import org.jclouds.http.handlers.DelegatingRetryHandler; |
| import org.jclouds.http.internal.BaseHttpCommandExecutorService; |
| import org.jclouds.io.Payload; |
| import org.jclouds.io.Payloads; |
| import org.jclouds.io.payloads.FilePayload; |
| import org.jclouds.rest.internal.RestAnnotationProcessor; |
| |
| import com.google.common.base.Function; |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.LinkedHashMultimap; |
| import com.google.common.collect.Multimap; |
| import com.google.common.io.Closeables; |
| import com.google.common.util.concurrent.AbstractFuture; |
| import com.google.common.util.concurrent.Futures; |
| import com.google.common.util.concurrent.ListenableFuture; |
| import com.google.inject.Inject; |
| import com.ning.http.client.AsyncHttpClient; |
| import com.ning.http.client.Request; |
| import com.ning.http.client.RequestBuilder; |
| import com.ning.http.client.Response; |
| import com.ning.http.client.generators.InputStreamBodyGenerator; |
| |
| /** |
| * Todo Write me |
| * |
| * @author Sam Tunnicliffe |
| * @author Adrian Cole |
| */ |
| public class NingHttpCommandExecutorService implements HttpCommandExecutorService { |
| |
| public static final String USER_AGENT = "jclouds/1.0 ning http/1.0.0"; |
| |
| private final AsyncHttpClient client; |
| private final ConvertToNingRequest convertToNingRequest; |
| private final ConvertToJCloudsResponse convertToJCloudsResponse; |
| private final DelegatingRetryHandler retryHandler; |
| private final DelegatingErrorHandler errorHandler; |
| |
| @Inject |
| public NingHttpCommandExecutorService(AsyncHttpClient client, ConvertToNingRequest convertToNingRequest, |
| ConvertToJCloudsResponse convertToJCloudsResponse, DelegatingRetryHandler retryHandler, |
| DelegatingErrorHandler errorHandler) { |
| this.client = client; |
| this.convertToNingRequest = convertToNingRequest; |
| this.convertToJCloudsResponse = convertToJCloudsResponse; |
| this.retryHandler = retryHandler; |
| this.errorHandler = errorHandler; |
| } |
| |
| @Override |
| public ListenableFuture<HttpResponse> submit(HttpCommand command) { |
| try { |
| for (;;) { |
| Future<Response> responseF = client.executeRequest(convertToNingRequest.apply(command.getCurrentRequest())); |
| final HttpResponse httpResponse = convertToJCloudsResponse.apply(responseF.get()); |
| int statusCode = httpResponse.getStatusCode(); |
| if (statusCode >= 300) { |
| if (retryHandler.shouldRetryRequest(command, httpResponse)) { |
| continue; |
| } else { |
| errorHandler.handleError(command, httpResponse); |
| return wrapAsFuture(httpResponse); |
| } |
| } else { |
| return wrapAsFuture(httpResponse); |
| } |
| } |
| |
| } catch (IOException e) { |
| throw Throwables.propagate(e); |
| } catch (InterruptedException e) { |
| throw Throwables.propagate(e); |
| } catch (ExecutionException e) { |
| throw Throwables.propagate(e); |
| } |
| } |
| |
| private ListenableFuture<HttpResponse> wrapAsFuture(final HttpResponse httpResponse) { |
| return Futures.makeListenable(new AbstractFuture<HttpResponse>() { |
| @Override |
| public HttpResponse get() throws InterruptedException, ExecutionException { |
| return httpResponse; |
| } |
| }); |
| } |
| |
| @Singleton |
| public static class ConvertToNingRequest implements Function<HttpRequest, Request> { |
| |
| public Request apply(HttpRequest request) { |
| |
| for (HttpRequestFilter filter : request.getFilters()) { |
| request = filter.filter(request); |
| } |
| |
| RequestBuilder builder = new RequestBuilder(request.getMethod()); |
| builder.setUrl(request.getEndpoint().toASCIIString()); |
| Payload payload = request.getPayload(); |
| if (payload != null) { |
| boolean chunked = "chunked".equals(request.getFirstHeaderOrNull("Transfer-Encoding")); |
| |
| if (request.getPayload().getContentMetadata().getContentMD5() != null) |
| builder.addHeader("Content-MD5", CryptoStreams.base64(request.getPayload().getContentMetadata() |
| .getContentMD5())); |
| if (request.getPayload().getContentMetadata().getContentType() != null) |
| builder.addHeader(HttpHeaders.CONTENT_TYPE, request.getPayload().getContentMetadata().getContentType()); |
| if (request.getPayload().getContentMetadata().getContentLanguage() != null) |
| builder.addHeader(HttpHeaders.CONTENT_LANGUAGE, request.getPayload().getContentMetadata() |
| .getContentLanguage()); |
| if (request.getPayload().getContentMetadata().getContentEncoding() != null) |
| builder.addHeader(HttpHeaders.CONTENT_ENCODING, request.getPayload().getContentMetadata() |
| .getContentEncoding()); |
| if (request.getPayload().getContentMetadata().getContentDisposition() != null) |
| builder.addHeader("Content-Disposition", request.getPayload().getContentMetadata() |
| .getContentDisposition()); |
| if (!chunked) { |
| Long length = checkNotNull(request.getPayload().getContentMetadata().getContentLength(), |
| "payload.getContentLength"); |
| builder.addHeader(HttpHeaders.CONTENT_LENGTH, length.toString()); |
| } |
| setPayload(builder, payload); |
| } else { |
| builder.addHeader(HttpHeaders.CONTENT_LENGTH, "0"); |
| } |
| |
| builder.addHeader(HttpHeaders.USER_AGENT, USER_AGENT); |
| for (String header : request.getHeaders().keySet()) { |
| for (String value : request.getHeaders().get(header)) { |
| builder.addHeader(header, value); |
| } |
| } |
| |
| return builder.build(); |
| } |
| |
| void setPayload(RequestBuilder requestBuilder, Payload payload) { |
| if (payload instanceof FilePayload) { |
| requestBuilder.setBody(((FilePayload) payload).getRawContent()); |
| } else { |
| requestBuilder.setBody(new InputStreamBodyGenerator(payload.getInput())); |
| } |
| } |
| } |
| |
| @Singleton |
| public static class ConvertToJCloudsResponse implements Function<Response, HttpResponse> { |
| |
| public HttpResponse apply(Response nativeResponse) { |
| |
| InputStream in = null; |
| try { |
| in = BaseHttpCommandExecutorService.consumeOnClose(nativeResponse.getResponseBodyAsStream()); |
| } catch (IOException e) { |
| Closeables.closeQuietly(in); |
| propagate(e); |
| assert false : "should have propagated exception"; |
| } |
| |
| Payload payload = in != null ? Payloads.newInputStreamPayload(in) : null; |
| Multimap<String, String> headers = LinkedHashMultimap.create(); |
| for (Entry<String, List<String>> header : nativeResponse.getHeaders()) { |
| headers.putAll(header.getKey(), header.getValue()); |
| } |
| if (payload != null) |
| payload.getContentMetadata().setPropertiesFromHttpHeaders(headers); |
| return new HttpResponse(nativeResponse.getStatusCode(), nativeResponse.getStatusText(), payload, |
| RestAnnotationProcessor.filterOutContentHeaders(headers)); |
| } |
| } |
| } |