From c81a3de1669d8f8f76119ef082ae5221e8d37371 Mon Sep 17 00:00:00 2001 From: Everett Toews Date: Mon, 9 Dec 2013 20:53:23 -0600 Subject: [PATCH] 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. --- .../rest/internal/RestAnnotationProcessor.java | 12 +- .../rest/internal/RestAnnotationProcessorTest.java | 173 +++++++++++++++++++++ 2 files changed, 183 insertions(+), 2 deletions(-) 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 GeneratedHttpRequest apply(Invocation invocation) { formParams.putAll(addFormParams(tokenValues, invocation)); } else { formParams = addFormParams(tokenValues, invocation); - } + } + Multimap queryParams = addQueryParams(tokenValues, invocation); - Multimap headers = buildHeaders(tokenValues, invocation); + + Multimap 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()); 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 @@ protected void configure() { @GET @Path("/{path}") ListenableFuture onePath(@PathParam("path") String path); + + @POST + ListenableFuture testWithoutProducesAndConsumes(); + + @POST + @Produces(MediaType.APPLICATION_XML) + @Consumes(MediaType.APPLICATION_XML) + ListenableFuture testProducesAndConsumesOnMethod(); + } + + @Path("/client/{jclouds.api-version}") + @Produces(MediaType.APPLICATION_XML) + @Consumes(MediaType.APPLICATION_XML) + public interface AsyncCalleeWithProducesAndConsumesOnClass extends Closeable { + @POST + ListenableFuture testProducesAndConsumesOnClass(); } @Path("/client/{jclouds.api-version}") @@ -213,10 +229,28 @@ protected void configure() { @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 @@ protected void configure() { @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 HttpResponse invoke(HttpCommand command) { 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 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 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 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 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() { -- 1.8.5.1