From 921781b9f95004091a9b0684f2c3eab03d8ba084 Mon Sep 17 00:00:00 2001 From: Ethan Katnic Date: Thu, 29 Aug 2024 15:46:42 -0700 Subject: [PATCH] Simplify decoder logic for empty create constructor. Update documentation. --- ...edentialsProviderPropertyValueDecoder.java | 75 ++++++++----------- .../src/test/resources/multilang.properties | 2 +- .../configuring-credential-providers.md | 50 +++++++------ 3 files changed, 60 insertions(+), 67 deletions(-) diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java index 1e94ab56..9a3f4599 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java @@ -99,27 +99,34 @@ class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecode final String[] varargs = Arrays.copyOfRange(nameAndArgs, 1, nameAndArgs.length); // attempt to invoke an explicit N-arg constructor of FooClass(String, String, ...) - provider = constructProvider( - providerName, - () -> { - Class[] argTypes = new Class[nameAndArgs.length - 1]; - Arrays.fill(argTypes, String.class); - return clazz.getConstructor(argTypes).newInstance(varargs); - }, - clazz); + provider = constructProvider(providerName, () -> { + Class[] argTypes = new Class[nameAndArgs.length - 1]; + Arrays.fill(argTypes, String.class); + return clazz.getConstructor(argTypes).newInstance(varargs); + }); if (provider == null) { // attempt to invoke a public varargs/array constructor of FooClass(String[]) - provider = constructProvider( - providerName, - () -> clazz.getConstructor(String[].class).newInstance((Object) varargs), - clazz); + provider = constructProvider(providerName, () -> clazz.getConstructor(String[].class) + .newInstance((Object) varargs)); } } if (provider == null) { - // regardless of parameters, fallback to invoke a public no-arg constructor - provider = constructProvider(providerName, clazz::newInstance, clazz); + // fallback to invoke a public no-arg constructor + provider = constructProvider(providerName, clazz::newInstance); + } + + if (provider == null) { + // if still not found, try empty create() method + try { + Method createMethod = clazz.getDeclaredMethod("create"); + if (Modifier.isStatic(createMethod.getModifiers())) { + provider = constructProvider(providerName, () -> clazz.cast(createMethod.invoke(null))); + } + } catch (NoSuchMethodException e) { + // No create() method found for class + } } if (provider != null) { @@ -145,17 +152,18 @@ class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecode private static List getPossibleFullClassNames(final String provider) { return Stream.of( - // Customer provides a short name of common providers in com.amazonaws.auth package - // (e.g., any classes implementing the AWSCredentialsProvider interface) - // @see - // http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/AWSCredentialsProvider.html - "com.amazonaws.auth.", - // Customer provides a short name of a provider offered by this multi-lang package "software.amazon.kinesis.multilang.auth.", + // Customer provides a short name of common providers in software.amazon.awssdk.auth.credentials + // package (e.g., any classes implementing the AWSCredentialsProvider interface) + // @see + // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.html "software.amazon.awssdk.auth.credentials.", // Customer provides a fully-qualified provider name, or a custom credentials provider - // (e.g., com.amazonaws.auth.ClasspathFileCredentialsProvider, org.mycompany.FooProvider) + // (e.g., software.amazon.awssdk.auth.credentials.AwsCredentialsProvider) + + // Sts auth provider + "software.amazon.awssdk.services.sts.auth.", "") .map(prefix -> prefix + provider) .collect(Collectors.toList()); @@ -173,39 +181,18 @@ class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecode * @param providerName Raw, unmodified provider name. Should there be an * Exception during construction, this parameter will be logged. * @param constructor supplier-like function that will perform the construction - * @param clazz the class to attempt to construct * @return the constructed provider, if successful; otherwise, null * * @param type of the CredentialsProvider to construct */ private static T constructProvider( - final String providerName, - final CredentialsProviderConstructor constructor, - Class clazz) { + final String providerName, final CredentialsProviderConstructor constructor) { try { // Try to create an instance using the given constructor return constructor.construct(); - } catch (InstantiationException e) { - try { - // Try to create an instance using .create() - Method createMethod = clazz.getDeclaredMethod("create"); - if (Modifier.isStatic(createMethod.getModifiers())) { - Object provider = createMethod.invoke(null); - if (provider instanceof AwsCredentialsProvider) { - return (T) provider; - } else { - log.warn("Returned provider is not an instance of {}", AwsCredentialsProvider.class.getName()); - return null; - } - } else { - log.warn("Found non-static create() method in {}", providerName); - } - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e1) { - log.warn("Exception thrown by create() method in {}", providerName, e1.getCause()); - } } catch (NoSuchMethodException ignored) { // ignore - } catch (IllegalAccessException | InvocationTargetException | RuntimeException e) { + } catch (IllegalAccessException | InvocationTargetException | InstantiationException | RuntimeException e) { log.warn("Failed to construct {}", providerName, e); } return null; diff --git a/amazon-kinesis-client-multilang/src/test/resources/multilang.properties b/amazon-kinesis-client-multilang/src/test/resources/multilang.properties index 347d4e8a..067afcd8 100644 --- a/amazon-kinesis-client-multilang/src/test/resources/multilang.properties +++ b/amazon-kinesis-client-multilang/src/test/resources/multilang.properties @@ -19,7 +19,7 @@ applicationName = MultiLangTest # Users can change the credentials provider the KCL will use to retrieve credentials. # The DefaultAWSCredentialsProviderChain checks several other providers, which is # described here: -# http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html +# https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html AWSCredentialsProvider = DefaultCredentialsProvider # Appended to the user agent of the KCL. Does not impact the functionality of the diff --git a/docs/multilang/configuring-credential-providers.md b/docs/multilang/configuring-credential-providers.md index 9b85baaa..c8401d9e 100644 --- a/docs/multilang/configuring-credential-providers.md +++ b/docs/multilang/configuring-credential-providers.md @@ -7,21 +7,22 @@ However, KCL now provides better extensibility to handle, and be enhanced to han This document should help multilang customers configure a suitable `CredentialProvider` (or contribute changes to support a new use case!). ## Sample Provider Configuration +DEPRECATED: StsAssumeRoleCredentialsProvider can no longer be constructed in this way: +``` +AWSCredentialsProvider = StsAssumeRoleCredentialsProvider||` +``` -In a Properties file, an `AWSCredentialsProperty` configuration might look like: -``` -AWSCredentialsProvider = STSAssumeRoleSessionCredentialsProvider|| -``` -This basic configuration creates an [STSAssumeRoleSessionCredentialsProvider][sts-assume-provider] with an ARN and session name. -While functional, this configuration is limited. -For example, this configuration cannot set a regional endpoint (e.g., VPC use case). +To create a [StsAssumeRoleCredentialsProvider][sts-assume-provider], see KclStsAssumeRoleCredentialsProvider below. -Leveraging nested properties, an `AWSCredentialsProperty` value might change to: +You can create a default [DefaultCredentialsProvider][default-credentials-provider] or [AnonymousCredentialsProvider][anonymous-credentials-provider] +by passing it in the config like: ``` -AWSCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider||\ - |endpointRegion=us-east-1|externalId=spartacus +AWSCredentialsProvider = DefaultCredentialsProvider ``` -N.B. Backslash (`\`) is for multi-line legibility and is not required. + +If you wish to customize properties on an AWS SDK provider that uses a builder, like the StsASsumeRoleCredentialsProvider, +you will need to wrap this provider class, provide a constructor, and manage the build of the provider. +See implementation of [KclStsAssumeRoleCredentialsProvider][kcl-sts-provider] ## Nested Properties @@ -36,9 +37,6 @@ The [Backus-Naur form][bnf] of the value: # this depends on the nested key ``` -In general, required parameters are passed directly to the class' constructor -(e.g., [STSAssumeRoleSessionCredentialsProvider(String, String)][sts-assume-provider-constructor]). - Nested properties are a custom mapping provided by KCL multilang, and do not exist in the AWS SDK. See [NestedPropertyKey][nested-property-key] for the supported keys, and details on their expected values. @@ -54,18 +52,26 @@ A backwards-compatible addition might look like: } ``` -### KclSTSAssumeRoleSessionCredentialsProvider - -KCL multilang includes a [custom nested property processor for `STSAssumeRole`][kcl-sts-provider]. -Multilang configurations that use `STSAssumeRoleSessionCredentialsProvider` need only prefix `Kcl` to exercise this new provider: +Leveraging nested properties, an `AWSCredentialsProperty` value might look like: ``` -AWSCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider|| +AWSCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider||\ + |endpointRegion=us-east-1|externalId=spartacus +``` + +N.B. Backslash (`\`) is for multi-line legibility and is not required. +### KclStsAssumeRoleCredentialsProvider + +KCL multilang includes a [custom nested property processor for `StsAssumeRole`][kcl-sts-provider]. +Multilang configurations that use `StsAssumeRoleSessionCredentialsProvider` need only prefix `Kcl` to exercise this new provider: +``` +AWSCredentialsProvider = KclStsAssumeRoleCredentialsProvider|| ``` [aws-credentials-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.html [bnf]: https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form -[kcl-sts-provider]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProvider.java +[kcl-sts-provider]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProvider.java [nested-property-key]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java [nested-property-processor]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java -[sts-assume-provider]: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/STSAssumeRoleSessionCredentialsProvider.html -[sts-assume-provider-constructor]: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/STSAssumeRoleSessionCredentialsProvider.html#STSAssumeRoleSessionCredentialsProvider-java.lang.String-java.lang.String- +[sts-assume-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.html +[default-credentials-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html +[anonymous-credentials-provider]: https://sdk.amazonaws.com/java/api/2.0.0-preview-11/software/amazon/awssdk/auth/credentials/AnonymousCredentialsProvider.html