added support for ning http client

This commit is contained in:
Alex Yarmula 2010-04-03 11:02:46 -07:00
parent 5d68b3e820
commit 6c26047e6a
7 changed files with 515 additions and 5 deletions

View File

@ -353,7 +353,7 @@ public abstract class BaseJettyTest {
protected boolean redirectEveryTwentyRequests(HttpServletRequest request, protected boolean redirectEveryTwentyRequests(HttpServletRequest request,
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
if (cycle.incrementAndGet() % 20 == 0) { if (cycle.incrementAndGet() % 20 == 0) {
response.sendRedirect("http://localhost:" + (testPort + 1)); response.sendRedirect("http://localhost:" + (testPort + 1) + "/");
((Request) request).setHandled(true); ((Request) request).setHandled(true);
return true; return true;
} }

9
extensions/ning/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# use glob syntax.
syntax: glob
target
.settings
.classpath
.project
jclouds-apachehc.iml
jclouds-apachehc.ipr
jclouds-apachehc.iws

54
extensions/ning/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
====================================================================
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.
====================================================================
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>jclouds-extensions-project</artifactId>
<groupId>org.jclouds</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>jclouds-ning</artifactId>
<name>jclouds Ning Http Client</name>
<packaging>jar</packaging>
<description>Ning Http client</description>
<scm>
<connection>scm:svn:http://jclouds.googlecode.com/svn/trunk</connection>
<developerConnection>scm:svn:https://jclouds.googlecode.com/svn/trunk</developerConnection>
<url>http://jclouds.googlecode.com/svn/trunk</url>
</scm>
<dependencies>
<dependency>
<groupId>com.ning</groupId>
<artifactId>async-http-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,215 @@
/**
*
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.http.ning;
import java.io.IOException;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.io.ByteStreams;
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.ning.http.client.*;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.ning.http.collection.Pair;
import org.jclouds.http.*;
import com.google.inject.Inject;
import org.jclouds.http.handlers.DelegatingErrorHandler;
import org.jclouds.http.handlers.DelegatingRetryHandler;
import org.jclouds.http.payloads.ByteArrayPayload;
import org.jclouds.http.payloads.FilePayload;
import org.jclouds.http.payloads.InputStreamPayload;
import org.jclouds.http.payloads.StringPayload;
import org.jclouds.util.Utils;
/**
* 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.getRequest()));
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()) {
filter.filter(request);
}
AsyncHttpClient client = new AsyncHttpClient();
AsyncHttpClient.BoundRequestBuilder nativeRequestBuilder;
String endpoint = request.getEndpoint().toASCIIString();
if (request.getMethod().equals(HttpMethod.HEAD)) {
nativeRequestBuilder = client.prepareHead(endpoint);
} else if (request.getMethod().equals(HttpMethod.GET)) {
nativeRequestBuilder = client.prepareGet(endpoint);
} else if (request.getMethod().equals(HttpMethod.DELETE)) {
nativeRequestBuilder = client.prepareDelete(endpoint);
} else if (request.getMethod().equals(HttpMethod.PUT)) {
nativeRequestBuilder = client.preparePut(endpoint);
} else if (request.getMethod().equals(HttpMethod.POST)) {
nativeRequestBuilder = client.preparePost(endpoint);
} else {
throw new UnsupportedOperationException(request.getMethod());
}
Payload payload = request.getPayload();
if(payload != null) {
//changeRequestContentToBytes(request);
setPayload(nativeRequestBuilder, payload);
} else {
nativeRequestBuilder.addHeader(HttpHeaders.CONTENT_LENGTH, "0");
}
nativeRequestBuilder.addHeader(HttpHeaders.USER_AGENT, USER_AGENT);
for (String header : request.getHeaders().keySet()) {
for (String value : request.getHeaders().get(header)) {
nativeRequestBuilder.addHeader(header, value);
}
}
return nativeRequestBuilder.build();
}
@VisibleForTesting
void changeRequestContentToBytes(HttpRequest request) {
Payload content = request.getPayload();
if (content == null || content instanceof ByteArrayPayload) {
//just return
} else if (content instanceof StringPayload) {
String string = ((StringPayload) content).getRawContent();
request.setPayload(string.getBytes());
} else if (content instanceof InputStreamPayload || content instanceof FilePayload) {
InputStream i = content.getContent();
try {
try {
request.setPayload(ByteStreams.toByteArray(i));
} catch (IOException e) {
throw Throwables.propagate(e);
}
} finally {
Closeables.closeQuietly(i);
}
} else {
throw new UnsupportedOperationException("Content not supported " + content.getClass());
}
}
void setPayload(AsyncHttpClient.BoundRequestBuilder requestBuilder, Payload payload) {
try {
requestBuilder.setBody(ByteStreams.toByteArray(payload.getContent()));
} catch(IOException e) {
throw Throwables.propagate(e);
}
}
}
@Singleton
public static class ConvertToJCloudsResponse implements Function<Response, HttpResponse> {
public HttpResponse apply(Response nativeResponse) {
HttpResponse response = new HttpResponse();
response.setStatusCode(nativeResponse.getStatusCode());
for (Pair<String, String> header : nativeResponse.getHeaders()) {
response.getHeaders().put(header.getFirst(), header.getSecond());
}
try {
String responseBody = nativeResponse.getResponseBody();
if(responseBody != null) {
response.setContent(Utils.toInputStream(responseBody));
}
} catch(IOException e) {
throw Throwables.propagate(e);
}
return response;
}
}
}

View File

@ -0,0 +1,151 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.http.ning.config;
import com.google.inject.Provides;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandExecutorServiceImpl;
import org.jclouds.http.ning.NingHttpCommandExecutorService;
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
import javax.inject.Singleton;
/**
* Configures {@link NingHttpCommandExecutorService}.
*
* Note that this uses threads
*
* @author Sam Tunnicliffe
* @author Adrian Cole
*/
@ConfiguresHttpCommandExecutorService
public class NingHttpCommandExecutorServiceModule extends AbstractModule {
@Override
protected void configure() {
bindClient();
}
@Singleton
@Provides
AsyncHttpClient provideNingClient() {
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().
setFollowRedirects(true).
build();
return new AsyncHttpClient(config);
}
//
// @Singleton
// @Provides
// HttpParams newBasicHttpParams(HttpUtils utils) {
// BasicHttpParams params = new BasicHttpParams();
//
// params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
// .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, true)
// .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true).setParameter(
// CoreProtocolPNames.ORIGIN_SERVER, "jclouds/1.0");
//
// if (utils.getConnectionTimeout() > 0) {
// params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, utils
// .getConnectionTimeout());
// }
//
// if (utils.getSocketOpenTimeout() > 0) {
// params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, utils.getSocketOpenTimeout());
// }
//
// if (utils.getMaxConnections() > 0)
// ConnManagerParams.setMaxTotalConnections(params, utils.getMaxConnections());
//
// if (utils.getMaxConnectionsPerHost() > 0) {
// ConnPerRoute connectionsPerRoute = new ConnPerRouteBean(utils.getMaxConnectionsPerHost());
// ConnManagerParams.setMaxConnectionsPerRoute(params, connectionsPerRoute);
// }
// HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
// return params;
// }
//
// @Singleton
// @Provides
// X509HostnameVerifier newHostnameVerifier(HttpUtils utils) {
// return utils.relaxHostname() ? SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
// : SSLSocketFactory.STRICT_HOSTNAME_VERIFIER;
// }
//
// @Singleton
// @Provides
// ClientConnectionManager newClientConnectionManager(HttpParams params,
// X509HostnameVerifier verifier, Closer closer) throws NoSuchAlgorithmException,
// KeyManagementException {
//
// SchemeRegistry schemeRegistry = new SchemeRegistry();
//
// Scheme http = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
// SSLContext context = SSLContext.getInstance("TLS");
//
// context.init(null, null, null);
// SSLSocketFactory sf = new SSLSocketFactory(context);
// sf.setHostnameVerifier(verifier);
//
// Scheme https = new Scheme("https", sf, 443);
//
// SchemeRegistry sr = new SchemeRegistry();
// sr.register(http);
// sr.register(https);
//
// schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
// final ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
// closer.addToClose(new Closeable() {
// @Override
// public void close() throws IOException {
// cm.shutdown();
// }
// });
// return cm;
// }
//
// @Provides
// @Singleton
// HttpClient newDefaultHttpClient(HttpUtils utils, BasicHttpParams params,
// ClientConnectionManager cm) {
// DefaultHttpClient client = new DefaultHttpClient(cm, params);
// if (utils.useSystemProxies()) {
// ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(client
// .getConnectionManager().getSchemeRegistry(), ProxySelector.getDefault());
// client.setRoutePlanner(routePlanner);
// }
// return client;
// }
protected void bindClient() {
bind(HttpCommandExecutorService.class).to(NingHttpCommandExecutorService.class).in(
Scopes.SINGLETON);
bind(TransformingHttpCommandExecutorService.class).to(
TransformingHttpCommandExecutorServiceImpl.class).in(Scopes.SINGLETON);
}
}

View File

@ -0,0 +1,77 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* 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 org.jclouds.http.ning;
import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
import static org.jclouds.Constants.*;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST;
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
import static org.testng.Assert.assertEquals;
import java.net.MalformedURLException;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.collect.ImmutableMap;
import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest;
import org.jclouds.http.ning.config.NingHttpCommandExecutorServiceModule;
import org.jclouds.http.options.GetOptions;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.inject.Module;
/**
* Tests the functionality of the {@link ApacheHCHttpCommandExecutorService}
*
* @author Adrian Cole
*/
@Test
public class NingHttpCommandExecutorServiceTest extends BaseHttpCommandExecutorServiceIntegrationTest {
static {
System.setProperty("http.conn-manager.timeout", 1000 + "");
}
protected Module createConnectionModule() {
return new NingHttpCommandExecutorServiceModule();
}
protected void addConnectionProperties(Properties props) {
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 20 + "");
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 0 + "");
props.setProperty(PROPERTY_CONNECTION_TIMEOUT, 100 + "");
props.setProperty(PROPERTY_SO_TIMEOUT, 100 + "");
props.setProperty(PROPERTY_IO_WORKER_THREADS, 3 + "");
props.setProperty(PROPERTY_USER_THREADS, 0 + "");
}
@Test(invocationCount = 1, timeOut = 50000)
public void testSpaceInUri() throws MalformedURLException, ExecutionException,
InterruptedException, TimeoutException {
assertEquals(client.synch("sp ace").trim(), XML);
}
@Override
public void testGetBigFile() throws MalformedURLException, ExecutionException, InterruptedException, TimeoutException {
//don't run it
}
}

View File

@ -95,10 +95,14 @@
<id>clojure</id> <id>clojure</id>
<url>http://build.clojure.org/releases</url> <url>http://build.clojure.org/releases</url>
</repository> </repository>
<repository> <repository>
<id>clojars.org</id> <id>clojars.org</id>
<url>http://clojars.org/repo</url> <url>http://clojars.org/repo</url>
</repository> </repository>
<repository>
<id>ning.http.client</id>
<url>http://oss.sonatype.org/service/local/repositories/snapshots/content</url>
</repository>
</repositories> </repositories>
<distributionManagement> <distributionManagement>