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.interceptor; 17 18 import java.io.BufferedReader; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.io.InputStreamReader; 22 import java.net.URL; 23 import java.nio.charset.StandardCharsets; 24 import java.util.ArrayList; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.Enumeration; 28 import java.util.List; 29 import java.util.stream.Collectors; 30 import java.util.stream.Stream; 31 import software.amazon.awssdk.annotations.SdkProtectedApi; 32 import software.amazon.awssdk.core.exception.SdkClientException; 33 import software.amazon.awssdk.core.internal.util.ClassLoaderHelper; 34 import software.amazon.awssdk.utils.Validate; 35 36 /** 37 * Factory for creating request/response handler chains from the classpath. 38 */ 39 @SdkProtectedApi 40 public final class ClasspathInterceptorChainFactory { 41 42 private static final String GLOBAL_INTERCEPTOR_PATH = "software/amazon/awssdk/global/handlers/execution.interceptors"; 43 44 /** 45 * Constructs a new request handler chain by analyzing the specified classpath resource. 46 * 47 * @param resource The resource to load from the classpath containing the list of request handlers to instantiate. 48 * @return A list of request handlers based on the handlers referenced in the specified resource. 49 */ getInterceptors(String resource)50 public List<ExecutionInterceptor> getInterceptors(String resource) { 51 return new ArrayList<>(createExecutionInterceptorsFromClasspath(resource)); 52 } 53 54 /** 55 * Load the global handlers by reading the global execution interceptors resource. 56 */ getGlobalInterceptors()57 public List<ExecutionInterceptor> getGlobalInterceptors() { 58 return new ArrayList<>(createExecutionInterceptorsFromClasspath(GLOBAL_INTERCEPTOR_PATH)); 59 } 60 createExecutionInterceptorsFromClasspath(String path)61 private Collection<ExecutionInterceptor> createExecutionInterceptorsFromClasspath(String path) { 62 try { 63 return createExecutionInterceptorsFromResources(classLoader().getResources(path)) 64 .collect(Collectors.toMap(p -> p.getClass().getSimpleName(), p -> p, (p1, p2) -> p1)).values(); 65 } catch (IOException e) { 66 throw SdkClientException.builder() 67 .message("Unable to instantiate execution interceptor chain.") 68 .cause(e) 69 .build(); 70 } 71 } 72 createExecutionInterceptorsFromResources(Enumeration<URL> resources)73 private Stream<ExecutionInterceptor> createExecutionInterceptorsFromResources(Enumeration<URL> resources) { 74 if (resources == null) { 75 return Stream.empty(); 76 } 77 78 return Collections.list(resources).stream().flatMap(this::createExecutionInterceptorFromResource); 79 } 80 createExecutionInterceptorFromResource(URL resource)81 private Stream<ExecutionInterceptor> createExecutionInterceptorFromResource(URL resource) { 82 try { 83 if (resource == null) { 84 return Stream.empty(); 85 } 86 87 List<ExecutionInterceptor> interceptors = new ArrayList<>(); 88 89 try (InputStream stream = resource.openStream(); 90 InputStreamReader streamReader = new InputStreamReader(stream, StandardCharsets.UTF_8); 91 BufferedReader fileReader = new BufferedReader(streamReader)) { 92 93 String interceptorClassName = fileReader.readLine(); 94 while (interceptorClassName != null) { 95 ExecutionInterceptor interceptor = createExecutionInterceptor(interceptorClassName); 96 if (interceptor != null) { 97 interceptors.add(interceptor); 98 } 99 interceptorClassName = fileReader.readLine(); 100 } 101 } 102 103 return interceptors.stream(); 104 } catch (IOException e) { 105 throw SdkClientException.builder() 106 .message("Unable to instantiate execution interceptor chain.") 107 .cause(e) 108 .build(); 109 } 110 } 111 createExecutionInterceptor(String interceptorClassName)112 private ExecutionInterceptor createExecutionInterceptor(String interceptorClassName) { 113 if (interceptorClassName == null) { 114 return null; 115 } 116 117 interceptorClassName = interceptorClassName.trim(); 118 if (interceptorClassName.equals("")) { 119 return null; 120 } 121 122 try { 123 Class<?> executionInterceptorClass = ClassLoaderHelper.loadClass(interceptorClassName, 124 ExecutionInterceptor.class, getClass()); 125 Object executionInterceptorObject = executionInterceptorClass.newInstance(); 126 127 if (executionInterceptorObject instanceof ExecutionInterceptor) { 128 return (ExecutionInterceptor) executionInterceptorObject; 129 } else { 130 throw SdkClientException.builder() 131 .message("Unable to instantiate request handler chain for client. Listed" 132 + " request handler ('" + interceptorClassName + "') does not implement" + 133 " the " + ExecutionInterceptor.class + " API.") 134 .build(); 135 } 136 } catch (IllegalAccessException | ClassNotFoundException | InstantiationException e) { 137 throw SdkClientException.builder() 138 .message("Unable to instantiate executor interceptor for client.") 139 .cause(e) 140 .build(); 141 } 142 } 143 classLoader()144 private ClassLoader classLoader() { 145 return Validate.notNull(ClassLoaderHelper.classLoader(getClass()), 146 "Failed to load the classloader of this class or the system."); 147 } 148 } 149