From 2573d399cab90cbb6f94440b255dd89ed150397c Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Mon, 13 Jul 2009 22:47:45 +0000 Subject: [PATCH] Issue 76: new annotation for RequestFilters git-svn-id: http://jclouds.googlecode.com/svn/trunk@1623 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../java/org/jclouds/aws/s3/S3Connection.java | 3 + .../org/jclouds/aws/s3/S3ContextBuilder.java | 2 +- .../aws/s3/config/RestS3ConnectionModule.java | 17 ++--- .../jclouds/cloud/CloudContextBuilder.java | 6 ++ .../java/org/jclouds/http/HttpConstants.java | 2 + .../java/org/jclouds/http/HttpRequest.java | 12 ++++ .../org/jclouds/http/functions/ParseSax.java | 2 +- .../BaseHttpCommandExecutorService.java | 9 +-- .../rest/JaxrsAnnotationProcessor.java | 69 ++++++++++++++++--- .../java/org/jclouds/rest/RequestFilters.java | 44 ++++++++++++ .../org/jclouds/rest/RestClientFactory.java | 5 +- .../org/jclouds/rest/RestClientProxy.java | 49 +++++++------ .../java/org/jclouds/http/BaseJettyTest.java | 23 +------ .../jclouds/http/IntegrationTestClient.java | 9 +++ .../rest/JaxrsAnnotationProcessorTest.java | 67 +++++++++++++++++- .../pool/NioHttpCommandExecutionHandler.java | 16 +---- 16 files changed, 243 insertions(+), 92 deletions(-) create mode 100644 core/src/main/java/org/jclouds/rest/RequestFilters.java diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java index c11c106fc7..e96213ff77 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java @@ -39,6 +39,7 @@ import org.jclouds.aws.s3.binders.S3ObjectBinder; import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.S3Bucket; 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.ParseMetadataFromHeaders; 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.PathParamParser; import org.jclouds.rest.Query; +import org.jclouds.rest.RequestFilters; import org.jclouds.rest.ResponseParser; import org.jclouds.rest.SkipEncoding; import org.jclouds.rest.VirtualHost; @@ -84,6 +86,7 @@ import org.jclouds.rest.XMLResponseParser; */ @VirtualHost @SkipEncoding('/') +@RequestFilters(RequestAuthorizeSignature.class) public interface S3Connection { /** diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java index 34694957ad..07111cc02b 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java @@ -73,8 +73,8 @@ public class S3ContextBuilder extends CloudContextBuilder provideRequestFilters(RequestAuthorizeSignature requestAuthorizeSignature) { - List filters = new ArrayList(); - filters.add(requestAuthorizeSignature); - return filters; - } - @Singleton @Provides protected URI provideAddress(@Named(HttpConstants.PROPERTY_HTTP_ADDRESS) String address, diff --git a/core/src/main/java/org/jclouds/cloud/CloudContextBuilder.java b/core/src/main/java/org/jclouds/cloud/CloudContextBuilder.java index db0bb35678..d788766919 100644 --- a/core/src/main/java/org/jclouds/cloud/CloudContextBuilder.java +++ b/core/src/main/java/org/jclouds/cloud/CloudContextBuilder.java @@ -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_RELAX_HOSTNAME; 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.pool.PoolConstants.PROPERTY_POOL_IO_WORKER_THREADS; import static org.jclouds.http.pool.PoolConstants.PROPERTY_POOL_MAX_CONNECTIONS; @@ -100,6 +101,11 @@ public abstract class CloudContextBuilder> { return this; } + public CloudContextBuilder withJsonDebug() { + properties.setProperty(PROPERTY_JSON_DEBUG, "true"); + return this; + } + /** * allow mismatches between the certificate and the hostname of ssl requests. */ diff --git a/core/src/main/java/org/jclouds/http/HttpConstants.java b/core/src/main/java/org/jclouds/http/HttpConstants.java index dce0c80ae7..ef8e13c7ff 100644 --- a/core/src/main/java/org/jclouds/http/HttpConstants.java +++ b/core/src/main/java/org/jclouds/http/HttpConstants.java @@ -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_REDIRECTS = "jclouds.http.max-redirects"; 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. */ diff --git a/core/src/main/java/org/jclouds/http/HttpRequest.java b/core/src/main/java/org/jclouds/http/HttpRequest.java index 80a53b0d8e..c1b55160e8 100644 --- a/core/src/main/java/org/jclouds/http/HttpRequest.java +++ b/core/src/main/java/org/jclouds/http/HttpRequest.java @@ -26,10 +26,12 @@ package org.jclouds.http; import static com.google.common.base.Preconditions.checkNotNull; import java.net.URI; +import java.util.List; import org.jclouds.command.Request; import com.google.common.collect.Multimap; +import com.google.inject.internal.Lists; /** * 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 { + private List requestFilters = Lists.newArrayList(); + private final HttpMethod method; private final URI endpoint; private Object entity; @@ -94,4 +98,12 @@ public class HttpRequest extends HttpMessage implements Request { return endpoint; } + public void addFilter(HttpRequestFilter filter) { + requestFilters.add(filter); + } + + public List getFilters() { + return requestFilters; + } + } diff --git a/core/src/main/java/org/jclouds/http/functions/ParseSax.java b/core/src/main/java/org/jclouds/http/functions/ParseSax.java index 351994eef9..bca83c50a2 100644 --- a/core/src/main/java/org/jclouds/http/functions/ParseSax.java +++ b/core/src/main/java/org/jclouds/http/functions/ParseSax.java @@ -94,7 +94,6 @@ public class ParseSax implements Function { } private void parseAndCloseStream(InputStream xml, ContentHandler handler) throws HttpException { - parser.setContentHandler(handler); String response = null; try { if (suckFirst) { @@ -103,6 +102,7 @@ public class ParseSax implements Function { IOUtils.closeQuietly(xml); xml = IOUtils.toInputStream(response); } + parser.setContentHandler(handler); InputSource input = new InputSource(new InputStreamReader(xml, "UTF-8")); parser.parse(input); } catch (Exception e) { diff --git a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java index 67c00d3d72..454228cc96 100644 --- a/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/internal/BaseHttpCommandExecutorService.java @@ -24,8 +24,6 @@ package org.jclouds.http.internal; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -41,8 +39,6 @@ import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.logging.Logger; -import com.google.inject.Inject; - public abstract class BaseHttpCommandExecutorService implements HttpCommandExecutorService { private final DelegatingRetryHandler retryHandler; @@ -52,9 +48,6 @@ public abstract class BaseHttpCommandExecutorService implements HttpCommandEx @Resource protected Logger logger = Logger.NULL; - @Inject(optional = true) - protected List requestFilters = Collections.emptyList(); - protected BaseHttpCommandExecutorService(ExecutorService executorService, DelegatingRetryHandler retryHandler, DelegatingErrorHandler errorHandler) { this.retryHandler = retryHandler; @@ -81,7 +74,7 @@ public abstract class BaseHttpCommandExecutorService implements HttpCommandEx Q nativeRequest = null; try { logger.trace("%s - converting request %s", request.getEndpoint(), request); - for (HttpRequestFilter filter : requestFilters) { + for (HttpRequestFilter filter : request.getFilters()) { filter.filter(request); } nativeRequest = convert(request); diff --git a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java index 8b1bc76c5b..3df278e686 100644 --- a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java @@ -36,6 +36,7 @@ import java.util.Set; import java.util.Map.Entry; import java.util.concurrent.Future; +import javax.annotation.Resource; import javax.ws.rs.HeaderParam; import javax.ws.rs.PathParam; import javax.ws.rs.core.HttpHeaders; @@ -44,12 +45,14 @@ import javax.ws.rs.core.UriBuilder; import org.jboss.resteasy.util.IsHttpMethod; import org.jclouds.http.HttpMethod; import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.options.HttpRequestOptions; +import org.jclouds.logging.Logger; import com.google.common.base.Function; 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.inject.Inject; import com.google.inject.Injector; +import com.google.inject.Key; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.assistedinject.Assisted; import com.google.inject.internal.Lists; +import com.google.inject.name.Named; /** * Tests behavior of JaxrsUtil @@ -74,6 +79,9 @@ import com.google.inject.internal.Lists; @Singleton public class JaxrsAnnotationProcessor { + @Resource + protected Logger logger = Logger.NULL; + private final Class declaring; private final Map>> methodToIndexOfParamToEntityAnnotation = createMethodToIndexOfParamToAnnotation(EntityParam.class); @@ -170,14 +178,19 @@ public class JaxrsAnnotationProcessor { private void seedCache(Class declaring) { Set methods = Sets.newHashSet(declaring.getMethods()); for (Method method : Sets.difference(methods, Sets.newHashSet(Object.class.getMethods()))) { - getHttpMethodOrThrowException(method); - for (int index = 0; index < method.getParameterTypes().length; index++) { - methodToIndexOfParamToEntityAnnotation.get(method).get(index); - methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index); - methodToIndexOfParamToHostPrefixParamAnnotations.get(method).get(index); - methodToindexOfParamToPathParamAnnotations.get(method).get(index); - methodToindexOfParamToPathParamParserAnnotations.get(method).get(index); - methodToIndexesOfOptions.get(method); + if (isHttpMethod(method)) { + for (int index = 0; index < method.getParameterTypes().length; index++) { + methodToIndexOfParamToEntityAnnotation.get(method).get(index); + methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index); + methodToIndexOfParamToHostPrefixParamAnnotations.get(method).get(index); + methodToindexOfParamToPathParamAnnotations.get(method).get(index); + methodToindexOfParamToPathParamParserAnnotations.get(method).get(index); + 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; public HttpRequest createRequest(URI endpoint, Method method, Object[] args) { - HttpMethod httpMethod = getHttpMethodOrThrowException(method); + HttpMethod httpMethod = getHttpMethodOrConstantOrThrowException(method); UriBuilder builder = addHostPrefixIfPresent(endpoint, method, args); + builder.path(declaring); builder.path(method); if (method.isAnnotationPresent(Query.class)) { @@ -227,11 +241,27 @@ public class JaxrsAnnotationProcessor { } HttpRequest request = new HttpRequest(httpMethod, endPoint, headers); addHostHeaderIfAnnotatedWithVirtualHost(headers, request.getEndpoint().getHost(), method); + addFiltersIfAnnotated(method, request); buildEntityIfPostOrPutRequest(method, args, request); return request; } + private void addFiltersIfAnnotated(Method method, HttpRequest request) { + if (declaring.isAnnotationPresent(RequestFilters.class)) { + for (Class clazz : declaring.getAnnotation( + RequestFilters.class).value()) { + request.getFilters().add(injector.getInstance(clazz)); + } + } + if (method.isAnnotationPresent(RequestFilters.class)) { + for (Class clazz : method.getAnnotation(RequestFilters.class) + .value()) { + request.getFilters().add(injector.getInstance(clazz)); + } + } + } + private UriBuilder addHostPrefixIfPresent(URI endpoint, Method method, Object[] args) { Map> map = getIndexToHostPrefixAnnotation(method); UriBuilder builder = UriBuilder.fromUri(endpoint); @@ -280,11 +310,27 @@ public class JaxrsAnnotationProcessor { return null; } - public static HttpMethod getHttpMethodOrThrowException(Method method) { + private Map 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 httpMethods = IsHttpMethod.getHttpMethods(method); if (httpMethods == null || httpMethods.size() != 1) { 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()); } return HttpMethod.valueOf(httpMethods.iterator().next()); @@ -406,6 +452,7 @@ public class JaxrsAnnotationProcessor { private Map getEncodedPathParamKeyValues(Method method, Object[] args, char... skipEncode) throws UnsupportedEncodingException { Map pathParamValues = Maps.newHashMap(); + pathParamValues.putAll(constants); Map> indexToPathParam = methodToindexOfParamToPathParamAnnotations .get(method); Map> indexToPathParamExtractor = methodToindexOfParamToPathParamParserAnnotations diff --git a/core/src/main/java/org/jclouds/rest/RequestFilters.java b/core/src/main/java/org/jclouds/rest/RequestFilters.java new file mode 100644 index 0000000000..9b5a4da4d2 --- /dev/null +++ b/core/src/main/java/org/jclouds/rest/RequestFilters.java @@ -0,0 +1,44 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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[] value(); +} diff --git a/core/src/main/java/org/jclouds/rest/RestClientFactory.java b/core/src/main/java/org/jclouds/rest/RestClientFactory.java index bcc7ca9b63..fccb5103d8 100644 --- a/core/src/main/java/org/jclouds/rest/RestClientFactory.java +++ b/core/src/main/java/org/jclouds/rest/RestClientFactory.java @@ -24,6 +24,7 @@ package org.jclouds.rest; import java.lang.reflect.Proxy; +import java.net.URI; import org.jclouds.rest.RestClientProxy.RestClientProxyFactory; @@ -38,9 +39,9 @@ public class RestClientFactory { } @SuppressWarnings("unchecked") - public T create(Class clazz) { + public T create(URI endPoint, Class clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz }, - proxyFactory.create(clazz)); + proxyFactory.create(endPoint, clazz)); } } diff --git a/core/src/main/java/org/jclouds/rest/RestClientProxy.java b/core/src/main/java/org/jclouds/rest/RestClientProxy.java index de1e0ee003..d38e370d54 100644 --- a/core/src/main/java/org/jclouds/rest/RestClientProxy.java +++ b/core/src/main/java/org/jclouds/rest/RestClientProxy.java @@ -65,12 +65,13 @@ public class RestClientProxy implements InvocationHandler { protected Logger logger = Logger.NULL; public static interface RestClientProxyFactory { - RestClientProxy create(Class clazz); + RestClientProxy create(URI endPoint, Class clazz); } @Inject - public RestClientProxy(JaxrsAnnotationProcessor.Factory utilFactory, URI endPoint, - TransformingHttpCommand.Factory factory, @Assisted Class declaring) { + public RestClientProxy(JaxrsAnnotationProcessor.Factory utilFactory, + TransformingHttpCommand.Factory factory, @Assisted URI endPoint, + @Assisted Class declaring) { this.util = utilFactory.create(declaring); this.declaring = declaring; this.endPoint = endPoint; @@ -83,29 +84,35 @@ public class RestClientProxy implements InvocationHandler { return this.equals(o); } else if (method.getName().equals("hashCode")) { return this.hashCode(); - } - logger.trace("%s - converting method to request", method); - HttpRequest request = util.createRequest(endPoint, method, args); - logger.trace("%s - converted method to request %s", method, request); + } else if (util.isHttpMethod(method)) { + logger.trace("%s - converting method to request", method); + HttpRequest request = util.createRequest(endPoint, method, args); + logger.trace("%s - converted method to request %s", method, request); - Function transformer = util.createResponseParser(method); - Function exceptionParser = util.createExceptionParserOrNullIfNotFound(method); + Function transformer = util.createResponseParser(method); + Function exceptionParser = util + .createExceptionParserOrNullIfNotFound(method); - logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s", - method, request, transformer, exceptionParser); - Future result = commandFactory.create(request, transformer, exceptionParser).execute(); + logger.trace("%s - creating command for request %s, transformer %s, exceptionParser %s", + method, request, transformer, exceptionParser); + Future result = commandFactory.create(request, transformer, exceptionParser).execute(); - if (exceptionParser != null) { - logger.trace("%s - wrapping future for request %s in exceptionParser %s", method, request, - exceptionParser); - result = new FutureExceptionParser(result, exceptionParser); - } + if (exceptionParser != null) { + logger.trace("%s - wrapping future for request %s in exceptionParser %s", method, + request, exceptionParser); + result = new FutureExceptionParser(result, exceptionParser); + } - if (method.getReturnType().isAssignableFrom(Future.class)) { - return result; + if (method.getReturnType().isAssignableFrom(Future.class)) { + return result; + } else { + logger + .trace("%s - invoking request synchronously %s", method, request, + exceptionParser); + return result.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + } } else { - logger.trace("%s - invoking request synchronously %s", method, request, exceptionParser); - return result.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + throw new RuntimeException("method is intended solely to set constants: " + method); } } diff --git a/core/src/test/java/org/jclouds/http/BaseJettyTest.java b/core/src/test/java/org/jclouds/http/BaseJettyTest.java index 4c72fa4498..a7b21f7874 100644 --- a/core/src/test/java/org/jclouds/http/BaseJettyTest.java +++ b/core/src/test/java/org/jclouds/http/BaseJettyTest.java @@ -25,7 +25,6 @@ package org.jclouds.http; import java.io.IOException; import java.net.URI; -import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; @@ -55,7 +54,6 @@ import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Module; -import com.google.inject.TypeLiteral; import com.google.inject.name.Names; public abstract class BaseJettyTest { @@ -133,33 +131,18 @@ public abstract class BaseJettyTest { properties.put(HttpConstants.PROPERTY_HTTP_PORT, testPort + ""); properties.put(HttpConstants.PROPERTY_HTTP_SECURE, "false"); addConnectionProperties(properties); - final List filters = new ArrayList(1); - filters.add(new HttpRequestFilter() { - public void filter(HttpRequest request) throws HttpException { - if (request.getHeaders().containsKey("filterme")) { - request.getHeaders().put("test", "test"); - } - } - }); List modules = Lists.newArrayList(new AbstractModule() { @Override protected void configure() { Names.bindProperties(binder(), properties); - bind(URI.class).toInstance(URI.create("http://localhost:" + testPort)); } - }, new JDKLoggingModule(), - new JaxrsModule(), createClientModule(), new AbstractModule() { - @Override - protected void configure() { - bind(new TypeLiteral>() { - }).toInstance(filters); - } - }); + }, new JDKLoggingModule(), new JaxrsModule(), createClientModule()); CloudContextBuilder.addExecutorServiceIfNotPresent(modules); injector = Guice.createInjector(modules); 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); assert client != null; } diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestClient.java b/core/src/test/java/org/jclouds/http/IntegrationTestClient.java index 36a0d946ea..30490a7642 100644 --- a/core/src/test/java/org/jclouds/http/IntegrationTestClient.java +++ b/core/src/test/java/org/jclouds/http/IntegrationTestClient.java @@ -36,6 +36,7 @@ import org.jclouds.http.functions.ParseSax; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.rest.EntityParam; import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.RequestFilters; import org.jclouds.rest.XMLResponseParser; import com.google.common.base.Function; @@ -83,8 +84,16 @@ public interface IntegrationTestClient { @GET @Path("objects/{id}") + @RequestFilters(Filter.class) Future 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 @Path("objects/{id}") Future download(@PathParam("id") String id, @HeaderParam("test") String header); diff --git a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java index c1529c9880..30ecd968b0 100644 --- a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java @@ -41,8 +41,10 @@ import javax.ws.rs.PathParam; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.HttpException; import org.jclouds.http.HttpMethod; import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpResponse; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; 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.inject.AbstractModule; 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 */ @Test(groups = "unit", testName = "jaxrs.JaxrsUtilTest") 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('/') public class TestEncoding { @@ -103,6 +142,31 @@ public class JaxrsAnnotationProcessorTest { 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 { @GET @@ -627,6 +691,7 @@ public class JaxrsAnnotationProcessorTest { factory = Guice.createInjector(new AbstractModule() { @Override protected void configure() { + bindConstant().annotatedWith(Names.named("testaccount")).to("ralphie"); bind(URI.class).toInstance(URI.create("http://localhost:8080")); } }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), diff --git a/extensions/httpnio/src/main/java/org/jclouds/http/httpnio/pool/NioHttpCommandExecutionHandler.java b/extensions/httpnio/src/main/java/org/jclouds/http/httpnio/pool/NioHttpCommandExecutionHandler.java index ea06ea4509..196433a8e7 100644 --- a/extensions/httpnio/src/main/java/org/jclouds/http/httpnio/pool/NioHttpCommandExecutionHandler.java +++ b/extensions/httpnio/src/main/java/org/jclouds/http/httpnio/pool/NioHttpCommandExecutionHandler.java @@ -24,8 +24,6 @@ package org.jclouds.http.httpnio.pool; import java.io.IOException; -import java.util.Collections; -import java.util.List; import java.util.concurrent.BlockingQueue; import javax.annotation.Resource; @@ -56,16 +54,6 @@ public class NioHttpCommandExecutionHandler implements NHttpRequestExecutionHand private final ConsumingNHttpEntityFactory entityFactory; private final DelegatingRetryHandler retryHandler; private final DelegatingErrorHandler errorHandler; - private List requestFilters = Collections.emptyList(); - - public List getRequestFilters() { - return requestFilters; - } - - @Inject(optional = true) - public void setRequestFilters(List requestFilters) { - this.requestFilters = requestFilters; - } /** * inputOnly: nothing is taken from this queue. @@ -97,7 +85,7 @@ public class NioHttpCommandExecutionHandler implements NHttpRequestExecutionHand .removeAttribute("command"); if (rendezvous != null) { HttpRequest request = rendezvous.getCommand().getRequest(); - for (HttpRequestFilter filter : getRequestFilters()) { + for (HttpRequestFilter filter : request.getFilters()) { filter.filter(request); } return NioHttpUtils.convertToApacheRequest(request); @@ -119,7 +107,7 @@ public class NioHttpCommandExecutionHandler implements NHttpRequestExecutionHand HttpCommandRendezvous rendezvous = handle.getCommandRendezvous(); HttpCommand command = rendezvous.getCommand(); org.jclouds.http.HttpResponse response = NioHttpUtils.convertToJavaCloudsResponse( - command.getRequest().getEndpoint().toURL(), apacheResponse); + command.getRequest().getEndpoint().toURL(), apacheResponse); int statusCode = response.getStatusCode(); // TODO determine how to get the original request here so we don't need to build each // time