Merge branch '6.0.x'

This commit is contained in:
Sébastien Deleuze 2023-05-26 19:15:03 +02:00
commit 8a5f655a5c
16 changed files with 107 additions and 166 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,11 +19,14 @@ package org.springframework.aot;
import org.springframework.core.NativeDetector; import org.springframework.core.NativeDetector;
import org.springframework.core.SpringProperties; import org.springframework.core.SpringProperties;
import static org.springframework.core.NativeDetector.Context;
/** /**
* Utility for determining if AOT-processed optimizations must be used rather * Utility for determining if AOT-processed optimizations must be used rather
* than the regular runtime. Strictly for internal use within the framework. * than the regular runtime. Strictly for internal use within the framework.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Sebastien Deleuze
* @since 6.0 * @since 6.0
*/ */
public abstract class AotDetector { public abstract class AotDetector {
@ -36,6 +39,8 @@ public abstract class AotDetector {
*/ */
public static final String AOT_ENABLED = "spring.aot.enabled"; public static final String AOT_ENABLED = "spring.aot.enabled";
private static final boolean inNativeImage = NativeDetector.inNativeImage(Context.RUNTIME, Context.BUILD_TIME);
/** /**
* Determine whether AOT optimizations must be considered at runtime. This * Determine whether AOT optimizations must be considered at runtime. This
* is mandatory in a native image but can be triggered on the JVM using * is mandatory in a native image but can be triggered on the JVM using
@ -43,7 +48,7 @@ public abstract class AotDetector {
* @return whether AOT optimizations must be considered * @return whether AOT optimizations must be considered
*/ */
public static boolean useGeneratedArtifacts() { public static boolean useGeneratedArtifacts() {
return (NativeDetector.inNativeImage() || SpringProperties.getFlag(AOT_ENABLED)); return (inNativeImage || SpringProperties.getFlag(AOT_ENABLED));
} }
} }

View File

@ -33,8 +33,9 @@ import org.graalvm.nativeimage.hosted.Feature;
class PreComputeFieldFeature implements Feature { class PreComputeFieldFeature implements Feature {
private static Pattern[] patterns = { private static Pattern[] patterns = {
Pattern.compile(Pattern.quote("org.springframework.core.NativeDetector#imageCode")), Pattern.compile(Pattern.quote("org.springframework.core.NativeDetector#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.cglib.core.AbstractClassGenerator#imageCode")), Pattern.compile(Pattern.quote("org.springframework.cglib.core.AbstractClassGenerator#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.aot.AotDetector#inNativeImage")),
Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*Present"), Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*Present"),
Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*PRESENT"), Pattern.compile(Pattern.quote("org.springframework.") + ".*#.*PRESENT"),
Pattern.compile(Pattern.quote("reactor.") + ".*#.*Available"), Pattern.compile(Pattern.quote("reactor.") + ".*#.*Available"),

View File

@ -43,8 +43,12 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
private static final boolean DEFAULT_USE_CACHE = private static final boolean DEFAULT_USE_CACHE =
Boolean.parseBoolean(System.getProperty("cglib.useCache", "true")); Boolean.parseBoolean(System.getProperty("cglib.useCache", "true"));
// See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java private static final boolean inNativeImage;
private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
static {
String imageCode = System.getProperty("org.graalvm.nativeimage.imagecode");
inNativeImage = "buildtime".equals(imageCode) || "runtime".equals(imageCode);
}
private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE; private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
@ -354,7 +358,7 @@ abstract public class AbstractClassGenerator<T> implements ClassGenerator {
} }
} }
// SPRING PATCH BEGIN // SPRING PATCH BEGIN
if (imageCode) { if (inNativeImage) {
throw new UnsupportedOperationException("CGLIB runtime enhancement not supported on native image. " + throw new UnsupportedOperationException("CGLIB runtime enhancement not supported on native image. " +
"Make sure to include a pre-generated class on the classpath instead: " + getClassName()); "Make sure to include a pre-generated class on the classpath instead: " + getClassName());
} }

View File

@ -16,6 +16,8 @@
package org.springframework.core; package org.springframework.core;
import org.springframework.lang.Nullable;
/** /**
* A common delegate for detecting a GraalVM native image environment. * A common delegate for detecting a GraalVM native image environment.
* *
@ -25,12 +27,61 @@ package org.springframework.core;
public abstract class NativeDetector { public abstract class NativeDetector {
// See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java // See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java
private static final boolean imageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null); @Nullable
private static final String imageCode = System.getProperty("org.graalvm.nativeimage.imagecode");
private static final boolean inNativeImage = (imageCode != null);
/** /**
* Returns {@code true} if invoked in the context of image building or during image runtime, else {@code false}. * Returns {@code true} if running in a native image context (for example {@code buildtime}, {@code runtime} or
* {@code agent}) expressed by setting {@code org.graalvm.nativeimage.imagecode} system property to any value, else {@code false}.
*/ */
public static boolean inNativeImage() { public static boolean inNativeImage() {
return imageCode; return inNativeImage;
} }
/**
* Returns {@code true} if running in any of the specified native image context(s), else {@code false}.
* @param contexts the native image context(s)
* @since 6.0.10
*/
public static boolean inNativeImage(Context... contexts) {
for (Context context: contexts) {
if (context.key.equals(imageCode)) {
return true;
}
}
return false;
}
/**
* Native image context as defined in
* <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">ImageInfo.java</a>.
*
* @since 6.0.10
*/
public enum Context {
/**
* The code is executing in the context of image building.
*/
BUILD_TIME("buildtime"),
/**
* The code is executing at image runtime.
*/
RUNTIME("runtime");
private final String key;
Context(final String key) {
this.key = key;
}
@Override
public String toString() {
return this.key;
}
}
} }

