Browse Source
We now use the AOT infrastructure of Spring Framework 6 and data commons to provide AOT support building the foundation for native image compilation. Additionally we register hints for GraalVM native image. See: #4022 Original Pull Request: #4093pull/4109/head
24 changed files with 932 additions and 19 deletions
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb; |
||||
|
||||
import java.util.function.Consumer; |
||||
|
||||
import org.springframework.data.domain.ManagedTypes; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 4.0 |
||||
*/ |
||||
public final class MongoManagedTypes implements ManagedTypes { |
||||
|
||||
private final ManagedTypes delegate; |
||||
|
||||
public MongoManagedTypes(ManagedTypes types) { |
||||
this.delegate = types; |
||||
} |
||||
|
||||
public static MongoManagedTypes from(ManagedTypes managedTypes) { |
||||
return new MongoManagedTypes(managedTypes); |
||||
} |
||||
|
||||
public static MongoManagedTypes of(Iterable<? extends Class<?>> types) { |
||||
return from(ManagedTypes.fromIterable(types)); |
||||
} |
||||
|
||||
public static MongoManagedTypes none() { |
||||
return from(ManagedTypes.empty()); |
||||
} |
||||
|
||||
@Override |
||||
public void forEach(Consumer<Class<?>> action) { |
||||
delegate.forEach(action); |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@
@@ -0,0 +1,42 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.data.aot.AotRepositoryContext; |
||||
import org.springframework.data.aot.RepositoryRegistrationAotProcessor; |
||||
import org.springframework.data.aot.TypeContributor; |
||||
import org.springframework.data.aot.TypeUtils; |
||||
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
*/ |
||||
public class AotMongoRepositoryPostProcessor extends RepositoryRegistrationAotProcessor { |
||||
|
||||
private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor(); |
||||
|
||||
@Override |
||||
protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { |
||||
// do some custom type registration here
|
||||
super.contribute(repositoryContext, generationContext); |
||||
|
||||
repositoryContext.getResolvedTypes().stream().filter(MongoAotPredicates.IS_SIMPLE_TYPE.negate()).forEach(type -> { |
||||
TypeContributor.contribute(type, it -> true, generationContext); |
||||
lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type, generationContext); |
||||
}); |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@
@@ -0,0 +1,47 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.springframework.aot.hint.MemberCategory; |
||||
import org.springframework.aot.hint.RuntimeHints; |
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar; |
||||
import org.springframework.aot.hint.TypeReference; |
||||
import org.springframework.data.mongodb.core.mapping.event.AfterConvertCallback; |
||||
import org.springframework.data.mongodb.core.mapping.event.AfterSaveCallback; |
||||
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertCallback; |
||||
import org.springframework.data.mongodb.core.mapping.event.BeforeSaveCallback; |
||||
import org.springframework.data.mongodb.repository.support.SimpleMongoRepository; |
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 4.0 |
||||
*/ |
||||
public class DataMongoRuntimeHints implements RuntimeHintsRegistrar { |
||||
|
||||
@Override |
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { |
||||
|
||||
hints.reflection().registerTypes( |
||||
Arrays.asList(TypeReference.of(SimpleMongoRepository.class), TypeReference.of(BeforeConvertCallback.class), |
||||
TypeReference.of(BeforeSaveCallback.class), TypeReference.of(AfterConvertCallback.class), |
||||
TypeReference.of(AfterSaveCallback.class)), |
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, |
||||
MemberCategory.INVOKE_PUBLIC_METHODS)); |
||||
} |
||||
} |
||||
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import java.lang.annotation.Annotation; |
||||
import java.lang.reflect.Field; |
||||
import java.util.ArrayList; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.aot.hint.TypeReference; |
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.core.annotation.AnnotatedElementUtils; |
||||
import org.springframework.core.annotation.MergedAnnotations; |
||||
import org.springframework.data.annotation.Reference; |
||||
import org.springframework.data.aot.TypeUtils; |
||||
import org.springframework.data.mongodb.core.mapping.DBRef; |
||||
import org.springframework.data.mongodb.core.mapping.DocumentReference; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 4.0 |
||||
*/ |
||||
public class LazyLoadingProxyAotProcessor { |
||||
|
||||
private boolean generalLazyLoadingProxyContributed = false; |
||||
|
||||
void registerLazyLoadingProxyIfNeeded(Class<?> type, GenerationContext generationContext) { |
||||
|
||||
Set<Field> refFields = getFieldsWithAnnotationPresent(type, Reference.class); |
||||
if (refFields.isEmpty()) { |
||||
return; |
||||
} |
||||
|
||||
refFields.stream() //
|
||||
.filter(LazyLoadingProxyAotProcessor::isLazyLoading) //
|
||||
.forEach(field -> { |
||||
|
||||
if (!generalLazyLoadingProxyContributed) { |
||||
generationContext.getRuntimeHints().proxies().registerJdkProxy( |
||||
TypeReference.of(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class), |
||||
TypeReference.of(org.springframework.aop.SpringProxy.class), |
||||
TypeReference.of(org.springframework.aop.framework.Advised.class), |
||||
TypeReference.of(org.springframework.core.DecoratingProxy.class)); |
||||
generalLazyLoadingProxyContributed = true; |
||||
} |
||||
|
||||
if (field.getType().isInterface()) { |
||||
|
||||
List<Class<?>> interfaces = new ArrayList<>( |
||||
TypeUtils.resolveTypesInSignature(ResolvableType.forField(field, type))); |
||||
|
||||
interfaces.add(0, org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class); |
||||
interfaces.add(org.springframework.aop.SpringProxy.class); |
||||
interfaces.add(org.springframework.aop.framework.Advised.class); |
||||
interfaces.add(org.springframework.core.DecoratingProxy.class); |
||||
|
||||
generationContext.getRuntimeHints().proxies().registerJdkProxy(interfaces.toArray(Class[]::new)); |
||||
} else { |
||||
|
||||
generationContext.getRuntimeHints().proxies().registerClassProxy(field.getType(), builder -> { |
||||
builder.proxiedInterfaces(org.springframework.data.mongodb.core.convert.LazyLoadingProxy.class); |
||||
}); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
private static boolean isLazyLoading(Field field) { |
||||
if (AnnotatedElementUtils.isAnnotated(field, DBRef.class)) { |
||||
return AnnotatedElementUtils.findMergedAnnotation(field, DBRef.class).lazy(); |
||||
} |
||||
if (AnnotatedElementUtils.isAnnotated(field, DocumentReference.class)) { |
||||
return AnnotatedElementUtils.findMergedAnnotation(field, DocumentReference.class).lazy(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
private static Set<Field> getFieldsWithAnnotationPresent(Class<?> type, Class<? extends Annotation> annotation) { |
||||
|
||||
Set<Field> fields = new LinkedHashSet<>(); |
||||
for (Field field : type.getDeclaredFields()) { |
||||
if (MergedAnnotations.from(field).get(annotation).isPresent()) { |
||||
fields.add(field); |
||||
} |
||||
} |
||||
return fields; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,31 @@
@@ -0,0 +1,31 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import java.util.function.Predicate; |
||||
|
||||
import org.springframework.data.aot.TypeUtils; |
||||
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 4.0 |
||||
*/ |
||||
class MongoAotPredicates { |
||||
|
||||
static final Predicate<Class<?>> IS_SIMPLE_TYPE = (type) -> MongoSimpleTypes.HOLDER.isSimpleType(type) || TypeUtils.type(type).isPartOf("org.bson"); |
||||
|
||||
} |
||||
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.core.ResolvableType; |
||||
import org.springframework.data.aot.ManagedTypesBeanRegistrationAotProcessor; |
||||
import org.springframework.data.mongodb.MongoManagedTypes; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ClassUtils; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/06 |
||||
*/ |
||||
public class MongoManagedTypesBeanRegistrationAotProcessor extends ManagedTypesBeanRegistrationAotProcessor { |
||||
|
||||
private LazyLoadingProxyAotProcessor lazyLoadingProxyAotProcessor = new LazyLoadingProxyAotProcessor(); |
||||
|
||||
public MongoManagedTypesBeanRegistrationAotProcessor() { |
||||
setModuleIdentifier("mongo"); |
||||
} |
||||
|
||||
@Override |
||||
protected boolean isMatch(@Nullable Class<?> beanType, @Nullable String beanName) { |
||||
return isMongoManagedTypes(beanType) || super.isMatch(beanType, beanName); |
||||
} |
||||
|
||||
protected boolean isMongoManagedTypes(@Nullable Class<?> beanType) { |
||||
return beanType != null && ClassUtils.isAssignable(MongoManagedTypes.class, beanType); |
||||
} |
||||
|
||||
@Override |
||||
protected void contributeType(ResolvableType type, GenerationContext generationContext) { |
||||
|
||||
if (MongoAotPredicates.IS_SIMPLE_TYPE.test(type.toClass())) { |
||||
return; |
||||
} |
||||
|
||||
super.contributeType(type, generationContext); |
||||
lazyLoadingProxyAotProcessor.registerLazyLoadingProxyIfNeeded(type.toClass(), generationContext); |
||||
} |
||||
} |
||||
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar=\ |
||||
org.springframework.data.mongodb.aot.DataMongoRuntimeHints |
||||
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\ |
||||
org.springframework.data.mongodb.aot.MongoManagedTypesBeanRegistrationAotProcessor |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import static org.assertj.core.api.Assertions.*; |
||||
import static org.springframework.data.mongodb.aot.RepositoryRegistrationAotContributionAssert.*; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
||||
import org.springframework.beans.factory.config.BeanDefinition; |
||||
import org.springframework.beans.factory.support.RegisteredBean; |
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||
import org.springframework.core.annotation.SynthesizedAnnotation; |
||||
import org.springframework.data.annotation.LastModifiedDate; |
||||
import org.springframework.data.annotation.Transient; |
||||
import org.springframework.data.aot.RepositoryRegistrationAotContribution; |
||||
import org.springframework.data.mongodb.aot.configs.ImperativeConfig; |
||||
import org.springframework.data.mongodb.core.convert.LazyLoadingProxy; |
||||
import org.springframework.data.mongodb.core.mapping.Address; |
||||
import org.springframework.data.mongodb.core.mapping.DBRef; |
||||
import org.springframework.data.mongodb.core.mapping.Document; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class AotMongoRepositoryPostProcessorUnitTests { |
||||
|
||||
@Test |
||||
void contributesProxiesForDataAnnotations() { |
||||
|
||||
RepositoryRegistrationAotContribution repositoryBeanContribution = computeConfiguration(ImperativeConfig.class) |
||||
.forRepository(ImperativeConfig.PersonRepository.class); |
||||
|
||||
assertThatContribution(repositoryBeanContribution) //
|
||||
.codeContributionSatisfies(contribution -> { |
||||
|
||||
contribution.contributesJdkProxy(Transient.class, SynthesizedAnnotation.class); |
||||
contribution.contributesJdkProxy(LastModifiedDate.class, SynthesizedAnnotation.class); |
||||
contribution.contributesJdkProxy(Document.class, SynthesizedAnnotation.class); |
||||
contribution.contributesJdkProxy(DBRef.class, SynthesizedAnnotation.class); |
||||
// TODO: not supported yet contribution.contributesClassProxy(Address.class, LazyLoadingProxy.class);
|
||||
}); |
||||
} |
||||
|
||||
BeanContributionBuilder computeConfiguration(Class<?> configuration) { |
||||
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); |
||||
ctx.register(configuration); |
||||
ctx.refreshForAotProcessing(); |
||||
|
||||
return it -> { |
||||
|
||||
String[] repoBeanNames = ctx.getBeanNamesForType(it); |
||||
assertThat(repoBeanNames).describedAs("Unable to find repository %s in configuration %s.", it, configuration) |
||||
.hasSize(1); |
||||
|
||||
String beanName = repoBeanNames[0]; |
||||
|
||||
AotMongoRepositoryPostProcessor postProcessor = ctx.getBean(AotMongoRepositoryPostProcessor.class); |
||||
|
||||
postProcessor.setBeanFactory(ctx.getDefaultListableBeanFactory()); |
||||
|
||||
BeanRegistrationAotContribution beanRegistrationAotContribution = postProcessor |
||||
.processAheadOfTime(RegisteredBean.of(ctx.getBeanFactory(), beanName)); |
||||
assertThat(beanRegistrationAotContribution).isInstanceOf(RepositoryRegistrationAotContribution.class); |
||||
return (RepositoryRegistrationAotContribution) beanRegistrationAotContribution; |
||||
}; |
||||
} |
||||
|
||||
interface BeanContributionBuilder { |
||||
RepositoryRegistrationAotContribution forRepository(Class<?> repositoryInterface); |
||||
} |
||||
} |
||||
@ -0,0 +1,45 @@
@@ -0,0 +1,45 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import static org.assertj.core.api.Assertions.*; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.assertj.core.api.AbstractAssert; |
||||
import org.springframework.aot.hint.ClassProxyHint; |
||||
import org.springframework.aot.hint.TypeReference; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class ClassProxyAssert extends AbstractAssert<ClassProxyAssert, ClassProxyHint> { |
||||
|
||||
protected ClassProxyAssert(ClassProxyHint classProxyHint) { |
||||
super(classProxyHint, ClassProxyAssert.class); |
||||
} |
||||
|
||||
public void matches(Class<?>... proxyInterfaces) { |
||||
assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName)) |
||||
.containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new)); |
||||
} |
||||
|
||||
public List<TypeReference> getProxiedInterfaces() { |
||||
return actual.getProxiedInterfaces(); |
||||
} |
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import static org.assertj.core.api.Assertions.*; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.stream.Stream; |
||||
|
||||
import org.assertj.core.api.AbstractAssert; |
||||
import org.springframework.aot.generate.GenerationContext; |
||||
import org.springframework.aot.hint.ClassProxyHint; |
||||
import org.springframework.aot.hint.JdkProxyHint; |
||||
import org.springframework.aot.hint.RuntimeHintsPredicates; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class CodeContributionAssert extends AbstractAssert<CodeContributionAssert, GenerationContext> { |
||||
|
||||
public CodeContributionAssert(GenerationContext contribution) { |
||||
super(contribution, CodeContributionAssert.class); |
||||
} |
||||
|
||||
public CodeContributionAssert contributesReflectionFor(Class<?>... types) { |
||||
|
||||
for (Class<?> type : types) { |
||||
assertThat(this.actual.getRuntimeHints()) |
||||
.describedAs("No reflection entry found for [%s]", type) |
||||
.matches(RuntimeHintsPredicates.reflection().onType(type)); |
||||
} |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public CodeContributionAssert doesNotContributeReflectionFor(Class<?>... types) { |
||||
|
||||
for (Class<?> type : types) { |
||||
assertThat(this.actual.getRuntimeHints()) |
||||
.describedAs("Reflection entry found for [%s]", type) |
||||
.matches(RuntimeHintsPredicates.reflection().onType(type).negate()); |
||||
} |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public CodeContributionAssert contributesJdkProxyFor(Class<?> entryPoint) { |
||||
|
||||
assertThat(jdkProxiesFor(entryPoint).findFirst()) |
||||
.describedAs("No JDK proxy found for [%s]", entryPoint) |
||||
.isPresent(); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public CodeContributionAssert doesNotContributeJdkProxyFor(Class<?> entryPoint) { |
||||
|
||||
assertThat(jdkProxiesFor(entryPoint).findFirst()) |
||||
.describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint) |
||||
.isNotPresent(); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public CodeContributionAssert contributesJdkProxy(Class<?>... proxyInterfaces) { |
||||
|
||||
assertThat(jdkProxiesFor(proxyInterfaces[0])) |
||||
.describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) |
||||
.anySatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public CodeContributionAssert doesNotContributeJdkProxy(Class<?>... proxyInterfaces) { |
||||
|
||||
assertThat(jdkProxiesFor(proxyInterfaces[0])) |
||||
.describedAs("Found JDK proxy matching [%s] though it should not be present", |
||||
Arrays.asList(proxyInterfaces)) |
||||
.noneSatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
private Stream<JdkProxyHint> jdkProxiesFor(Class<?> entryPoint) { |
||||
|
||||
return this.actual.getRuntimeHints().proxies().jdkProxies() |
||||
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() |
||||
.equals(entryPoint.getCanonicalName())); |
||||
} |
||||
|
||||
public CodeContributionAssert contributesClassProxy(Class<?>... proxyInterfaces) { |
||||
|
||||
assertThat(classProxiesFor(proxyInterfaces[0])) |
||||
.describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) |
||||
.anySatisfy(it -> new ClassProxyAssert(it).matches(proxyInterfaces)); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
private Stream<ClassProxyHint> classProxiesFor(Class<?> entryPoint) { |
||||
|
||||
return this.actual.getRuntimeHints().proxies().classProxies() |
||||
.filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() |
||||
.equals(entryPoint.getCanonicalName())); |
||||
} |
||||
} |
||||
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import static org.assertj.core.api.Assertions.*; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
import org.assertj.core.api.AbstractAssert; |
||||
import org.springframework.aot.hint.JdkProxyHint; |
||||
import org.springframework.aot.hint.TypeReference; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class JdkProxyAssert extends AbstractAssert<JdkProxyAssert, JdkProxyHint> { |
||||
|
||||
public JdkProxyAssert(JdkProxyHint jdkProxyHint) { |
||||
super(jdkProxyHint, JdkProxyAssert.class); |
||||
} |
||||
|
||||
public void matches(Class<?>... proxyInterfaces) { |
||||
assertThat(actual.getProxiedInterfaces().stream().map(TypeReference::getCanonicalName)) |
||||
.containsExactly(Arrays.stream(proxyInterfaces).map(Class::getCanonicalName).toArray(String[]::new)); |
||||
} |
||||
|
||||
public List<TypeReference> getProxiedInterfaces() { |
||||
return actual.getProxiedInterfaces(); |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot; |
||||
|
||||
import static org.assertj.core.api.Assertions.*; |
||||
import static org.mockito.Mockito.*; |
||||
|
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
import java.util.function.Consumer; |
||||
|
||||
import org.assertj.core.api.AbstractAssert; |
||||
import org.springframework.aot.generate.ClassNameGenerator; |
||||
import org.springframework.aot.generate.DefaultGenerationContext; |
||||
import org.springframework.aot.generate.InMemoryGeneratedFiles; |
||||
import org.springframework.aot.hint.RuntimeHints; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; |
||||
import org.springframework.beans.factory.aot.BeanRegistrationCode; |
||||
import org.springframework.data.aot.RepositoryRegistrationAotContribution; |
||||
import org.springframework.data.repository.core.RepositoryInformation; |
||||
import org.springframework.data.repository.core.support.RepositoryFragment; |
||||
import org.springframework.lang.NonNull; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class RepositoryRegistrationAotContributionAssert |
||||
extends AbstractAssert<RepositoryRegistrationAotContributionAssert, RepositoryRegistrationAotContribution> { |
||||
|
||||
@NonNull |
||||
public static RepositoryRegistrationAotContributionAssert assertThatContribution( |
||||
@NonNull RepositoryRegistrationAotContribution actual) { |
||||
|
||||
return new RepositoryRegistrationAotContributionAssert(actual); |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert(@NonNull RepositoryRegistrationAotContribution actual) { |
||||
super(actual, RepositoryRegistrationAotContributionAssert.class); |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert targetRepositoryTypeIs(Class<?> expected) { |
||||
|
||||
assertThat(getRepositoryInformation().getRepositoryInterface()).isEqualTo(expected); |
||||
|
||||
return this.myself; |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert hasNoFragments() { |
||||
|
||||
assertThat(getRepositoryInformation().getFragments()).isEmpty(); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert hasFragments() { |
||||
|
||||
assertThat(getRepositoryInformation().getFragments()).isNotEmpty(); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert verifyFragments(Consumer<Set<RepositoryFragment<?>>> consumer) { |
||||
|
||||
assertThat(getRepositoryInformation().getFragments()) |
||||
.satisfies(it -> consumer.accept(new LinkedHashSet<>(it))); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
public RepositoryRegistrationAotContributionAssert codeContributionSatisfies( |
||||
Consumer<CodeContributionAssert> assertWith) { |
||||
|
||||
BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class); |
||||
|
||||
DefaultGenerationContext generationContext = |
||||
new DefaultGenerationContext(new ClassNameGenerator(), new InMemoryGeneratedFiles(), new RuntimeHints()); |
||||
|
||||
this.actual.applyTo(generationContext, mockBeanRegistrationCode); |
||||
|
||||
assertWith.accept(new CodeContributionAssert(generationContext)); |
||||
|
||||
return this; |
||||
} |
||||
|
||||
private RepositoryInformation getRepositoryInformation() { |
||||
|
||||
assertThat(this.actual) |
||||
.describedAs("No repository interface found on null bean contribution") |
||||
.isNotNull(); |
||||
|
||||
assertThat(this.actual.getRepositoryInformation()) |
||||
.describedAs("No repository interface found on null repository information") |
||||
.isNotNull(); |
||||
|
||||
return this.actual.getRepositoryInformation(); |
||||
} |
||||
} |
||||
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot.configs; |
||||
|
||||
import org.springframework.context.annotation.ComponentScan.Filter; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.FilterType; |
||||
import org.springframework.data.mongodb.aot.domaintypes.Person; |
||||
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; |
||||
import org.springframework.data.repository.CrudRepository; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
*/ |
||||
@Configuration |
||||
@EnableMongoRepositories(considerNestedRepositories = true, |
||||
includeFilters = { @Filter(type = FilterType.REGEX, pattern = ".*PersonRepository$") }) |
||||
public class ImperativeConfig { |
||||
|
||||
public interface PersonRepository extends CrudRepository<Person, String> { |
||||
|
||||
} |
||||
} |
||||
@ -0,0 +1,29 @@
@@ -0,0 +1,29 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot.domaintypes; |
||||
|
||||
import org.springframework.data.mongodb.core.mapping.Field; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
public class Address { |
||||
|
||||
@Field("the-street") |
||||
String street; |
||||
|
||||
} |
||||
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
/* |
||||
* Copyright 2022 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.data.mongodb.aot.domaintypes; |
||||
|
||||
import java.time.Instant; |
||||
|
||||
import org.springframework.data.annotation.Id; |
||||
import org.springframework.data.annotation.LastModifiedDate; |
||||
import org.springframework.data.annotation.Transient; |
||||
import org.springframework.data.mongodb.core.mapping.DBRef; |
||||
import org.springframework.data.mongodb.core.mapping.Document; |
||||
|
||||
/** |
||||
* @author Christoph Strobl |
||||
* @since 2022/04 |
||||
*/ |
||||
@Document("persons") |
||||
public class Person { |
||||
|
||||
@Id String id; |
||||
@DBRef(lazy = true) Address address; |
||||
|
||||
@Transient String transientProperty; |
||||
|
||||
@LastModifiedDate Instant modifiedAt; |
||||
} |
||||
Loading…
Reference in new issue