1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15
16 package software.amazon.awssdk.core.internal.http.loader;
17
18 import java.util.Iterator;
19 import java.util.Optional;
20 import java.util.ServiceLoader;
21 import software.amazon.awssdk.annotations.SdkInternalApi;
22 import software.amazon.awssdk.annotations.SdkTestInternalApi;
23 import software.amazon.awssdk.core.SdkSystemSetting;
24 import software.amazon.awssdk.core.exception.SdkClientException;
25 import software.amazon.awssdk.http.SdkHttpService;
26 import software.amazon.awssdk.http.async.SdkAsyncHttpService;
27 import software.amazon.awssdk.utils.SystemSetting;
28
29 /**
30  * {@link SdkHttpServiceProvider} implementation that uses {@link ServiceLoader} to find HTTP implementations on the
31  * classpath. If more than one implementation is found on the classpath then an exception is thrown.
32  */

33 @SdkInternalApi
34 final class ClasspathSdkHttpServiceProvider<T> implements SdkHttpServiceProvider<T> {
35
36     private final SdkServiceLoader serviceLoader;
37     private final SystemSetting implSystemProperty;
38     private final Class<T> serviceClass;
39
40     @SdkTestInternalApi
41     ClasspathSdkHttpServiceProvider(SdkServiceLoader serviceLoader, SystemSetting implSystemProperty, Class<T> serviceClass) {
42         this.serviceLoader = serviceLoader;
43         this.implSystemProperty = implSystemProperty;
44         this.serviceClass = serviceClass;
45     }
46
47     @Override
48     public Optional<T> loadService() {
49         Iterator<T> httpServices = serviceLoader.loadServices(serviceClass);
50         if (!httpServices.hasNext()) {
51             return Optional.empty();
52         }
53         T httpService = httpServices.next();
54
55         if (httpServices.hasNext()) {
56             throw SdkClientException.builder().message(
57                     String.format(
58                             "Multiple HTTP implementations were found on the classpath. To avoid non-deterministic loading " +
59                             "implementations, please explicitly provide an HTTP client via the client builders, set the %s " +
60                             "system property with the FQCN of the HTTP service to use as the default, or remove all but one " +
61                             "HTTP implementation from the classpath", implSystemProperty.property()))
62                     .build();
63         }
64         return Optional.of(httpService);
65     }
66
67     /**
68      * @return ClasspathSdkHttpServiceProvider that loads an {@link SdkHttpService} (sync) from the classpath.
69      */

70     static SdkHttpServiceProvider<SdkHttpService> syncProvider() {
71         return new ClasspathSdkHttpServiceProvider<>(SdkServiceLoader.INSTANCE,
72                                                      SdkSystemSetting.SYNC_HTTP_SERVICE_IMPL,
73                                                      SdkHttpService.class);
74     }
75
76     /**
77      * @return ClasspathSdkHttpServiceProvider that loads an {@link SdkAsyncHttpService} (async) from the classpath.
78      */

79     static SdkHttpServiceProvider<SdkAsyncHttpService> asyncProvider() {
80         return new ClasspathSdkHttpServiceProvider<>(SdkServiceLoader.INSTANCE,
81                                                      SdkSystemSetting.ASYNC_HTTP_SERVICE_IMPL,
82                                                      SdkAsyncHttpService.class);
83     }
84
85 }
86