View File

@ -16,6 +16,7 @@
package org.springframework.test.context.aot; package org.springframework.test.context.aot;
import org.springframework.aot.AotDetector;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -26,7 +27,7 @@ import org.springframework.lang.Nullable;
* and run-time. At build time, test components can {@linkplain #setAttribute contribute} * and run-time. At build time, test components can {@linkplain #setAttribute contribute}
* attributes during the AOT processing phase. At run time, test components can * attributes during the AOT processing phase. At run time, test components can
* {@linkplain #getString(String) retrieve} attributes that were contributed at * {@linkplain #getString(String) retrieve} attributes that were contributed at
* build time. If {@link TestAotDetector#useGeneratedArtifacts()} returns {@code true}, * build time. If {@link AotDetector#useGeneratedArtifacts()} returns {@code true},
* run-time mode applies. * run-time mode applies.
* *
* <p>For example, if a test component computes something at build time that * <p>For example, if a test component computes something at build time that
@ -43,7 +44,7 @@ import org.springframework.lang.Nullable;
* &mdash; can choose to contribute an attribute at any point in time. Note that * &mdash; can choose to contribute an attribute at any point in time. Note that
* contributing an attribute during standard JVM test execution will not have any * contributing an attribute during standard JVM test execution will not have any
* adverse side effect since AOT attributes will be ignored in that scenario. In * adverse side effect since AOT attributes will be ignored in that scenario. In
* any case, you should use {@link TestAotDetector#useGeneratedArtifacts()} to determine * any case, you should use {@link AotDetector#useGeneratedArtifacts()} to determine
* if invocations of {@link #setAttribute(String, String)} and * if invocations of {@link #setAttribute(String, String)} and
* {@link #removeAttribute(String)} are permitted. * {@link #removeAttribute(String)} are permitted.
* *
@ -70,12 +71,12 @@ public interface AotTestAttributes {
* @param name the unique attribute name * @param name the unique attribute name
* @param value the associated attribute value * @param value the associated attribute value
* @throws UnsupportedOperationException if invoked during * @throws UnsupportedOperationException if invoked during
* {@linkplain TestAotDetector#useGeneratedArtifacts() AOT run-time execution} * {@linkplain AotDetector#useGeneratedArtifacts() AOT run-time execution}
* @throws IllegalArgumentException if the provided value is {@code null} or * @throws IllegalArgumentException if the provided value is {@code null} or
* if an attempt is made to override an existing attribute * if an attempt is made to override an existing attribute
* @see #setAttribute(String, boolean) * @see #setAttribute(String, boolean)
* @see #removeAttribute(String) * @see #removeAttribute(String)
* @see TestAotDetector#useGeneratedArtifacts() * @see AotDetector#useGeneratedArtifacts()
*/ */
void setAttribute(String name, String value); void setAttribute(String name, String value);
@ -87,13 +88,13 @@ public interface AotTestAttributes {
* @param name the unique attribute name * @param name the unique attribute name
* @param value the associated attribute value * @param value the associated attribute value
* @throws UnsupportedOperationException if invoked during * @throws UnsupportedOperationException if invoked during
* {@linkplain TestAotDetector#useGeneratedArtifacts() AOT run-time execution} * {@linkplain AotDetector#useGeneratedArtifacts() AOT run-time execution}
* @throws IllegalArgumentException if an attempt is made to override an * @throws IllegalArgumentException if an attempt is made to override an
* existing attribute * existing attribute
* @see #setAttribute(String, String) * @see #setAttribute(String, String)
* @see #removeAttribute(String) * @see #removeAttribute(String)
* @see Boolean#toString(boolean) * @see Boolean#toString(boolean)
* @see TestAotDetector#useGeneratedArtifacts() * @see AotDetector#useGeneratedArtifacts()
*/ */
default void setAttribute(String name, boolean value) { default void setAttribute(String name, boolean value) {
setAttribute(name, Boolean.toString(value)); setAttribute(name, Boolean.toString(value));
@ -103,8 +104,8 @@ public interface AotTestAttributes {
* Remove the attribute stored under the provided name. * Remove the attribute stored under the provided name.
* @param name the unique attribute name * @param name the unique attribute name
* @throws UnsupportedOperationException if invoked during * @throws UnsupportedOperationException if invoked during
* {@linkplain TestAotDetector#useGeneratedArtifacts() AOT run-time execution} * {@linkplain AotDetector#useGeneratedArtifacts() AOT run-time execution}
* @see TestAotDetector#useGeneratedArtifacts() * @see AotDetector#useGeneratedArtifacts()
* @see #setAttribute(String, String) * @see #setAttribute(String, String)
*/ */
void removeAttribute(String name); void removeAttribute(String name);

View File

@ -19,6 +19,7 @@ package org.springframework.test.context.aot;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.springframework.aot.AotDetector;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -39,7 +40,7 @@ final class AotTestAttributesFactory {
/** /**
* Get the underlying attributes map. * Get the underlying attributes map.
* <p>If the map is not already loaded, this method loads the map from the * <p>If the map is not already loaded, this method loads the map from the
* generated class when running in {@linkplain TestAotDetector#useGeneratedArtifacts() * generated class when running in {@linkplain AotDetector#useGeneratedArtifacts()
* AOT execution mode} and otherwise creates a new map for storing attributes * AOT execution mode} and otherwise creates a new map for storing attributes
* during the AOT processing phase. * during the AOT processing phase.
*/ */
@ -49,7 +50,7 @@ final class AotTestAttributesFactory {
synchronized (AotTestAttributesFactory.class) { synchronized (AotTestAttributesFactory.class) {
attrs = attributes; attrs = attributes;
if (attrs == null) { if (attrs == null) {
attrs = (TestAotDetector.useGeneratedArtifacts() ? loadAttributesMap() : new ConcurrentHashMap<>()); attrs = (AotDetector.useGeneratedArtifacts() ? loadAttributesMap() : new ConcurrentHashMap<>());
attributes = attrs; attributes = attrs;
} }
} }

View File

@ -19,6 +19,7 @@ package org.springframework.test.context.aot;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.aot.AotDetector;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -29,7 +30,7 @@ import org.springframework.lang.Nullable;
* *
* <p>Intended solely for internal use within the framework. * <p>Intended solely for internal use within the framework.
* *
* <p>If we are not running in {@linkplain TestAotDetector#useGeneratedArtifacts() * <p>If we are not running in {@linkplain AotDetector#useGeneratedArtifacts()
* AOT mode} or if a test class is not {@linkplain #isSupportedTestClass(Class) * AOT mode} or if a test class is not {@linkplain #isSupportedTestClass(Class)
* supported} in AOT mode, {@link #getContextInitializer(Class)} and * supported} in AOT mode, {@link #getContextInitializer(Class)} and
* {@link #getContextInitializerClass(Class)} will return {@code null}. * {@link #getContextInitializerClass(Class)} will return {@code null}.

View File

@ -19,6 +19,7 @@ package org.springframework.test.context.aot;
import java.util.Map; import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.springframework.aot.AotDetector;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -44,7 +45,7 @@ final class AotTestContextInitializersFactory {
/** /**
* Get the underlying map. * Get the underlying map.
* <p>If the map is not already loaded, this method loads the map from the * <p>If the map is not already loaded, this method loads the map from the
* generated class when running in {@linkplain TestAotDetector#useGeneratedArtifacts() * generated class when running in {@linkplain AotDetector#useGeneratedArtifacts()
* AOT execution mode} and otherwise creates an immutable, empty map. * AOT execution mode} and otherwise creates an immutable, empty map.
*/ */
static Map<String, Supplier<ApplicationContextInitializer<ConfigurableApplicationContext>>> getContextInitializers() { static Map<String, Supplier<ApplicationContextInitializer<ConfigurableApplicationContext>>> getContextInitializers() {
@ -53,7 +54,7 @@ final class AotTestContextInitializersFactory {
synchronized (AotTestContextInitializersFactory.class) { synchronized (AotTestContextInitializersFactory.class) {
initializers = contextInitializers; initializers = contextInitializers;
if (initializers == null) { if (initializers == null) {
initializers = (TestAotDetector.useGeneratedArtifacts() ? loadContextInitializersMap() : Map.of()); initializers = (AotDetector.useGeneratedArtifacts() ? loadContextInitializersMap() : Map.of());
contextInitializers = initializers; contextInitializers = initializers;
} }
} }
@ -67,7 +68,7 @@ final class AotTestContextInitializersFactory {
synchronized (AotTestContextInitializersFactory.class) { synchronized (AotTestContextInitializersFactory.class) {
initializerClasses = contextInitializerClasses; initializerClasses = contextInitializerClasses;
if (initializerClasses == null) { if (initializerClasses == null) {
initializerClasses = (TestAotDetector.useGeneratedArtifacts() ? loadContextInitializerClassesMap() : Map.of()); initializerClasses = (AotDetector.useGeneratedArtifacts() ? loadContextInitializerClassesMap() : Map.of());
contextInitializerClasses = initializerClasses; contextInitializerClasses = initializerClasses;
} }
} }

View File

@ -18,6 +18,7 @@ package org.springframework.test.context.aot;
import java.util.Map; import java.util.Map;
import org.springframework.aot.AotDetector;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -60,7 +61,7 @@ class DefaultAotTestAttributes implements AotTestAttributes {
private static void assertNotInAotRuntime() { private static void assertNotInAotRuntime() {
if (TestAotDetector.useGeneratedArtifacts()) { if (AotDetector.useGeneratedArtifacts()) {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"AOT attributes cannot be modified during AOT run-time execution"); "AOT attributes cannot be modified during AOT run-time execution");
} }

View File

@ -1,58 +0,0 @@
/*
* Copyright 2002-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.test.context.aot;
import org.springframework.aot.AotDetector;
import org.springframework.core.SpringProperties;
import org.springframework.util.StringUtils;
/**
* TestContext framework specific utility for determining if AOT-processed
* optimizations must be used rather than the regular runtime.
*
* <p>Strictly for internal use within the framework.
*
* @author Sam Brannen
* @since 6.0.9
*/
public abstract class TestAotDetector {
/**
* Determine whether AOT optimizations must be considered at runtime.
* <p>This can be triggered using the {@value AotDetector#AOT_ENABLED}
* Spring property or via GraalVM's {@code "org.graalvm.nativeimage.imagecode"}
* JVM system property (if set to any non-empty value other than {@code agent}).
* @return {@code true} if AOT optimizations must be considered
* @see <a href="https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java">GraalVM's ImageInfo.java</a>
* @see AotDetector#useGeneratedArtifacts()
*/
public static boolean useGeneratedArtifacts() {
return (SpringProperties.getFlag(AotDetector.AOT_ENABLED) || inNativeImage());
}
/**
* Determine if we are currently running within a GraalVM native image from
* the perspective of the TestContext framework.
* @return {@code true} if the {@code org.graalvm.nativeimage.imagecode} JVM
* system property has been set to any value other than {@code agent}.
*/
private static boolean inNativeImage() {
String imageCode = System.getProperty("org.graalvm.nativeimage.imagecode");
return (StringUtils.hasText(imageCode) && !"agent".equalsIgnoreCase(imageCode.trim()));
}
}

View File

@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aot.AotDetector;
import org.springframework.aot.generate.ClassNameGenerator; import org.springframework.aot.generate.ClassNameGenerator;
import org.springframework.aot.generate.DefaultGenerationContext; import org.springframework.aot.generate.DefaultGenerationContext;
import org.springframework.aot.generate.GeneratedClasses; import org.springframework.aot.generate.GeneratedClasses;
@ -122,7 +123,7 @@ public class TestContextAotGenerator {
* @throws TestContextAotException if an error occurs during AOT processing * @throws TestContextAotException if an error occurs during AOT processing
*/ */
public void processAheadOfTime(Stream<Class<?>> testClasses) throws TestContextAotException { public void processAheadOfTime(Stream<Class<?>> testClasses) throws TestContextAotException {
Assert.state(!TestAotDetector.useGeneratedArtifacts(), "Cannot perform AOT processing during AOT run-time execution"); Assert.state(!AotDetector.useGeneratedArtifacts(), "Cannot perform AOT processing during AOT run-time execution");
try { try {
resetAotFactories(); resetAotFactories();

View File

@ -21,6 +21,7 @@ import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aot.AotDetector;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
@ -35,7 +36,6 @@ import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.SmartContextLoader; import org.springframework.test.context.SmartContextLoader;
import org.springframework.test.context.aot.AotContextLoader; import org.springframework.test.context.aot.AotContextLoader;
import org.springframework.test.context.aot.AotTestContextInitializers; import org.springframework.test.context.aot.AotTestContextInitializers;
import org.springframework.test.context.aot.TestAotDetector;
import org.springframework.test.context.aot.TestContextAotException; import org.springframework.test.context.aot.TestContextAotException;
import org.springframework.test.context.util.TestContextSpringFactoriesUtils; import org.springframework.test.context.util.TestContextSpringFactoriesUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -248,7 +248,7 @@ public class DefaultCacheAwareContextLoaderDelegate implements CacheAwareContext
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private MergedContextConfiguration replaceIfNecessary(MergedContextConfiguration mergedConfig) { private MergedContextConfiguration replaceIfNecessary(MergedContextConfiguration mergedConfig) {
if (TestAotDetector.useGeneratedArtifacts()) { if (AotDetector.useGeneratedArtifacts()) {
Class<?> testClass = mergedConfig.getTestClass(); Class<?> testClass = mergedConfig.getTestClass();
Class<? extends ApplicationContextInitializer<?>> contextInitializerClass = Class<? extends ApplicationContextInitializer<?>> contextInitializerClass =
this.aotTestContextInitializers.getContextInitializerClass(testClass); this.aotTestContextInitializers.getContextInitializerClass(testClass);

View File

@ -21,6 +21,7 @@ import java.lang.reflect.InvocationTargetException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.core.NativeDetector;
import org.springframework.core.io.support.SpringFactoriesLoader.FailureHandler; import org.springframework.core.io.support.SpringFactoriesLoader.FailureHandler;
/** /**
@ -53,6 +54,13 @@ class TestContextFailureHandler implements FailureHandler {
available.""".formatted(factoryType.getSimpleName(), factoryImplementationName), ex); available.""".formatted(factoryType.getSimpleName(), factoryImplementationName), ex);
} }
} }
// Workaround for https://github.com/oracle/graal/issues/6691
else if (NativeDetector.inNativeImage() && ex instanceof IllegalStateException) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping candidate %1$s [%2$s] due to an error when loading it in a native image."
.formatted(factoryType.getSimpleName(), factoryImplementationName));
}
}
else { else {
if (ex instanceof RuntimeException runtimeException) { if (ex instanceof RuntimeException runtimeException) {
throw runtimeException; throw runtimeException;

View File

@ -1,76 +0,0 @@
/*
* Copyright 2002-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.test.context.aot;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.aot.generate.InMemoryGeneratedFiles;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests for error cases in {@link TestContextAotGenerator}.
*
* @author Sam Brannen
* @since 6.0.9
*/
class TestContextAotGeneratorErrorCaseTests {
@ParameterizedTest
@CsvSource(delimiter = '=', textBlock = """
'spring.aot.enabled' = 'true'
'org.graalvm.nativeimage.imagecode' = 'buildtime'
'org.graalvm.nativeimage.imagecode' = 'runtime'
'org.graalvm.nativeimage.imagecode' = 'bogus'
""")
void attemptToProcessWhileRunningInAotMode(String property, String value) {
try {
System.setProperty(property, value);
assertThatIllegalStateException()
.isThrownBy(() -> generator().processAheadOfTime(Stream.empty()))
.withMessage("Cannot perform AOT processing during AOT run-time execution");
}
finally {
System.clearProperty(property);
}
}
@Test
void attemptToProcessWhileRunningInGraalVmNativeBuildToolsAgentMode() {
final String IMAGECODE = "org.graalvm.nativeimage.imagecode";
try {
System.setProperty(IMAGECODE, "AgenT");
assertThatNoException().isThrownBy(() -> generator().processAheadOfTime(Stream.empty()));
}
finally {
System.clearProperty(IMAGECODE);
}
}
private static TestContextAotGenerator generator() {
InMemoryGeneratedFiles generatedFiles = new InMemoryGeneratedFiles();
return new TestContextAotGenerator(generatedFiles);
}
}

View File

@ -18,6 +18,7 @@ package org.springframework.test.context.aot.samples.basic;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.aot.AotDetector;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
@ -27,7 +28,6 @@ import org.springframework.test.context.ContextLoader;
import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.aot.AotTestAttributes; import org.springframework.test.context.aot.AotTestAttributes;
import org.springframework.test.context.aot.TestAotDetector;
import org.springframework.test.context.aot.samples.basic.BasicSpringVintageTests.CustomXmlBootstrapper; import org.springframework.test.context.aot.samples.basic.BasicSpringVintageTests.CustomXmlBootstrapper;
import org.springframework.test.context.aot.samples.common.MessageService; import org.springframework.test.context.aot.samples.common.MessageService;
import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.junit4.SpringRunner;
@ -78,7 +78,7 @@ public class BasicSpringVintageTests {
String booleanKey1 = "@SpringBootConfiguration-" + mergedConfig.getTestClass().getName() + "-active1"; String booleanKey1 = "@SpringBootConfiguration-" + mergedConfig.getTestClass().getName() + "-active1";
String booleanKey2 = "@SpringBootConfiguration-" + mergedConfig.getTestClass().getName() + "-active2"; String booleanKey2 = "@SpringBootConfiguration-" + mergedConfig.getTestClass().getName() + "-active2";
AotTestAttributes aotAttributes = AotTestAttributes.getInstance(); AotTestAttributes aotAttributes = AotTestAttributes.getInstance();
if (TestAotDetector.useGeneratedArtifacts()) { if (AotDetector.useGeneratedArtifacts()) {
assertThat(aotAttributes.getString(stringKey)) assertThat(aotAttributes.getString(stringKey))
.as("AOT String attribute must already be present during AOT run-time execution") .as("AOT String attribute must already be present during AOT run-time execution")
.isEqualTo("org.example.Main"); .isEqualTo("org.example.Main");

View File

@ -19,6 +19,7 @@ package org.springframework.test.context.aot.samples.basic;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.springframework.aot.AotDetector;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotatedBeanDefinitionReader; import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
@ -27,7 +28,6 @@ import org.springframework.test.context.ContextConfigurationAttributes;
import org.springframework.test.context.ContextCustomizer; import org.springframework.test.context.ContextCustomizer;
import org.springframework.test.context.ContextCustomizerFactory; import org.springframework.test.context.ContextCustomizerFactory;
import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.aot.TestAotDetector;
/** /**
* Emulates {@code ImportsContextCustomizerFactory} from Spring Boot's testing support. * Emulates {@code ImportsContextCustomizerFactory} from Spring Boot's testing support.
@ -41,7 +41,7 @@ class ImportsContextCustomizerFactory implements ContextCustomizerFactory {
public ContextCustomizer createContextCustomizer(Class<?> testClass, public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) { List<ContextConfigurationAttributes> configAttributes) {
if (TestAotDetector.useGeneratedArtifacts()) { if (AotDetector.useGeneratedArtifacts()) {
return null; return null;
} }
if (testClass.getName().startsWith("org.springframework.test.context.aot.samples") && if (testClass.getName().startsWith("org.springframework.test.context.aot.samples") &&