issue 107: revised hc module, and included it in the enterprise config

This commit is contained in:
Adrian Cole 2010-02-04 21:51:37 -08:00
parent 4582c57c72
commit 268bc2ec04
10 changed files with 169 additions and 70 deletions

View File

@ -5,6 +5,7 @@ and reuse your java development skills. Our api allows you to freedom to use
portable abstractions or cloud-specific features.
our current version is 1.0-beta-3
our dev version is 1.0-SNAPSHOT
our compute api supports: ec2, rackspace, rimuhosting, vcloud, terremark,
hosting.com
@ -51,4 +52,4 @@ Downloads:
Links:
* project page: http://code.google.com/p/jclouds/
* dev group: http://groups.google.com/group/jclouds-dev
* twitter: http://twitter.com/jclouds
* twitter: http://twitter.com/jclouds

View File

@ -137,7 +137,7 @@ public abstract class BaseJettyTest {
protected Injector injector;
private AtomicInteger cycle = new AtomicInteger(0);
private Server server2;
private RestContext<IntegrationTestAsyncClient, IntegrationTestClient> context;
protected RestContext<IntegrationTestAsyncClient, IntegrationTestClient> context;
private int testPort;
static final Pattern actionPattern = Pattern.compile("/objects/(.*)/action/([a-z]*);?(.*)");

View File

@ -19,20 +19,20 @@
package org.jclouds.http.apachehc;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import java.io.IOException;
import java.net.URI;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import javax.annotation.PreDestroy;
import javax.inject.Named;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpHost;
import org.apache.http.HttpVersion;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRoute;
import org.apache.http.conn.params.ConnPerRouteBean;
@ -43,19 +43,16 @@ import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.jclouds.Constants;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.handlers.DelegatingErrorHandler;
import org.jclouds.http.handlers.DelegatingRetryHandler;
import org.jclouds.http.internal.BaseHttpCommandExecutorService;
import org.jclouds.http.internal.HttpWire;
import com.google.common.base.Function;
import com.google.common.collect.MapMaker;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
/**
@ -66,8 +63,11 @@ import com.google.inject.Inject;
*/
public class ApacheHCHttpCommandExecutorService extends
BaseHttpCommandExecutorService<HttpEntityEnclosingRequest> {
private final ConcurrentMap<URI, HttpClient> poolMap;
@VisibleForTesting
boolean isOpen = true;
private final BasicHttpParams params;
private final ThreadSafeClientConnManager cm;
private final ApacheHCUtils utils;
@Inject
ApacheHCHttpCommandExecutorService(
@ -76,58 +76,64 @@ public class ApacheHCHttpCommandExecutorService extends
DelegatingErrorHandler errorHandler,
HttpWire wire,
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT) final int globalMaxConnections,
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST) final int globalMaxConnectionsPerHost) {
@Named(Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST) final int globalMaxConnectionsPerHost,
ApacheHCUtils utils) {
super(ioWorkerExecutor, retryHandler, errorHandler, wire);
checkArgument(globalMaxConnections > 0, Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT
+ " must be greater than zero");
checkArgument(globalMaxConnectionsPerHost > 0, Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST
+ " must be greater than zero");
poolMap = new MapMaker().makeComputingMap(new Function<URI, HttpClient>() {
public HttpClient apply(URI endPoint) {
checkArgument(endPoint.getHost() != null, String.format(
"endPoint.getHost() is null for %s", endPoint));
HttpParams params = new BasicHttpParams();
try {
// TODO: have this use our executor service
// TODO: implement wire logging
ConnManagerParams.setMaxTotalConnections(params, globalMaxConnections);
ConnPerRoute connectionsPerRoute = new ConnPerRouteBean(globalMaxConnectionsPerHost);
ConnManagerParams.setMaxConnectionsPerRoute(params, connectionsPerRoute);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
SchemeRegistry schemeRegistry = new SchemeRegistry();
if (endPoint.getScheme().equals("http"))
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(),
80));
else
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(),
443));
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
return new DefaultHttpClient(cm, params);
} catch (RuntimeException e) {
logger.error(e, "error creating entry for %s", endPoint);
throw e;
}
}
});
this.utils = utils;
params = new BasicHttpParams();
// TODO: have this use our executor service, if possible
if (globalMaxConnections > 0)
ConnManagerParams.setMaxTotalConnections(params, globalMaxConnections);
if (globalMaxConnectionsPerHost > 0) {
ConnPerRoute connectionsPerRoute = new ConnPerRouteBean(globalMaxConnectionsPerHost);
ConnManagerParams.setMaxConnectionsPerRoute(params, connectionsPerRoute);
}
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
cm = new ThreadSafeClientConnManager(params, schemeRegistry);
}
@Override
protected HttpEntityEnclosingRequest convert(HttpRequest request) throws IOException {
return ApacheHCUtils.convertToApacheRequest(request);
return utils.convertToApacheRequest(request);
}
@Override
protected HttpResponse invoke(HttpEntityEnclosingRequest nativeRequest) throws IOException {
checkState(isOpen, "http executor not open");
org.apache.http.HttpResponse nativeResponse = executeRequest(nativeRequest);
return utils.convertToJCloudsResponse(nativeResponse);
}
private org.apache.http.HttpResponse executeRequest(HttpEntityEnclosingRequest nativeRequest)
throws IOException, ClientProtocolException {
URI endpoint = URI.create(nativeRequest.getRequestLine().getUri());
HttpClient client = poolMap.get(HttpUtils.createBaseEndpointFor(endpoint));
assert (client != null) : "pool for endpoint null " + endpoint;
HttpClient client = new DefaultHttpClient(cm, params);
HttpHost host = new HttpHost(endpoint.getHost(), endpoint.getPort(), endpoint.getScheme());
org.apache.http.HttpResponse nativeResponse = client.execute(host, nativeRequest);
return ApacheHCUtils.convertToJCloudsResponse(nativeResponse);
return nativeResponse;
}
@PreDestroy
public void close() {
// TODO test
isOpen = false;
cm.shutdown();
}
@Override
protected void finalize() throws Throwable {
try {
close();
} finally {
super.finalize();
}
}
@Override
protected void cleanup(HttpEntityEnclosingRequest nativeResponse) {
// TODO
// No cleanup necessary
}
}

