diff --git a/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java index 36f15a85fe..317b899d36 100644 --- a/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java +++ b/core/src/main/java/org/jclouds/http/TransformingHttpCommandExecutorService.java @@ -22,6 +22,7 @@ import java.util.concurrent.Future; import com.google.common.base.Function; import com.google.common.util.concurrent.ListenableFuture; +import com.google.inject.ImplementedBy; /** * Executor which will invoke and transform the response of an {@code EndpointCommand} into generic @@ -29,6 +30,7 @@ import com.google.common.util.concurrent.ListenableFuture; * * @author Adrian Cole */ +@ImplementedBy(TransformingHttpCommandExecutorServiceImpl.class) public interface TransformingHttpCommandExecutorService { /** * diff --git a/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java b/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java index 0a2643014b..90f5de6f14 100644 --- a/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java +++ b/core/src/main/java/org/jclouds/http/TransformingHttpCommandImpl.java @@ -29,6 +29,7 @@ import org.jclouds.logging.Logger; import org.jclouds.rest.internal.GeneratedHttpRequest; import com.google.common.base.Function; +import com.google.common.base.Objects; import com.google.common.util.concurrent.ListenableFuture; /** @@ -144,6 +145,20 @@ public class TransformingHttpCommandImpl implements TransformingHttpCommand) diff --git a/core/src/main/java/org/jclouds/rest/RestContextSpec.java b/core/src/main/java/org/jclouds/rest/RestContextSpec.java index a1a4cefa91..7d92bf2d92 100644 --- a/core/src/main/java/org/jclouds/rest/RestContextSpec.java +++ b/core/src/main/java/org/jclouds/rest/RestContextSpec.java @@ -71,20 +71,13 @@ public class RestContextSpec { (Class) RestContextBuilder.class, EMPTY_LIST); } - /** - * this uses the inefficient {@link Objects} implementation as the object count will be - * relatively small and therefore efficiency is not a concern. - */ + @Override public int hashCode() { return Objects.hashCode(provider, endpoint, apiVersion, iso3166Codes, identity, credential, sync, async, propertiesBuilderClass, contextBuilderClass, modules); } - /** - * this uses the inefficient {@link Objects} implementation as the object count will be - * relatively small and therefore efficiency is not a concern. - */ @Override public boolean equals(Object that) { if (that == null) @@ -92,10 +85,6 @@ public class RestContextSpec { return Objects.equal(this.toString(), that.toString()); } - /** - * this uses the inefficient {@link Objects} implementation as the object count will be - * relatively small and therefore efficiency is not a concern. - */ @Override public String toString() { return Objects.toStringHelper(this).add("provider", provider).add("endpoint", endpoint).add("apiVersion", diff --git a/core/src/main/java/org/jclouds/util/Throwables2.java b/core/src/main/java/org/jclouds/util/Throwables2.java index b7c1de840e..559dfe74f7 100644 --- a/core/src/main/java/org/jclouds/util/Throwables2.java +++ b/core/src/main/java/org/jclouds/util/Throwables2.java @@ -80,12 +80,15 @@ public class Throwables2 { return (Exception) throwable; } } - for (Class propagatableExceptionType : new Class[] { IllegalStateException.class, + for (Class propagatableExceptionType : new Class[] { IllegalStateException.class, AssertionError.class, UnsupportedOperationException.class, IllegalArgumentException.class, AuthorizationException.class, ResourceNotFoundException.class, InsufficientResourcesException.class, HttpResponseException.class }) { Throwable throwable = getFirstThrowableOfType(exception, propagatableExceptionType); if (throwable != null) { - throw (Exception) throwable; + if (throwable instanceof AssertionError) + throw (AssertionError) throwable; + else + throw (Exception) throwable; } } Throwables.propagateIfPossible(exception.getCause(), Exception.class); diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java b/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java index 50d516dece..694c298aab 100644 --- a/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java +++ b/core/src/test/java/org/jclouds/http/IntegrationTestAsyncClient.java @@ -48,6 +48,7 @@ import org.jclouds.rest.annotations.XMLResponseParser; import org.jclouds.rest.binders.BindMapToMatrixParams; import org.jclouds.rest.binders.BindToJsonPayload; import org.jclouds.rest.binders.BindToStringPayload; +import org.jclouds.rest.functions.ReturnFalseOnNotFoundOr404; import org.jclouds.util.Strings2; import com.google.common.base.Function; @@ -73,6 +74,7 @@ public interface IntegrationTestAsyncClient { @HEAD @Path("/objects/{id}") + @ExceptionParser(ReturnFalseOnNotFoundOr404.class) ListenableFuture exists(@PathParam("id") String path); @GET diff --git a/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java b/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java new file mode 100644 index 0000000000..d584ef7f34 --- /dev/null +++ b/core/src/test/java/org/jclouds/http/IntegrationTestClientExpectTest.java @@ -0,0 +1,57 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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; + +import static org.testng.Assert.assertEquals; + +import java.net.URI; + +import org.jclouds.rest.BaseRestClientExpectTest; +import org.jclouds.rest.BaseRestClientExpectTest.RegisterContext; +import org.testng.annotations.Test; + +/** + * + * Allows us to test a client via its side effects. + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "IntegrationTestClientExpectTest") +// only needed as IntegrationTestClient is not registered in rest.properties +@RegisterContext(sync = IntegrationTestClient.class, async = IntegrationTestAsyncClient.class) +public class IntegrationTestClientExpectTest extends BaseRestClientExpectTest { + + public void testWhenResponseIs2xxExistsReturnsTrue() { + + IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint( + URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(200).build()); + + assertEquals(client.exists("rabbit"), true); + + } + + public void testWhenResponseIs404ExistsReturnsFalse() { + + IntegrationTestClient client = requestSendsResponse(HttpRequest.builder().method("HEAD").endpoint( + URI.create("http://mock/objects/rabbit")).build(), HttpResponse.builder().statusCode(404).build()); + + assertEquals(client.exists("rabbit"), false); + + } +} diff --git a/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java b/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java new file mode 100644 index 0000000000..a4d57d4c6f --- /dev/null +++ b/core/src/test/java/org/jclouds/rest/BaseRestClientExpectTest.java @@ -0,0 +1,232 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except fn 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 fn 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 com.google.common.base.Preconditions.checkNotNull; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.jclouds.rest.RestContextFactory.contextSpec; +import static org.jclouds.rest.RestContextFactory.createContext; +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Properties; +import java.util.Map.Entry; +import java.util.concurrent.ExecutorService; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.Constants; +import org.jclouds.concurrent.MoreExecutors; +import org.jclouds.concurrent.SingleThreaded; +import org.jclouds.concurrent.config.ConfiguresExecutorService; +import org.jclouds.http.HttpCommandExecutorService; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpUtils; +import org.jclouds.http.IOExceptionRetryHandler; +import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; +import org.jclouds.http.handlers.DelegatingErrorHandler; +import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.http.internal.BaseHttpCommandExecutorService; +import org.jclouds.http.internal.HttpWire; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.logging.config.NullLoggingModule; +import org.jclouds.util.Strings2; +import org.testng.annotations.Test; + +import com.google.common.annotations.Beta; +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableSet; +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.Module; +import com.google.inject.TypeLiteral; +import com.google.inject.name.Names; + +/** + * + * Allows us to test a client via its side effects. + * + * @author Adrian Cole + */ +@Test(groups = "unit") +@Beta +public abstract class BaseRestClientExpectTest { + /** + * only needed when the client is simple and not registered fn rest.properties + */ + @Target(TYPE) + @Retention(RUNTIME) + public static @interface RegisterContext { + Class sync(); + + Class async(); + } + + protected String provider = "mock"; + + protected Module createModule() { + return new Module() { + + @Override + public void configure(Binder binder) { + + } + + }; + } + + protected Payload payloadFromResource(String resource) { + return Payloads.newInputStreamPayload(getClass().getResourceAsStream(resource)); + } + + @SingleThreaded + @Singleton + public static class ExpectHttpCommandExecutorService extends BaseHttpCommandExecutorService { + + private final Function fn; + + @Inject + public ExpectHttpCommandExecutorService(Function fn, HttpUtils utils, + @Named(Constants.PROPERTY_IO_WORKER_THREADS) ExecutorService ioExecutor, + IOExceptionRetryHandler ioRetryHandler, DelegatingRetryHandler retryHandler, + DelegatingErrorHandler errorHandler, HttpWire wire) { + super(utils, ioExecutor, retryHandler, ioRetryHandler, errorHandler, wire); + this.fn = checkNotNull(fn, "fn"); + } + + @Override + protected void cleanup(HttpRequest nativeResponse) { + if (nativeResponse.getPayload() != null) + nativeResponse.getPayload().release(); + } + + @Override + protected HttpRequest convert(HttpRequest request) throws IOException, InterruptedException { + return request; + } + + @Override + protected HttpResponse invoke(HttpRequest nativeRequest) throws IOException, InterruptedException { + return fn.apply(nativeRequest); + } + } + + @ConfiguresHttpCommandExecutorService + @ConfiguresExecutorService + public static class ExpectModule extends AbstractModule { + private final Function fn; + + public ExpectModule(Function fn) { + this.fn = checkNotNull(fn, "fn"); + } + + @Override + protected void configure() { + bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_USER_THREADS)).toInstance( + MoreExecutors.sameThreadExecutor()); + bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance( + MoreExecutors.sameThreadExecutor()); + bind(new TypeLiteral>() { + }).toInstance(fn); + bind(HttpCommandExecutorService.class).to(ExpectHttpCommandExecutorService.class); + } + } + + protected S requestSendsResponse(final HttpRequest fn, final HttpResponse out) { + return createClient(new Function() { + + @Override + public HttpResponse apply(HttpRequest command) { + assertEquals(renderRequest(command), renderRequest(fn)); + return out; + } + }); + } + + private String renderRequest(HttpRequest request) { + StringBuilder builder = new StringBuilder().append(request.getRequestLine()).append('\n'); + for (Entry header : request.getHeaders().entries()) { + builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n'); + } + if (request.getPayload() != null) { + for (Entry header : HttpUtils.getContentHeadersFromMetadata( + request.getPayload().getContentMetadata()).entries()) { + builder.append(header.getKey()).append(": ").append(header.getValue()).append('\n'); + } + try { + builder.append('\n').append(Strings2.toStringAndClose(request.getPayload().getInput())); + } catch (IOException e) { + throw Throwables.propagate(e); + } + + } else { + builder.append('\n'); + } + return builder.toString(); + } + + protected S createClient(Function fn) { + return createClient(fn, createModule(), setupProperties()); + } + + protected S createClient(Function fn, Module module) { + return createClient(fn, module, setupProperties()); + + } + + protected S createClient(Function fn, Properties props) { + return createClient(fn, createModule(), props); + + } + + protected S createClient(Function fn, Module module, Properties props) { + RestContextSpec contextSpec = makeContextSpec(); + + return createContext(contextSpec, + ImmutableSet. of(new ExpectModule(fn), new NullLoggingModule(), module), props).getApi(); + } + + @SuppressWarnings("unchecked") + private RestContextSpec makeContextSpec() { + if (getClass().isAnnotationPresent(RegisterContext.class)) + return (RestContextSpec) contextSpec(provider, "http://mock", "1", "", "userfoo", null, getClass() + .getAnnotation(RegisterContext.class).sync(), + getClass().getAnnotation(RegisterContext.class).async(), ImmutableSet. of()); + else + return new RestContextFactory(setupRestProperties()).createContextSpec(provider, "identity", "credential", + new Properties()); + } + + + protected Properties setupRestProperties() { + return RestContextFactory.getPropertiesFromResource("/rest.properties"); + } + + protected Properties setupProperties() { + return new Properties(); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java b/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java index b307a22108..b052b05c15 100644 --- a/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java +++ b/core/src/test/java/org/jclouds/rest/BaseRestClientTest.java @@ -35,8 +35,8 @@ import org.jclouds.concurrent.MoreExecutors; import org.jclouds.concurrent.config.ConfiguresExecutorService; import org.jclouds.crypto.Crypto; import org.jclouds.crypto.CryptoStreams; +import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpRequest; -import org.jclouds.http.TransformingHttpCommandExecutorService; import org.jclouds.http.config.ConfiguresHttpCommandExecutorService; import org.jclouds.http.functions.ParseSax; import org.jclouds.io.MutableContentMetadata; @@ -65,13 +65,13 @@ public abstract class BaseRestClientTest { @ConfiguresHttpCommandExecutorService @ConfiguresExecutorService public static class MockModule extends AbstractModule { - private final TransformingHttpCommandExecutorService mock; + private final HttpCommandExecutorService mock; public MockModule() { - this(createMock(TransformingHttpCommandExecutorService.class)); + this(createMock(HttpCommandExecutorService.class)); } - public MockModule(TransformingHttpCommandExecutorService mock) { + public MockModule(HttpCommandExecutorService mock) { this.mock = mock; } @@ -81,7 +81,7 @@ public abstract class BaseRestClientTest { MoreExecutors.sameThreadExecutor()); bind(ExecutorService.class).annotatedWith(Names.named(Constants.PROPERTY_IO_WORKER_THREADS)).toInstance( MoreExecutors.sameThreadExecutor()); - bind(TransformingHttpCommandExecutorService.class).toInstance(mock); + bind(HttpCommandExecutorService.class).toInstance(mock); } } diff --git a/core/src/test/java/org/jclouds/rest/RestClientTest.java b/core/src/test/java/org/jclouds/rest/RestClientTest.java index c4bf0d79f5..627f07d3d5 100644 --- a/core/src/test/java/org/jclouds/rest/RestClientTest.java +++ b/core/src/test/java/org/jclouds/rest/RestClientTest.java @@ -65,6 +65,7 @@ public abstract class RestClientTest extends BaseRestClientTest { @BeforeClass protected void setupFactory() throws IOException { RestContextSpec contextSpec = createContextSpec(); + injector = createContextBuilder(contextSpec, ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()), getProperties()) .buildInjector(); 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 5f2bbdcf95..e86d913917 100644 --- a/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/internal/RestAnnotationProcessorTest.java @@ -20,12 +20,6 @@ package org.jclouds.rest.internal; import static com.google.common.base.Charsets.UTF_8; import static com.google.common.base.Preconditions.checkNotNull; -import static org.easymock.EasyMock.eq; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.reportMatcher; -import static org.easymock.classextension.EasyMock.createNiceMock; -import static org.easymock.classextension.EasyMock.replay; -import static org.easymock.classextension.EasyMock.verify; import static org.jclouds.io.Payloads.calculateMD5; import static org.jclouds.io.Payloads.newInputStreamPayload; import static org.jclouds.io.Payloads.newStringPayload; @@ -53,6 +47,7 @@ import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.inject.Named; @@ -75,26 +70,24 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriBuilder; import javax.xml.bind.annotation.XmlRootElement; -import org.easymock.IArgumentMatcher; import org.eclipse.jetty.http.HttpHeaders; import org.jclouds.concurrent.Timeout; import org.jclouds.crypto.Crypto; import org.jclouds.date.DateService; import org.jclouds.date.internal.SimpleDateFormatDateService; import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpCommandExecutorService; import org.jclouds.http.HttpException; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpResponse; import org.jclouds.http.IOExceptionRetryHandler; import org.jclouds.http.RequiresHttp; -import org.jclouds.http.TransformingHttpCommandExecutorService; import org.jclouds.http.functions.ParseFirstJsonValueNamed; import org.jclouds.http.functions.ParseJson; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.functions.ParseXMLWithJAXB; -import org.jclouds.http.functions.ReleasePayloadAndReturn; import org.jclouds.http.functions.ReturnInputStream; import org.jclouds.http.functions.ReturnStringIf2xx; import org.jclouds.http.functions.ReturnTrueIf2xx; @@ -180,8 +173,8 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { @RequiresHttp @ConfiguresRestClient - protected static class CallerCalleeModule extends RestClientModule { - CallerCalleeModule() { + protected static class CallerModule extends RestClientModule { + CallerModule() { super(Caller.class, AsyncCaller.class, ImmutableMap., Class> of(Callee.class, AsyncCallee.class)); } @@ -227,13 +220,17 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { public AsyncCallee getCallee(@EndpointParam URI endpoint); } - @SuppressWarnings("unchecked") - public void testDelegateAsyncIncludesVersion() throws SecurityException, NoSuchMethodException, - InterruptedException, ExecutionException { - Injector child = injectorForClient(); - TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class); + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { - ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class); + @Override + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), + "GET http://localhost:9999/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); + } + + }); try { child.getInstance(AsyncCallee.class); @@ -242,42 +239,21 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } - AsyncCaller caller = child.getInstance(AsyncCaller.class); - expect(mock.submit(requestLineEquals("GET http://localhost:9999/client/1/foo HTTP/1.1"), eq(function))) - .andReturn(createNiceMock(ListenableFuture.class)).atLeastOnce(); - replay(mock); - - caller.getCallee().onePath("foo"); - - verify(mock); + child.getInstance(AsyncCaller.class).getCallee().onePath("foo").get(); } - public static HttpCommand requestLineEquals(final String requestLine) { - reportMatcher(new IArgumentMatcher() { + public void testDelegateIsLazyLoadedAndRequestIncludesVersionAndPath() throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { @Override - public void appendTo(StringBuffer buffer) { - buffer.append("requestLineEquals("); - buffer.append(requestLine); - buffer.append(")"); - } - - @Override - public boolean matches(Object arg) { - return ((HttpCommand) arg).getCurrentRequest().getRequestLine().equals(requestLine); + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), + "GET http://localhost:1111/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); } }); - return null; - } - - public void testDelegateWithOverridingEndpoint() throws SecurityException, NoSuchMethodException, - InterruptedException, ExecutionException { - Injector child = injectorForClient(); - TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class); - - ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class); try { child.getInstance(Callee.class); @@ -286,23 +262,45 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } - Caller caller = child.getInstance(Caller.class); - expect(mock.submit(requestLineEquals("GET http://localhost:1111/client/1/foo HTTP/1.1"), eq(function))) - .andReturn(Futures. immediateFuture(null)).atLeastOnce(); - replay(mock); + child.getInstance(Caller.class).getCallee().onePath("foo"); - caller.getCallee().onePath("foo"); + } + - verify(mock); + public void testAsyncDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { + + @Override + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), + "GET http://howdyboys/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); + } + + }); + + try { + child.getInstance(AsyncCallee.class); + assert false : "Callee shouldn't be bound yet"; + } catch (ConfigurationException e) { + + } + + child.getInstance(AsyncCaller.class).getCallee(URI.create("http://howdyboys")).onePath("foo").get(); } - public void testDelegateWithOverridingEndpointOnMethod() throws SecurityException, NoSuchMethodException, - InterruptedException, ExecutionException { - Injector child = injectorForClient(); - TransformingHttpCommandExecutorService mock = child.getInstance(TransformingHttpCommandExecutorService.class); + public void testDelegateIsLazyLoadedAndRequestIncludesEndpointVersionAndPath() throws InterruptedException, ExecutionException { + Injector child = injectorForCaller(new HttpCommandExecutorService() { - ReleasePayloadAndReturn function = child.getInstance(ReleasePayloadAndReturn.class); + @Override + public Future submit(HttpCommand command) { + assertEquals(command.getCurrentRequest().getRequestLine(), + "GET http://howdyboys/client/1/foo HTTP/1.1"); + return Futures.immediateFuture(HttpResponse.builder().build()); + } + + }); try { child.getInstance(Callee.class); @@ -311,22 +309,15 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest { } - Caller caller = child.getInstance(Caller.class); - expect(mock.submit(requestLineEquals("GET http://howdyboys/client/1/foo HTTP/1.1"), eq(function))).andReturn( - Futures. immediateFuture(null)).atLeastOnce(); - replay(mock); - - caller.getCallee(URI.create("http://howdyboys")).onePath("foo"); - - verify(mock); + child.getInstance(Caller.class).getCallee(URI.create("http://howdyboys")).onePath("foo"); } - - private Injector injectorForClient() { + + private Injector injectorForCaller(HttpCommandExecutorService service) { RestContextSpec contextSpec = contextSpec("test", "http://localhost:9999", "1", "", "userfoo", null, Caller.class, AsyncCaller.class, - ImmutableSet. of(new MockModule(), new NullLoggingModule(), new CallerCalleeModule())); + ImmutableSet. of(new MockModule(service), new NullLoggingModule(), new CallerModule())); return createContextBuilder(contextSpec).buildInjector(); diff --git a/core/src/test/java/org/jclouds/util/Throwables2Test.java b/core/src/test/java/org/jclouds/util/Throwables2Test.java index f449fe7bf3..ecc89b9a9c 100644 --- a/core/src/test/java/org/jclouds/util/Throwables2Test.java +++ b/core/src/test/java/org/jclouds/util/Throwables2Test.java @@ -116,6 +116,12 @@ public class Throwables2Test { returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e)); } + @Test(expectedExceptions = AssertionError.class) + public void testPropagateStandardExceptionAssertionError() throws Exception { + AssertionError e = new AssertionError(); + returnFirstExceptionIfInListOrThrowStandardExceptionOrCause(new Class[] {}, new RuntimeException(e)); + } + @Test(expectedExceptions = AuthorizationException.class) public void testPropagateStandardExceptionAuthorizationException() throws Exception { Exception e = new AuthorizationException();