Add native image hints and AOT pre-processor.
Closes gh-747
This commit is contained in:
@@ -23,6 +23,11 @@
|
||||
<directory>../src/main/resources</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
</resource>
|
||||
|
||||
<resource>
|
||||
<directory>src/main/resources/META-INF</directory>
|
||||
<targetPath>META-INF</targetPath>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 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
|
||||
*
|
||||
* http://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.vault.annotation;
|
||||
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragments;
|
||||
import org.springframework.beans.factory.aot.BeanRegistrationCodeFragmentsDecorator;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.vault.core.lease.domain.RequestedSecret;
|
||||
import org.springframework.vault.core.lease.domain.RequestedSecret.Mode;
|
||||
import org.springframework.vault.core.util.PropertyTransformers;
|
||||
import org.springframework.vault.core.util.PropertyTransformers.KeyPrefixPropertyTransformer;
|
||||
import org.springframework.vault.core.util.PropertyTransformers.NoOpPropertyTransformer;
|
||||
|
||||
/**
|
||||
* AOT processor to serialize
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0.1
|
||||
*/
|
||||
class PropertySourceAotProcessor implements BeanRegistrationAotProcessor {
|
||||
|
||||
@Override
|
||||
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
|
||||
|
||||
if (registeredBean.getBeanClass() == org.springframework.vault.core.env.LeaseAwareVaultPropertySource.class) {
|
||||
return BeanRegistrationAotContribution.withCustomCodeFragments(AotContribution::new);
|
||||
}
|
||||
|
||||
if (registeredBean.getBeanClass() == org.springframework.vault.core.env.VaultPropertySource.class) {
|
||||
return BeanRegistrationAotContribution.withCustomCodeFragments(AotContribution::new);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static class AotContribution extends BeanRegistrationCodeFragmentsDecorator {
|
||||
|
||||
protected AotContribution(BeanRegistrationCodeFragments delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeBlock generateSetBeanDefinitionPropertiesCode(GenerationContext generationContext,
|
||||
BeanRegistrationCode beanRegistrationCode, RootBeanDefinition beanDefinition,
|
||||
Predicate<String> attributeFilter) {
|
||||
|
||||
CodeBlock.Builder code = CodeBlock.builder();
|
||||
|
||||
ConstructorArgumentValues values = beanDefinition.getConstructorArgumentValues();
|
||||
|
||||
for (Entry<Integer, ValueHolder> entry : values.getIndexedArgumentValues().entrySet()) {
|
||||
|
||||
CodeBlock renderedValue = render(entry.getValue().getValue());
|
||||
code.addStatement("$N.getConstructorArgumentValues().addIndexedArgumentValue($L, $L)",
|
||||
BeanRegistrationCodeFragments.BEAN_DEFINITION_VARIABLE, entry.getKey(), renderedValue);
|
||||
}
|
||||
|
||||
return code.build();
|
||||
}
|
||||
|
||||
private static CodeBlock render(@Nullable Object value) {
|
||||
|
||||
if (value instanceof RuntimeBeanReference runtimeBeanReference
|
||||
&& runtimeBeanReference.getBeanType() != null) {
|
||||
return CodeBlock.of("new $T($T.class)", RuntimeBeanReference.class, runtimeBeanReference.getBeanType());
|
||||
}
|
||||
|
||||
if (value instanceof BeanReference beanReference) {
|
||||
return CodeBlock.of("new $T($S)", RuntimeBeanReference.class, beanReference.getBeanName());
|
||||
}
|
||||
|
||||
if (value instanceof String) {
|
||||
return CodeBlock.of("$S", value.toString());
|
||||
}
|
||||
|
||||
if (value == null || ClassUtils.isPrimitiveOrWrapper(value.getClass())) {
|
||||
return CodeBlock.of("$L", value != null ? value.toString() : "null");
|
||||
}
|
||||
|
||||
if (value instanceof NoOpPropertyTransformer) {
|
||||
return CodeBlock.of("$T.$N()", PropertyTransformers.class, "noop");
|
||||
}
|
||||
|
||||
if (value instanceof KeyPrefixPropertyTransformer kpt) {
|
||||
return CodeBlock.of("$T.$N($S)", PropertyTransformers.class, "propertyNamePrefix",
|
||||
kpt.getPropertyNamePrefix());
|
||||
}
|
||||
|
||||
if (value instanceof RequestedSecret rs) {
|
||||
return CodeBlock.of("$T.$N($S)", RequestedSecret.class,
|
||||
rs.getMode() == Mode.ROTATE ? "rotating" : "renewable", rs.getPath());
|
||||
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Unsupported value type: " + value.getClass());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -135,6 +135,7 @@ class VaultPropertySourceRegistrar
|
||||
|
||||
AbstractBeanDefinition beanDefinition = createBeanDefinition(ref, renewal, propertyTransformer,
|
||||
ignoreSecretNotFound, potentiallyResolveRequiredPlaceholders(propertyPath));
|
||||
beanDefinition.setSource(annotationMetadata.getClassName());
|
||||
|
||||
do {
|
||||
String beanName = "vaultPropertySource#" + counter;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 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
|
||||
*
|
||||
* http://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.vault.aot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
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.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Runtime hints for Spring Vault.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.0.1
|
||||
*/
|
||||
class VaultRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private final CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
|
||||
|
||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(classLoader);
|
||||
|
||||
ReflectionHints reflection = hints.reflection();
|
||||
MemberCategory[] dataObjectCategories = new MemberCategory[] { MemberCategory.DECLARED_FIELDS,
|
||||
MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS,
|
||||
MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_METHODS };
|
||||
try {
|
||||
Resource[] resources = resolver.getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
|
||||
+ ClassUtils.convertClassNameToResourcePath("org.springframework.vault.support") + "/*.class");
|
||||
|
||||
for (Resource resource : resources) {
|
||||
|
||||
MetadataReader metadataReader = factory.getMetadataReader(resource);
|
||||
String className = metadataReader.getClassMetadata().getClassName();
|
||||
|
||||
if (className.contains("-")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
reflection.registerType(TypeReference.of(className), dataObjectCategories);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
Stream.of("org.springframework.vault.core.VaultSysTemplate$GetMounts$VaultMountsResponse",
|
||||
"org.springframework.vault.core.VaultVersionedKeyValueTemplate$VersionedResponse",
|
||||
"org.springframework.vault.core.ReactiveVaultTemplate$VaultListResponse",
|
||||
"org.springframework.vault.core.VaultListResponse",
|
||||
|
||||
"org.springframework.vault.core.VaultTransitTemplate$RawTransitKeyImpl",
|
||||
"org.springframework.vault.core.VaultTransitTemplate$VaultTransitKeyImpl",
|
||||
|
||||
"org.springframework.vault.core.VaultSysTemplate$GetMounts",
|
||||
"org.springframework.vault.core.VaultSysTemplate$GetUnsealStatus",
|
||||
"org.springframework.vault.core.VaultSysTemplate$Health",
|
||||
"org.springframework.vault.core.VaultSysTemplate$Seal",
|
||||
"org.springframework.vault.core.VaultSysTemplate$VaultHealthImpl",
|
||||
"org.springframework.vault.core.VaultSysTemplate$VaultInitializationResponseImpl",
|
||||
"org.springframework.vault.core.VaultSysTemplate$VaultUnsealStatusImpl",
|
||||
|
||||
"org.springframework.vault.core.VaultVersionedKeyValueTemplate$VersionedResponse")
|
||||
.forEach(cls -> reflection.registerType(TypeReference.of(cls), dataObjectCategories));
|
||||
|
||||
reflection.registerTypeIfPresent(classLoader, "com.google.api.client.json.jackson2.JacksonFactory",
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
|
||||
reflection.registerTypeIfPresent(classLoader, "com.google.api.client.json.gson.GsonFactory",
|
||||
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Ahead-of-Time support.
|
||||
*/
|
||||
@org.springframework.lang.NonNullApi
|
||||
@org.springframework.lang.NonNullFields
|
||||
package org.springframework.vault.aot;
|
||||
@@ -65,28 +65,14 @@ import static org.springframework.vault.client.ClientHttpRequestFactoryFactory.*
|
||||
*/
|
||||
public class ClientHttpConnectorFactory {
|
||||
|
||||
private static final boolean REACTOR_NETTY_PRESENT = isPresent("reactor.netty.http.client.HttpClient");
|
||||
private static final boolean REACTOR_NETTY_PRESENT = ClassUtils.isPresent("reactor.netty.http.client.HttpClient",
|
||||
ClientHttpConnectorFactory.class.getClassLoader());
|
||||
|
||||
private static final boolean HTTP_COMPONENTS_PRESENT = isPresent("org.apache.hc.client5.http.impl.async");
|
||||
private static final boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent("org.apache.hc.client5.http.impl.async",
|
||||
ClientHttpConnectorFactory.class.getClassLoader());
|
||||
|
||||
private static final boolean JETTY_PRESENT = isPresent("org.eclipse.jetty.client.HttpClient");
|
||||
|
||||
/**
|
||||
* Checks for presence of all {@code classNames} using this class' classloader.
|
||||
* @param classNames
|
||||
* @return {@literal true} if all classes are present; {@literal false} if at least
|
||||
* one class cannot be found.
|
||||
*/
|
||||
private static boolean isPresent(String... classNames) {
|
||||
|
||||
for (String className : classNames) {
|
||||
if (!ClassUtils.isPresent(className, ClientHttpConnectorFactory.class.getClassLoader())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private static final boolean JETTY_PRESENT = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient",
|
||||
ClientHttpConnectorFactory.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Create a {@link ClientHttpConnector} for the given {@link ClientOptions} and
|
||||
|
||||
@@ -89,27 +89,12 @@ public class ClientHttpRequestFactoryFactory {
|
||||
@SuppressWarnings("FieldMayBeFinal") // allow setting via reflection.
|
||||
private static Log logger = LogFactory.getLog(ClientHttpRequestFactoryFactory.class);
|
||||
|
||||
private static final boolean HTTP_COMPONENTS_PRESENT = isPresent(
|
||||
"org.apache.hc.client5.http.impl.classic.HttpClientBuilder");
|
||||
private static final boolean HTTP_COMPONENTS_PRESENT = ClassUtils.isPresent(
|
||||
"org.apache.hc.client5.http.impl.classic.HttpClientBuilder",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
private static final boolean OKHTTP3_PRESENT = isPresent("okhttp3.OkHttpClient");
|
||||
|
||||
/**
|
||||
* Checks for presence of all {@code classNames} using this class' classloader.
|
||||
* @param classNames
|
||||
* @return {@literal true} if all classes are present; {@literal false} if at least
|
||||
* one class cannot be found.
|
||||
*/
|
||||
private static boolean isPresent(String... classNames) {
|
||||
|
||||
for (String className : classNames) {
|
||||
if (!ClassUtils.isPresent(className, ClientHttpRequestFactoryFactory.class.getClassLoader())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
private static final boolean OKHTTP3_PRESENT = ClassUtils.isPresent("okhttp3.OkHttpClient",
|
||||
ClientHttpRequestFactoryFactory.class.getClassLoader());
|
||||
|
||||
/**
|
||||
* Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and
|
||||
|
||||
@@ -57,7 +57,7 @@ public abstract class PropertyTransformers {
|
||||
* {@link PropertyTransformer} that passes the given properties through without
|
||||
* returning changed properties.
|
||||
*/
|
||||
static class NoOpPropertyTransformer implements PropertyTransformer {
|
||||
public static class NoOpPropertyTransformer implements PropertyTransformer {
|
||||
|
||||
static NoOpPropertyTransformer INSTANCE = new NoOpPropertyTransformer();
|
||||
|
||||
@@ -118,7 +118,7 @@ public abstract class PropertyTransformers {
|
||||
/**
|
||||
* {@link PropertyTransformer} that adds a prefix to each key name.
|
||||
*/
|
||||
static class KeyPrefixPropertyTransformer implements PropertyTransformer {
|
||||
public static class KeyPrefixPropertyTransformer implements PropertyTransformer {
|
||||
|
||||
private final String propertyNamePrefix;
|
||||
|
||||
@@ -129,6 +129,10 @@ public abstract class PropertyTransformers {
|
||||
this.propertyNamePrefix = propertyNamePrefix;
|
||||
}
|
||||
|
||||
public String getPropertyNamePrefix() {
|
||||
return propertyNamePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link KeyPrefixPropertyTransformer} that adds a prefix to each
|
||||
* key name.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=org.springframework.vault.aot.VaultRuntimeHints
|
||||
|
||||
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=org.springframework.vault.annotation.PropertySourceAotProcessor
|
||||
Reference in New Issue
Block a user