diff --git a/core/src/main/java/org/microshed/testing/ApplicationEnvironment.java b/core/src/main/java/org/microshed/testing/ApplicationEnvironment.java
index d2081e0b..bfe537cd 100644
--- a/core/src/main/java/org/microshed/testing/ApplicationEnvironment.java
+++ b/core/src/main/java/org/microshed/testing/ApplicationEnvironment.java
@@ -37,6 +37,78 @@
*/
public interface ApplicationEnvironment {
+ public static class Resolver {
+ private static ApplicationEnvironment loaded = null;
+
+ private Resolver() {
+ // static singleton
+ }
+
+ /**
+ * @return The selected {@link ApplicationEnvironment}. The selection is made using the following criteria:
+ *
+ * - If the {@link #ENV_CLASS} system property or environment variable is set, the class is used
+ * - The {@link ServiceLoader} is used to load all {@link ApplicationEnvironment} instances, which are filtered
+ * based on {@link #isAvailable()} and then sorted based on {@link #getPriority()} where higher numbers are chosen
+ * first.
+ *
+ */
+ public static ApplicationEnvironment load() {
+ if (loaded != null)
+ return loaded;
+
+ // First check explicilty configured environment via system property or env var
+ String strategy = System.getProperty(ENV_CLASS);
+ if (strategy == null || strategy.isEmpty())
+ strategy = System.getenv(ENV_CLASS);
+ if (strategy != null && !strategy.isEmpty()) {
+ Class> found;
+ try {
+ found = Class.forName(strategy);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Unable to load the selected ApplicationEnvironment class: " + strategy, e);
+ }
+ if (!ApplicationEnvironment.class.isAssignableFrom(found)) {
+ throw new IllegalStateException("ApplicationEnvironment class " + strategy +
+ " was found, but it does not implement the required interface " + ApplicationEnvironment.class);
+ } else {
+ try {
+ loaded = (ApplicationEnvironment) found.newInstance();
+ return loaded;
+ } catch (InstantiationException | IllegalAccessException e) {
+ throw new IllegalStateException("Unable to initialize " + found, e);
+ }
+ }
+ }
+
+ Logger LOG = LoggerFactory.getLogger(ApplicationEnvironment.class);
+
+ // If nothing explicitly defined in sysprops or env, check ServiceLoader
+ Set envs = new HashSet<>();
+ ServiceLoader.load(ApplicationEnvironment.class).forEach(envs::add);
+ Optional selectedEnv = envs.stream()
+ .map(env -> {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Found ApplicationEnvironment " + env.getClass() + " with priority=" + env.getPriority() + ", available=" + env.isAvailable());
+ return env;
+ })
+ .filter(env -> env.isAvailable())
+ .sorted((c1, c2) -> c1.getClass().getCanonicalName().compareTo(c2.getClass().getCanonicalName()))
+ .sorted((c1, c2) -> Integer.compare(c2.getPriority(), c1.getPriority()))
+ .findFirst();
+ loaded = selectedEnv.orElseThrow(() -> new IllegalStateException("No available " + ApplicationEnvironment.class.getSimpleName() + " was discovered."));
+ return loaded;
+ }
+
+ /**
+ * @param clazz The {@link ApplicationEnvironment} class to check is active
+ * @return True if the provided {@link ApplicationEnvironment} is currently active, false otherwise
+ */
+ public static boolean isSelected(Class extends ApplicationEnvironment> clazz) {
+ return load().getClass().getCanonicalName().equals(clazz.getCanonicalName());
+ }
+ }
+
/**
* The default priority returned by an implementation of {@link ApplicationEnvironment#isAvailable}
* In general, built-in ApplicationEnvironment implementations have a priority less than the default
@@ -47,69 +119,10 @@ public interface ApplicationEnvironment {
/**
* The name of the system property or environment variable that indicates a specific {@link ApplicationEnvironment}
* to use. If this property is set, it will be used regardless of the priority or availability. If this property is
- * NOT set, the normal resolution rules will be applied as defined in {@link #load()}
+ * NOT set, the normal resolution rules will be applied as defined in {@link ApplicationEnvironment.Resolver#load()}
*/
public static final String ENV_CLASS = "MICROSHED_TEST_ENV_CLASS";
- /**
- * @return The selected {@link ApplicationEnvironment}. The selection is made using the following criteria:
- *
- * - If the {@link #ENV_CLASS} system property or environment variable is set, the class is used
- * - The {@link ServiceLoader} is used to load all {@link ApplicationEnvironment} instances, which are filtered
- * based on {@link #isAvailable()} and then sorted based on {@link #getPriority()} where higher numbers are chosen
- * first.
- *
- */
- public static ApplicationEnvironment load() {
- // First check explicilty configured environment via system property or env var
- String strategy = System.getProperty(ENV_CLASS);
- if (strategy == null || strategy.isEmpty())
- strategy = System.getenv(ENV_CLASS);
- if (strategy != null && !strategy.isEmpty()) {
- Class> found;
- try {
- found = Class.forName(strategy);
- } catch (ClassNotFoundException e) {
- throw new IllegalStateException("Unable to load the selected ApplicationEnvironment class: " + strategy, e);
- }
- if (!ApplicationEnvironment.class.isAssignableFrom(found)) {
- throw new IllegalStateException("ApplicationEnvironment class " + strategy +
- " was found, but it does not implement the required interface " + ApplicationEnvironment.class);
- } else {
- try {
- return (ApplicationEnvironment) found.newInstance();
- } catch (InstantiationException | IllegalAccessException e) {
- throw new IllegalStateException("Unable to initialize " + found, e);
- }
- }
- }
-
- Logger LOG = LoggerFactory.getLogger(ApplicationEnvironment.class);
-
- // If nothing explicitly defined in sysprops or env, check ServiceLoader
- Set envs = new HashSet<>();
- ServiceLoader.load(ApplicationEnvironment.class).forEach(envs::add);
- Optional selectedEnv = envs.stream()
- .map(env -> {
- if (LOG.isDebugEnabled())
- LOG.debug("Found ApplicationEnvironment " + env.getClass() + " with priority=" + env.getPriority() + ", available=" + env.isAvailable());
- return env;
- })
- .filter(env -> env.isAvailable())
- .sorted((c1, c2) -> c1.getClass().getCanonicalName().compareTo(c2.getClass().getCanonicalName()))
- .sorted((c1, c2) -> Integer.compare(c2.getPriority(), c1.getPriority()))
- .findFirst();
- return selectedEnv.orElseThrow(() -> new IllegalStateException("No available " + ApplicationEnvironment.class.getSimpleName() + " was discovered."));
- }
-
- /**
- * @param clazz The {@link ApplicationEnvironment} class to check is active
- * @return True if the provided {@link ApplicationEnvironment} is currently active, false otherwise
- */
- public static boolean isSelected(Class extends ApplicationEnvironment> clazz) {
- return load().getClass().equals(clazz);
- }
-
/**
* @return true if the ApplicationEnvironment is currently available
* false otherwise
diff --git a/core/src/main/java/org/microshed/testing/jaxrs/RestClientBuilder.java b/core/src/main/java/org/microshed/testing/jaxrs/RestClientBuilder.java
index 06839746..c6bb39b9 100644
--- a/core/src/main/java/org/microshed/testing/jaxrs/RestClientBuilder.java
+++ b/core/src/main/java/org/microshed/testing/jaxrs/RestClientBuilder.java
@@ -99,7 +99,7 @@ public RestClientBuilder withProviders(Class>... providers) {
public T build(Class clazz) {
// Apply default values if unspecified
if (appContextRoot == null)
- appContextRoot = ApplicationEnvironment.load().getApplicationURL();
+ appContextRoot = ApplicationEnvironment.Resolver.load().getApplicationURL();
if (jaxrsPath == null)
jaxrsPath = locateApplicationPath(clazz);
if (providers == null)
diff --git a/core/src/main/java/org/microshed/testing/jupiter/MicroShedTestExtension.java b/core/src/main/java/org/microshed/testing/jupiter/MicroShedTestExtension.java
index 7e43eb04..31ded9a3 100644
--- a/core/src/main/java/org/microshed/testing/jupiter/MicroShedTestExtension.java
+++ b/core/src/main/java/org/microshed/testing/jupiter/MicroShedTestExtension.java
@@ -31,6 +31,7 @@
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.platform.commons.support.AnnotationSupport;
import org.microshed.testing.ApplicationEnvironment;
+import org.microshed.testing.SharedContainerConfig;
import org.microshed.testing.jaxrs.RESTClient;
import org.microshed.testing.jaxrs.RestClientBuilder;
import org.microshed.testing.jwt.JwtBuilder;
@@ -50,7 +51,13 @@ class MicroShedTestExtension implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
Class> testClass = context.getRequiredTestClass();
- ApplicationEnvironment config = ApplicationEnvironment.load();
+
+ // Explicitly trigger static initialization of any SharedContainerConfig before we do further processing
+ if (testClass.isAnnotationPresent(SharedContainerConfig.class)) {
+ Class.forName(testClass.getAnnotation(SharedContainerConfig.class).value().getName());
+ }
+
+ ApplicationEnvironment config = ApplicationEnvironment.Resolver.load();
LOG.info("Using ApplicationEnvironment class: " + config.getClass().getCanonicalName());
config.applyConfiguration(testClass);
config.start();
diff --git a/modules/testcontainers/src/integrationTest/java/org/microshed/testing/testcontainers/config/TestcontainersConfigurationIT.java b/modules/testcontainers/src/integrationTest/java/org/microshed/testing/testcontainers/config/TestcontainersConfigurationIT.java
index 1cee84d5..42cff4e3 100644
--- a/modules/testcontainers/src/integrationTest/java/org/microshed/testing/testcontainers/config/TestcontainersConfigurationIT.java
+++ b/modules/testcontainers/src/integrationTest/java/org/microshed/testing/testcontainers/config/TestcontainersConfigurationIT.java
@@ -53,8 +53,8 @@ public class TestcontainersConfigurationIT {
@Test
public void testCorrectEnvironment() {
- assertEquals(TestcontainersConfiguration.class, ApplicationEnvironment.load().getClass());
- assertTrue(ApplicationEnvironment.isSelected(TestcontainersConfiguration.class),
+ assertEquals(TestcontainersConfiguration.class, ApplicationEnvironment.Resolver.load().getClass());
+ assertTrue(ApplicationEnvironment.Resolver.isSelected(TestcontainersConfiguration.class),
"Expected TestcontainersConfiguration to be selected but it was not");
}
@@ -66,7 +66,7 @@ public void testExposedPort() {
@Test
public void testApplicationURL() {
- String appUrl = ApplicationEnvironment.load().getApplicationURL();
+ String appUrl = ApplicationEnvironment.Resolver.load().getApplicationURL();
assertNotNull(appUrl);
assertEquals(appUrl, app.getApplicationURL());
assertTrue(appUrl.startsWith("http://"), "Application URL did not start with 'http://' " + appUrl);
diff --git a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java
index 19fe81a9..412142f6 100644
--- a/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java
+++ b/modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java
@@ -118,7 +118,7 @@ private static Future resolveImage(Optional dockerfile) {
}
private static boolean isHollow() {
- ApplicationEnvironment current = ApplicationEnvironment.load();
+ ApplicationEnvironment current = ApplicationEnvironment.Resolver.load();
return !(current instanceof TestcontainersConfiguration) ||
current instanceof HollowTestcontainersConfiguration;
}
@@ -429,7 +429,7 @@ private String readMpRestClientConfigKey(Class> restClientClass) {
public ApplicationContainer withMpRestClient(String restClientClass, String hostUrl) {
// If we will be running in Docker, sanitize environment variable name using Environment Variables Mapping Rules defined in MP Config:
// https://github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc#environment-variables-mapping-rules
- if (ApplicationEnvironment.isSelected(TestcontainersConfiguration.class)) {
+ if (ApplicationEnvironment.Resolver.isSelected(TestcontainersConfiguration.class)) {
restClientClass = restClientClass.replaceAll("[^a-zA-Z0-9_]", "_") + "_mp_rest_url";
} else {
restClientClass += "/mp-rest/url";
diff --git a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java
index 9136262f..5020da3d 100644
--- a/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java
+++ b/modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java
@@ -54,8 +54,8 @@ public class HollowTestcontainersConfigurationTest {
@Test
public void testCorrectEnvironment() {
- assertEquals(HollowTestcontainersConfiguration.class, ApplicationEnvironment.load().getClass());
- assertTrue(ApplicationEnvironment.isSelected(HollowTestcontainersConfiguration.class),
+ assertEquals(HollowTestcontainersConfiguration.class, ApplicationEnvironment.Resolver.load().getClass());
+ assertTrue(ApplicationEnvironment.Resolver.isSelected(HollowTestcontainersConfiguration.class),
"Expected HollowTestcontainersConfiguration to be selected but it was not");
assertTrue(HollowTestcontainersConfiguration.available(),
"Expected HollowTestcontainersConfiguration to be available but it was not");
@@ -88,7 +88,7 @@ public void testEnvVarUnchanged() {
@Test
public void testApplicationURL() {
- assertEquals("http://localhost:9080", ApplicationEnvironment.load().getApplicationURL());
+ assertEquals("http://localhost:9080", ApplicationEnvironment.Resolver.load().getApplicationURL());
}
}
\ No newline at end of file