Skip to content

Commit f22dcfd

Browse files
authored
Merge pull request #144 from aguibert/appenv-loading
Explicitly trigger static init of SharedContainerConfig early in extension lifecycle
2 parents af0018a + fe3e428 commit f22dcfd

6 files changed

Lines changed: 90 additions & 70 deletions

File tree

core/src/main/java/org/microshed/testing/ApplicationEnvironment.java

Lines changed: 73 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,78 @@
3737
*/
3838
public interface ApplicationEnvironment {
3939

40+
public static class Resolver {
41+
private static ApplicationEnvironment loaded = null;
42+
43+
private Resolver() {
44+
// static singleton
45+
}
46+
47+
/**
48+
* @return The selected {@link ApplicationEnvironment}. The selection is made using the following criteria:
49+
* <ol>
50+
* <li>If the {@link #ENV_CLASS} system property or environment variable is set, the class is used</li>
51+
* <li>The {@link ServiceLoader} is used to load all {@link ApplicationEnvironment} instances, which are filtered
52+
* based on {@link #isAvailable()} and then sorted based on {@link #getPriority()} where higher numbers are chosen
53+
* first.</li>
54+
* </ol>
55+
*/
56+
public static ApplicationEnvironment load() {
57+
if (loaded != null)
58+
return loaded;
59+
60+
// First check explicilty configured environment via system property or env var
61+
String strategy = System.getProperty(ENV_CLASS);
62+
if (strategy == null || strategy.isEmpty())
63+
strategy = System.getenv(ENV_CLASS);
64+
if (strategy != null && !strategy.isEmpty()) {
65+
Class<?> found;
66+
try {
67+
found = Class.forName(strategy);
68+
} catch (ClassNotFoundException e) {
69+
throw new IllegalStateException("Unable to load the selected ApplicationEnvironment class: " + strategy, e);
70+
}
71+
if (!ApplicationEnvironment.class.isAssignableFrom(found)) {
72+
throw new IllegalStateException("ApplicationEnvironment class " + strategy +
73+
" was found, but it does not implement the required interface " + ApplicationEnvironment.class);
74+
} else {
75+
try {
76+
loaded = (ApplicationEnvironment) found.newInstance();
77+
return loaded;
78+
} catch (InstantiationException | IllegalAccessException e) {
79+
throw new IllegalStateException("Unable to initialize " + found, e);
80+
}
81+
}
82+
}
83+
84+
Logger LOG = LoggerFactory.getLogger(ApplicationEnvironment.class);
85+
86+
// If nothing explicitly defined in sysprops or env, check ServiceLoader
87+
Set<ApplicationEnvironment> envs = new HashSet<>();
88+
ServiceLoader.load(ApplicationEnvironment.class).forEach(envs::add);
89+
Optional<ApplicationEnvironment> selectedEnv = envs.stream()
90+
.map(env -> {
91+
if (LOG.isDebugEnabled())
92+
LOG.debug("Found ApplicationEnvironment " + env.getClass() + " with priority=" + env.getPriority() + ", available=" + env.isAvailable());
93+
return env;
94+
})
95+
.filter(env -> env.isAvailable())
96+
.sorted((c1, c2) -> c1.getClass().getCanonicalName().compareTo(c2.getClass().getCanonicalName()))
97+
.sorted((c1, c2) -> Integer.compare(c2.getPriority(), c1.getPriority()))
98+
.findFirst();
99+
loaded = selectedEnv.orElseThrow(() -> new IllegalStateException("No available " + ApplicationEnvironment.class.getSimpleName() + " was discovered."));
100+
return loaded;
101+
}
102+
103+
/**
104+
* @param clazz The {@link ApplicationEnvironment} class to check is active
105+
* @return True if the provided {@link ApplicationEnvironment} is currently active, false otherwise
106+
*/
107+
public static boolean isSelected(Class<? extends ApplicationEnvironment> clazz) {
108+
return load().getClass().getCanonicalName().equals(clazz.getCanonicalName());
109+
}
110+
}
111+
40112
/**
41113
* The default priority returned by an implementation of {@link ApplicationEnvironment#isAvailable}
42114
* In general, built-in ApplicationEnvironment implementations have a priority less than the default
@@ -47,69 +119,10 @@ public interface ApplicationEnvironment {
47119
/**
48120
* The name of the system property or environment variable that indicates a specific {@link ApplicationEnvironment}
49121
* to use. If this property is set, it will be used regardless of the priority or availability. If this property is
50-
* NOT set, the normal resolution rules will be applied as defined in {@link #load()}
122+
* NOT set, the normal resolution rules will be applied as defined in {@link ApplicationEnvironment.Resolver#load()}
51123
*/
52124
public static final String ENV_CLASS = "MICROSHED_TEST_ENV_CLASS";
53125

54-
/**
55-
* @return The selected {@link ApplicationEnvironment}. The selection is made using the following criteria:
56-
* <ol>
57-
* <li>If the {@link #ENV_CLASS} system property or environment variable is set, the class is used</li>
58-
* <li>The {@link ServiceLoader} is used to load all {@link ApplicationEnvironment} instances, which are filtered
59-
* based on {@link #isAvailable()} and then sorted based on {@link #getPriority()} where higher numbers are chosen
60-
* first.</li>
61-
* </ol>
62-
*/
63-
public static ApplicationEnvironment load() {
64-
// First check explicilty configured environment via system property or env var
65-
String strategy = System.getProperty(ENV_CLASS);
66-
if (strategy == null || strategy.isEmpty())
67-
strategy = System.getenv(ENV_CLASS);
68-
if (strategy != null && !strategy.isEmpty()) {
69-
Class<?> found;
70-
try {
71-
found = Class.forName(strategy);
72-
} catch (ClassNotFoundException e) {
73-
throw new IllegalStateException("Unable to load the selected ApplicationEnvironment class: " + strategy, e);
74-
}
75-
if (!ApplicationEnvironment.class.isAssignableFrom(found)) {
76-
throw new IllegalStateException("ApplicationEnvironment class " + strategy +
77-
" was found, but it does not implement the required interface " + ApplicationEnvironment.class);
78-
} else {
79-
try {
80-
return (ApplicationEnvironment) found.newInstance();
81-
} catch (InstantiationException | IllegalAccessException e) {
82-
throw new IllegalStateException("Unable to initialize " + found, e);
83-
}
84-
}
85-
}
86-
87-
Logger LOG = LoggerFactory.getLogger(ApplicationEnvironment.class);
88-
89-
// If nothing explicitly defined in sysprops or env, check ServiceLoader
90-
Set<ApplicationEnvironment> envs = new HashSet<>();
91-
ServiceLoader.load(ApplicationEnvironment.class).forEach(envs::add);
92-
Optional<ApplicationEnvironment> selectedEnv = envs.stream()
93-
.map(env -> {
94-
if (LOG.isDebugEnabled())
95-
LOG.debug("Found ApplicationEnvironment " + env.getClass() + " with priority=" + env.getPriority() + ", available=" + env.isAvailable());
96-
return env;
97-
})
98-
.filter(env -> env.isAvailable())
99-
.sorted((c1, c2) -> c1.getClass().getCanonicalName().compareTo(c2.getClass().getCanonicalName()))
100-
.sorted((c1, c2) -> Integer.compare(c2.getPriority(), c1.getPriority()))
101-
.findFirst();
102-
return selectedEnv.orElseThrow(() -> new IllegalStateException("No available " + ApplicationEnvironment.class.getSimpleName() + " was discovered."));
103-
}
104-
105-
/**
106-
* @param clazz The {@link ApplicationEnvironment} class to check is active
107-
* @return True if the provided {@link ApplicationEnvironment} is currently active, false otherwise
108-
*/
109-
public static boolean isSelected(Class<? extends ApplicationEnvironment> clazz) {
110-
return load().getClass().equals(clazz);
111-
}
112-
113126
/**
114127
* @return true if the ApplicationEnvironment is currently available
115128
* false otherwise

core/src/main/java/org/microshed/testing/jaxrs/RestClientBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public RestClientBuilder withProviders(Class<?>... providers) {
9999
public <T> T build(Class<T> clazz) {
100100
// Apply default values if unspecified
101101
if (appContextRoot == null)
102-
appContextRoot = ApplicationEnvironment.load().getApplicationURL();
102+
appContextRoot = ApplicationEnvironment.Resolver.load().getApplicationURL();
103103
if (jaxrsPath == null)
104104
jaxrsPath = locateApplicationPath(clazz);
105105
if (providers == null)

core/src/main/java/org/microshed/testing/jupiter/MicroShedTestExtension.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.junit.jupiter.api.extension.ExtensionContext;
3232
import org.junit.platform.commons.support.AnnotationSupport;
3333
import org.microshed.testing.ApplicationEnvironment;
34+
import org.microshed.testing.SharedContainerConfig;
3435
import org.microshed.testing.jaxrs.RESTClient;
3536
import org.microshed.testing.jaxrs.RestClientBuilder;
3637
import org.microshed.testing.jwt.JwtBuilder;
@@ -50,7 +51,13 @@ class MicroShedTestExtension implements BeforeAllCallback {
5051
@Override
5152
public void beforeAll(ExtensionContext context) throws Exception {
5253
Class<?> testClass = context.getRequiredTestClass();
53-
ApplicationEnvironment config = ApplicationEnvironment.load();
54+
55+
// Explicitly trigger static initialization of any SharedContainerConfig before we do further processing
56+
if (testClass.isAnnotationPresent(SharedContainerConfig.class)) {
57+
Class.forName(testClass.getAnnotation(SharedContainerConfig.class).value().getName());
58+
}
59+
60+
ApplicationEnvironment config = ApplicationEnvironment.Resolver.load();
5461
LOG.info("Using ApplicationEnvironment class: " + config.getClass().getCanonicalName());
5562
config.applyConfiguration(testClass);
5663
config.start();

modules/testcontainers/src/integrationTest/java/org/microshed/testing/testcontainers/config/TestcontainersConfigurationIT.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ public class TestcontainersConfigurationIT {
5353

5454
@Test
5555
public void testCorrectEnvironment() {
56-
assertEquals(TestcontainersConfiguration.class, ApplicationEnvironment.load().getClass());
57-
assertTrue(ApplicationEnvironment.isSelected(TestcontainersConfiguration.class),
56+
assertEquals(TestcontainersConfiguration.class, ApplicationEnvironment.Resolver.load().getClass());
57+
assertTrue(ApplicationEnvironment.Resolver.isSelected(TestcontainersConfiguration.class),
5858
"Expected TestcontainersConfiguration to be selected but it was not");
5959
}
6060

@@ -66,7 +66,7 @@ public void testExposedPort() {
6666

6767
@Test
6868
public void testApplicationURL() {
69-
String appUrl = ApplicationEnvironment.load().getApplicationURL();
69+
String appUrl = ApplicationEnvironment.Resolver.load().getApplicationURL();
7070
assertNotNull(appUrl);
7171
assertEquals(appUrl, app.getApplicationURL());
7272
assertTrue(appUrl.startsWith("http://"), "Application URL did not start with 'http://' " + appUrl);

modules/testcontainers/src/main/java/org/microshed/testing/testcontainers/ApplicationContainer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private static Future<String> resolveImage(Optional<Path> dockerfile) {
118118
}
119119

120120
private static boolean isHollow() {
121-
ApplicationEnvironment current = ApplicationEnvironment.load();
121+
ApplicationEnvironment current = ApplicationEnvironment.Resolver.load();
122122
return !(current instanceof TestcontainersConfiguration) ||
123123
current instanceof HollowTestcontainersConfiguration;
124124
}
@@ -429,7 +429,7 @@ private String readMpRestClientConfigKey(Class<?> restClientClass) {
429429
public ApplicationContainer withMpRestClient(String restClientClass, String hostUrl) {
430430
// If we will be running in Docker, sanitize environment variable name using Environment Variables Mapping Rules defined in MP Config:
431431
// https://github.com/eclipse/microprofile-config/blob/master/spec/src/main/asciidoc/configsources.asciidoc#environment-variables-mapping-rules
432-
if (ApplicationEnvironment.isSelected(TestcontainersConfiguration.class)) {
432+
if (ApplicationEnvironment.Resolver.isSelected(TestcontainersConfiguration.class)) {
433433
restClientClass = restClientClass.replaceAll("[^a-zA-Z0-9_]", "_") + "_mp_rest_url";
434434
} else {
435435
restClientClass += "/mp-rest/url";

modules/testcontainers/src/test/java/org/microshed/testing/testcontainers/config/HollowTestcontainersConfigurationTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ public class HollowTestcontainersConfigurationTest {
5454

5555
@Test
5656
public void testCorrectEnvironment() {
57-
assertEquals(HollowTestcontainersConfiguration.class, ApplicationEnvironment.load().getClass());
58-
assertTrue(ApplicationEnvironment.isSelected(HollowTestcontainersConfiguration.class),
57+
assertEquals(HollowTestcontainersConfiguration.class, ApplicationEnvironment.Resolver.load().getClass());
58+
assertTrue(ApplicationEnvironment.Resolver.isSelected(HollowTestcontainersConfiguration.class),
5959
"Expected HollowTestcontainersConfiguration to be selected but it was not");
6060
assertTrue(HollowTestcontainersConfiguration.available(),
6161
"Expected HollowTestcontainersConfiguration to be available but it was not");
@@ -88,7 +88,7 @@ public void testEnvVarUnchanged() {
8888

8989
@Test
9090
public void testApplicationURL() {
91-
assertEquals("http://localhost:9080", ApplicationEnvironment.load().getApplicationURL());
91+
assertEquals("http://localhost:9080", ApplicationEnvironment.Resolver.load().getApplicationURL());
9292
}
9393

9494
}

0 commit comments

Comments
 (0)