Add native image runtime hints, register singletons instead of beans when transferring instances from the Config Data API.
Closes gh-667
This commit is contained in:
@@ -84,11 +84,11 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
*/
|
||||
class ClientAuthenticationFactory {
|
||||
|
||||
private static final boolean GOOGLE_CREDENTIAL_AVAILABLE = ClassUtils.isPresent(
|
||||
private static final boolean GOOGLE_CREDENTIAL_PRESENT = ClassUtils.isPresent(
|
||||
"com.google.api.client.googleapis.auth.oauth2.GoogleCredential",
|
||||
ClientAuthenticationFactory.class.getClassLoader());
|
||||
|
||||
private static final boolean GOOGLE_CREDENTIALS_AVAILABLE = ClassUtils
|
||||
private static final boolean GOOGLE_CREDENTIALS_PRESENT = ClassUtils
|
||||
.isPresent("com.google.auth.oauth2.GoogleCredentials", ClientAuthenticationFactory.class.getClassLoader());
|
||||
|
||||
private final VaultProperties vaultProperties;
|
||||
@@ -347,11 +347,11 @@ class ClientAuthenticationFactory {
|
||||
|
||||
private ClientAuthentication gcpIamAuthentication(VaultProperties vaultProperties) {
|
||||
|
||||
if (GOOGLE_CREDENTIAL_AVAILABLE) {
|
||||
if (GOOGLE_CREDENTIAL_PRESENT) {
|
||||
return GcpIamAuthenticationFactory.create(vaultProperties, this.restOperations);
|
||||
}
|
||||
|
||||
if (GOOGLE_CREDENTIALS_AVAILABLE) {
|
||||
if (GOOGLE_CREDENTIALS_PRESENT) {
|
||||
return GcpIamCredentialsAuthenticationFactory.create(vaultProperties, this.restOperations);
|
||||
}
|
||||
|
||||
|
||||
@@ -245,12 +245,12 @@ public class VaultAutoConfiguration {
|
||||
this(taskScheduler, true);
|
||||
}
|
||||
|
||||
TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler, boolean acceptAfterPropertiesSet) {
|
||||
public TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler, boolean acceptAfterPropertiesSet) {
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.acceptAfterPropertiesSet = acceptAfterPropertiesSet;
|
||||
}
|
||||
|
||||
ThreadPoolTaskScheduler getTaskScheduler() {
|
||||
public ThreadPoolTaskScheduler getTaskScheduler() {
|
||||
return this.taskScheduler;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.BootstrapRegistryInitializer;
|
||||
@@ -62,6 +62,7 @@ import org.springframework.vault.client.SimpleVaultEndpointProvider;
|
||||
import org.springframework.vault.client.VaultEndpointProvider;
|
||||
import org.springframework.vault.client.WebClientBuilder;
|
||||
import org.springframework.vault.client.WebClientFactory;
|
||||
import org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
import org.springframework.vault.core.ReactiveVaultTemplate;
|
||||
import org.springframework.vault.core.VaultTemplate;
|
||||
import org.springframework.vault.core.env.LeaseAwareVaultPropertySource;
|
||||
@@ -70,8 +71,6 @@ import org.springframework.vault.core.lease.domain.RequestedSecret;
|
||||
import org.springframework.vault.core.lease.event.LeaseErrorListener;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.springframework.vault.config.AbstractVaultConfiguration.ClientFactoryWrapper;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLoader} for Vault for {@link VaultConfigLocation}. This class
|
||||
* materializes {@link PropertySource property sources} by using Vault and
|
||||
@@ -94,13 +93,13 @@ public class VaultConfigDataLoader implements ConfigDataLoader<VaultConfigLocati
|
||||
|
||||
private final static ConfigData SKIP_LOCATION = null;
|
||||
|
||||
private final static boolean FLUX_AVAILABLE = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
final static boolean reactorPresent = ClassUtils.isPresent("reactor.core.publisher.Flux",
|
||||
VaultConfigDataLoader.class.getClassLoader());
|
||||
|
||||
private final static boolean WEBCLIENT_AVAILABLE = ClassUtils.isPresent(
|
||||
final static boolean webclientPresent = ClassUtils.isPresent(
|
||||
"org.springframework.web.reactive.function.client.WebClient", VaultConfigDataLoader.class.getClassLoader());
|
||||
|
||||
private final static boolean REGISTER_REACTIVE_INFRASTRUCTURE = FLUX_AVAILABLE && WEBCLIENT_AVAILABLE;
|
||||
private final static boolean REGISTER_REACTIVE_INFRASTRUCTURE = reactorPresent && webclientPresent;
|
||||
|
||||
private final DeferredLogFactory logFactory;
|
||||
|
||||
@@ -366,10 +365,16 @@ public class VaultConfigDataLoader implements ConfigDataLoader<VaultConfigLocati
|
||||
|
||||
GenericApplicationContext gac = (GenericApplicationContext) event.getApplicationContext();
|
||||
|
||||
ConfigurableListableBeanFactory factory = gac.getBeanFactory();
|
||||
|
||||
if (factory.containsSingleton(beanName) || factory.containsBeanDefinition(beanName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
contextCustomizer.accept(gac);
|
||||
T instance = event.getBootstrapContext().get(instanceType);
|
||||
|
||||
gac.registerBean(beanName, instanceType, () -> instance);
|
||||
factory.registerSingleton(beanName, instance);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -389,7 +394,6 @@ public class VaultConfigDataLoader implements ConfigDataLoader<VaultConfigLocati
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(null, logFactory.getLog(type));
|
||||
|
||||
}, VaultConfigDataLoader::isUpdateableLogField);
|
||||
}
|
||||
|
||||
@@ -399,7 +403,6 @@ public class VaultConfigDataLoader implements ConfigDataLoader<VaultConfigLocati
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(object, logFactory.getLog(object.getClass()));
|
||||
|
||||
}, VaultConfigDataLoader::isUpdateableLogField);
|
||||
}
|
||||
|
||||
@@ -419,7 +422,7 @@ public class VaultConfigDataLoader implements ConfigDataLoader<VaultConfigLocati
|
||||
|
||||
/**
|
||||
* Support class to register imperative infrastructure bootstrap instances and beans.
|
||||
*
|
||||
* <p>
|
||||
* Mirrors {@link VaultAutoConfiguration}.
|
||||
*/
|
||||
static class ImperativeInfrastructure {
|
||||
|
||||
@@ -36,7 +36,6 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.core.util.PropertyTransformer;
|
||||
import org.springframework.vault.core.util.PropertyTransformers;
|
||||
@@ -201,17 +200,17 @@ public class VaultConfigDataLocationResolver implements ConfigDataLocationResolv
|
||||
|
||||
private static List<VaultSecretBackendDescriptor> findDescriptors(Binder binder) {
|
||||
|
||||
List<String> descriptorClasses = new ArrayList<>();
|
||||
descriptorClasses.addAll(SpringFactoriesLoader.loadFactoryNames(VaultSecretBackendDescriptor.class,
|
||||
List<Object> descriptorsOrFactories = new ArrayList<>();
|
||||
descriptorsOrFactories.addAll(SpringFactoriesLoader.loadFactories(VaultSecretBackendDescriptor.class,
|
||||
VaultConfigDataLocationResolver.class.getClassLoader()));
|
||||
descriptorClasses.addAll(SpringFactoriesLoader.loadFactoryNames(VaultSecretBackendDescriptorFactory.class,
|
||||
descriptorsOrFactories.addAll(SpringFactoriesLoader.loadFactories(VaultSecretBackendDescriptorFactory.class,
|
||||
VaultConfigDataLocationResolver.class.getClassLoader()));
|
||||
|
||||
List<VaultSecretBackendDescriptor> descriptors = new ArrayList<>(descriptorClasses.size());
|
||||
List<VaultSecretBackendDescriptor> descriptors = new ArrayList<>(descriptorsOrFactories.size());
|
||||
|
||||
for (String className : descriptorClasses) {
|
||||
for (Object descriptorOrFactory : descriptorsOrFactories) {
|
||||
|
||||
Class<?> descriptorClass = loadClass(className);
|
||||
Class<?> descriptorClass = descriptorOrFactory.getClass();
|
||||
|
||||
MergedAnnotations annotations = MergedAnnotations.from(descriptorClass);
|
||||
if (annotations.isPresent(ConfigurationProperties.class)) {
|
||||
@@ -228,12 +227,13 @@ public class VaultConfigDataLocationResolver implements ConfigDataLocationResolv
|
||||
else {
|
||||
throw new IllegalStateException(String.format(
|
||||
"Descriptor %s is neither implements VaultSecretBackendDescriptorFactory nor VaultSecretBackendDescriptor",
|
||||
className));
|
||||
ClassUtils.getQualifiedName(descriptorOrFactory.getClass())));
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(String.format(
|
||||
"VaultSecretBackendDescriptor %s is not annotated with @ConfigurationProperties", className));
|
||||
throw new IllegalStateException(
|
||||
String.format("VaultSecretBackendDescriptor %s is not annotated with @ConfigurationProperties",
|
||||
ClassUtils.getQualifiedName(descriptorOrFactory.getClass())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,17 +246,4 @@ public class VaultConfigDataLocationResolver implements ConfigDataLocationResolv
|
||||
VaultConfigDataLocationResolver.class.getClassLoader());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<?> loadClass(String className) {
|
||||
try {
|
||||
return ClassUtils.forName(className, VaultConfigDataLocationResolver.class.getClassLoader());
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
ReflectionUtils.rethrowRuntimeException(e);
|
||||
|
||||
// should never happen.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2018-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.vault.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.LifecycleAwareSessionManagerSupport;
|
||||
import org.springframework.vault.authentication.ReactiveLifecycleAwareSessionManager;
|
||||
import org.springframework.vault.authentication.SimpleSessionManager;
|
||||
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
|
||||
import org.springframework.vault.core.env.LeaseAwareVaultPropertySource;
|
||||
|
||||
/**
|
||||
* Runtime hints for Spring Cloud Vault usage with native images.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class VaultRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
|
||||
ReflectionHints reflection = hints.reflection();
|
||||
|
||||
// reflection required for ConfigDataLoader, early logging capture
|
||||
reflection.registerTypes(Arrays.asList(SimpleSessionManager.class, LifecycleAwareSessionManager.class,
|
||||
LifecycleAwareSessionManagerSupport.class, ClientHttpRequestFactoryFactory.class,
|
||||
org.springframework.vault.core.env.VaultPropertySource.class, LeaseAwareVaultPropertySource.class)
|
||||
.stream().map(TypeReference::of).collect(Collectors.toList()),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
reflection.registerTypes(
|
||||
Arrays.asList(VaultKeyValueBackendProperties.class).stream().map(TypeReference::of)
|
||||
.collect(Collectors.toList()),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS,
|
||||
MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
|
||||
reflection.registerType(
|
||||
TypeReference.of("org.springframework.vault.core.lease.SecretLeaseContainer$LeaseRenewalScheduler"),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
reflection.registerType(
|
||||
TypeReference.of("org.springframework.vault.core.lease.SecretLeaseEventPublisher$LoggingErrorListener"),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
reflection.registerType(TypeReference
|
||||
.of("org.springframework.cloud.vault.config.VaultReactiveConfiguration$ReactiveSessionManagerAdapter"),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
|
||||
if (VaultConfigDataLoader.webclientPresent && VaultConfigDataLoader.reactorPresent) {
|
||||
reflection
|
||||
.registerTypes(
|
||||
Arrays.asList(ReactiveLifecycleAwareSessionManager.class).stream().map(TypeReference::of)
|
||||
.collect(Collectors.toList()),
|
||||
builder -> builder.withMembers(MemberCategory.DECLARED_FIELDS));
|
||||
}
|
||||
|
||||
// presence checks
|
||||
reflection.registerTypeIfPresent(classLoader, "reactor.core.publisher.Flux", MemberCategory.PUBLIC_CLASSES);
|
||||
reflection.registerTypeIfPresent(classLoader, "org.springframework.web.reactive.function.client.WebClient",
|
||||
MemberCategory.PUBLIC_CLASSES);
|
||||
|
||||
reflection.registerTypeIfPresent(classLoader, "org.bouncycastle.crypto.signers.PSSSigner",
|
||||
MemberCategory.PUBLIC_CLASSES);
|
||||
reflection.registerTypeIfPresent(classLoader, "com.google.api.client.googleapis.auth.oauth2.GoogleCredential",
|
||||
MemberCategory.PUBLIC_CLASSES);
|
||||
reflection.registerTypeIfPresent(classLoader, "com.google.auth.oauth2.GoogleCredentials",
|
||||
MemberCategory.PUBLIC_CLASSES);
|
||||
|
||||
// reflection for pluggable config properties bindings
|
||||
List<Object> pluggableDescriptors = new ArrayList<>();
|
||||
|
||||
pluggableDescriptors
|
||||
.addAll(SpringFactoriesLoader.loadFactories(SecretBackendMetadataFactory.class, classLoader));
|
||||
pluggableDescriptors
|
||||
.addAll(SpringFactoriesLoader.loadFactories(VaultSecretBackendDescriptor.class, classLoader));
|
||||
pluggableDescriptors
|
||||
.addAll(SpringFactoriesLoader.loadFactories(VaultSecretBackendDescriptorFactory.class, classLoader));
|
||||
|
||||
List<TypeReference> pluggableDescriptorReferences = pluggableDescriptors.stream().map(Object::getClass)
|
||||
.map(TypeReference::of).collect(Collectors.toList());
|
||||
|
||||
reflection.registerTypes(pluggableDescriptorReferences, builder -> {
|
||||
builder.withMembers(MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_METHODS);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.cloud.vault.config.VaultRuntimeHints
|
||||
@@ -20,7 +20,7 @@
|
||||
<description>Spring Cloud Vault Dependencies</description>
|
||||
|
||||
<properties>
|
||||
<spring-vault.version>3.0.0</spring-vault.version>
|
||||
<spring-vault.version>3.0.1-SNAPSHOT</spring-vault.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
Reference in New Issue
Block a user