Issue 76: new annotation for RequestFilters

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1623 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-07-13 22:47:45 +00:00
parent c4d8f5ff96
commit 2573d399ca
16 changed files with 243 additions and 92 deletions

View File

@ -39,6 +39,7 @@ import org.jclouds.aws.s3.binders.S3ObjectBinder;
import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket; import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.functions.ParseETagHeader; import org.jclouds.aws.s3.functions.ParseETagHeader;
import org.jclouds.aws.s3.functions.ParseMetadataFromHeaders; import org.jclouds.aws.s3.functions.ParseMetadataFromHeaders;
import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent;
@ -67,6 +68,7 @@ import org.jclouds.rest.HostPrefixParam;
import org.jclouds.rest.HttpRequestOptionsBinder; import org.jclouds.rest.HttpRequestOptionsBinder;
import org.jclouds.rest.PathParamParser; import org.jclouds.rest.PathParamParser;
import org.jclouds.rest.Query; import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.ResponseParser; import org.jclouds.rest.ResponseParser;
import org.jclouds.rest.SkipEncoding; import org.jclouds.rest.SkipEncoding;
import org.jclouds.rest.VirtualHost; import org.jclouds.rest.VirtualHost;
@ -84,6 +86,7 @@ import org.jclouds.rest.XMLResponseParser;
*/ */
@VirtualHost @VirtualHost
@SkipEncoding('/') @SkipEncoding('/')
@RequestFilters(RequestAuthorizeSignature.class)
public interface S3Connection { public interface S3Connection {
/** /**

View File

@ -73,8 +73,8 @@ public class S3ContextBuilder extends CloudContextBuilder<S3Connection, S3Contex
Properties properties = new Properties(); Properties properties = new Properties();
properties.setProperty(PROPERTY_HTTP_ADDRESS, "s3.amazonaws.com"); properties.setProperty(PROPERTY_HTTP_ADDRESS, "s3.amazonaws.com");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_SECURE, "true"); properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5"); properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5");
properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, "5"); properties.setProperty(PROPERTY_HTTP_MAX_REDIRECTS, "5");
properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, "75"); properties.setProperty(PROPERTY_POOL_MAX_CONNECTION_REUSE, "75");

View File

@ -25,8 +25,6 @@ package org.jclouds.aws.s3.config;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -38,7 +36,6 @@ import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent;
import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpConstants; import org.jclouds.http.HttpConstants;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError; import org.jclouds.http.annotation.ClientError;
@ -51,6 +48,7 @@ import org.jclouds.rest.config.JaxrsModule;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Inject; 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.name.Named; import com.google.inject.name.Named;
@ -78,6 +76,7 @@ public class RestS3ConnectionModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
install(new JaxrsModule()); install(new JaxrsModule());
bind(RequestAuthorizeSignature.class).in(Scopes.SINGLETON);
bindErrorHandlers(); bindErrorHandlers();
bindRetryHandlers(); bindRetryHandlers();
requestInjection(this); requestInjection(this);
@ -86,8 +85,8 @@ public class RestS3ConnectionModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
protected S3Connection provideS3Connection(RestClientFactory factory) { protected S3Connection provideS3Connection(URI uri, RestClientFactory factory) {
return factory.create(S3Connection.class); return factory.create(uri, S3Connection.class);
} }
protected void bindErrorHandlers() { protected void bindErrorHandlers() {
@ -106,14 +105,6 @@ public class RestS3ConnectionModule extends AbstractModule {
AWSClientErrorRetryHandler.class); AWSClientErrorRetryHandler.class);
} }
@Provides
@Singleton
List<HttpRequestFilter> provideRequestFilters(RequestAuthorizeSignature requestAuthorizeSignature) {
List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>();
filters.add(requestAuthorizeSignature);
return filters;
}
@Singleton @Singleton
@Provides @Provides
protected URI provideAddress(@Named(HttpConstants.PROPERTY_HTTP_ADDRESS) String address, protected URI provideAddress(@Named(HttpConstants.PROPERTY_HTTP_ADDRESS) String address,

View File

@ -30,6 +30,7 @@ import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_PORT; import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_PORT;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_RELAX_HOSTNAME; import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_RELAX_HOSTNAME;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_SECURE; import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_SECURE;
import static org.jclouds.http.HttpConstants.PROPERTY_JSON_DEBUG;
import static org.jclouds.http.HttpConstants.PROPERTY_SAX_DEBUG; import static org.jclouds.http.HttpConstants.PROPERTY_SAX_DEBUG;
import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS; import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS;
import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS; import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS;
@ -100,6 +101,11 @@ public abstract class CloudContextBuilder<C, X extends CloudContext<C>> {
return this; return this;
} }
public CloudContextBuilder<C, X> withJsonDebug() {
properties.setProperty(PROPERTY_JSON_DEBUG, "true");
return this;
}
/** /**
* allow mismatches between the certificate and the hostname of ssl requests. * allow mismatches between the certificate and the hostname of ssl requests.
*/ */

View File

@ -35,6 +35,8 @@ public interface HttpConstants {
public static final String PROPERTY_HTTP_MAX_RETRIES = "jclouds.http.max-retries"; public static final String PROPERTY_HTTP_MAX_RETRIES = "jclouds.http.max-retries";
public static final String PROPERTY_HTTP_MAX_REDIRECTS = "jclouds.http.max-redirects"; public static final String PROPERTY_HTTP_MAX_REDIRECTS = "jclouds.http.max-redirects";
public static final String PROPERTY_SAX_DEBUG = "jclouds.http.sax.debug"; public static final String PROPERTY_SAX_DEBUG = "jclouds.http.sax.debug";
public static final String PROPERTY_JSON_DEBUG = "jclouds.http.json.debug";
/** /**
* longest time a single request can take before throwing an exception. * longest time a single request can take before throwing an exception.
*/ */

View File

@ -26,10 +26,12 @@ package org.jclouds.http;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI; import java.net.URI;
import java.util.List;
import org.jclouds.command.Request; import org.jclouds.command.Request;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.inject.internal.Lists;
/** /**
* Represents a request that can be executed within {@link HttpCommandExecutorService} * Represents a request that can be executed within {@link HttpCommandExecutorService}
@ -38,6 +40,8 @@ import com.google.common.collect.Multimap;
*/ */
public class HttpRequest extends HttpMessage implements Request<URI> { public class HttpRequest extends HttpMessage implements Request<URI> {
private List<HttpRequestFilter> requestFilters = Lists.newArrayList();
private final HttpMethod method; private final HttpMethod method;
private final URI endpoint; private final URI endpoint;
private Object entity; private Object entity;
@ -94,4 +98,12 @@ public class HttpRequest extends HttpMessage implements Request<URI> {
return endpoint; return endpoint;
} }
public void addFilter(HttpRequestFilter filter) {
requestFilters.add(filter);
}
public List<HttpRequestFilter> getFilters() {
return requestFilters;
}
} }

View File

@ -94,7 +94,6 @@ public class ParseSax<T> implements Function<HttpResponse, T> {
} }
private void parseAndCloseStream(InputStream xml, ContentHandler handler) throws HttpException { private void parseAndCloseStream(InputStream xml, ContentHandler handler) throws HttpException {
parser.setContentHandler(handler);
String response = null; String response = null;
try { try {
if (suckFirst) { if (suckFirst) {
@ -103,6 +102,7 @@ public class ParseSax<T> implements Function<HttpResponse, T> {
IOUtils.closeQuietly(xml); IOUtils.closeQuietly(xml);
xml = IOUtils.toInputStream(response); xml = IOUtils.toInputStream(response);
} }
parser.setContentHandler(handler);
InputSource input = new InputSource(new InputStreamReader(xml, "UTF-8")); InputSource input = new InputSource(new InputStreamReader(xml, "UTF-8"));
parser.parse(input); parser.parse(input);
} catch (Exception e) { } catch (Exception e) {

View File

@ -24,8 +24,6 @@
package org.jclouds.http.internal; package org.jclouds.http.internal;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -41,8 +39,6 @@ import org.jclouds.http.handlers.DelegatingErrorHandler;
import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.handlers.DelegatingRetryHandler;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import com.google.inject.Inject;
public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService { public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandExecutorService {
private final DelegatingRetryHandler retryHandler; private final DelegatingRetryHandler retryHandler;
@ -52,9 +48,6 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
@Resource @Resource
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@Inject(optional = true)
protected List<HttpRequestFilter> requestFilters = Collections.emptyList();
protected BaseHttpCommandExecutorService(ExecutorService executorService, protected BaseHttpCommandExecutorService(ExecutorService executorService,
DelegatingRetryHandler retryHandler, DelegatingErrorHandler errorHandler) { DelegatingRetryHandler retryHandler, DelegatingErrorHandler errorHandler) {
this.retryHandler = retryHandler; this.retryHandler = retryHandler;
@ -81,7 +74,7 @@ public abstract class BaseHttpCommandExecutorService<Q> implements HttpCommandEx
Q nativeRequest = null; Q nativeRequest = null;
try { try {
logger.trace("%s - converting request %s", request.getEndpoint(), request); logger.trace("%s - converting request %s", request.getEndpoint(), request);
for (HttpRequestFilter filter : requestFilters) { for (HttpRequestFilter filter : request.getFilters()) {
filter.filter(request); filter.filter(request);
} }
nativeRequest = convert(request); nativeRequest = convert(request);

View File

@ -36,6 +36,7 @@ import java.util.Set;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import javax.annotation.Resource;
import javax.ws.rs.HeaderParam; import javax.ws.rs.HeaderParam;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
@ -44,12 +45,14 @@ import javax.ws.rs.core.UriBuilder;
import org.jboss.resteasy.util.IsHttpMethod; import org.jboss.resteasy.util.IsHttpMethod;
import org.jclouds.http.HttpMethod; import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnStringIf200;
import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.logging.Logger;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -61,10 +64,12 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import com.google.inject.internal.Lists; import com.google.inject.internal.Lists;
import com.google.inject.name.Named;
/** /**
* Tests behavior of JaxrsUtil * Tests behavior of JaxrsUtil
@ -74,6 +79,9 @@ import com.google.inject.internal.Lists;
@Singleton @Singleton
public class JaxrsAnnotationProcessor { public class JaxrsAnnotationProcessor {
@Resource
protected Logger logger = Logger.NULL;
private final Class<?> declaring; private final Class<?> declaring;
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEntityAnnotation = createMethodToIndexOfParamToAnnotation(EntityParam.class); private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEntityAnnotation = createMethodToIndexOfParamToAnnotation(EntityParam.class);
@ -170,7 +178,7 @@ public class JaxrsAnnotationProcessor {
private void seedCache(Class<?> declaring) { private void seedCache(Class<?> declaring) {
Set<Method> methods = Sets.newHashSet(declaring.getMethods()); Set<Method> methods = Sets.newHashSet(declaring.getMethods());
for (Method method : Sets.difference(methods, Sets.newHashSet(Object.class.getMethods()))) { for (Method method : Sets.difference(methods, Sets.newHashSet(Object.class.getMethods()))) {
getHttpMethodOrThrowException(method); if (isHttpMethod(method)) {
for (int index = 0; index < method.getParameterTypes().length; index++) { for (int index = 0; index < method.getParameterTypes().length; index++) {
methodToIndexOfParamToEntityAnnotation.get(method).get(index); methodToIndexOfParamToEntityAnnotation.get(method).get(index);
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index); methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
@ -179,6 +187,11 @@ public class JaxrsAnnotationProcessor {
methodToindexOfParamToPathParamParserAnnotations.get(method).get(index); methodToindexOfParamToPathParamParserAnnotations.get(method).get(index);
methodToIndexesOfOptions.get(method); methodToIndexesOfOptions.get(method);
} }
} else if (isConstantDeclaration(method)) {
bindConstant(method);
} else {
throw new RuntimeException("Method is not annotated as either http or constant");
}
} }
} }
@ -187,9 +200,10 @@ public class JaxrsAnnotationProcessor {
private HttpRequestOptionsBinder optionsBinder; private HttpRequestOptionsBinder optionsBinder;
public HttpRequest createRequest(URI endpoint, Method method, Object[] args) { public HttpRequest createRequest(URI endpoint, Method method, Object[] args) {
HttpMethod httpMethod = getHttpMethodOrThrowException(method); HttpMethod httpMethod = getHttpMethodOrConstantOrThrowException(method);
UriBuilder builder = addHostPrefixIfPresent(endpoint, method, args); UriBuilder builder = addHostPrefixIfPresent(endpoint, method, args);
builder.path(declaring);
builder.path(method); builder.path(method);
if (method.isAnnotationPresent(Query.class)) { if (method.isAnnotationPresent(Query.class)) {
@ -227,11 +241,27 @@ public class JaxrsAnnotationProcessor {
} }
HttpRequest request = new HttpRequest(httpMethod, endPoint, headers); HttpRequest request = new HttpRequest(httpMethod, endPoint, headers);
addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method); addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method);
addFiltersIfAnnotated(method, request);
buildEntityIfPostOrPutRequest(method, args, request); buildEntityIfPostOrPutRequest(method, args, request);
return request; return request;
} }
private void addFiltersIfAnnotated(Method method, HttpRequest request) {
if (declaring.isAnnotationPresent(RequestFilters.class)) {
for (Class<? extends HttpRequestFilter> clazz : declaring.getAnnotation(
RequestFilters.class).value()) {
request.getFilters().add(injector.getInstance(clazz));
}
}
if (method.isAnnotationPresent(RequestFilters.class)) {
for (Class<? extends HttpRequestFilter> clazz : method.getAnnotation(RequestFilters.class)
.value()) {
request.getFilters().add(injector.getInstance(clazz));
}
}
}
private UriBuilder addHostPrefixIfPresent(URI endpoint, Method method, Object[] args) { private UriBuilder addHostPrefixIfPresent(URI endpoint, Method method, Object[] args) {
Map<Integer, Set<Annotation>> map = getIndexToHostPrefixAnnotation(method); Map<Integer, Set<Annotation>> map = getIndexToHostPrefixAnnotation(method);
UriBuilder builder = UriBuilder.fromUri(endpoint); UriBuilder builder = UriBuilder.fromUri(endpoint);
@ -280,11 +310,27 @@ public class JaxrsAnnotationProcessor {
return null; return null;
} }
public static HttpMethod getHttpMethodOrThrowException(Method method) { private Map<String, String> constants = Maps.newHashMap();
public boolean isHttpMethod(Method method) {
return IsHttpMethod.getHttpMethods(method) != null;
}
public boolean isConstantDeclaration(Method method) {
return method.isAnnotationPresent(PathParam.class) && method.isAnnotationPresent(Named.class);
}
public void bindConstant(Method method) {
String key = method.getAnnotation(PathParam.class).value();
String value = injector.getInstance(Key.get(String.class, method.getAnnotation(Named.class)));
constants.put(key, value);
}
public HttpMethod getHttpMethodOrConstantOrThrowException(Method method) {
Set<String> httpMethods = IsHttpMethod.getHttpMethods(method); Set<String> httpMethods = IsHttpMethod.getHttpMethods(method);
if (httpMethods == null || httpMethods.size() != 1) { if (httpMethods == null || httpMethods.size() != 1) {
throw new IllegalStateException( throw new IllegalStateException(
"You must use at least one, but no more than one http method annotation on: " "You must use at least one, but no more than one http method or pathparam annotation on: "
+ method.toString()); + method.toString());
} }
return HttpMethod.valueOf(httpMethods.iterator().next()); return HttpMethod.valueOf(httpMethods.iterator().next());
@ -406,6 +452,7 @@ public class JaxrsAnnotationProcessor {
private Map<String, Object> getEncodedPathParamKeyValues(Method method, Object[] args, private Map<String, Object> getEncodedPathParamKeyValues(Method method, Object[] args,
char... skipEncode) throws UnsupportedEncodingException { char... skipEncode) throws UnsupportedEncodingException {
Map<String, Object> pathParamValues = Maps.newHashMap(); Map<String, Object> pathParamValues = Maps.newHashMap();
pathParamValues.putAll(constants);
Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations Map<Integer, Set<Annotation>> indexToPathParam = methodToindexOfParamToPathParamAnnotations
.get(method); .get(method);
Map<Integer, Set<Annotation>> indexToPathParamExtractor = methodToindexOfParamToPathParamParserAnnotations Map<Integer, Set<Annotation>> indexToPathParamExtractor = methodToindexOfParamToPathParamParserAnnotations

View File

@ -0,0 +1,44 @@
/**
*
* 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.rest;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import org.jclouds.http.HttpRequestFilter;
/**
* Filters that should be applied to the request
*
* @author Adrian Cole
*/
@Target( { TYPE, METHOD })
@Retention(RUNTIME)
public @interface RequestFilters {
Class<? extends HttpRequestFilter>[] value();
}

View File

@ -24,6 +24,7 @@
package org.jclouds.rest; package org.jclouds.rest;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.net.URI;
import org.jclouds.rest.RestClientProxy.RestClientProxyFactory; import org.jclouds.rest.RestClientProxy.RestClientProxyFactory;
@ -38,9 +39,9 @@ public class RestClientFactory {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T create(Class<T> clazz) { public <T> T create(URI endPoint, Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz },
proxyFactory.create(clazz)); proxyFactory.create(endPoint, clazz));
} }
} }

View File

@ -65,12 +65,13 @@ public class RestClientProxy implements InvocationHandler {
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
public static interface RestClientProxyFactory { public static interface RestClientProxyFactory {
RestClientProxy create(Class<?> clazz); RestClientProxy create(URI endPoint, Class<?> clazz);
} }
@Inject @Inject
public RestClientProxy(JaxrsAnnotationProcessor.Factory utilFactory, URI endPoint, public RestClientProxy(JaxrsAnnotationProcessor.Factory utilFactory,
TransformingHttpCommand.Factory factory, @Assisted Class<?> declaring) { TransformingHttpCommand.Factory factory, @Assisted URI endPoint,
@Assisted Class<?> declaring) {
this.util = utilFactory.create(declaring); this.util = utilFactory.create(declaring);
this.declaring = declaring; this.declaring = declaring;
this.endPoint = endPoint; this.endPoint = endPoint;
@ -83,30 +84,36 @@ public class RestClientProxy implements InvocationHandler {
return this.equals(o); return this.equals(o);
} else if (method.getName().equals("hashCode")) { } else if (method.getName().equals("hashCode")) {
return this.hashCode(); return this.hashCode();
} } else if (util.isHttpMethod(method)) {
logger.trace("%s - converting method to request", method); logger.trace("%s - converting method to request", method);
HttpRequest request = util.createRequest(endPoint, method, args); HttpRequest request = util.createRequest(endPoint, method, args);
logger.trace("%s - converted method to request %s", method, request); logger.trace("%s - converted method to request %s", method, request);
Function<HttpResponse, ?> transformer = util.createResponseParser(method); Function<HttpResponse, ?> transformer = util.createResponseParser(method);
Function<Exception, ?> exceptionParser = util.createExceptionParserOrNullIfNotFound(method); Function<Exception, ?> exceptionParser = util
.createExceptionParserOrNullIfNotFound(method);
logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s", logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s",
method, request, transformer, exceptionParser); method, request, transformer, exceptionParser);
Future<?> result = commandFactory.create(request, transformer, exceptionParser).execute(); Future<?> result = commandFactory.create(request, transformer, exceptionParser).execute();
if (exceptionParser != null) { if (exceptionParser != null) {
logger.trace("%s - wrapping future for request %s in exceptionParser %s", method, request, logger.trace("%s - wrapping future for request %s in exceptionParser %s", method,
exceptionParser); request, exceptionParser);
result = new FutureExceptionParser(result, exceptionParser); result = new FutureExceptionParser(result, exceptionParser);
} }
if (method.getReturnType().isAssignableFrom(Future.class)) { if (method.getReturnType().isAssignableFrom(Future.class)) {
return result; return result;
} else { } else {
logger.trace("%s - invoking request synchronously %s", method, request, exceptionParser); logger
.trace("%s - invoking request synchronously %s", method, request,
exceptionParser);
return result.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); return result.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} }
} else {
throw new RuntimeException("method is intended solely to set constants: " + method);
}
} }
@Override @Override

View File

@ -25,7 +25,6 @@ package org.jclouds.http;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -55,7 +54,6 @@ import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Module; import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names; import com.google.inject.name.Names;
public abstract class BaseJettyTest { public abstract class BaseJettyTest {
@ -133,33 +131,18 @@ public abstract class BaseJettyTest {
properties.put(HttpConstants.PROPERTY_HTTP_PORT, testPort + ""); properties.put(HttpConstants.PROPERTY_HTTP_PORT, testPort + "");
properties.put(HttpConstants.PROPERTY_HTTP_SECURE, "false"); properties.put(HttpConstants.PROPERTY_HTTP_SECURE, "false");
addConnectionProperties(properties); addConnectionProperties(properties);
final List<HttpRequestFilter> filters = new ArrayList<HttpRequestFilter>(1);
filters.add(new HttpRequestFilter() {
public void filter(HttpRequest request) throws HttpException {
if (request.getHeaders().containsKey("filterme")) {
request.getHeaders().put("test", "test");
}
}
});
List<Module> modules = Lists.newArrayList(new AbstractModule() { List<Module> modules = Lists.newArrayList(new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
Names.bindProperties(binder(), properties); Names.bindProperties(binder(), properties);
bind(URI.class).toInstance(URI.create("http://localhost:" + testPort));
} }
}, new JDKLoggingModule(), }, new JDKLoggingModule(), new JaxrsModule(), createClientModule());
new JaxrsModule(), createClientModule(), new AbstractModule() {
@Override
protected void configure() {
bind(new TypeLiteral<List<HttpRequestFilter>>() {
}).toInstance(filters);
}
});
CloudContextBuilder.addExecutorServiceIfNotPresent(modules); CloudContextBuilder.addExecutorServiceIfNotPresent(modules);
injector = Guice.createInjector(modules); injector = Guice.createInjector(modules);
RestClientFactory factory = injector.getInstance(RestClientFactory.class); RestClientFactory factory = injector.getInstance(RestClientFactory.class);
client = factory.create(IntegrationTestClient.class); client = factory.create(URI.create("http://localhost:" + testPort),
IntegrationTestClient.class);
closer = injector.getInstance(Closer.class); closer = injector.getInstance(Closer.class);
assert client != null; assert client != null;
} }

View File

@ -36,6 +36,7 @@ import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.rest.EntityParam; import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser; import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.XMLResponseParser; import org.jclouds.rest.XMLResponseParser;
import com.google.common.base.Function; import com.google.common.base.Function;
@ -83,8 +84,16 @@ public interface IntegrationTestClient {
@GET @GET
@Path("objects/{id}") @Path("objects/{id}")
@RequestFilters(Filter.class)
Future<String> downloadFilter(@PathParam("id") String id, @HeaderParam("filterme") String header); Future<String> downloadFilter(@PathParam("id") String id, @HeaderParam("filterme") String header);
static class Filter implements HttpRequestFilter {
public void filter(HttpRequest request) throws HttpException {
if (request.getHeaders().containsKey("filterme")) {
request.getHeaders().put("test", "test");
}
}
}
@GET @GET
@Path("objects/{id}") @Path("objects/{id}")
Future<String> download(@PathParam("id") String id, @HeaderParam("test") String header); Future<String> download(@PathParam("id") String id, @HeaderParam("test") String header);

View File

@ -41,8 +41,10 @@ import javax.ws.rs.PathParam;
import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpMethod; import org.jclouds.http.HttpMethod;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpRequestFilter;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnStringIf200;
@ -62,15 +64,52 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.name.Named;
import com.google.inject.name.Names;
/** /**
* Tests behavior of Guice.createInjector().getInstance(JaxrsUtil.Factory.class) * Tests behavior of {@code JaxrsAnnotationProcessor}
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit", testName = "jaxrs.JaxrsUtilTest") @Test(groups = "unit", testName = "jaxrs.JaxrsUtilTest")
public class JaxrsAnnotationProcessorTest { public class JaxrsAnnotationProcessorTest {
static class TestRequestFilter1 implements HttpRequestFilter {
public void filter(HttpRequest request) throws HttpException {
}
}
static class TestRequestFilter2 implements HttpRequestFilter {
public void filter(HttpRequest request) throws HttpException {
}
}
@RequestFilters(TestRequestFilter1.class)
static class TestRequestFilter {
@GET
@RequestFilters(TestRequestFilter2.class)
public void get() {
}
}
@Test
public void testRequestFilter() throws SecurityException, NoSuchMethodException {
Method method = TestRequestFilter.class.getMethod("get");
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestRequestFilter.class).createRequest(endpoint,
method, new Object[] {});
assertEquals(httpMethod.getFilters().size(), 2);
assertEquals(httpMethod.getFilters().get(0).getClass(), TestRequestFilter1.class);
assertEquals(httpMethod.getFilters().get(1).getClass(), TestRequestFilter2.class);
}
@SkipEncoding('/') @SkipEncoding('/')
public class TestEncoding { public class TestEncoding {
@ -103,6 +142,31 @@ public class JaxrsAnnotationProcessorTest {
assertEquals(httpMethod.getHeaders().size(), 0); assertEquals(httpMethod.getHeaders().size(), 0);
} }
@SkipEncoding('/')
@Path("/v1/{account}")
public interface TestConstantPathParam {
@Named("testaccount")
@PathParam("account")
void setUsername();
@GET
@Path("{path1}/{path2}")
public void twoPaths(@PathParam("path1") String path, @PathParam("path2") String path2);
}
@Test
public void testConstantPathParam() throws SecurityException, NoSuchMethodException {
Method method = TestConstantPathParam.class.getMethod("twoPaths", String.class, String.class);
URI endpoint = URI.create("http://localhost");
HttpRequest httpMethod = factory.create(TestConstantPathParam.class).createRequest(endpoint,
method, new Object[] { "1", "localhost" });
assertEquals(httpMethod.getEndpoint().getPath(), "/v1/ralphie/1/localhost");
assertEquals(httpMethod.getMethod(), HttpMethod.GET);
assertEquals(httpMethod.getHeaders().size(), 0);
}
public class TestPath { public class TestPath {
@GET @GET
@ -627,6 +691,7 @@ public class JaxrsAnnotationProcessorTest {
factory = Guice.createInjector(new AbstractModule() { factory = Guice.createInjector(new AbstractModule() {
@Override @Override
protected void configure() { protected void configure() {
bindConstant().annotatedWith(Names.named("testaccount")).to("ralphie");
bind(URI.class).toInstance(URI.create("http://localhost:8080")); bind(URI.class).toInstance(URI.create("http://localhost:8080"));
} }
}, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()),

View File

@ -24,8 +24,6 @@
package org.jclouds.http.httpnio.pool; package org.jclouds.http.httpnio.pool;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -56,16 +54,6 @@ public class NioHttpCommandExecutionHandler implements NHttpRequestExecutionHand
private final ConsumingNHttpEntityFactory entityFactory; private final ConsumingNHttpEntityFactory entityFactory;
private final DelegatingRetryHandler retryHandler; private final DelegatingRetryHandler retryHandler;
private final DelegatingErrorHandler errorHandler; private final DelegatingErrorHandler errorHandler;
private List<HttpRequestFilter> requestFilters = Collections.emptyList();
public List<HttpRequestFilter> getRequestFilters() {
return requestFilters;
}
@Inject(optional = true)
public void setRequestFilters(List<HttpRequestFilter> requestFilters) {
this.requestFilters = requestFilters;
}
/** /**
* inputOnly: nothing is taken from this queue. * inputOnly: nothing is taken from this queue.
@ -97,7 +85,7 @@ public class NioHttpCommandExecutionHandler implements NHttpRequestExecutionHand
.removeAttribute("command"); .removeAttribute("command");
if (rendezvous != null) { if (rendezvous != null) {
HttpRequest request = rendezvous.getCommand().getRequest(); HttpRequest request = rendezvous.getCommand().getRequest();
for (HttpRequestFilter filter : getRequestFilters()) { for (HttpRequestFilter filter : request.getFilters()) {
filter.filter(request); filter.filter(request);
} }
return NioHttpUtils.convertToApacheRequest(request); return NioHttpUtils.convertToApacheRequest(request);