Updated Branches: refs/heads/master 83e77d230 -> c40dc996d
JCLOUDS-400: Allow the HeaderParam annotation to be used in a Caller. This allows jclouds to factor out common headers into the Caller so they don't have to be repeated in the Callee. The Produces/Consumes annotations (Content-Type/Accept headers) will also propagate from the Caller to the Callee. Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/c40dc996 Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/c40dc996 Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/c40dc996 Branch: refs/heads/master Commit: c40dc996d96b05ce755f28728a4fb2bcd632b247 Parents: 83e77d2 Author: Everett Toews <[email protected]> Authored: Mon Dec 9 20:53:23 2013 -0600 Committer: Andrew Phillips <[email protected]> Committed: Wed Dec 11 13:24:07 2013 +0100 ---------------------------------------------------------------------- .../rest/internal/RestAnnotationProcessor.java | 12 +- .../internal/RestAnnotationProcessorTest.java | 173 +++++++++++++++++++ 2 files changed, 183 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/c40dc996/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java index d48d876..9624c8f 100644 --- a/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/internal/RestAnnotationProcessor.java @@ -234,9 +234,17 @@ public class RestAnnotationProcessor implements Function<Invocation, HttpRequest formParams.putAll(addFormParams(tokenValues, invocation)); } else { formParams = addFormParams(tokenValues, invocation); - } + } + Multimap<String, Object> queryParams = addQueryParams(tokenValues, invocation); - Multimap<String, String> headers = buildHeaders(tokenValues, invocation); + + Multimap<String, String> headers; + if (caller != null) { + headers = buildHeaders(tokenValues, caller); + headers.putAll(buildHeaders(tokenValues, invocation)); + } else { + headers = buildHeaders(tokenValues, invocation); + } if (r != null) headers.putAll(r.getHeaders()); http://git-wip-us.apache.org/repos/asf/jclouds/blob/c40dc996/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java index 3506572..879bb3b 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -181,6 +181,22 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest { @GET @Path("/{path}") ListenableFuture<Void> onePath(@PathParam("path") String path); + + @POST + ListenableFuture<Void> testWithoutProducesAndConsumes(); + + @POST + @Produces(MediaType.APPLICATION_XML) + @Consumes(MediaType.APPLICATION_XML) + ListenableFuture<Void> testProducesAndConsumesOnMethod(); + } + + @Path("/client/{jclouds.api-version}") + @Produces(MediaType.APPLICATION_XML) + @Consumes(MediaType.APPLICATION_XML) + public interface AsyncCalleeWithProducesAndConsumesOnClass extends Closeable { + @POST + ListenableFuture<Void> testProducesAndConsumesOnClass(); } @Path("/client/{jclouds.api-version}") @@ -213,10 +229,28 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest { @Delegate @Path("/testing/testing/{wibble}") Callee getCalleeWithPath(@EndpointParam URI endpoint, @PathParam("wibble") String wibble); + + @Delegate + Callee getCalleeWithHeader(@EndpointParam URI endpoint, @HeaderParam("header") String header); + + @Delegate + Callee getCalleeWithoutProducesAndConsumes(); + + @Delegate + Callee getCalleeWithProducesAndConsumesOnMethod(); + + @Delegate + CalleeWithProducesAndConsumesOnClass getCalleeWithProducesAndConsumesOnClass(); } public interface Callee extends Closeable { void onePath(String path); + void testWithoutProducesAndConsumes(); + void testProducesAndConsumesOnMethod(); + } + + public interface CalleeWithProducesAndConsumesOnClass extends Closeable { + void testProducesAndConsumesOnClass(); } public interface Callee2 { @@ -243,6 +277,24 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest { @Delegate @Path("/testing/testing/{wibble}") AsyncCallee getCalleeWithPath(@EndpointParam URI endpoint, @PathParam("wibble") String wibble); + + @Delegate + AsyncCallee getCalleeWithHeader(@EndpointParam URI endpoint, @HeaderParam("header") String header); + + @Delegate + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + AsyncCallee getCalleeWithoutProducesAndConsumes(); + + @Delegate + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + AsyncCallee getCalleeWithProducesAndConsumesOnMethod(); + + @Delegate + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + AsyncCalleeWithProducesAndConsumesOnClass getCalleeWithProducesAndConsumesOnClass(); } public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, @@ -371,6 +423,127 @@ public class RestAnnotationProcessorTest extends BaseRestApiTest { assertEquals(child.getInstance(AsyncCaller.class).getURI(), URI.create("http://localhost:1111")); } + public void testAsyncDelegateWithHeaderParamIsLazyLoadedAndRequestIncludesEndpointVersionAndHeader() + throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public ListenableFuture<HttpResponse> submit(HttpCommand command) { + return Futures.immediateFuture(invoke(command)); + } + + @Override + public HttpResponse invoke(HttpCommand command) { + assertEquals(command.getCurrentRequest().getFirstHeaderOrNull("header"), "theheaderparam"); + return HttpResponse.builder().build(); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + fail("Callee shouldn't be bound yet"); + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getCalleeWithHeader(URI.create("http://howdyboys"), "theheaderparam") + .onePath("foo").get(); + } + + public void testAsyncDelegateWithoutProducesAndConsumes() + throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public ListenableFuture<HttpResponse> submit(HttpCommand command) { + return Futures.immediateFuture(invoke(command)); + } + + @Override + public HttpResponse invoke(HttpCommand command) { + assertEquals( + command.getCurrentRequest().getPayload().getContentMetadata().getContentType(), + MediaType.APPLICATION_JSON); + assertTrue(command.getCurrentRequest().getHeaders().get("Accept").contains(MediaType.APPLICATION_JSON)); + return HttpResponse.builder().build(); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + fail("Callee shouldn't be bound yet"); + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getCalleeWithoutProducesAndConsumes() + .testWithoutProducesAndConsumes().get(); + } + + public void testAsyncDelegateWithProducesAndConsumesOnMethodIsLazyLoaded() + throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public ListenableFuture<HttpResponse> submit(HttpCommand command) { + return Futures.immediateFuture(invoke(command)); + } + + @Override + public HttpResponse invoke(HttpCommand command) { + assertEquals( + command.getCurrentRequest().getPayload().getContentMetadata().getContentType(), + MediaType.APPLICATION_XML); + assertTrue(command.getCurrentRequest().getHeaders().get("Accept").contains(MediaType.APPLICATION_XML)); + return HttpResponse.builder().build(); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + fail("Callee shouldn't be bound yet"); + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getCalleeWithProducesAndConsumesOnMethod() + .testProducesAndConsumesOnMethod().get(); + } + + public void testAsyncDelegateWithProducesAndConsumesOnClassIsLazyLoaded() + throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public ListenableFuture<HttpResponse> submit(HttpCommand command) { + return Futures.immediateFuture(invoke(command)); + } + + @Override + public HttpResponse invoke(HttpCommand command) { + assertEquals( + command.getCurrentRequest().getPayload().getContentMetadata().getContentType(), + MediaType.APPLICATION_XML); + assertTrue(command.getCurrentRequest().getHeaders().get("Accept").contains(MediaType.APPLICATION_XML)); + return HttpResponse.builder().build(); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + fail("Callee shouldn't be bound yet"); + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getCalleeWithProducesAndConsumesOnClass() + .testProducesAndConsumesOnClass().get(); + } + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPathOptionalPresent() throws InterruptedException, ExecutionException { Injector child = injectorForCaller(new HttpCommandExecutorService() {
