|
15 | 15 |
|
16 | 16 | package software.amazon.awssdk.core.internal.http.loader;
|
17 | 17 |
|
18 |
| -import java.util.List; |
| 18 | +import java.util.Comparator; |
| 19 | +import java.util.Map; |
19 | 20 | import java.util.Optional;
|
| 21 | +import java.util.PriorityQueue; |
| 22 | +import java.util.Queue; |
20 | 23 | import java.util.ServiceLoader;
|
21 |
| -import java.util.stream.Collectors; |
22 |
| -import java.util.stream.StreamSupport; |
| 24 | +import java.util.StringJoiner; |
23 | 25 | import software.amazon.awssdk.annotations.SdkInternalApi;
|
24 | 26 | import software.amazon.awssdk.annotations.SdkTestInternalApi;
|
25 |
| -import software.amazon.awssdk.core.SdkSystemSetting; |
26 |
| -import software.amazon.awssdk.core.exception.SdkClientException; |
27 | 27 | import software.amazon.awssdk.http.SdkHttpService;
|
28 | 28 | import software.amazon.awssdk.http.async.SdkAsyncHttpService;
|
29 |
| -import software.amazon.awssdk.utils.SystemSetting; |
| 29 | +import software.amazon.awssdk.utils.ImmutableMap; |
| 30 | +import software.amazon.awssdk.utils.Logger; |
30 | 31 |
|
31 | 32 | /**
|
32 | 33 | * {@link SdkHttpServiceProvider} implementation that uses {@link ServiceLoader} to find HTTP implementations on the
|
33 |
| - * classpath. If more than one implementation is found on the classpath then an exception is thrown. |
| 34 | + * classpath. If more than one implementation is found on the classpath, then the SDK will choose based on priority order. |
34 | 35 | */
|
35 | 36 | @SdkInternalApi
|
36 | 37 | final class ClasspathSdkHttpServiceProvider<T> implements SdkHttpServiceProvider<T> {
|
37 | 38 |
|
| 39 | + static final Map<String, Integer> SYNC_HTTP_SERVICES_PRIORITY = |
| 40 | + ImmutableMap.<String, Integer>builder() |
| 41 | + .put("software.amazon.awssdk.http.apache.ApacheSdkHttpService", 1) |
| 42 | + .put("software.amazon.awssdk.http.crt.AwsCrtSdkHttpService", 2) |
| 43 | + .put("software.amazon.awssdk.http.urlconnection.UrlConnectionSdkHttpService", 3) |
| 44 | + .build(); |
| 45 | + |
| 46 | + static final Map<String, Integer> ASYNC_HTTP_SERVICES_PRIORITY = |
| 47 | + ImmutableMap.<String, Integer>builder() |
| 48 | + .put("software.amazon.awssdk.http.nio.netty.NettySdkAsyncHttpService", 1) |
| 49 | + .put("software.amazon.awssdk.http.crt.AwsCrtSdkHttpService", 2) |
| 50 | + .build(); |
| 51 | + |
| 52 | + private static final Logger log = Logger.loggerFor(ClasspathSdkHttpServiceProvider.class); |
| 53 | + |
| 54 | + private final Map<String, Integer> httpServicesPriority; |
| 55 | + |
38 | 56 | private final SdkServiceLoader serviceLoader;
|
39 |
| - private final SystemSetting implSystemProperty; |
40 | 57 | private final Class<T> serviceClass;
|
41 | 58 |
|
42 | 59 | @SdkTestInternalApi
|
43 |
| - ClasspathSdkHttpServiceProvider(SdkServiceLoader serviceLoader, SystemSetting implSystemProperty, Class<T> serviceClass) { |
| 60 | + ClasspathSdkHttpServiceProvider(SdkServiceLoader serviceLoader, |
| 61 | + Class<T> serviceClass, |
| 62 | + Map<String, Integer> httpServicesPriority) { |
44 | 63 | this.serviceLoader = serviceLoader;
|
45 |
| - this.implSystemProperty = implSystemProperty; |
46 | 64 | this.serviceClass = serviceClass;
|
| 65 | + this.httpServicesPriority = httpServicesPriority; |
47 | 66 | }
|
48 | 67 |
|
49 | 68 | @Override
|
50 | 69 | public Optional<T> loadService() {
|
| 70 | + Queue<T> impls = new PriorityQueue<>(Comparator.comparingInt(o -> httpServicesPriority.getOrDefault(o.getClass(), |
| 71 | + Integer.MAX_VALUE))); |
51 | 72 | Iterable<T> iterable = () -> serviceLoader.loadServices(serviceClass);
|
52 |
| - List<T> impls = StreamSupport |
53 |
| - .stream(iterable.spliterator(), false) |
54 |
| - .collect(Collectors.toList()); |
| 73 | + iterable.forEach(impl -> impls.add(impl)); |
55 | 74 |
|
56 | 75 | if (impls.isEmpty()) {
|
57 | 76 | return Optional.empty();
|
58 | 77 | }
|
59 | 78 |
|
60 |
| - if (impls.size() > 1) { |
61 |
| - |
62 |
| - String implText = |
63 |
| - impls.stream() |
64 |
| - .map(clazz -> clazz.getClass().getName()) |
65 |
| - .collect(Collectors.joining(",", "[", "]")); |
| 79 | + log.debug(() -> logServices(impls)); |
| 80 | + return Optional.of(impls.poll()); |
| 81 | + } |
66 | 82 |
|
67 |
| - throw SdkClientException.builder().message( |
68 |
| - String.format( |
69 |
| - "Multiple HTTP implementations were found on the classpath. To avoid non-deterministic loading " + |
70 |
| - "implementations, please explicitly provide an HTTP client via the client builders, set the %s " + |
71 |
| - "system property with the FQCN of the HTTP service to use as the default, or remove all but one " + |
72 |
| - "HTTP implementation from the classpath. The multiple implementations found were: %s", |
73 |
| - implSystemProperty.property(), implText)) |
74 |
| - .build(); |
| 83 | + private String logServices(Queue<T> impls) { |
| 84 | + StringJoiner joiner = new StringJoiner(",", "[", "]"); |
| 85 | + int count = 0; |
| 86 | + for (T clazz : impls) { |
| 87 | + String name = clazz.getClass().getName(); |
| 88 | + joiner.add(name); |
| 89 | + count++; |
75 | 90 | }
|
| 91 | + String implText = joiner.toString(); |
| 92 | + T impl = impls.peek(); |
| 93 | + String message = count == 1 ? "The HTTP implementation loaded is " + impl : |
| 94 | + String.format( |
| 95 | + "Multiple HTTP implementations were found on the classpath. The SDK will use %s since it has the " |
| 96 | + + "highest. The multiple implementations found were: %s", |
| 97 | + impl, |
| 98 | + implText); |
76 | 99 |
|
77 |
| - return impls.stream().findFirst(); |
| 100 | + return message; |
78 | 101 | }
|
79 | 102 |
|
80 | 103 | /**
|
81 | 104 | * @return ClasspathSdkHttpServiceProvider that loads an {@link SdkHttpService} (sync) from the classpath.
|
82 | 105 | */
|
83 | 106 | static SdkHttpServiceProvider<SdkHttpService> syncProvider() {
|
84 | 107 | return new ClasspathSdkHttpServiceProvider<>(SdkServiceLoader.INSTANCE,
|
85 |
| - SdkSystemSetting.SYNC_HTTP_SERVICE_IMPL, |
86 |
| - SdkHttpService.class); |
| 108 | + SdkHttpService.class, |
| 109 | + SYNC_HTTP_SERVICES_PRIORITY); |
87 | 110 | }
|
88 | 111 |
|
89 | 112 | /**
|
90 | 113 | * @return ClasspathSdkHttpServiceProvider that loads an {@link SdkAsyncHttpService} (async) from the classpath.
|
91 | 114 | */
|
92 | 115 | static SdkHttpServiceProvider<SdkAsyncHttpService> asyncProvider() {
|
93 | 116 | return new ClasspathSdkHttpServiceProvider<>(SdkServiceLoader.INSTANCE,
|
94 |
| - SdkSystemSetting.ASYNC_HTTP_SERVICE_IMPL, |
95 |
| - SdkAsyncHttpService.class); |
| 117 | + SdkAsyncHttpService.class, |
| 118 | + ASYNC_HTTP_SERVICES_PRIORITY); |
96 | 119 | }
|
97 |
| - |
98 | 120 | }
|
0 commit comments