Skip to content

Commit

Permalink
Add native image runtime hints, register singletons instead of beans …
Browse files Browse the repository at this point in the history
…when transferring instances from the Config Data API.

Closes gh-667
  • Loading branch information
mp911de committed Jan 31, 2023
1 parent 85ca4cd commit a600842
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@
*/
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;
Expand Down Expand Up @@ -347,11 +347,11 @@ private ClientAuthentication gcpGceAuthentication(VaultProperties vaultPropertie

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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,12 @@ public TaskSchedulerWrapper(ThreadPoolTaskScheduler taskScheduler) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
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;
Expand Down Expand Up @@ -62,6 +62,7 @@
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;
Expand All @@ -70,8 +71,6 @@
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
Expand All @@ -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;

Expand Down Expand Up @@ -366,10 +365,16 @@ static <T> void registerIfAbsent(ConfigurableBootstrapContext bootstrap, String

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);
});
}

Expand All @@ -389,7 +394,6 @@ static void reconfigureLogger(Class<?> type, DeferredLogFactory logFactory) {

field.setAccessible(true);
field.set(null, logFactory.getLog(type));

}, VaultConfigDataLoader::isUpdateableLogField);
}

Expand All @@ -399,7 +403,6 @@ static void reconfigureLogger(Object object, DeferredLogFactory logFactory) {

field.setAccessible(true);
field.set(object, logFactory.getLog(object.getClass()));

}, VaultConfigDataLoader::isUpdateableLogField);
}

Expand All @@ -419,7 +422,7 @@ static Class<?> forName(String name) {

/**
* Support class to register imperative infrastructure bootstrap instances and beans.
*
* <p>
* Mirrors {@link VaultAutoConfiguration}.
*/
static class ImperativeInfrastructure {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
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;
Expand Down Expand Up @@ -201,17 +200,17 @@ private static VaultKeyValueBackendProperties getKeyValueProperties(ConfigDataLo

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)) {
Expand All @@ -228,12 +227,13 @@ else if (hydratedDescriptor instanceof VaultSecretBackendDescriptor) {
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())));
}
}

Expand All @@ -246,17 +246,4 @@ private static List<SecretBackendMetadataFactory<? super VaultSecretBackendDescr
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;
}
}

}
Original file line number Diff line number Diff line change
@@ -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);
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.cloud.vault.config.VaultRuntimeHints
2 changes: 1 addition & 1 deletion spring-cloud-vault-dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit a600842

Please sign in to comment.