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. portable abstractions or cloud-specific features.
our current version is 1.0-beta-3 our current version is 1.0-beta-3
our dev version is 1.0-SNAPSHOT
our compute api supports: ec2, rackspace, rimuhosting, vcloud, terremark, our compute api supports: ec2, rackspace, rimuhosting, vcloud, terremark,
hosting.com hosting.com

View File

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

View File

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

View File

@ -19,10 +19,13 @@
package org.jclouds.http.apachehc; package org.jclouds.http.apachehc;
import java.io.File; import java.io.File;
import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import javax.annotation.Resource;
import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import org.apache.http.Header; import org.apache.http.Header;
@ -36,15 +39,20 @@ import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.Payload; import org.jclouds.http.Payload;
import org.jclouds.logging.Logger;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton
public class ApacheHCUtils { public class ApacheHCUtils {
public static final String USER_AGENT = "jclouds/1.0 httpclient/4.0.1"; 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(); String uri = request.getEndpoint().toASCIIString();
if (request.getEndpoint().getQuery() != null) if (request.getEndpoint().getQuery() != null)
@ -77,7 +85,7 @@ public class ApacheHCUtils {
return apacheRequest; return apacheRequest;
} }
public static void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Object content, public void addEntityForContent(HttpEntityEnclosingRequest apacheRequest, Object content,
String contentType, long length) { String contentType, long length) {
if (content instanceof InputStream) { if (content instanceof InputStream) {
InputStream inputStream = (InputStream) content; InputStream inputStream = (InputStream) content;
@ -109,11 +117,12 @@ public class ApacheHCUtils {
assert (apacheRequest.getEntity() != null); assert (apacheRequest.getEntity() != null);
} }
public static HttpResponse convertToJCloudsResponse(org.apache.http.HttpResponse apacheResponse) public HttpResponse convertToJCloudsResponse(org.apache.http.HttpResponse apacheResponse)
throws IOException { throws IOException {
HttpResponse response = new HttpResponse(); HttpResponse response = new HttpResponse();
if (apacheResponse.getEntity() != null) { if (apacheResponse.getEntity() != null) {
response.setContent(apacheResponse.getEntity().getContent()); response
.setContent(new ConsumeOnCloseInputStream(apacheResponse.getEntity().getContent()));
} }
for (Header header : apacheResponse.getAllHeaders()) { for (Header header : apacheResponse.getAllHeaders()) {
response.getHeaders().put(header.getName(), header.getValue()); response.getHeaders().put(header.getName(), header.getValue());
@ -122,4 +131,32 @@ public class ApacheHCUtils {
response.setMessage(apacheResponse.getStatusLine().getReasonPhrase()); response.setMessage(apacheResponse.getStatusLine().getReasonPhrase());
return response; 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; 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 java.util.Properties;
import org.jclouds.http.BaseHttpCommandExecutorServiceTest; import org.jclouds.http.BaseHttpCommandExecutorServiceTest;
@ -44,11 +39,7 @@ public class ApacheHCHttpCommandExecutorServiceTest extends BaseHttpCommandExecu
} }
protected void addConnectionProperties(Properties props) { 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> <artifactId>jclouds-bouncycastle</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </dependency>
<!-- not reliable enough yet
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>org.mortbay.jetty</groupId>
<artifactId>jclouds-httpnio</artifactId> <artifactId>jetty</artifactId>
<version>${project.version}</version> <scope>test</scope>
</dependency> </dependency>
-->
</dependencies> </dependencies>
<build> <build>

View File

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

View File

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