/* * Copyright 2011-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file 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 com.amazonaws.client.builder; import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.ClientConfiguration; import com.amazonaws.ClientConfigurationFactory; import com.amazonaws.PredefinedClientConfigurations; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.amazonaws.client.AwsAsyncClientParams; import com.amazonaws.client.AwsSyncClientParams; import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.internal.StaticCredentialsProvider; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.regions.AwsRegionProvider; import com.amazonaws.regions.Regions; import org.junit.Test; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import utils.builder.StaticExecutorFactory; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class AwsClientBuilderTest { // Note that the tests rely on the socket timeout being set to some arbitrary unique value private static final ClientConfiguration DEFAULT_CLIENT_CONFIG = new ClientConfiguration() .withSocketTimeout(9001); private static class ConcreteRequestHandler extends RequestHandler2 { } private static class MockClientConfigurationFactory extends ClientConfigurationFactory { @Override protected ClientConfiguration getDefaultConfig() { return DEFAULT_CLIENT_CONFIG; } } private static class ConcreteAsyncBuilder extends AwsAsyncClientBuilder<ConcreteAsyncBuilder, AmazonConcreteClient> { private ConcreteAsyncBuilder() { super(new MockClientConfigurationFactory()); } private ConcreteAsyncBuilder(AwsRegionProvider mockRegionProvider) { super(new MockClientConfigurationFactory(), mockRegionProvider); } @Override protected AmazonConcreteClient build(AwsAsyncClientParams asyncClientParams) { return new AmazonConcreteClient(asyncClientParams); } } private static class ConcreteSyncBuilder extends AwsSyncClientBuilder<ConcreteSyncBuilder, AmazonConcreteClient> { private ConcreteSyncBuilder() { super(new MockClientConfigurationFactory()); } @Override protected AmazonConcreteClient build(AwsSyncClientParams asyncClientParams) { return new AmazonConcreteClient(asyncClientParams); } } /** * Dummy client used by both the {@link ConcreteSyncBuilder} and {@link ConcreteAsyncBuilder}. * Captures the param object the client was created for for verification in tests. */ private static class AmazonConcreteClient extends AmazonWebServiceClient { private AwsAsyncClientParams asyncParams; private AwsSyncClientParams syncParams; private AmazonConcreteClient(AwsAsyncClientParams asyncParams) { super(new ClientConfiguration()); this.asyncParams = asyncParams; } private AmazonConcreteClient(AwsSyncClientParams syncParams) { super(new ClientConfiguration()); this.syncParams = syncParams; } @Override public String getServiceNameIntern() { return "mockservice"; } @Override public String getEndpointPrefix() { return "mockprefix"; } public URI getEndpoint() { return this.endpoint; } public AwsAsyncClientParams getAsyncParams() { return asyncParams; } public AwsSyncClientParams getSyncParams() { return syncParams; } } /** * The sync client is tested less thoroughly then the async client primarily because the async * client exercises most of the same code paths so a bug introduced in the sync client builder * should be exposed via tests written against the async builder. This test is mainly for * additional coverage of the sync builder in case there is a regression specific to sync * builders. */ @Test public void syncClientBuilder() { final List<RequestHandler2> requestHandlers = createRequestHandlerList( new ConcreteRequestHandler(), new ConcreteRequestHandler()); final AWSCredentialsProvider credentials = mock(AWSCredentialsProvider.class); final RequestMetricCollector metrics = mock(RequestMetricCollector.class); //@formatter:off AmazonConcreteClient client = new ConcreteSyncBuilder() .withRegion(Regions.EU_CENTRAL_1) .withClientConfiguration(new ClientConfiguration().withSocketTimeout(1234)) .withCredentials(credentials) .withMetricsCollector(metrics) .withRequestHandlers(requestHandlers.toArray(new RequestHandler2[requestHandlers.size()])) .build(); //@formatter:on assertEquals(URI.create("https://mockprefix.eu-central-1.amazonaws.com"), client.getEndpoint()); assertEquals(1234, client.getSyncParams().getClientConfiguration().getSocketTimeout()); assertEquals(requestHandlers, client.getSyncParams().getRequestHandlers()); assertEquals(credentials, client.getSyncParams().getCredentialsProvider()); assertEquals(metrics, client.getSyncParams().getRequestMetricCollector()); } @Test public void credentialsNotExplicitlySet_UsesDefaultCredentialChain() throws Exception { AwsAsyncClientParams params = builderWithRegion().build().getAsyncParams(); assertThat(params.getCredentialsProvider(), instanceOf(DefaultAWSCredentialsProviderChain.class)); } @Test public void credentialsExplicitlySet_UsesExplicitCredentials() throws Exception { AWSCredentialsProvider provider = new StaticCredentialsProvider( new BasicAWSCredentials("akid", "skid")); AwsAsyncClientParams params = builderWithRegion().withCredentials(provider).build() .getAsyncParams(); assertEquals(provider, params.getCredentialsProvider()); } @Test public void metricCollectorNotExplicitlySet_UsesNullMetricsCollector() throws Exception { assertNull(builderWithRegion().build().getAsyncParams().getRequestMetricCollector()); } @Test public void metricsCollectorExplicitlySet_UsesExplicitMetricsCollector() throws Exception { RequestMetricCollector metricCollector = RequestMetricCollector.NONE; AwsAsyncClientParams params = builderWithRegion().withMetricsCollector(metricCollector) .build().getAsyncParams(); assertEquals(metricCollector, params.getRequestMetricCollector()); } @Test public void clientConfigurationNotExplicitlySet_UsesServiceDefaultClientConfiguration() { AwsAsyncClientParams params = builderWithRegion().build().getAsyncParams(); ClientConfiguration actualConfig = params.getClientConfiguration(); assertEquals(DEFAULT_CLIENT_CONFIG.getSocketTimeout(), actualConfig.getSocketTimeout()); } @Test public void clientConfigurationExplicitlySet_UsesExplicitConfiguration() { ClientConfiguration config = new ClientConfiguration().withSocketTimeout(1000); AwsAsyncClientParams params = builderWithRegion().withClientConfiguration(config).build() .getAsyncParams(); assertEquals(config.getSocketTimeout(), params.getClientConfiguration().getSocketTimeout()); } @Test public void explicitRegionIsSet_UsesRegionToConstructEndpoint() { URI actualUri = new ConcreteAsyncBuilder().withRegion(Regions.US_WEST_2).build() .getEndpoint(); assertEquals(URI.create("https://mockprefix.us-west-2.amazonaws.com"), actualUri); } /** * If no region is explicitly given and no region can be found from the {@link * AwsRegionProvider} implementation then the builder should fail to build clients. We mock the * provider to yield consistent results for the tests. */ @Test(expected = AmazonClientException.class) public void noRegionProvidedExplicitlyOrImplicitly_ThrowsException() { AwsRegionProvider mockRegionProvider = mock(AwsRegionProvider.class); when(mockRegionProvider.getRegion()).thenReturn(null); new ConcreteAsyncBuilder(mockRegionProvider).build(); } /** * Customers may not need to explicitly configure a builder with a region if one can be found * from the {@link AwsRegionProvider} implementation. We mock the provider to yield consistent * results for the tests. */ @Test public void regionImplicitlyProvided_UsesRegionToConstructEndpoint() { AwsRegionProvider mockRegionProvider = mock(AwsRegionProvider.class); when(mockRegionProvider.getRegion()).thenReturn("ap-southeast-2"); final URI actualUri = new ConcreteAsyncBuilder(mockRegionProvider).build().getEndpoint(); assertEquals(URI.create("https://mockprefix.ap-southeast-2.amazonaws.com"), actualUri); } @Test public void endpointAndSigningRegionCanBeUsedInPlaceOfSetRegion() { AmazonConcreteClient client = new ConcreteSyncBuilder() .withEndpointConfiguration(new EndpointConfiguration("https://mockprefix.ap-southeast-2.amazonaws.com", "us-east-1")) .build(); assertEquals("us-east-1", client.getSignerRegionOverride()); assertEquals(URI.create("https://mockprefix.ap-southeast-2.amazonaws.com"), client.getEndpoint()); } @Test(expected = IllegalStateException.class) public void cannotSetBothEndpointConfigurationAndRegionOnBuilder() { new ConcreteSyncBuilder() .withEndpointConfiguration(new EndpointConfiguration("http://localhost:3030", "us-west-2")) .withRegion("us-east-1") .build(); } @Test public void defaultClientConfigAndNoExplicitExecutor_UsesDefaultExecutorBasedOnMaxConns() { ExecutorService executor = builderWithRegion().build().getAsyncParams().getExecutor(); assertThat(executor, instanceOf(ThreadPoolExecutor.class)); assertEquals(PredefinedClientConfigurations.defaultConfig().getMaxConnections(), ((ThreadPoolExecutor) executor).getMaximumPoolSize()); } @Test public void customMaxConnsAndNoExplicitExecutor_UsesDefaultExecutorBasedOnMaxConns() { final int maxConns = 10; ExecutorService executor = builderWithRegion() .withClientConfiguration(new ClientConfiguration().withMaxConnections(maxConns)) .build().getAsyncParams().getExecutor(); assertThat(executor, instanceOf(ThreadPoolExecutor.class)); assertEquals(maxConns, ((ThreadPoolExecutor) executor).getMaximumPoolSize()); } /** * If a custom executor is set then the Max Connections in Client Configuration should be * ignored and the executor should be used as is. */ @Test public void customMaxConnsAndExplicitExecutor_UsesExplicitExecutor() throws Exception { final int clientConfigMaxConns = 10; final int customExecutorThreadCount = 15; final ExecutorService customExecutor = Executors .newFixedThreadPool(customExecutorThreadCount); ExecutorService actualExecutor = builderWithRegion().withClientConfiguration( new ClientConfiguration().withMaxConnections(clientConfigMaxConns)) .withExecutorFactory(new StaticExecutorFactory(customExecutor)).build() .getAsyncParams().getExecutor(); assertThat(actualExecutor, instanceOf(ThreadPoolExecutor.class)); assertEquals(customExecutor, actualExecutor); assertEquals(customExecutorThreadCount, ((ThreadPoolExecutor) actualExecutor).getMaximumPoolSize()); } @Test public void noRequestHandlersExplicitlySet_UsesEmptyRequestHandlerList() throws Exception { List<RequestHandler2> requestHandlers = builderWithRegion().build().getAsyncParams() .getRequestHandlers(); assertThat(requestHandlers, empty()); } @Test public void requestHandlersExplicitlySet_UsesClonedListOfExplicitRequestHandlers() throws Exception { List<RequestHandler2> expectedHandlers = createRequestHandlerList( new ConcreteRequestHandler(), new ConcreteRequestHandler()); List<RequestHandler2> actualHandlers = builderWithRegion() .withRequestHandlers(expectedHandlers.toArray(new RequestHandler2[0])).build() .getAsyncParams().getRequestHandlers(); assertEquals(expectedHandlers, actualHandlers); // List should be copied or cloned assertThat(actualHandlers, not(sameInstance(expectedHandlers))); } @Test public void requestHandlersExplicitlySetWithVarArgs_UsesExplicitRequestHandlers() throws Exception { RequestHandler2 handlerOne = new ConcreteRequestHandler(); RequestHandler2 handlerTwo = new ConcreteRequestHandler(); RequestHandler2 handlerThree = new ConcreteRequestHandler(); List<RequestHandler2> actualHandlers = builderWithRegion() .withRequestHandlers(handlerOne, handlerTwo, handlerThree).build().getAsyncParams() .getRequestHandlers(); assertEquals(createRequestHandlerList(handlerOne, handlerTwo, handlerThree), actualHandlers); } /** * @return A {@link ConcreteAsyncBuilder} instance with an explicitly configured region. */ private ConcreteAsyncBuilder builderWithRegion() { return new ConcreteAsyncBuilder().withRegion(Regions.AP_NORTHEAST_1); } private List<RequestHandler2> createRequestHandlerList(RequestHandler2... handlers) { List<RequestHandler2> requestHandlers = new ArrayList<RequestHandler2>(); Collections.addAll(requestHandlers, handlers); return requestHandlers; } }