diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java index 5c5f3bcb..5d9a00b4 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/project/ProjectGenerator.java @@ -21,12 +21,9 @@ import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.ImportSelector; +import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.io.support.SpringFactoriesLoader; -import org.springframework.core.type.AnnotationMetadata; /** * Main entry point for project generation that processes a {@link ProjectDescription} by @@ -104,8 +101,8 @@ public class ProjectGenerator { public T generate(ProjectDescription description, ProjectAssetGenerator projectAssetGenerator) throws ProjectGenerationException { try (ProjectGenerationContext context = this.contextFactory.get()) { - context.registerBean(ProjectDescription.class, resolve(description, context)); - context.register(CoreConfiguration.class); + registerProjectDescription(context, description); + registerProjectContributors(context, description); this.contextConsumer.accept(context); context.refresh(); try { @@ -117,6 +114,30 @@ public class ProjectGenerator { } } + /** + * Return the {@link ProjectGenerationConfiguration} class names that should be + * considered. By default this method will load candidates using + * {@link SpringFactoriesLoader} with {@link ProjectGenerationConfiguration}. + * @param description the description of the project to generate + * @return a list of candidate configurations + */ + protected List getCandidateProjectGenerationConfigurations(ProjectDescription description) { + return SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, + getClass().getClassLoader()); + } + + private void registerProjectDescription(ProjectGenerationContext context, ProjectDescription description) { + context.registerBean(ProjectDescription.class, resolve(description, context)); + } + + private void registerProjectContributors(ProjectGenerationContext context, ProjectDescription description) { + getCandidateProjectGenerationConfigurations(description).forEach((configurationClassName) -> { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClassName(configurationClassName); + context.registerBeanDefinition(configurationClassName, beanDefinition); + }); + } + private Supplier resolve(ProjectDescription description, ProjectGenerationContext context) { return () -> { if (description instanceof MutableProjectDescription) { @@ -134,30 +155,4 @@ public class ProjectGenerator { }; } - /** - * {@link Configuration} class that registers all available - * {@link ProjectGenerationConfiguration} classes. - */ - @Configuration - @Import(ProjectGenerationImportSelector.class) - static class CoreConfiguration { - - } - - /** - * {@link ImportSelector} for loading classes configured in {@code spring.factories} - * using the - * {@code io.spring.initializr.generator.project.ProjectGenerationConfiguration} key. - */ - static class ProjectGenerationImportSelector implements ImportSelector { - - @Override - public String[] selectImports(AnnotationMetadata importingClassMetadata) { - List factories = SpringFactoriesLoader.loadFactoryNames(ProjectGenerationConfiguration.class, - getClass().getClassLoader()); - return factories.toArray(new String[0]); - } - - } - } diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java index d2cec6ec..49d8a5e5 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/ProjectGeneratorTests.java @@ -17,10 +17,16 @@ package io.spring.initializr.generator.project; import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.Consumer; +import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem; +import io.spring.initializr.generator.project.contributor.TestProjectGenerationConfiguration; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.mockito.InOrder; import org.springframework.beans.factory.support.BeanDefinitionOverrideException; @@ -166,6 +172,27 @@ public class ProjectGeneratorTests { assertThat(candidates).containsOnly(entry("testBean", "duplicate")); } + @Test + void generateCanBeExtendedToFilterProjectContributors(@TempDir Path projectDir) { + ProjectDescription description = mock(ProjectDescription.class); + given(description.getArtifactId()).willReturn("test-custom-contributor"); + given(description.getBuildSystem()).willReturn(new MavenBuildSystem()); + ProjectGenerator generator = new ProjectGenerator(mockContextInitializr()) { + @Override + protected List getCandidateProjectGenerationConfigurations(ProjectDescription description) { + assertThat(description).isSameAs(description); + return Collections.singletonList(TestProjectGenerationConfiguration.class.getName()); + } + }; + DefaultProjectAssetGenerator assetGenerator = new DefaultProjectAssetGenerator((desc) -> projectDir); + Path outputDir = generator.generate(description, assetGenerator); + Path expectedFile = outputDir.resolve("artifact-id.txt"); + assertThat(expectedFile).isRegularFile(); + assertThat(expectedFile).hasContent("test-custom-contributor"); + verify(description).getArtifactId(); + verify(description).getBuildSystem(); + } + @SuppressWarnings("unchecked") private Consumer mockContextInitializr() { return mock(Consumer.class); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration.java b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration.java new file mode 100644 index 00000000..1ee52c71 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/project/contributor/TestProjectGenerationConfiguration.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2020 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.initializr.generator.project.contributor; + +import java.nio.file.Files; +import java.nio.file.Path; + +import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem; +import io.spring.initializr.generator.condition.ConditionalOnBuildSystem; +import io.spring.initializr.generator.project.ProjectDescription; +import io.spring.initializr.generator.project.ProjectGenerationConfiguration; + +import org.springframework.context.annotation.Bean; +import org.springframework.util.FileCopyUtils; + +/** + * Test contributors. + * + * @author Stephane Nicoll + */ +@ProjectGenerationConfiguration +public class TestProjectGenerationConfiguration { + + @Bean + @ConditionalOnBuildSystem(MavenBuildSystem.ID) + public ProjectContributor testArtifactIdContributor(ProjectDescription description) { + return (projectRoot) -> { + Path testFile = projectRoot.resolve("artifact-id.txt"); + Files.createFile(testFile); + FileCopyUtils.copy(description.getArtifactId(), Files.newBufferedWriter(testFile)); + }; + } + +}