View File

@ -19,10 +19,13 @@
package org.jclouds.http.apachehc;
import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import javax.annotation.Resource;
import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders;
import org.apache.http.Header;
@ -36,15 +39,20 @@ import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.Payload;
import org.jclouds.logging.Logger;
/**
*
* @author Adrian Cole
*/
@Singleton
public class ApacheHCUtils {
public static final String USER_AGENT = "jclouds/1.0 httpclient/4.0.1";
public static HttpEntityEnclosingRequest convertToApacheRequest(HttpRequest request) {
@Resource
protected Logger logger = Logger.NULL;
public HttpEntityEnclosingRequest convertToApacheRequest(HttpRequest request) {
String uri = request.getEndpoint().toASCIIString();
if (request.getEndpoint().getQuery() != null)
@ -77,7 +85,7 @@ public class ApacheHCUtils {
return apacheRequest;
}
public static void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Object content,
public void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Object content,
String contentType, long length) {
if (content instanceof InputStream) {
InputStream inputStream = (InputStream) content;
@ -109,11 +117,12 @@ public class ApacheHCUtils {
assert (apacheRequest.getEntity() != null);
}
public static HttpResponse convertToJCloudsResponse(org.apache.http.HttpResponse apacheResponse)
public HttpResponse convertToJCloudsResponse(org.apache.http.HttpResponse apacheResponse)
throws IOException {
HttpResponse response = new HttpResponse();
if (apacheResponse.getEntity() != null) {
response.setContent(apacheResponse.getEntity().getContent());
response
.setContent(new ConsumeOnCloseInputStream(apacheResponse.getEntity().getContent()));
}
for (Header header : apacheResponse.getAllHeaders()) {
response.getHeaders().put(header.getName(), header.getValue());
@ -122,4 +131,32 @@ public class ApacheHCUtils {
response.setMessage(apacheResponse.getStatusLine().getReasonPhrase());
return response;
}
class ConsumeOnCloseInputStream extends FilterInputStream {
protected ConsumeOnCloseInputStream(InputStream in) {
super(in);
}
boolean closed;
@Override
public void close() throws IOException {
try {
if (!closed) {
int result = 0;
while (result != -1) {
result = read();
}
}
} catch (IOException e) {
logger.warn(e, "error reading stream");
} finally {
closed = true;
super.close();
}
}
}
}

View File

@ -18,11 +18,6 @@
*/
package org.jclouds.http.apachehc;
import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST;
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
import java.util.Properties;
import org.jclouds.http.BaseHttpCommandExecutorServiceTest;
@ -44,11 +39,7 @@ public class ApacheHCHttpCommandExecutorServiceTest extends BaseHttpCommandExecu
}
protected void addConnectionProperties(Properties props) {
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 50 + "");
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 50 + "");
// IO workers not used in this executor
props.setProperty(PROPERTY_IO_WORKER_THREADS, 0 + "");
props.setProperty(PROPERTY_USER_THREADS, 5 + "");
}
}

View File

@ -47,13 +47,11 @@
<artifactId>jclouds-bouncycastle</artifactId>
<version>${project.version}</version>
</dependency>
<!-- not reliable enough yet
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-httpnio</artifactId>
<version>${project.version}</version>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<scope>test</scope>
</dependency>
-->
</dependencies>
<build>

View File

@ -24,6 +24,8 @@ import org.jclouds.concurrent.config.ConfiguresExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.date.joda.config.JodaDateServiceModule;
import org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceModule;
import org.jclouds.http.apachehc.config.ApacheHCHttpCommandExecutorServiceModule;
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
/**
* Configures Enterprise-grade components
@ -32,6 +34,7 @@ import org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceM
*
*/
@ConfiguresExecutorService
@ConfiguresHttpCommandExecutorService
public class EnterpriseConfigurationModule extends ExecutorServiceModule {
public EnterpriseConfigurationModule(ExecutorService userThreads, ExecutorService ioThreads) {
@ -46,6 +49,7 @@ public class EnterpriseConfigurationModule extends ExecutorServiceModule {
protected void configure() {
install(new BouncyCastleEncryptionServiceModule());
install(new JodaDateServiceModule());
install(new ApacheHCHttpCommandExecutorServiceModule());
}
}

View File

@ -0,0 +1,53 @@
/**
*
* 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.enterprise.config;
import static org.jclouds.Constants.PROPERTY_IO_WORKER_THREADS;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT;
import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST;
import static org.jclouds.Constants.PROPERTY_USER_THREADS;
import java.util.Properties;
import org.jclouds.http.BaseHttpCommandExecutorServiceTest;
import org.testng.annotations.Test;
import com.google.inject.Module;
/**
* Tests the functionality of the {@link EnterpriseConfigurationModule}
*
* @author Adrian Cole
*/
@Test
public class EnterpriseConfigurationModuleTest extends BaseHttpCommandExecutorServiceTest {
protected Module createConnectionModule() {
return new EnterpriseConfigurationModule();
}
protected void addConnectionProperties(Properties props) {
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_CONTEXT, 50 + "");
props.setProperty(PROPERTY_MAX_CONNECTIONS_PER_HOST, 50 + "");
// IO workers not used in this executor
props.setProperty(PROPERTY_IO_WORKER_THREADS, 0 + "");
props.setProperty(PROPERTY_USER_THREADS, 5 + "");
}
}

View File

@ -41,7 +41,12 @@
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-enterprise</artifactId>
<artifactId>jclouds-joda</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-bouncycastle</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -20,7 +20,9 @@ package org.jclouds.gae.config;
import org.jclouds.concurrent.SingleThreaded;
import org.jclouds.concurrent.config.ConfiguresExecutorService;
import org.jclouds.enterprise.config.EnterpriseConfigurationModule;
import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.date.joda.config.JodaDateServiceModule;
import org.jclouds.encryption.bouncycastle.config.BouncyCastleEncryptionServiceModule;
import org.jclouds.gae.GaeHttpCommandExecutorService;
import org.jclouds.http.HttpCommandExecutorService;
import org.jclouds.http.TransformingHttpCommandExecutorService;
@ -40,7 +42,7 @@ import com.google.inject.Provides;
@ConfiguresHttpCommandExecutorService
@ConfiguresExecutorService
@SingleThreaded
public class GoogleAppEngineConfigurationModule extends EnterpriseConfigurationModule {
public class GoogleAppEngineConfigurationModule extends ExecutorServiceModule {
public GoogleAppEngineConfigurationModule() {
super(Executors.sameThreadExecutor(), Executors.sameThreadExecutor());
@ -49,6 +51,8 @@ public class GoogleAppEngineConfigurationModule extends EnterpriseConfigurationM
@Override
protected void configure() {
super.configure();
install(new BouncyCastleEncryptionServiceModule());
install(new JodaDateServiceModule());
bind(HttpCommandExecutorService.class).to(GaeHttpCommandExecutorService.class);
bind(TransformingHttpCommandExecutorService.class).to(
TransformingHttpCommandExecutorServiceImpl.class);