From ddc2f06fa1e2cd1c7e5ac3600160edce315ab55c Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Wed, 22 Mar 2023 16:45:51 +0100 Subject: [PATCH] Support for Spring Modulith dependencies. This commit adds support to add Spring Modulith to a project. We add the Event Publication Registry matching the (optionally) also selected persistence flavor (JPA, JDBC, MongoDB). Also, the observability support in case any of the supported observability backends is selected as dependency. --- .../SpringModulithBuildCustomizer.java | 101 +++++++++++++++ ...odulithProjectGenerationConfiguration.java | 36 ++++++ .../springmodulith/package-info.java | 16 +++ .../main/resources/META-INF/spring.factories | 1 + start-site/src/main/resources/application.yml | 22 ++++ .../SpringModulithBuildCustomizerTests.java | 117 ++++++++++++++++++ 6 files changed, 293 insertions(+) create mode 100644 start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizer.java create mode 100644 start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithProjectGenerationConfiguration.java create mode 100644 start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/package-info.java create mode 100644 start-site/src/test/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizerTests.java diff --git a/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizer.java b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizer.java new file mode 100644 index 00000000000..79957683ffc --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizer.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012-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 io.spring.start.site.extension.dependency.springmodulith; + +import io.spring.initializr.generator.buildsystem.Build; +import io.spring.initializr.generator.buildsystem.Dependency; +import io.spring.initializr.generator.buildsystem.Dependency.Builder; +import io.spring.initializr.generator.buildsystem.DependencyContainer; +import io.spring.initializr.generator.buildsystem.DependencyScope; +import io.spring.initializr.generator.spring.build.BuildCustomizer; + +import java.util.Collection; +import java.util.List; + +/** + * Registers Spring Modulith dependencies to the build file of the project to be created, in particular registering + * integration dependencies depending on others registered for inclusion as well (observability and persistence). + * + * @author Oliver Drotbohm + */ +class SpringModulithBuildCustomizer implements BuildCustomizer { + + static final String GROUP_ID = "org.springframework.experimental"; + static final String BOM_ARTIFACT_ID = "spring-modulith-bom"; + + static final Collection OBSERVABILITY_DEPENDENCIES = List.of("datadog", "graphite", "influx", "new-relic", + "prometheus", "wavefront", "zipkin"); + + private static final String ARTIFACT_PREFIX = "spring-modulith-"; + + private static final Builder ACTUATOR = Dependency.withCoordinates(GROUP_ID, ARTIFACT_PREFIX + "actuator") + .scope(DependencyScope.RUNTIME); + + private static final Builder OBSERVABILITY = Dependency + .withCoordinates(GROUP_ID, ARTIFACT_PREFIX + "observability") + .scope(DependencyScope.RUNTIME); + + private static final Builder STARTER_JDBC = Dependency.withCoordinates(GROUP_ID, + ARTIFACT_PREFIX + "starter-jdbc"); + + private static final Builder STARTER_JPA = Dependency.withCoordinates(GROUP_ID, ARTIFACT_PREFIX + "starter-jpa"); + + private static final Builder STARTER_MONGODB = Dependency.withCoordinates(GROUP_ID, + ARTIFACT_PREFIX + "starter-mongodb"); + + private static final Builder TEST = Dependency.withCoordinates(GROUP_ID, ARTIFACT_PREFIX + "starter-test") + .scope(DependencyScope.TEST_COMPILE); + + @Override + public void customize(Build build) { + DependencyContainer dependencies = build.dependencies(); + + // Actuator + + if (dependencies.has("actuator")) { + dependencies.add("modulith-actuator", ACTUATOR); + } + + // Observability + + if (OBSERVABILITY_DEPENDENCIES.stream().anyMatch(dependencies::has)) { + dependencies.add("modulith-observability", OBSERVABILITY); + } + + // Event publication registry support + + addEventPublicationRegistryBackend(build); + + dependencies.add("modulith-starter-test", TEST); + } + + private void addEventPublicationRegistryBackend(Build build) { + + var dependencies = build.dependencies(); + + if (dependencies.has("data-mongodb")) { + dependencies.add("modulith-starter-mongodb", STARTER_MONGODB); + } + + if (dependencies.has("data-jdbc")) { + dependencies.add("modulith-starter-jdbc", STARTER_JDBC); + } + + if (dependencies.has("data-jpa")) { + dependencies.add("modulith-starter-jpa", STARTER_JPA); + } + } +} diff --git a/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithProjectGenerationConfiguration.java b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithProjectGenerationConfiguration.java new file mode 100644 index 00000000000..ef385e1dede --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithProjectGenerationConfiguration.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-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 io.spring.start.site.extension.dependency.springmodulith; + +import io.spring.initializr.generator.condition.ConditionalOnRequestedDependency; +import io.spring.initializr.generator.project.ProjectGenerationConfiguration; + +import org.springframework.context.annotation.Bean; + +/** + * Registers {@link SpringModulithBuildCustomizer} with the context to allow selecting Spring Modulith dependencies. + * + * @author Oliver Drotbohm + */ +@ProjectGenerationConfiguration +@ConditionalOnRequestedDependency("modulith") +class SpringModulithProjectGenerationConfiguration { + + @Bean + SpringModulithBuildCustomizer springModulithBuildCustomizer() { + return new SpringModulithBuildCustomizer(); + } +} diff --git a/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/package-info.java b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/package-info.java new file mode 100644 index 00000000000..fc162668824 --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/package-info.java @@ -0,0 +1,16 @@ +/* + * Copyright 2012-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 io.spring.start.site.extension.dependency.springmodulith; diff --git a/start-site/src/main/resources/META-INF/spring.factories b/start-site/src/main/resources/META-INF/spring.factories index cf085aea047..5b483db2a06 100644 --- a/start-site/src/main/resources/META-INF/spring.factories +++ b/start-site/src/main/resources/META-INF/spring.factories @@ -25,6 +25,7 @@ io.spring.start.site.extension.dependency.springboot.SpringBootProjectGeneration io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springintegration.SpringIntegrationProjectGenerationConfiguration,\ +io.spring.start.site.extension.dependency.springmodulith.SpringModulithProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springpulsar.SpringPulsarProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springrestdocs.SpringRestDocsProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.sqlserver.SqlServerProjectGenerationConfiguration,\ diff --git a/start-site/src/main/resources/application.yml b/start-site/src/main/resources/application.yml index 8b8f0ccf9cb..526a8e4be1e 100644 --- a/start-site/src/main/resources/application.yml +++ b/start-site/src/main/resources/application.yml @@ -139,6 +139,14 @@ initializr: version: 3.5.0 - compatibilityRange: "[3.0.0,3.1.0-M1)" version: 4.0.0 + spring-modulith: + groupId: org.springframework.modulith + artifactId: spring-modulith-bom + version: 1.0.0-M1 + mappings: + - compatibility-range: "[3.0.0,3.2.0-M1)" + version: 1.0.0-M1 + repositories: spring-milestones spring-shell: groupId: org.springframework.shell artifactId: spring-shell-dependencies @@ -199,12 +207,26 @@ initializr: spring-releases: name: Spring Releases url: https://repo.spring.io/release + spring-milestones: + name: Spring Milestones + url: https://repo.spring.io/milestone sonatype-snapshots: name: Sonatype Snapshots url: https://oss.sonatype.org/content/repositories/snapshots/ releasesEnabled: false snapshotsEnabled: true dependencies: + - name: Architecture + content: + - name: Spring Modulith + id: modulith + group-id: org.springframework.experimental + artifact-id: spring-modulith-starter-core + description: Support for building modular monolithic applications. + bom: spring-modulith + links: + - rel: reference + href: https://docs.spring.io/spring-modulith/docs/current/reference/html/ - name: Developer Tools content: - name: GraalVM Native Support diff --git a/start-site/src/test/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizerTests.java b/start-site/src/test/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizerTests.java new file mode 100644 index 00000000000..b474a0774bf --- /dev/null +++ b/start-site/src/test/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizerTests.java @@ -0,0 +1,117 @@ +/* + * Copyright 2012-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 io.spring.start.site.extension.dependency.springmodulith; + +import static io.spring.start.site.extension.dependency.springmodulith.SpringModulithBuildCustomizer.*; +import static org.assertj.core.api.Assertions.*; + +import io.spring.initializr.generator.buildsystem.Build; +import io.spring.initializr.generator.buildsystem.maven.MavenBuild; +import io.spring.initializr.generator.version.Version; +import io.spring.initializr.metadata.InitializrMetadata; +import io.spring.initializr.metadata.support.MetadataBuildItemResolver; +import io.spring.start.site.extension.AbstractExtensionTests; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.Named; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +/** + * Tests for {@link SpringModulithBuildCustomizer}. + * + * @author Oliver Drotbohm + */ +class SpringModulithBuildCustomizerTests extends AbstractExtensionTests { + + private final SpringModulithBuildCustomizer customizer = new SpringModulithBuildCustomizer(); + + @Test + void registersCoreStarterByDefault() { + + Build build = createBuild(); + customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith"); + } + + @Test + void registersTestStarterByDefault() { + + Build build = createBuild(); + customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-starter-test"); + } + + @Test + void registersActuatorStarterIfActuatorsIsPresent() { + + Build build = createBuild(); + build.dependencies().add("actuator"); + + customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-actuator"); + } + + @TestFactory + Stream registersObservabilityStarterIfObservabilityDependencyIsPresent() { + + Stream> namedDependencies = OBSERVABILITY_DEPENDENCIES.stream().map(it -> Named.of(it, it)); + + return DynamicTest.stream(namedDependencies, it -> { + + Build build = createBuild(); + build.dependencies().add(it); + + customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-observability"); + }); + } + + @ParameterizedTest + @ValueSource(strings = { "jdbc", "jpa", "mongodb" }) + void presenceOfSpringDataModuleAddsModuleEventStarter(String store) { + + Build build = createBuild(); + build.dependencies().add("data-" + store); + + customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-starter-" + store); + assertThat(build.boms().has("spring-modulith")); + } + + private Build createBuild() { + + InitializrMetadata metadata = getMetadata(); + + Build build = new MavenBuild(new MetadataBuildItemResolver(metadata, getDefaultPlatformVersion(metadata))); + build.dependencies().add("modulith"); + + return build; + } + + private static Version getDefaultPlatformVersion(InitializrMetadata metadata) { + return Version.parse(metadata.getBootVersions().getDefault().getId()); + } +}