From 5df35cfb23c5ee17b5b7c767dcc39b184da11857 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 | 104 +++++++++++++++++ ...odulithProjectGenerationConfiguration.java | 39 +++++++ .../springmodulith/package-info.java | 20 ++++ .../main/resources/META-INF/spring.factories | 1 + start-site/src/main/resources/application.yml | 16 +++ .../SpringModulithBuildCustomizerTests.java | 109 ++++++++++++++++++ 6 files changed, 289 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..3fe786493e2 --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizer.java @@ -0,0 +1,104 @@ +/* + * 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 java.util.Collection; +import java.util.List; + +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; + +/** + * 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) { + + DependencyContainer 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..b9c2eacff77 --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithProjectGenerationConfiguration.java @@ -0,0 +1,39 @@ +/* + * 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..f8b21749736 --- /dev/null +++ b/start-site/src/main/java/io/spring/start/site/extension/dependency/springmodulith/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ + +/** + * Extensions for generation of projects that depend on Spring Modulith. + */ +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..c06ee5eb658 100644 --- a/start-site/src/main/resources/application.yml +++ b/start-site/src/main/resources/application.yml @@ -139,6 +139,13 @@ 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 + mappings: + - compatibility-range: "[3.1.0,3.2.0-M1)" + version: 1.0.0-M1 + repositories: spring-milestones spring-shell: groupId: org.springframework.shell artifactId: spring-shell-dependencies @@ -252,6 +259,15 @@ initializr: links: - rel: reference href: https://docs.spring.io/spring-boot/docs/{bootVersion}/reference/htmlsingle/#features.docker-compose + - name: Spring Modulith + id: modulith + group-id: org.springframework.modulith + 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: Web content: - name: Spring Web 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..c919698a367 --- /dev/null +++ b/start-site/src/test/java/io/spring/start/site/extension/dependency/springmodulith/SpringModulithBuildCustomizerTests.java @@ -0,0 +1,109 @@ +/* + * 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 java.util.Collection; + +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 org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link SpringModulithBuildCustomizer}. + * + * @author Oliver Drotbohm + */ +class SpringModulithBuildCustomizerTests extends AbstractExtensionTests { + + private final SpringModulithBuildCustomizer customizer = new SpringModulithBuildCustomizer(); + + @Test + void registersCoreStarterByDefault() { + Build build = createBuild(); + this.customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith"); + } + + @Test + void registersTestStarterByDefault() { + Build build = createBuild(); + this.customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-starter-test"); + } + + @Test + void registersActuatorStarterIfActuatorsIsPresent() { + Build build = createBuild(); + build.dependencies().add("actuator"); + + this.customizer.customize(build); + + assertThat(build.dependencies().ids()).contains("modulith-actuator"); + } + + @ParameterizedTest + @MethodSource("observabilityDependencies") + void registersObservabilityStarterIfObservabilityDependencyIsPresent(String dependency) { + Build build = createBuild(); + build.dependencies().add(dependency); + + this.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); + + this.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()); + } + + private static Collection observabilityDependencies() { + return SpringModulithBuildCustomizer.OBSERVABILITY_DEPENDENCIES; + } + +}