Add functionality to decoder for create(arg) providers and refactor provider creation

This commit is contained in:
Ethan Katnic 2024-09-04 10:31:43 -07:00
parent 1e708e1196
commit 892cc79dfc

View file

@ -79,58 +79,24 @@ class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecode
for (String providerName : providerNames) {
final String[] nameAndArgs = providerName.split("\\" + ARG_DELIMITER);
final Class<? extends AwsCredentialsProvider> clazz;
try {
final Class<?> c = Class.forName(nameAndArgs[0]);
if (!AwsCredentialsProvider.class.isAssignableFrom(c)) {
continue;
}
clazz = (Class<? extends AwsCredentialsProvider>) c;
} catch (ClassNotFoundException cnfe) {
// Providers are a product of prefixed Strings to cover multiple
// namespaces (e.g., "Foo" -> { "some.auth.Foo", "kcl.auth.Foo" }).
// It's expected that many class names will not resolve.
final Class<? extends AwsCredentialsProvider> clazz = getClass(nameAndArgs[0]);
if (clazz == null) {
continue;
}
log.info("Attempting to construct {}", clazz);
AwsCredentialsProvider provider = null;
if (nameAndArgs.length > 1) {
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);
});
provider = tryConstructorWithArgs(providerName, clazz, 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));
provider = tryCreateWithArgs(providerName, clazz, varargs);
}
}
if (provider == null) {
// regardless of parameters, fallback to invoke a public no-arg constructor
provider = constructProvider(providerName, clazz::newInstance);
provider = tryConstructorWithNoArgs(providerName, clazz);
}
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)));
} else {
log.warn("Found non-static create() method in {}", providerName);
}
} catch (NoSuchMethodException e) {
// No create() method found for class
}
provider = tryCreateWithNoArgs(providerName, clazz);
}
if (provider != null) {
credentialsProviders.add(provider);
}
@ -138,6 +104,98 @@ class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecode
return credentialsProviders;
}
private static AwsCredentialsProvider tryConstructorWithArgs(
String providerName, Class<? extends AwsCredentialsProvider> clazz, String[] varargs) {
AwsCredentialsProvider provider =
constructProvider(providerName, () -> getConstructorWithVarArgs(clazz, varargs));
if (provider == null) {
provider = constructProvider(providerName, () -> getConstructorWithArgs(clazz, varargs));
}
return provider;
}
private static AwsCredentialsProvider tryCreateWithArgs(
String providerName, Class<? extends AwsCredentialsProvider> clazz, String[] varargs) {
AwsCredentialsProvider provider =
constructProvider(providerName, () -> getCreateMethod(clazz, (Object) varargs));
if (provider == null) {
provider = constructProvider(providerName, () -> getCreateMethod(clazz, varargs));
}
return provider;
}
private static AwsCredentialsProvider tryConstructorWithNoArgs(
String providerName, Class<? extends AwsCredentialsProvider> clazz) {
return constructProvider(providerName, clazz::newInstance);
}
private static AwsCredentialsProvider tryCreateWithNoArgs(
String providerName, Class<? extends AwsCredentialsProvider> clazz) {
return constructProvider(providerName, () -> getCreateMethod(clazz));
}
private static AwsCredentialsProvider getConstructorWithVarArgs(
Class<? extends AwsCredentialsProvider> clazz, String[] varargs) {
try {
return clazz.getConstructor(String[].class).newInstance((Object) varargs);
} catch (Exception e) {
return null;
}
}
private static AwsCredentialsProvider getConstructorWithArgs(
Class<? extends AwsCredentialsProvider> clazz, String[] varargs) {
try {
Class<?>[] argTypes = new Class<?>[varargs.length];
Arrays.fill(argTypes, String.class);
return clazz.getConstructor(argTypes).newInstance((Object[]) varargs);
} catch (Exception e) {
return null;
}
}
private static AwsCredentialsProvider getCreateMethod(
Class<? extends AwsCredentialsProvider> clazz, Object... args) {
try {
Class<?>[] argTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
argTypes[i] = args[i].getClass();
}
Method createMethod = clazz.getDeclaredMethod("create", argTypes);
if (Modifier.isStatic(createMethod.getModifiers())) {
return clazz.cast(createMethod.invoke(null, args));
} else {
log.warn("Found non-static create() method in {}", clazz.getName());
}
} catch (NoSuchMethodException e) {
// No matching create method found for class
} catch (Exception e) {
log.warn("Failed to invoke create() method in {}", clazz.getName(), e);
}
return null;
}
private static Class<? extends AwsCredentialsProvider> getClass(String providerName) {
String className = providerName.replace(
"software.amazon.awssdk.auth.credentials.StsAssumeRoleCredentialsProvider",
"software.amazon.kinesis.multilang.auth.KclStsAssumeRoleCredentialsProvider");
final Class<? extends AwsCredentialsProvider> clazz;
try {
final Class<?> c = Class.forName(className);
if (!AwsCredentialsProvider.class.isAssignableFrom(c)) {
return null;
}
clazz = (Class<? extends AwsCredentialsProvider>) c;
} catch (ClassNotFoundException cnfe) {
// Providers are a product of prefixed Strings to cover multiple
// namespaces (e.g., "Foo" -> { "some.auth.Foo", "kcl.auth.Foo" }).
// It's expected that many class names will not resolve.
return null;
}
log.info("Attempting to construct {}", clazz);
return clazz;
}
private static List<String> getProviderNames(String property) {
// assume list delimiter is ","
String[] elements = property.split(LIST_DELIMITER);