From 1bd6a46be546dcab9266946236d329a07fcd0e55 Mon Sep 17 00:00:00 2001 From: Ivan Straka Date: Fri, 20 Jan 2023 14:50:33 +0100 Subject: [PATCH] [native clouds] add support for setup task --- .../java/sunstone/api/AbstractSetupTask.java | 7 +++ .../src/main/java/sunstone/api/Setup.java | 24 ++++++++++ .../java/sunstone/core/SunstoneExtension.java | 31 +++++++++++++ .../core/di/AbstractStaticDITask.java | 18 ++++++++ .../sunstone/core/di/SetupTaskDITest.java | 23 ++++++++++ .../java/sunstone/core/di/StaticDITask.java | 44 +++++++++++++++++++ .../java/sunstone/core/setup/SetupSuite.java | 11 +++++ .../setup/suitetests/RegularClassTask.java | 24 ++++++++++ .../suitetests/SetupCleanupSecondTest.java | 21 +++++++++ .../core/setup/suitetests/SetupFirstTest.java | 38 ++++++++++++++++ 10 files changed, 241 insertions(+) create mode 100644 clouds/clouds-core/src/main/java/sunstone/api/AbstractSetupTask.java create mode 100644 clouds/clouds-core/src/main/java/sunstone/api/Setup.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/di/AbstractStaticDITask.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/di/SetupTaskDITest.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/di/StaticDITask.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/setup/SetupSuite.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/RegularClassTask.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupCleanupSecondTest.java create mode 100644 clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupFirstTest.java diff --git a/clouds/clouds-core/src/main/java/sunstone/api/AbstractSetupTask.java b/clouds/clouds-core/src/main/java/sunstone/api/AbstractSetupTask.java new file mode 100644 index 00000000..ed944a43 --- /dev/null +++ b/clouds/clouds-core/src/main/java/sunstone/api/AbstractSetupTask.java @@ -0,0 +1,7 @@ +package sunstone.api; + + +public abstract class AbstractSetupTask { + public abstract void setup() throws Exception; + public abstract void cleanup() throws Exception; +} diff --git a/clouds/clouds-core/src/main/java/sunstone/api/Setup.java b/clouds/clouds-core/src/main/java/sunstone/api/Setup.java new file mode 100644 index 00000000..a784961a --- /dev/null +++ b/clouds/clouds-core/src/main/java/sunstone/api/Setup.java @@ -0,0 +1,24 @@ +package sunstone.api; + +import org.junit.jupiter.api.extension.ExtendWith; +import sunstone.core.SunstoneExtension; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Define {@link AbstractSetupTask} that configure environment right after Cloud resources are deployed and before. + * + * The class may inject static/non-static resources using annotations annotated by {@link SunstoneInjectionAnnotation} + * that are brought bu modules like sunstone-clouds-aws or sunstone-clouds-azure. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE}) +@ExtendWith(SunstoneExtension.class) +@Inherited +public @interface Setup { + Class [] value(); +} diff --git a/clouds/clouds-core/src/main/java/sunstone/core/SunstoneExtension.java b/clouds/clouds-core/src/main/java/sunstone/core/SunstoneExtension.java index f75ea5a7..1b340e70 100644 --- a/clouds/clouds-core/src/main/java/sunstone/core/SunstoneExtension.java +++ b/clouds/clouds-core/src/main/java/sunstone/core/SunstoneExtension.java @@ -10,6 +10,8 @@ import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.support.HierarchyTraversalMode; import org.wildfly.extras.sunstone.api.impl.ObjectProperties; +import sunstone.api.AbstractSetupTask; +import sunstone.api.Setup; import sunstone.api.SunstoneInjectionAnnotation; import sunstone.api.SunstoneArchiveDeployTargetAnotation; import sunstone.api.Deployment; @@ -31,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -69,10 +72,38 @@ public void beforeAll(ExtensionContext ctx) throws Exception { if (ctx.getRequiredTestClass().getAnnotationsByType(WithAwsCfTemplate.class).length > 0) { handleAwsCloudFormationAnnotations(ctx); } + if (ctx.getRequiredTestClass().getAnnotationsByType(Setup.class).length > 0) { + handleSetup(ctx); + } performDeploymentOperation(ctx); injectStaticResources(ctx, ctx.getRequiredTestClass()); } + static void handleSetup(ExtensionContext ctx) throws IllegalArgumentSunstoneException { + SunstoneStore store = new SunstoneStore(ctx); + if (ctx.getRequiredTestClass().getAnnotationsByType(Setup.class).length != 1) { + throw new IllegalArgumentSunstoneException("Only one setup task may be defined."); + } + Setup setup = ctx.getRequiredTestClass().getAnnotationsByType(Setup.class)[0]; + for (Class setupTask : setup.value()) { + injectStaticResources(ctx, setupTask); + Optional> constructor = Arrays.stream(setupTask.getDeclaredConstructors()) + .filter(c -> c.getParameterCount() == 0) + .findAny(); + constructor.orElseThrow(() -> new IllegalArgumentSunstoneException("Setup task must have a constructor with 0 parameters")); + constructor.get().setAccessible(true); + AbstractSetupTask abstractSetupTask = null; + try { + abstractSetupTask = (AbstractSetupTask) constructor.get().newInstance(); + injectInstanceResources(ctx, abstractSetupTask); + store.addClosable((AutoCloseable) abstractSetupTask::cleanup); + abstractSetupTask.setup(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + static void handleAwsCloudFormationAnnotations(ExtensionContext ctx) { Optional deployer = getDeployer(WithAwsCfTemplate.class); deployer.orElseThrow(() -> new RuntimeException("Unable to load a service via SPI that handles " + WithAwsCfTemplate.class.getName() + " annotation.")); diff --git a/clouds/clouds-core/src/test/java/sunstone/core/di/AbstractStaticDITask.java b/clouds/clouds-core/src/test/java/sunstone/core/di/AbstractStaticDITask.java new file mode 100644 index 00000000..4ddd3691 --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/di/AbstractStaticDITask.java @@ -0,0 +1,18 @@ +package sunstone.core.di; + + +import sunstone.api.AbstractSetupTask; + +abstract class AbstractStaticDITask extends AbstractSetupTask { + @DirectlyAnnotatedInject + static String directStaticInjectInAbstract; + + @DirectlyAnnotatedInject + String directNonStaticInjectInAbstract; + + @IndirectlyAnnotatedInject + static String indirectStaticInjectInAbstract; + + @IndirectlyAnnotatedInject + String indirectNonStaticInjectInAbstract; +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/di/SetupTaskDITest.java b/clouds/clouds-core/src/test/java/sunstone/core/di/SetupTaskDITest.java new file mode 100644 index 00000000..04fbc51e --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/di/SetupTaskDITest.java @@ -0,0 +1,23 @@ +package sunstone.core.di; + + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import sunstone.api.Setup; + +@Setup({StaticDITask.class}) +public class SetupTaskDITest { + + @AfterAll + public static void reset() { + StaticDITask.reset(); + TestSunstoneResourceInjector.reset(); + } + + @Test + public void test() { + Assertions.assertThat(StaticDITask.setupCalled).isTrue(); + } + +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/di/StaticDITask.java b/clouds/clouds-core/src/test/java/sunstone/core/di/StaticDITask.java new file mode 100644 index 00000000..9a6e6cd1 --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/di/StaticDITask.java @@ -0,0 +1,44 @@ +package sunstone.core.di; + + +import static org.assertj.core.api.Assertions.assertThat; + +class StaticDITask extends AbstractStaticDITask { + + @DirectlyAnnotatedInject + static String directStaticInject; + + @DirectlyAnnotatedInject + String directNonStaticInject; + + @IndirectlyAnnotatedInject + static String indirectStaticInject; + + @IndirectlyAnnotatedInject + String indirectNonStaticInject; + static boolean setupCalled = false; + static boolean cleanupCalled = false; + + @Override + public void setup() throws Exception { + setupCalled = true; + } + + @Override + public void cleanup() throws Exception { + assertThat(directStaticInject).isEqualTo("set"); + assertThat(directStaticInjectInAbstract).isEqualTo("set"); + assertThat(indirectStaticInject).isEqualTo("set"); + assertThat(indirectStaticInjectInAbstract).isEqualTo("set"); + assertThat(directNonStaticInject).isEqualTo("set"); + assertThat(directNonStaticInjectInAbstract).isEqualTo("set"); + assertThat(indirectNonStaticInject).isEqualTo("set"); + assertThat(indirectNonStaticInjectInAbstract).isEqualTo("set"); + cleanupCalled = true; + } + + public static void reset() { + setupCalled = false; + cleanupCalled = false; + } +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/setup/SetupSuite.java b/clouds/clouds-core/src/test/java/sunstone/core/setup/SetupSuite.java new file mode 100644 index 00000000..b78fda1d --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/setup/SetupSuite.java @@ -0,0 +1,11 @@ +package sunstone.core.setup; + +import org.junit.platform.suite.api.SelectClasses; +import org.junit.platform.suite.api.Suite; +import sunstone.core.setup.suitetests.SetupCleanupSecondTest; +import sunstone.core.setup.suitetests.SetupFirstTest; + +@Suite +@SelectClasses({SetupFirstTest.class, SetupCleanupSecondTest.class}) +public class SetupSuite { +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/RegularClassTask.java b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/RegularClassTask.java new file mode 100644 index 00000000..e1a6c805 --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/RegularClassTask.java @@ -0,0 +1,24 @@ +package sunstone.core.setup.suitetests; + + +import sunstone.api.AbstractSetupTask; + +class RegularClassTask extends AbstractSetupTask { + static boolean setupCalled = false; + static boolean cleanupCalled = false; + + @Override + public void setup() throws Exception { + setupCalled = true; + } + + @Override + public void cleanup() throws Exception { + cleanupCalled = true; + } + + public static void reset() { + setupCalled = false; + cleanupCalled = false; + } +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupCleanupSecondTest.java b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupCleanupSecondTest.java new file mode 100644 index 00000000..99900457 --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupCleanupSecondTest.java @@ -0,0 +1,21 @@ +package sunstone.core.setup.suitetests; + + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; + +public class SetupCleanupSecondTest { + + @Test + public void test() { + Assertions.assertThat(SetupFirstTest.StaticClassTask.cleanupCalled).isTrue(); + Assertions.assertThat(RegularClassTask.cleanupCalled).isTrue(); + } + + @AfterAll + public static void reset() { + RegularClassTask.reset(); + SetupFirstTest.StaticClassTask.reset(); + } +} diff --git a/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupFirstTest.java b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupFirstTest.java new file mode 100644 index 00000000..26cc7dd1 --- /dev/null +++ b/clouds/clouds-core/src/test/java/sunstone/core/setup/suitetests/SetupFirstTest.java @@ -0,0 +1,38 @@ +package sunstone.core.setup.suitetests; + + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import sunstone.api.AbstractSetupTask; +import sunstone.api.Setup; + +@Setup({SetupFirstTest.StaticClassTask.class, RegularClassTask.class}) +public class SetupFirstTest { + + @Test + public void test() { + Assertions.assertThat(StaticClassTask.setupCalled).isTrue(); + Assertions.assertThat(RegularClassTask.setupCalled).isTrue(); + } + + static class StaticClassTask extends AbstractSetupTask { + static boolean setupCalled = false; + static boolean cleanupCalled = false; + + @Override + public void setup() throws Exception { + setupCalled = true; + } + + @Override + public void cleanup() throws Exception { + cleanupCalled = true; + } + + public static void reset() { + setupCalled = false; + cleanupCalled = false; + } + } + +}