mirror of https://github.com/apache/jclouds.git
issue 66: refactored http clients to not be bound to a single endpoint such that redirects can be assigned to another host
git-svn-id: http://jclouds.googlecode.com/svn/trunk@1458 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
parent
00cc4c4c4e
commit
78d0b7afd3
|
@ -23,57 +23,131 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.httpnio.config;
|
package org.jclouds.http.httpnio.config;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.net.MalformedURLException;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.net.URI;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
import org.jclouds.http.HttpConstants;
|
import org.apache.http.ConnectionReuseStrategy;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.impl.DefaultConnectionReuseStrategy;
|
||||||
|
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
||||||
|
import org.apache.http.nio.NHttpConnection;
|
||||||
|
import org.apache.http.nio.entity.BufferingNHttpEntity;
|
||||||
|
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
||||||
|
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
|
||||||
|
import org.apache.http.nio.reactor.IOReactorException;
|
||||||
|
import org.apache.http.nio.util.ByteBufferAllocator;
|
||||||
|
import org.apache.http.nio.util.HeapByteBufferAllocator;
|
||||||
|
import org.apache.http.params.BasicHttpParams;
|
||||||
|
import org.apache.http.params.CoreConnectionPNames;
|
||||||
|
import org.apache.http.params.CoreProtocolPNames;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
|
import org.apache.http.protocol.BasicHttpProcessor;
|
||||||
|
import org.apache.http.protocol.RequestConnControl;
|
||||||
|
import org.apache.http.protocol.RequestContent;
|
||||||
|
import org.apache.http.protocol.RequestExpectContinue;
|
||||||
|
import org.apache.http.protocol.RequestTargetHost;
|
||||||
|
import org.apache.http.protocol.RequestUserAgent;
|
||||||
|
import org.jclouds.command.pool.PoolConstants;
|
||||||
|
import org.jclouds.command.pool.config.FutureCommandConnectionPoolClientModule;
|
||||||
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpFutureCommandClient;
|
import org.jclouds.http.HttpFutureCommandClient;
|
||||||
import org.jclouds.http.config.HttpFutureCommandClientModule;
|
|
||||||
import org.jclouds.http.httpnio.config.internal.NonSSLHttpNioConnectionPoolClientModule;
|
|
||||||
import org.jclouds.http.httpnio.config.internal.SSLHttpNioConnectionPoolClientModule;
|
|
||||||
import org.jclouds.http.httpnio.pool.HttpNioConnectionPoolClient;
|
import org.jclouds.http.httpnio.pool.HttpNioConnectionPoolClient;
|
||||||
|
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionPool;
|
||||||
|
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandExecutionHandler;
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
|
import com.google.inject.Scopes;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.TypeLiteral;
|
||||||
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
import com.google.inject.assistedinject.FactoryProvider;
|
||||||
import com.google.inject.name.Named;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures {@link HttpNioConnectionPoolClient}
|
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@HttpFutureCommandClientModule
|
public class HttpNioConnectionPoolClientModule extends
|
||||||
public class HttpNioConnectionPoolClientModule extends AbstractModule {
|
FutureCommandConnectionPoolClientModule<NHttpConnection> {
|
||||||
|
|
||||||
@Named(HttpConstants.PROPERTY_HTTP_SECURE)
|
@Provides
|
||||||
boolean isSecure;
|
// @Singleton per uri...
|
||||||
|
public AsyncNHttpClientHandler provideAsyncNttpClientHandler(BasicHttpProcessor httpProcessor,
|
||||||
|
NHttpRequestExecutionHandler execHandler, ConnectionReuseStrategy connStrategy,
|
||||||
|
ByteBufferAllocator allocator, HttpParams params) {
|
||||||
|
return new AsyncNHttpClientHandler(httpProcessor, execHandler, connStrategy, allocator,
|
||||||
|
params);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public BasicHttpProcessor provideClientProcessor() {
|
||||||
|
BasicHttpProcessor httpproc = new BasicHttpProcessor();
|
||||||
|
httpproc.addInterceptor(new RequestContent());
|
||||||
|
httpproc.addInterceptor(new RequestTargetHost());
|
||||||
|
httpproc.addInterceptor(new RequestConnControl());
|
||||||
|
httpproc.addInterceptor(new RequestUserAgent());
|
||||||
|
httpproc.addInterceptor(new RequestExpectContinue());
|
||||||
|
return httpproc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public HttpParams provideHttpParams() {
|
||||||
|
HttpParams params = new BasicHttpParams();
|
||||||
|
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000).setIntParameter(
|
||||||
|
CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024).setBooleanParameter(
|
||||||
|
CoreConnectionPNames.STALE_CONNECTION_CHECK, false).setBooleanParameter(
|
||||||
|
CoreConnectionPNames.TCP_NODELAY, true).setParameter(
|
||||||
|
CoreProtocolPNames.ORIGIN_SERVER, "jclouds/1.0");
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void configure() {
|
||||||
|
super.configure();
|
||||||
|
bind(HttpFutureCommandClient.class).to(HttpNioConnectionPoolClient.class);
|
||||||
|
bind(new TypeLiteral<BlockingQueue<HttpFutureCommand<?>>>() {
|
||||||
|
}).to(new TypeLiteral<LinkedBlockingQueue<HttpFutureCommand<?>>>() {
|
||||||
|
}).in(Scopes.SINGLETON);
|
||||||
|
bind(HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class).toProvider(
|
||||||
|
FactoryProvider.newFactory(
|
||||||
|
HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class,
|
||||||
|
InjectableBufferingNHttpEntity.class));// .in(Scopes.SINGLETON); but per URI
|
||||||
|
bind(NHttpRequestExecutionHandler.class).to(HttpNioFutureCommandExecutionHandler.class).in(
|
||||||
|
Scopes.SINGLETON);
|
||||||
|
bind(ConnectionReuseStrategy.class).to(DefaultConnectionReuseStrategy.class).in(
|
||||||
|
Scopes.SINGLETON);
|
||||||
|
bind(ByteBufferAllocator.class).to(HeapByteBufferAllocator.class);
|
||||||
|
bind(HttpNioFutureCommandConnectionPool.Factory.class).toProvider(
|
||||||
|
FactoryProvider.newFactory(
|
||||||
|
new TypeLiteral<HttpNioFutureCommandConnectionPool.Factory>() {
|
||||||
|
}, new TypeLiteral<HttpNioFutureCommandConnectionPool>() {
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class InjectableBufferingNHttpEntity extends BufferingNHttpEntity {
|
||||||
|
@Inject
|
||||||
|
public InjectableBufferingNHttpEntity(@Assisted HttpEntity httpEntity,
|
||||||
|
ByteBufferAllocator allocator) {
|
||||||
|
super(httpEntity, allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
public BlockingQueue<NHttpConnection> provideAvailablePool(
|
||||||
requestInjection(this);
|
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max) throws Exception {
|
||||||
if (isSecure)
|
return new ArrayBlockingQueue<NHttpConnection>(max, true);
|
||||||
install(new SSLHttpNioConnectionPoolClientModule());
|
|
||||||
else
|
|
||||||
install(new NonSSLHttpNioConnectionPoolClientModule());
|
|
||||||
bind(HttpFutureCommandClient.class).to(HttpNioConnectionPoolClient.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
@Provides
|
||||||
protected InetSocketAddress provideAddress(URI endPoint) {
|
// @Singleton per uri...
|
||||||
return new InetSocketAddress(endPoint.getHost(), endPoint.getPort());
|
public DefaultConnectingIOReactor provideDefaultConnectingIOReactor(
|
||||||
|
@Named(PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS) int ioWorkerThreads,
|
||||||
|
HttpParams params) throws IOReactorException {
|
||||||
|
return new DefaultConnectingIOReactor(ioWorkerThreads, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
protected URI provideAddress(@Named(HttpConstants.PROPERTY_HTTP_ADDRESS) String address,
|
|
||||||
@Named(HttpConstants.PROPERTY_HTTP_PORT) int port,
|
|
||||||
@Named(HttpConstants.PROPERTY_HTTP_SECURE) boolean isSecure)
|
|
||||||
throws MalformedURLException {
|
|
||||||
|
|
||||||
return URI.create(String.format("%1$s://%2$s:%3$s", isSecure ? "https" : "http", address,
|
|
||||||
port));
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,172 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF 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.httpnio.config.internal;
|
|
||||||
|
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
import org.apache.http.ConnectionReuseStrategy;
|
|
||||||
import org.apache.http.HttpEntity;
|
|
||||||
import org.apache.http.impl.DefaultConnectionReuseStrategy;
|
|
||||||
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
|
||||||
import org.apache.http.nio.NHttpConnection;
|
|
||||||
import org.apache.http.nio.entity.BufferingNHttpEntity;
|
|
||||||
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
|
||||||
import org.apache.http.nio.protocol.NHttpRequestExecutionHandler;
|
|
||||||
import org.apache.http.nio.reactor.IOEventDispatch;
|
|
||||||
import org.apache.http.nio.reactor.IOReactorException;
|
|
||||||
import org.apache.http.nio.util.ByteBufferAllocator;
|
|
||||||
import org.apache.http.nio.util.HeapByteBufferAllocator;
|
|
||||||
import org.apache.http.params.BasicHttpParams;
|
|
||||||
import org.apache.http.params.CoreConnectionPNames;
|
|
||||||
import org.apache.http.params.CoreProtocolPNames;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.apache.http.protocol.BasicHttpProcessor;
|
|
||||||
import org.apache.http.protocol.RequestConnControl;
|
|
||||||
import org.apache.http.protocol.RequestContent;
|
|
||||||
import org.apache.http.protocol.RequestExpectContinue;
|
|
||||||
import org.apache.http.protocol.RequestTargetHost;
|
|
||||||
import org.apache.http.protocol.RequestUserAgent;
|
|
||||||
import org.jclouds.command.pool.PoolConstants;
|
|
||||||
import org.jclouds.command.pool.config.FutureCommandConnectionPoolClientModule;
|
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
|
||||||
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionHandle;
|
|
||||||
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandConnectionPool;
|
|
||||||
import org.jclouds.http.httpnio.pool.HttpNioFutureCommandExecutionHandler;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.Provides;
|
|
||||||
import com.google.inject.Scopes;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import com.google.inject.TypeLiteral;
|
|
||||||
import com.google.inject.assistedinject.Assisted;
|
|
||||||
import com.google.inject.assistedinject.FactoryProvider;
|
|
||||||
import com.google.inject.name.Named;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* // TODO: Adrian: Document this!
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public abstract class BaseHttpNioConnectionPoolClientModule extends
|
|
||||||
FutureCommandConnectionPoolClientModule<NHttpConnection> {
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public AsyncNHttpClientHandler provideAsyncNttpClientHandler(
|
|
||||||
BasicHttpProcessor httpProcessor,
|
|
||||||
NHttpRequestExecutionHandler execHandler,
|
|
||||||
ConnectionReuseStrategy connStrategy,
|
|
||||||
ByteBufferAllocator allocator, HttpParams params) {
|
|
||||||
return new AsyncNHttpClientHandler(httpProcessor, execHandler,
|
|
||||||
connStrategy, allocator, params);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public BasicHttpProcessor provideClientProcessor() {
|
|
||||||
BasicHttpProcessor httpproc = new BasicHttpProcessor();
|
|
||||||
httpproc.addInterceptor(new RequestContent());
|
|
||||||
httpproc.addInterceptor(new RequestTargetHost());
|
|
||||||
httpproc.addInterceptor(new RequestConnControl());
|
|
||||||
httpproc.addInterceptor(new RequestUserAgent());
|
|
||||||
httpproc.addInterceptor(new RequestExpectContinue());
|
|
||||||
return httpproc;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public HttpParams provideHttpParams() {
|
|
||||||
HttpParams params = new BasicHttpParams();
|
|
||||||
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
|
|
||||||
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
|
|
||||||
8 * 1024).setBooleanParameter(
|
|
||||||
CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
|
|
||||||
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
|
|
||||||
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "jclouds/1.0");
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void configure() {
|
|
||||||
super.configure();
|
|
||||||
bind(new TypeLiteral<BlockingQueue<HttpFutureCommand<?>>>() {
|
|
||||||
}).to(new TypeLiteral<LinkedBlockingQueue<HttpFutureCommand<?>>>() {
|
|
||||||
}).in(Scopes.SINGLETON);
|
|
||||||
bind(
|
|
||||||
HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class)
|
|
||||||
.toProvider(
|
|
||||||
FactoryProvider
|
|
||||||
.newFactory(
|
|
||||||
HttpNioFutureCommandExecutionHandler.ConsumingNHttpEntityFactory.class,
|
|
||||||
InjectableBufferingNHttpEntity.class))
|
|
||||||
.in(Scopes.SINGLETON);
|
|
||||||
bind(NHttpRequestExecutionHandler.class).to(
|
|
||||||
HttpNioFutureCommandExecutionHandler.class)
|
|
||||||
.in(Scopes.SINGLETON);
|
|
||||||
bind(ConnectionReuseStrategy.class).to(
|
|
||||||
DefaultConnectionReuseStrategy.class).in(Scopes.SINGLETON);
|
|
||||||
bind(ByteBufferAllocator.class).to(HeapByteBufferAllocator.class);
|
|
||||||
bind(
|
|
||||||
HttpNioFutureCommandConnectionPool.FutureCommandConnectionHandleFactory.class)
|
|
||||||
.toProvider(
|
|
||||||
FactoryProvider
|
|
||||||
.newFactory(
|
|
||||||
new TypeLiteral<HttpNioFutureCommandConnectionPool.FutureCommandConnectionHandleFactory>() {
|
|
||||||
},
|
|
||||||
new TypeLiteral<HttpNioFutureCommandConnectionHandle>() {
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
static class InjectableBufferingNHttpEntity extends BufferingNHttpEntity {
|
|
||||||
@Inject
|
|
||||||
public InjectableBufferingNHttpEntity(@Assisted HttpEntity httpEntity,
|
|
||||||
ByteBufferAllocator allocator) {
|
|
||||||
super(httpEntity, allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockingQueue<NHttpConnection> provideAvailablePool(
|
|
||||||
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS) int max)
|
|
||||||
throws Exception {
|
|
||||||
return new ArrayBlockingQueue<NHttpConnection>(max, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public abstract IOEventDispatch provideClientEventDispatch(
|
|
||||||
AsyncNHttpClientHandler handler, HttpParams params)
|
|
||||||
throws Exception;
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
public DefaultConnectingIOReactor provideDefaultConnectingIOReactor(
|
|
||||||
@Named(PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS) int ioWorkerThreads,
|
|
||||||
HttpParams params) throws IOReactorException {
|
|
||||||
return new DefaultConnectingIOReactor(ioWorkerThreads, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF 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.httpnio.config.internal;
|
|
||||||
|
|
||||||
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
|
|
||||||
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
|
||||||
import org.apache.http.nio.reactor.IOEventDispatch;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.jclouds.http.httpnio.config.internal.BaseHttpNioConnectionPoolClientModule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* // TODO: Adrian: Document this!
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class NonSSLHttpNioConnectionPoolClientModule extends BaseHttpNioConnectionPoolClientModule {
|
|
||||||
public IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler, HttpParams params) throws Exception {
|
|
||||||
return new DefaultClientIOEventDispatch(
|
|
||||||
handler,
|
|
||||||
params);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
|
|
||||||
*
|
|
||||||
* ====================================================================
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF 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.httpnio.config.internal;
|
|
||||||
|
|
||||||
import org.apache.http.impl.nio.SSLClientIOEventDispatch;
|
|
||||||
import org.apache.http.nio.reactor.IOEventDispatch;
|
|
||||||
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
|
||||||
import org.apache.http.params.HttpParams;
|
|
||||||
import org.jclouds.http.httpnio.config.internal.BaseHttpNioConnectionPoolClientModule;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* // TODO: Adrian: Document this!
|
|
||||||
*
|
|
||||||
* @author Adrian Cole
|
|
||||||
*/
|
|
||||||
public class SSLHttpNioConnectionPoolClientModule extends BaseHttpNioConnectionPoolClientModule {
|
|
||||||
|
|
||||||
protected void configure() {
|
|
||||||
super.configure();
|
|
||||||
}
|
|
||||||
|
|
||||||
// note until a bug is fixed, you cannot annotate overriding methods with google annotations
|
|
||||||
// http://code.google.com/p/google-guice/issues/detail?id=347
|
|
||||||
@Override
|
|
||||||
public IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler, HttpParams params) throws Exception {
|
|
||||||
SSLContext context = SSLContext.getInstance("TLS");
|
|
||||||
context.init(null, null, null);
|
|
||||||
return new SSLClientIOEventDispatch(
|
|
||||||
handler,
|
|
||||||
context,
|
|
||||||
params);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,59 +23,61 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.httpnio.pool;
|
package org.jclouds.http.httpnio.pool;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import java.net.URI;
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import org.apache.http.nio.NHttpConnection;
|
|
||||||
import org.jclouds.command.pool.FutureCommandConnectionPoolClient;
|
|
||||||
import org.jclouds.http.*;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
import org.apache.http.nio.NHttpConnection;
|
||||||
|
import org.jclouds.command.pool.FutureCommandConnectionPoolClient;
|
||||||
|
import org.jclouds.http.HttpException;
|
||||||
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
|
import org.jclouds.http.HttpFutureCommandClient;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.http.HttpRequestFilter;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* // TODO: Adrian: Document this!
|
* // TODO: Adrian: Document this!
|
||||||
*
|
*
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class HttpNioConnectionPoolClient
|
public class HttpNioConnectionPoolClient extends
|
||||||
extends
|
FutureCommandConnectionPoolClient<URI, NHttpConnection, HttpFutureCommand<?>> implements
|
||||||
FutureCommandConnectionPoolClient<NHttpConnection, HttpFutureCommand<?>>
|
HttpFutureCommandClient {
|
||||||
implements HttpFutureCommandClient {
|
|
||||||
private List<HttpRequestFilter> requestFilters = Collections.emptyList();
|
|
||||||
|
|
||||||
public List<HttpRequestFilter> getRequestFilters() {
|
private List<HttpRequestFilter> requestFilters = Collections.emptyList();
|
||||||
return requestFilters;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(optional = true)
|
public List<HttpRequestFilter> getRequestFilters() {
|
||||||
public void setRequestFilters(List<HttpRequestFilter> requestFilters) {
|
return requestFilters;
|
||||||
this.requestFilters = requestFilters;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Inject(optional = true)
|
||||||
protected void invoke(HttpFutureCommand<?> command) {
|
public void setRequestFilters(List<HttpRequestFilter> requestFilters) {
|
||||||
HttpRequest request = (HttpRequest) command.getRequest();
|
this.requestFilters = requestFilters;
|
||||||
try {
|
}
|
||||||
for (HttpRequestFilter filter : getRequestFilters()) {
|
|
||||||
filter.filter(request);
|
|
||||||
}
|
|
||||||
super.invoke(command);
|
|
||||||
} catch (HttpException e) {
|
|
||||||
command.setException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
@Override
|
||||||
public HttpNioConnectionPoolClient(
|
protected void invoke(HttpFutureCommand<?> command) {
|
||||||
ExecutorService executor,
|
HttpRequest request = (HttpRequest) command.getRequest();
|
||||||
HttpNioFutureCommandConnectionPool httpFutureCommandConnectionHandleNHttpConnectionNioFutureCommandConnectionPool,
|
try {
|
||||||
|
for (HttpRequestFilter filter : getRequestFilters()) {
|
||||||
|
filter.filter(request);
|
||||||
|
}
|
||||||
|
super.invoke(command);
|
||||||
|
} catch (HttpException e) {
|
||||||
|
command.setException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public HttpNioConnectionPoolClient(ExecutorService executor,
|
||||||
|
HttpNioFutureCommandConnectionPool.Factory poolFactory,
|
||||||
BlockingQueue<HttpFutureCommand<?>> commandQueue) {
|
BlockingQueue<HttpFutureCommand<?>> commandQueue) {
|
||||||
super(
|
super(executor, poolFactory, commandQueue);
|
||||||
executor,
|
}
|
||||||
httpFutureCommandConnectionHandleNHttpConnectionNioFutureCommandConnectionPool,
|
|
||||||
commandQueue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
package org.jclouds.http.httpnio.pool;
|
package org.jclouds.http.httpnio.pool;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
@ -31,7 +32,6 @@ import org.apache.http.nio.NHttpConnection;
|
||||||
import org.jclouds.command.pool.FutureCommandConnectionHandle;
|
import org.jclouds.command.pool.FutureCommandConnectionHandle;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,25 +40,24 @@ import com.google.inject.assistedinject.Assisted;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class HttpNioFutureCommandConnectionHandle extends
|
public class HttpNioFutureCommandConnectionHandle extends
|
||||||
FutureCommandConnectionHandle<NHttpConnection, HttpFutureCommand<?>> {
|
FutureCommandConnectionHandle<URI, NHttpConnection, HttpFutureCommand<?>> {
|
||||||
|
|
||||||
@Inject
|
// currently not injected as we want to ensure we share the correct objects with the pool
|
||||||
public HttpNioFutureCommandConnectionHandle(
|
public HttpNioFutureCommandConnectionHandle(Semaphore maxConnections,
|
||||||
BlockingQueue<NHttpConnection> available, Semaphore maxConnections,
|
BlockingQueue<NHttpConnection> available, @Assisted URI endPoint,
|
||||||
@Assisted NHttpConnection conn,
|
@Assisted HttpFutureCommand<?> command, @Assisted NHttpConnection conn)
|
||||||
@Assisted HttpFutureCommand<?> command) throws InterruptedException {
|
throws InterruptedException {
|
||||||
super(maxConnections, command, conn, available);
|
super(maxConnections, available, endPoint, command, conn);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
public void startConnection() {
|
||||||
|
conn.getContext().setAttribute("command", command);
|
||||||
|
logger.trace("invoking %1$s on connection %2$s", command, conn);
|
||||||
|
conn.requestOutput();
|
||||||
|
}
|
||||||
|
|
||||||
public void startConnection() {
|
public void shutdownConnection() throws IOException {
|
||||||
conn.getContext().setAttribute("command", command);
|
conn.shutdown();
|
||||||
logger.trace("invoking %1$s on connection %2$s", command, conn);
|
}
|
||||||
conn.requestOutput();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdownConnection() throws IOException {
|
|
||||||
conn.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,20 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.httpnio.pool;
|
package org.jclouds.http.httpnio.pool;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import java.io.IOException;
|
||||||
import com.google.inject.name.Named;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.impl.nio.DefaultClientIOEventDispatch;
|
||||||
|
import org.apache.http.impl.nio.SSLClientIOEventDispatch;
|
||||||
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
|
||||||
import org.apache.http.nio.NHttpConnection;
|
import org.apache.http.nio.NHttpConnection;
|
||||||
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
import org.apache.http.nio.protocol.AsyncNHttpClientHandler;
|
||||||
|
@ -34,15 +45,16 @@ import org.apache.http.nio.reactor.IOEventDispatch;
|
||||||
import org.apache.http.nio.reactor.IOReactorStatus;
|
import org.apache.http.nio.reactor.IOReactorStatus;
|
||||||
import org.apache.http.nio.reactor.SessionRequest;
|
import org.apache.http.nio.reactor.SessionRequest;
|
||||||
import org.apache.http.nio.reactor.SessionRequestCallback;
|
import org.apache.http.nio.reactor.SessionRequestCallback;
|
||||||
|
import org.apache.http.params.HttpParams;
|
||||||
import org.jclouds.command.FutureCommand;
|
import org.jclouds.command.FutureCommand;
|
||||||
import org.jclouds.command.pool.FutureCommandConnectionHandle;
|
import org.jclouds.command.pool.FutureCommandConnectionHandle;
|
||||||
import org.jclouds.command.pool.FutureCommandConnectionPool;
|
import org.jclouds.command.pool.FutureCommandConnectionPool;
|
||||||
import org.jclouds.command.pool.PoolConstants;
|
import org.jclouds.command.pool.PoolConstants;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
|
|
||||||
import java.io.IOException;
|
import com.google.inject.Inject;
|
||||||
import java.net.InetSocketAddress;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import java.util.concurrent.*;
|
import com.google.inject.name.Named;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connection Pool for HTTP requests that utilizes Apache HTTPNio
|
* Connection Pool for HTTP requests that utilizes Apache HTTPNio
|
||||||
|
@ -50,238 +62,236 @@ import java.util.concurrent.*;
|
||||||
* @author Adrian Cole
|
* @author Adrian Cole
|
||||||
*/
|
*/
|
||||||
public class HttpNioFutureCommandConnectionPool extends
|
public class HttpNioFutureCommandConnectionPool extends
|
||||||
FutureCommandConnectionPool<NHttpConnection, HttpFutureCommand<?>>
|
FutureCommandConnectionPool<URI, NHttpConnection, HttpFutureCommand<?>> implements
|
||||||
implements EventListener {
|
EventListener {
|
||||||
|
|
||||||
private final NHttpClientConnectionPoolSessionRequestCallback sessionCallback;
|
private final NHttpClientConnectionPoolSessionRequestCallback sessionCallback;
|
||||||
private final DefaultConnectingIOReactor ioReactor;
|
private final DefaultConnectingIOReactor ioReactor;
|
||||||
private final IOEventDispatch dispatch;
|
private final IOEventDispatch dispatch;
|
||||||
private final InetSocketAddress target;
|
private final InetSocketAddress target;
|
||||||
private final int maxSessionFailures;
|
private final int maxSessionFailures;
|
||||||
|
|
||||||
@Inject
|
public static interface Factory extends
|
||||||
public HttpNioFutureCommandConnectionPool(
|
FutureCommandConnectionPool.Factory<URI, NHttpConnection, HttpFutureCommand<?>> {
|
||||||
ExecutorService executor,
|
HttpNioFutureCommandConnectionPool create(URI endPoint);
|
||||||
Semaphore allConnections,
|
}
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public HttpNioFutureCommandConnectionPool(ExecutorService executor, Semaphore allConnections,
|
||||||
BlockingQueue<HttpFutureCommand<?>> commandQueue,
|
BlockingQueue<HttpFutureCommand<?>> commandQueue,
|
||||||
BlockingQueue<NHttpConnection> available,
|
BlockingQueue<NHttpConnection> available, AsyncNHttpClientHandler clientHandler,
|
||||||
AsyncNHttpClientHandler clientHandler,
|
DefaultConnectingIOReactor ioReactor, HttpParams params,
|
||||||
DefaultConnectingIOReactor ioReactor,
|
|
||||||
IOEventDispatch dispatch,
|
|
||||||
FutureCommandConnectionHandleFactory requestHandleFactory,
|
|
||||||
InetSocketAddress target,
|
|
||||||
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE) int maxConnectionReuse,
|
@Named(PoolConstants.PROPERTY_POOL_MAX_CONNECTION_REUSE) int maxConnectionReuse,
|
||||||
@Named(PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES) int maxSessionFailures) {
|
@Named(PoolConstants.PROPERTY_POOL_MAX_SESSION_FAILURES) int maxSessionFailures,
|
||||||
super(executor, allConnections, commandQueue, requestHandleFactory,
|
@Assisted URI endPoint) throws Exception {
|
||||||
maxConnectionReuse, available);
|
super(executor, allConnections, commandQueue, maxConnectionReuse, available, endPoint);
|
||||||
this.ioReactor = ioReactor;
|
this.ioReactor = ioReactor;
|
||||||
this.dispatch = dispatch;
|
this.dispatch = endPoint.getScheme().equals("https") ? provideSSLClientEventDispatch(
|
||||||
this.target = target;
|
clientHandler, params) : provideClientEventDispatch(clientHandler, params);
|
||||||
this.maxSessionFailures = maxSessionFailures;
|
this.maxSessionFailures = maxSessionFailures;
|
||||||
this.sessionCallback = new NHttpClientConnectionPoolSessionRequestCallback();
|
this.sessionCallback = new NHttpClientConnectionPoolSessionRequestCallback();
|
||||||
clientHandler.setEventListener(this);
|
this.target = new InetSocketAddress(getEndPoint().getHost(), getEndPoint().getPort());
|
||||||
}
|
clientHandler.setEventListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public static IOEventDispatch provideSSLClientEventDispatch(AsyncNHttpClientHandler handler,
|
||||||
public void start() {
|
HttpParams params) throws Exception {
|
||||||
synchronized (this.statusLock) {
|
SSLContext context = SSLContext.getInstance("TLS");
|
||||||
if (this.status.compareTo(Status.INACTIVE) == 0) {
|
context.init(null, null, null);
|
||||||
executor.execute(new Runnable() {
|
return new SSLClientIOEventDispatch(handler, context, params);
|
||||||
public void run() {
|
}
|
||||||
try {
|
|
||||||
ioReactor.execute(dispatch);
|
|
||||||
} catch (IOException e) {
|
|
||||||
exception.set(e);
|
|
||||||
logger.error(e, "Error dispatching %1$s", dispatch);
|
|
||||||
status = Status.SHUTDOWN_REQUEST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdownReactor(long waitMs) {
|
public static IOEventDispatch provideClientEventDispatch(AsyncNHttpClientHandler handler,
|
||||||
try {
|
HttpParams params) throws Exception {
|
||||||
this.ioReactor.shutdown(waitMs);
|
return new DefaultClientIOEventDispatch(handler, params);
|
||||||
} catch (IOException e) {
|
}
|
||||||
logger.error(e, "Error shutting down reactor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean connectionValid(NHttpConnection conn) {
|
public void start() {
|
||||||
return conn.isOpen() && !conn.isStale()
|
synchronized (this.statusLock) {
|
||||||
&& conn.getMetrics().getRequestCount() < maxConnectionReuse;
|
if (this.status.compareTo(Status.INACTIVE) == 0) {
|
||||||
}
|
executor.execute(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
ioReactor.execute(dispatch);
|
||||||
|
} catch (IOException e) {
|
||||||
|
exception.set(e);
|
||||||
|
logger.error(e, "Error dispatching %1$s", dispatch);
|
||||||
|
status = Status.SHUTDOWN_REQUEST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public void shutdownReactor(long waitMs) {
|
||||||
public void shutdownConnection(NHttpConnection conn) {
|
try {
|
||||||
if (conn.getMetrics().getRequestCount() >= maxConnectionReuse)
|
this.ioReactor.shutdown(waitMs);
|
||||||
logger.debug(
|
} catch (IOException e) {
|
||||||
"%1$s - %2$d - closing connection due to overuse %1$s/%2$s",
|
logger.error(e, "Error shutting down reactor");
|
||||||
conn, conn.hashCode(), conn.getMetrics().getRequestCount(),
|
}
|
||||||
maxConnectionReuse);
|
}
|
||||||
if (conn.getStatus() == NHttpConnection.ACTIVE) {
|
|
||||||
try {
|
|
||||||
conn.shutdown();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error(e, "Error shutting down connection");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doWork() throws Exception {
|
public boolean connectionValid(NHttpConnection conn) {
|
||||||
createNewConnection();
|
return conn.isOpen() && !conn.isStale()
|
||||||
}
|
&& conn.getMetrics().getRequestCount() < maxConnectionReuse;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doShutdown() {
|
public void shutdownConnection(NHttpConnection conn) {
|
||||||
// Give the I/O reactor 1 sec to shut down
|
if (conn.getMetrics().getRequestCount() >= maxConnectionReuse)
|
||||||
shutdownReactor(1000);
|
logger.debug("%1$s - %2$d - closing connection due to overuse %1$s/%2$s", conn, conn
|
||||||
assert this.ioReactor.getStatus().equals(IOReactorStatus.SHUT_DOWN) : "incorrect status after io reactor shutdown :"
|
.hashCode(), conn.getMetrics().getRequestCount(), maxConnectionReuse);
|
||||||
+ this.ioReactor.getStatus();
|
if (conn.getStatus() == NHttpConnection.ACTIVE) {
|
||||||
}
|
try {
|
||||||
|
conn.shutdown();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error(e, "Error shutting down connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void createNewConnection() throws InterruptedException {
|
protected void doWork() throws Exception {
|
||||||
boolean acquired = allConnections.tryAcquire(1, TimeUnit.SECONDS);
|
createNewConnection();
|
||||||
if (acquired) {
|
}
|
||||||
if (shouldDoWork()) {
|
|
||||||
logger.debug("%1$s - opening new connection", target);
|
|
||||||
ioReactor.connect(target, null, null, sessionCallback);
|
|
||||||
} else {
|
|
||||||
allConnections.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void associateHandleWithConnection(
|
protected void doShutdown() {
|
||||||
FutureCommandConnectionHandle<NHttpConnection, HttpFutureCommand<?>> handle,
|
// Give the I/O reactor 1 sec to shut down
|
||||||
NHttpConnection connection) {
|
shutdownReactor(1000);
|
||||||
connection.getContext().setAttribute("command-handle", handle);
|
assert this.ioReactor.getStatus().equals(IOReactorStatus.SHUT_DOWN) : "incorrect status after io reactor shutdown :"
|
||||||
}
|
+ this.ioReactor.getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpNioFutureCommandConnectionHandle getHandleFromConnection(
|
protected void createNewConnection() throws InterruptedException {
|
||||||
NHttpConnection connection) {
|
boolean acquired = allConnections.tryAcquire(1, TimeUnit.SECONDS);
|
||||||
return (HttpNioFutureCommandConnectionHandle) connection.getContext()
|
if (acquired) {
|
||||||
.getAttribute("command-handle");
|
if (shouldDoWork()) {
|
||||||
}
|
logger.debug("%1$s - opening new connection", target);
|
||||||
|
ioReactor.connect(target, null, null, sessionCallback);
|
||||||
class NHttpClientConnectionPoolSessionRequestCallback implements
|
} else {
|
||||||
SessionRequestCallback {
|
|
||||||
|
|
||||||
public void completed(SessionRequest request) {
|
|
||||||
logger.trace("%1$s->%2$s[%3$s] - SessionRequest complete", request
|
|
||||||
.getLocalAddress(), request.getRemoteAddress(), request
|
|
||||||
.getAttachment());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancelled(SessionRequest request) {
|
|
||||||
logger.trace("%1$s->%2$s[%3$s] - SessionRequest cancelled", request
|
|
||||||
.getLocalAddress(), request.getRemoteAddress(), request
|
|
||||||
.getAttachment());
|
|
||||||
releaseConnectionAndCancelResponse(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void releaseConnectionAndCancelResponse(SessionRequest request) {
|
|
||||||
allConnections.release();
|
allConnections.release();
|
||||||
FutureCommand<?, ?, ?> frequest = (FutureCommand<?, ?, ?>) request
|
}
|
||||||
.getAttachment();
|
}
|
||||||
if (frequest != null) {
|
}
|
||||||
logger.error("%1$s->%2$s[%3$s] - Cancelling FutureCommand",
|
|
||||||
request.getLocalAddress(), request.getRemoteAddress(),
|
|
||||||
frequest);
|
|
||||||
frequest.cancel(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void releaseConnectionAndSetResponseException(
|
@Override
|
||||||
SessionRequest request, Exception e) {
|
protected void associateHandleWithConnection(
|
||||||
allConnections.release();
|
FutureCommandConnectionHandle<URI, NHttpConnection, HttpFutureCommand<?>> handle,
|
||||||
HttpFutureCommand<?> frequest = (HttpFutureCommand<?>) request
|
NHttpConnection connection) {
|
||||||
.getAttachment();
|
connection.getContext().setAttribute("command-handle", handle);
|
||||||
if (frequest != null) {
|
}
|
||||||
logger.error(e,
|
|
||||||
"%1$s->%2$s[%3$s] - Setting Exception on FutureCommand",
|
|
||||||
request.getLocalAddress(), request.getRemoteAddress(),
|
|
||||||
frequest);
|
|
||||||
frequest.setException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void failed(SessionRequest request) {
|
@Override
|
||||||
int count = currentSessionFailures.getAndIncrement();
|
protected HttpNioFutureCommandConnectionHandle getHandleFromConnection(NHttpConnection connection) {
|
||||||
logger.warn("%1$s->%2$s[%3$s] - SessionRequest failed", request
|
return (HttpNioFutureCommandConnectionHandle) connection.getContext().getAttribute(
|
||||||
.getLocalAddress(), request.getRemoteAddress(), request
|
"command-handle");
|
||||||
.getAttachment());
|
}
|
||||||
releaseConnectionAndSetResponseException(request, request
|
|
||||||
.getException());
|
|
||||||
if (count >= maxSessionFailures) {
|
|
||||||
logger
|
|
||||||
.error(
|
|
||||||
request.getException(),
|
|
||||||
"%1$s->%2$s[%3$s] - SessionRequest failures: %4$s, Disabling pool for %5$s",
|
|
||||||
request.getLocalAddress(), request
|
|
||||||
.getRemoteAddress(),
|
|
||||||
maxSessionFailures, target);
|
|
||||||
exception.set(request.getException());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
class NHttpClientConnectionPoolSessionRequestCallback implements SessionRequestCallback {
|
||||||
|
|
||||||
public void timeout(SessionRequest request) {
|
public void completed(SessionRequest request) {
|
||||||
logger.warn("%1$s->%2$s[%3$s] - SessionRequest timeout", request
|
logger.trace("%1$s->%2$s[%3$s] - SessionRequest complete", request.getLocalAddress(),
|
||||||
.getLocalAddress(), request.getRemoteAddress(), request
|
request.getRemoteAddress(), request.getAttachment());
|
||||||
.getAttachment());
|
}
|
||||||
releaseConnectionAndCancelResponse(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
public void cancelled(SessionRequest request) {
|
||||||
|
logger.trace("%1$s->%2$s[%3$s] - SessionRequest cancelled", request.getLocalAddress(),
|
||||||
|
request.getRemoteAddress(), request.getAttachment());
|
||||||
|
releaseConnectionAndCancelResponse(request);
|
||||||
|
}
|
||||||
|
|
||||||
public void connectionOpen(NHttpConnection conn) {
|
@SuppressWarnings("unchecked")
|
||||||
conn.setSocketTimeout(0);
|
private void releaseConnectionAndCancelResponse(SessionRequest request) {
|
||||||
available.offer(conn);
|
allConnections.release();
|
||||||
logger.trace("%1$s - %2$d - open", conn, conn.hashCode());
|
FutureCommand<URI, ?, ?, ?> frequest = (FutureCommand<URI, ?, ?, ?>) request
|
||||||
}
|
.getAttachment();
|
||||||
|
if (frequest != null) {
|
||||||
|
logger.error("%1$s->%2$s[%3$s] - Cancelling FutureCommand", request.getLocalAddress(),
|
||||||
|
request.getRemoteAddress(), frequest);
|
||||||
|
frequest.cancel(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void connectionTimeout(NHttpConnection conn) {
|
private void releaseConnectionAndSetResponseException(SessionRequest request, Exception e) {
|
||||||
String message = String.format("%1$s - %2$d - timeout %2$d", conn, conn
|
allConnections.release();
|
||||||
.hashCode(), conn.getSocketTimeout());
|
HttpFutureCommand<?> frequest = (HttpFutureCommand<?>) request.getAttachment();
|
||||||
logger.warn(message);
|
if (frequest != null) {
|
||||||
resubmitIfRequestIsReplayable(conn, new TimeoutException(message));
|
logger.error(e, "%1$s->%2$s[%3$s] - Setting Exception on FutureCommand", request
|
||||||
}
|
.getLocalAddress(), request.getRemoteAddress(), frequest);
|
||||||
|
frequest.setException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void connectionClosed(NHttpConnection conn) {
|
public void failed(SessionRequest request) {
|
||||||
logger.trace("%1$s - %2$d - closed", conn, conn.hashCode());
|
int count = currentSessionFailures.getAndIncrement();
|
||||||
}
|
logger.warn("%1$s->%2$s[%3$s] - SessionRequest failed", request.getLocalAddress(), request
|
||||||
|
.getRemoteAddress(), request.getAttachment());
|
||||||
|
releaseConnectionAndSetResponseException(request, request.getException());
|
||||||
|
if (count >= maxSessionFailures) {
|
||||||
|
logger.error(request.getException(),
|
||||||
|
"%1$s->%2$s[%3$s] - SessionRequest failures: %4$s, Disabling pool for %5$s",
|
||||||
|
request.getLocalAddress(), request.getRemoteAddress(), maxSessionFailures,
|
||||||
|
target);
|
||||||
|
exception.set(request.getException());
|
||||||
|
}
|
||||||
|
|
||||||
public void fatalIOException(IOException ex, NHttpConnection conn) {
|
}
|
||||||
logger.error(ex, "%3$s-%1$s{%2$d} - io error", conn, conn.hashCode(),
|
|
||||||
target);
|
|
||||||
resubmitIfRequestIsReplayable(conn, ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fatalProtocolException(HttpException ex, NHttpConnection conn) {
|
public void timeout(SessionRequest request) {
|
||||||
logger.error(ex, "%3$s-%1$s{%2$d} - http error", conn, conn.hashCode(),
|
logger.warn("%1$s->%2$s[%3$s] - SessionRequest timeout", request.getLocalAddress(),
|
||||||
target);
|
request.getRemoteAddress(), request.getAttachment());
|
||||||
setExceptionOnCommand(conn, ex);
|
releaseConnectionAndCancelResponse(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static interface FutureCommandConnectionHandleFactory
|
}
|
||||||
extends
|
|
||||||
FutureCommandConnectionPool.FutureCommandConnectionHandleFactory<NHttpConnection, HttpFutureCommand<?>> {
|
|
||||||
HttpNioFutureCommandConnectionHandle create(
|
|
||||||
HttpFutureCommand<?> command, NHttpConnection conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public void connectionOpen(NHttpConnection conn) {
|
||||||
protected boolean isReplayable(HttpFutureCommand<?> command) {
|
conn.setSocketTimeout(0);
|
||||||
return command.getRequest().isReplayable();
|
available.offer(conn);
|
||||||
}
|
logger.trace("%1$s - %2$d - open", conn, conn.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectionTimeout(NHttpConnection conn) {
|
||||||
|
String message = String.format("%1$s - %2$d - timeout %2$d", conn, conn.hashCode(), conn
|
||||||
|
.getSocketTimeout());
|
||||||
|
logger.warn(message);
|
||||||
|
resubmitIfRequestIsReplayable(conn, new TimeoutException(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connectionClosed(NHttpConnection conn) {
|
||||||
|
logger.trace("%1$s - %2$d - closed", conn, conn.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fatalIOException(IOException ex, NHttpConnection conn) {
|
||||||
|
logger.error(ex, "%3$s-%1$s{%2$d} - io error", conn, conn.hashCode(), target);
|
||||||
|
resubmitIfRequestIsReplayable(conn, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fatalProtocolException(HttpException ex, NHttpConnection conn) {
|
||||||
|
logger.error(ex, "%3$s-%1$s{%2$d} - http error", conn, conn.hashCode(), target);
|
||||||
|
setExceptionOnCommand(conn, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isReplayable(HttpFutureCommand<?> command) {
|
||||||
|
return command.getRequest().isReplayable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FutureCommandConnectionHandle<URI, NHttpConnection, HttpFutureCommand<?>> createHandle(
|
||||||
|
HttpFutureCommand<?> command, NHttpConnection conn) {
|
||||||
|
try {
|
||||||
|
return new HttpNioFutureCommandConnectionHandle(allConnections, available, endPoint,
|
||||||
|
command, conn);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException("Interrupted creating a handle to " + conn, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -39,8 +39,8 @@ import org.jclouds.http.HttpErrorHandler;
|
||||||
import org.jclouds.http.HttpFutureCommand;
|
import org.jclouds.http.HttpFutureCommand;
|
||||||
import org.jclouds.http.HttpRequest;
|
import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpRetryHandler;
|
import org.jclouds.http.HttpRetryHandler;
|
||||||
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
|
import org.jclouds.http.handlers.DelegatingErrorHandler;
|
||||||
import org.jclouds.http.handlers.CloseContentAndSetExceptionHandler;
|
import org.jclouds.http.handlers.DelegatingRetryHandler;
|
||||||
import org.jclouds.http.httpnio.util.HttpNioUtils;
|
import org.jclouds.http.httpnio.util.HttpNioUtils;
|
||||||
import org.jclouds.logging.Logger;
|
import org.jclouds.logging.Logger;
|
||||||
|
|
||||||
|
@ -56,24 +56,28 @@ public class HttpNioFutureCommandExecutionHandler implements NHttpRequestExecuti
|
||||||
@Resource
|
@Resource
|
||||||
protected Logger logger = Logger.NULL;
|
protected Logger logger = Logger.NULL;
|
||||||
private final ConsumingNHttpEntityFactory entityFactory;
|
private final ConsumingNHttpEntityFactory entityFactory;
|
||||||
private final BlockingQueue<HttpFutureCommand<?>> commandQueue;
|
|
||||||
|
/**
|
||||||
|
* inputOnly: nothing is taken from this queue.
|
||||||
|
*/
|
||||||
|
private final BlockingQueue<HttpFutureCommand<?>> resubmitQueue;
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
private HttpErrorHandler serverErrorHandler = new CloseContentAndSetExceptionHandler();
|
private HttpRetryHandler retryHandler = new DelegatingRetryHandler();
|
||||||
|
|
||||||
@Inject(optional = true)
|
@Inject(optional = true)
|
||||||
protected HttpRetryHandler httpRetryHandler = new BackoffLimitedRetryHandler(5);
|
private HttpErrorHandler errorHandler = new DelegatingErrorHandler();
|
||||||
|
|
||||||
public interface ConsumingNHttpEntityFactory {
|
|
||||||
public ConsumingNHttpEntity create(HttpEntity httpEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public HttpNioFutureCommandExecutionHandler(ConsumingNHttpEntityFactory entityFactory,
|
public HttpNioFutureCommandExecutionHandler(ConsumingNHttpEntityFactory entityFactory,
|
||||||
ExecutorService executor, BlockingQueue<HttpFutureCommand<?>> commandQueue) {
|
ExecutorService executor, BlockingQueue<HttpFutureCommand<?>> resubmitQueue) {
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.entityFactory = entityFactory;
|
this.entityFactory = entityFactory;
|
||||||
this.commandQueue = commandQueue;
|
this.resubmitQueue = resubmitQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ConsumingNHttpEntityFactory {
|
||||||
|
public ConsumingNHttpEntity create(HttpEntity httpEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initalizeContext(HttpContext context, Object attachment) {
|
public void initalizeContext(HttpContext context, Object attachment) {
|
||||||
|
@ -82,8 +86,8 @@ public class HttpNioFutureCommandExecutionHandler implements NHttpRequestExecuti
|
||||||
public HttpEntityEnclosingRequest submitRequest(HttpContext context) {
|
public HttpEntityEnclosingRequest submitRequest(HttpContext context) {
|
||||||
HttpFutureCommand<?> command = (HttpFutureCommand<?>) context.removeAttribute("command");
|
HttpFutureCommand<?> command = (HttpFutureCommand<?>) context.removeAttribute("command");
|
||||||
if (command != null) {
|
if (command != null) {
|
||||||
HttpRequest object = command.getRequest();
|
HttpRequest request = command.getRequest();
|
||||||
return HttpNioUtils.convertToApacheRequest(object);
|
return HttpNioUtils.convertToApacheRequest(request);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -102,24 +106,13 @@ public class HttpNioFutureCommandExecutionHandler implements NHttpRequestExecuti
|
||||||
HttpFutureCommand<?> command = handle.getCommand();
|
HttpFutureCommand<?> command = handle.getCommand();
|
||||||
org.jclouds.http.HttpResponse response = HttpNioUtils
|
org.jclouds.http.HttpResponse response = HttpNioUtils
|
||||||
.convertToJavaCloudsResponse(apacheResponse);
|
.convertToJavaCloudsResponse(apacheResponse);
|
||||||
|
int statusCode = response.getStatusCode();
|
||||||
int code = response.getStatusCode();
|
if (statusCode >= 300) {
|
||||||
if (code >= 500) {
|
if (retryHandler.shouldRetryRequest(command, response)) {
|
||||||
boolean retryRequest = false;
|
resubmitQueue.add(command);
|
||||||
try {
|
|
||||||
retryRequest = httpRetryHandler.shouldRetryRequest(command, response);
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
// TODO: Add interrupt exception to command and abort?
|
|
||||||
}
|
|
||||||
if (retryRequest) {
|
|
||||||
commandQueue.add(command);
|
|
||||||
} else {
|
} else {
|
||||||
serverErrorHandler.handle(command, response);
|
errorHandler.handleError(command, response);
|
||||||
}
|
}
|
||||||
} else if (code >= 400 && code < 500) {
|
|
||||||
serverErrorHandler.handle(command, response);
|
|
||||||
} else if (code >= 300 && code < 400) {
|
|
||||||
serverErrorHandler.handle(command, response);
|
|
||||||
} else {
|
} else {
|
||||||
processResponse(response, command);
|
processResponse(response, command);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,26 +41,29 @@ import org.jclouds.http.HttpRequest;
|
||||||
import org.jclouds.http.HttpResponse;
|
import org.jclouds.http.HttpResponse;
|
||||||
|
|
||||||
public class HttpNioUtils {
|
public class HttpNioUtils {
|
||||||
public static HttpEntityEnclosingRequest convertToApacheRequest(HttpRequest object) {
|
public static HttpEntityEnclosingRequest convertToApacheRequest(HttpRequest request) {
|
||||||
BasicHttpEntityEnclosingRequest apacheRequest = new BasicHttpEntityEnclosingRequest(object
|
BasicHttpEntityEnclosingRequest apacheRequest = new BasicHttpEntityEnclosingRequest(request
|
||||||
.getMethod().toString(), object.getUri(), HttpVersion.HTTP_1_1);
|
.getMethod().toString(), request.getUri(), HttpVersion.HTTP_1_1);
|
||||||
|
|
||||||
Object content = object.getPayload();
|
Object content = request.getPayload();
|
||||||
|
|
||||||
// Since we may remove headers, ensure they are added to the apache
|
// Since we may remove headers, ensure they are added to the apache
|
||||||
// request after this block
|
// request after this block
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
long contentLength = Long.parseLong(object
|
String lengthString = request.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
|
||||||
.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH));
|
if (lengthString == null) {
|
||||||
object.getHeaders().removeAll(HttpHeaders.CONTENT_LENGTH);
|
throw new IllegalStateException("no Content-Length header on request: " + apacheRequest);
|
||||||
String contentType = object.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE);
|
}
|
||||||
object.getHeaders().removeAll(HttpHeaders.CONTENT_TYPE);
|
long contentLength = Long.parseLong(lengthString);
|
||||||
|
String contentType = request.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE);
|
||||||
addEntityForContent(apacheRequest, content, contentType, contentLength);
|
addEntityForContent(apacheRequest, content, contentType, contentLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String header : object.getHeaders().keySet()) {
|
for (String header : request.getHeaders().keySet()) {
|
||||||
for (String value : object.getHeaders().get(header))
|
for (String value : request.getHeaders().get(header))
|
||||||
apacheRequest.addHeader(header, value);
|
// apache automatically tries to add content length header
|
||||||
|
if (!header.equals(HttpHeaders.CONTENT_LENGTH))
|
||||||
|
apacheRequest.addHeader(header, value);
|
||||||
}
|
}
|
||||||
return apacheRequest;
|
return apacheRequest;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.http.httpnio.pool;
|
package org.jclouds.http.httpnio.pool;
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.jclouds.command.pool.PoolConstants;
|
import org.jclouds.command.pool.PoolConstants;
|
||||||
import org.jclouds.http.BaseHttpFutureCommandClientTest;
|
import org.jclouds.http.BaseHttpFutureCommandClientTest;
|
||||||
|
@ -55,16 +52,4 @@ public class HttpNioConnectionPoolFutureCommandClientTest extends BaseHttpFuture
|
||||||
return new HttpNioConnectionPoolClientModule();
|
return new HttpNioConnectionPoolClientModule();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Test(enabled = false)
|
|
||||||
public void testGetStringRedirect() throws MalformedURLException, ExecutionException,
|
|
||||||
InterruptedException, TimeoutException {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Test(enabled = false)
|
|
||||||
public void testPutRedirect() throws MalformedURLException, ExecutionException,
|
|
||||||
InterruptedException, TimeoutException {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue