diff --git a/initializr-docs/src/main/asciidoc/configuration-guide.adoc b/initializr-docs/src/main/asciidoc/configuration-guide.adoc index cd3f74d7..de1ca48a 100644 --- a/initializr-docs/src/main/asciidoc/configuration-guide.adoc +++ b/initializr-docs/src/main/asciidoc/configuration-guide.adoc @@ -1088,13 +1088,19 @@ include::{code-examples}/doc/generator/project/CustomProjectGenerationController ---- If you inherit from `WebProjectRequest`, defaults can be automatically applied from the -metadata as shown above but you may also chose to ignore that. If you define a `@Bean` -for that controller, the auto-configuration will back-off and use yours instead. +metadata as shown above but you may also chose to ignore that. The next step is to make sure that those additional attributes are made available in the `ProjectGenerationContext`. The idiomatic way of doing this is to create your own interface that extends from `ProjectDescription` and expose your custom attributes. To -make sure your view of `ProjectGeneration` is made available in the +make sure your view of `ProjectDescription` is made available in the `ProjectGenerationContext`, a custom `ProjectRequestToDescriptionConverter` should be -defined. When such a bean exists in the context it replaces the default that the -auto-configuration provides. +defined and could reuse `DefaultProjectRequestToDescriptionConverter` to apply general +rules for standard fields. + +Finally, you should wire up everything: + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- +include::{code-examples}/doc/generator/project/CustomProjectGenerationConfigurationExample.java[tag=code] +---- diff --git a/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationConfigurationExample.java b/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationConfigurationExample.java new file mode 100644 index 00000000..4c97e688 --- /dev/null +++ b/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationConfigurationExample.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012-2019 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.doc.generator.project; + +import io.spring.initializr.generator.project.MutableProjectDescription; +import io.spring.initializr.generator.project.ProjectDescription; +import io.spring.initializr.metadata.InitializrMetadata; +import io.spring.initializr.metadata.InitializrMetadataProvider; +import io.spring.initializr.web.controller.ProjectGenerationController; +import io.spring.initializr.web.project.ProjectGenerationInvoker; +import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration example of a custom {@link ProjectGenerationController}. + * + * @author Stephane Nicoll + */ +@Configuration +public class CustomProjectGenerationConfigurationExample { + + // tag::code[] + @Bean + public CustomProjectGenerationController projectGenerationController(InitializrMetadataProvider metadataProvider, + ApplicationContext applicationContext) { + ProjectGenerationInvoker projectGenerationInvoker = new ProjectGenerationInvoker<>( + applicationContext, new CustomProjectRequestToDescriptionConverter()); + return new CustomProjectGenerationController(metadataProvider, projectGenerationInvoker); + } + // end::code[] + + static class CustomProjectRequestToDescriptionConverter + implements ProjectRequestToDescriptionConverter { + + @Override + public ProjectDescription convert(CustomProjectRequest request, InitializrMetadata metadata) { + return new MutableProjectDescription(); + } + + } + +} diff --git a/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationController.java b/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationController.java index bae50bad..32091f53 100644 --- a/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationController.java +++ b/initializr-docs/src/main/java/io/spring/initializr/doc/generator/project/CustomProjectGenerationController.java @@ -31,7 +31,7 @@ import io.spring.initializr.web.project.ProjectGenerationInvoker; public class CustomProjectGenerationController extends ProjectGenerationController { public CustomProjectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ProjectGenerationInvoker projectGenerationInvoker) { super(metadataProvider, projectGenerationInvoker); } diff --git a/initializr-web/src/main/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfiguration.java b/initializr-web/src/main/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfiguration.java index d5b9fe5b..079a6169 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfiguration.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfiguration.java @@ -40,7 +40,7 @@ import io.spring.initializr.web.controller.ProjectMetadataController; import io.spring.initializr.web.controller.SpringCliDistributionController; import io.spring.initializr.web.project.DefaultProjectRequestToDescriptionConverter; import io.spring.initializr.web.project.ProjectGenerationInvoker; -import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter; +import io.spring.initializr.web.project.ProjectRequest; import io.spring.initializr.web.support.DefaultDependencyMetadataProvider; import io.spring.initializr.web.support.DefaultInitializrMetadataProvider; import io.spring.initializr.web.support.DefaultInitializrMetadataUpdateStrategy; @@ -61,7 +61,6 @@ import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.support.NoOpCache; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -145,7 +144,9 @@ public class InitializrAutoConfiguration { @Bean @ConditionalOnMissingBean ProjectGenerationController projectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ApplicationContext applicationContext) { + ProjectGenerationInvoker projectGenerationInvoker = new ProjectGenerationInvoker<>( + applicationContext, new DefaultProjectRequestToDescriptionConverter()); return new DefaultProjectGenerationController(metadataProvider, projectGenerationInvoker); } @@ -169,15 +170,6 @@ public class InitializrAutoConfiguration { return new SpringCliDistributionController(metadataProvider); } - @Bean - @ConditionalOnMissingBean - ProjectGenerationInvoker projectGenerationInvoker(ApplicationContext applicationContext, - ApplicationEventPublisher eventPublisher, - ObjectProvider projectRequestToDescriptionConverter) { - return new ProjectGenerationInvoker(applicationContext, eventPublisher, projectRequestToDescriptionConverter - .getIfAvailable(DefaultProjectRequestToDescriptionConverter::new)); - } - @Bean InitializrModule InitializrJacksonModule() { return new InitializrModule(); diff --git a/initializr-web/src/main/java/io/spring/initializr/web/controller/DefaultProjectGenerationController.java b/initializr-web/src/main/java/io/spring/initializr/web/controller/DefaultProjectGenerationController.java index c7f94d91..0d1093c9 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/controller/DefaultProjectGenerationController.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/controller/DefaultProjectGenerationController.java @@ -32,7 +32,7 @@ import io.spring.initializr.web.project.WebProjectRequest; public class DefaultProjectGenerationController extends ProjectGenerationController { public DefaultProjectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ProjectGenerationInvoker projectGenerationInvoker) { super(metadataProvider, projectGenerationInvoker); } diff --git a/initializr-web/src/main/java/io/spring/initializr/web/controller/ProjectGenerationController.java b/initializr-web/src/main/java/io/spring/initializr/web/controller/ProjectGenerationController.java index d374406e..8a7d81e8 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/controller/ProjectGenerationController.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/controller/ProjectGenerationController.java @@ -73,10 +73,10 @@ public abstract class ProjectGenerationController { private final InitializrMetadataProvider metadataProvider; - private final ProjectGenerationInvoker projectGenerationInvoker; + private final ProjectGenerationInvoker projectGenerationInvoker; public ProjectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ProjectGenerationInvoker projectGenerationInvoker) { this.metadataProvider = metadataProvider; this.projectGenerationInvoker = projectGenerationInvoker; } diff --git a/initializr-web/src/main/java/io/spring/initializr/web/project/DefaultProjectRequestToDescriptionConverter.java b/initializr-web/src/main/java/io/spring/initializr/web/project/DefaultProjectRequestToDescriptionConverter.java index eed8b58c..c9b3ac05 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/project/DefaultProjectRequestToDescriptionConverter.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/project/DefaultProjectRequestToDescriptionConverter.java @@ -42,7 +42,8 @@ import org.springframework.util.StringUtils; * @author Madhura Bhave * @author HaiTao Zhang */ -public class DefaultProjectRequestToDescriptionConverter implements ProjectRequestToDescriptionConverter { +public class DefaultProjectRequestToDescriptionConverter + implements ProjectRequestToDescriptionConverter { private static final Version VERSION_1_5_0 = Version.parse("1.5.0.RELEASE"); diff --git a/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectGenerationInvoker.java b/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectGenerationInvoker.java index 787cf8ad..22919cb7 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectGenerationInvoker.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectGenerationInvoker.java @@ -45,20 +45,26 @@ import org.springframework.util.FileSystemUtils; * Invokes the project generation API. This is an intermediate layer that can consume a * {@link ProjectRequest} and trigger project generation based on the request. * + * @param the concrete {@link ProjectRequest} type * @author Madhura Bhave */ -public class ProjectGenerationInvoker { +public class ProjectGenerationInvoker { private final ApplicationContext parentApplicationContext; private final ApplicationEventPublisher eventPublisher; - private final ProjectRequestToDescriptionConverter requestConverter; + private final ProjectRequestToDescriptionConverter requestConverter; private transient Map> temporaryFiles = new LinkedHashMap<>(); public ProjectGenerationInvoker(ApplicationContext parentApplicationContext, - ApplicationEventPublisher eventPublisher, ProjectRequestToDescriptionConverter requestConverter) { + ProjectRequestToDescriptionConverter requestConverter) { + this(parentApplicationContext, parentApplicationContext, requestConverter); + } + + protected ProjectGenerationInvoker(ApplicationContext parentApplicationContext, + ApplicationEventPublisher eventPublisher, ProjectRequestToDescriptionConverter requestConverter) { this.parentApplicationContext = parentApplicationContext; this.eventPublisher = eventPublisher; this.requestConverter = requestConverter; @@ -70,7 +76,7 @@ public class ProjectGenerationInvoker { * @param request the project request * @return the {@link ProjectGenerationResult} */ - public ProjectGenerationResult invokeProjectStructureGeneration(ProjectRequest request) { + public ProjectGenerationResult invokeProjectStructureGeneration(R request) { InitializrMetadata metadata = this.parentApplicationContext.getBean(InitializrMetadataProvider.class).get(); try { ProjectDescription description = this.requestConverter.convert(request, metadata); @@ -86,7 +92,7 @@ public class ProjectGenerationInvoker { } } - private ProjectAssetGenerator generateProject(ProjectRequest request) { + private ProjectAssetGenerator generateProject(R request) { return (context) -> { Path projectDir = new DefaultProjectAssetGenerator().generate(context); publishProjectGeneratedEvent(request, context); @@ -101,7 +107,7 @@ public class ProjectGenerationInvoker { * @param request the project request * @return the generated build content */ - public byte[] invokeBuildGeneration(ProjectRequest request) { + public byte[] invokeBuildGeneration(R request) { InitializrMetadata metadata = this.parentApplicationContext.getBean(InitializrMetadataProvider.class).get(); try { ProjectDescription description = this.requestConverter.convert(request, metadata); @@ -115,7 +121,7 @@ public class ProjectGenerationInvoker { } } - private ProjectAssetGenerator generateBuild(ProjectRequest request) { + private ProjectAssetGenerator generateBuild(R request) { return (context) -> { byte[] content = generateBuild(context); publishProjectGeneratedEvent(request, context); @@ -180,13 +186,13 @@ public class ProjectGenerationInvoker { context.getBean(ProjectDescription.class).getPlatformVersion())); } - private void publishProjectGeneratedEvent(ProjectRequest request, ProjectGenerationContext context) { + private void publishProjectGeneratedEvent(R request, ProjectGenerationContext context) { InitializrMetadata metadata = context.getBean(InitializrMetadata.class); ProjectGeneratedEvent event = new ProjectGeneratedEvent(request, metadata); this.eventPublisher.publishEvent(event); } - private void publishProjectFailedEvent(ProjectRequest request, InitializrMetadata metadata, Exception cause) { + private void publishProjectFailedEvent(R request, InitializrMetadata metadata, Exception cause) { ProjectFailedEvent event = new ProjectFailedEvent(request, metadata, cause); this.eventPublisher.publishEvent(event); } diff --git a/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectRequestToDescriptionConverter.java b/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectRequestToDescriptionConverter.java index dd0239fb..9c3de83e 100644 --- a/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectRequestToDescriptionConverter.java +++ b/initializr-web/src/main/java/io/spring/initializr/web/project/ProjectRequestToDescriptionConverter.java @@ -22,10 +22,11 @@ import io.spring.initializr.metadata.InitializrMetadata; /** * Convert a {@link ProjectRequest} to a {@link ProjectDescription}. * + * @param the concrete {@link ProjectRequest} type * @author Stephane Nicoll */ @FunctionalInterface -public interface ProjectRequestToDescriptionConverter { +public interface ProjectRequestToDescriptionConverter { /** * Validate and convert the specified {@link ProjectRequest} to a @@ -35,6 +36,6 @@ public interface ProjectRequestToDescriptionConverter { * @return a validated {@link ProjectDescription} to use to generate a project that * matches the specified {@code request} */ - ProjectDescription convert(ProjectRequest request, InitializrMetadata metadata); + ProjectDescription convert(R request, InitializrMetadata metadata); } diff --git a/initializr-web/src/test/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfigurationTests.java b/initializr-web/src/test/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfigurationTests.java index 930a4b71..c5a36e81 100755 --- a/initializr-web/src/test/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfigurationTests.java +++ b/initializr-web/src/test/java/io/spring/initializr/web/autoconfigure/InitializrAutoConfigurationTests.java @@ -23,8 +23,6 @@ import io.spring.initializr.web.controller.CommandLineMetadataController; import io.spring.initializr.web.controller.ProjectGenerationController; import io.spring.initializr.web.controller.ProjectMetadataController; import io.spring.initializr.web.controller.SpringCliDistributionController; -import io.spring.initializr.web.project.ProjectGenerationInvoker; -import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter; import io.spring.initializr.web.support.DefaultInitializrMetadataUpdateStrategy; import io.spring.initializr.web.support.InitializrMetadataUpdateStrategy; import org.junit.jupiter.api.Test; @@ -129,7 +127,6 @@ class InitializrAutoConfigurationTests { .withConfiguration(BASIC_AUTO_CONFIGURATIONS); webContextRunner.run((context) -> { assertThat(context).hasSingleBean(InitializrWebConfig.class); - assertThat(context).hasSingleBean(ProjectGenerationInvoker.class); assertThat(context).hasSingleBean(ProjectGenerationController.class); assertThat(context).hasSingleBean(ProjectMetadataController.class); assertThat(context).hasSingleBean(CommandLineMetadataController.class); @@ -138,12 +135,12 @@ class InitializrAutoConfigurationTests { } @Test - void autoConfigWithCustomProjectRequestConverter() { + void autoConfigWithCustomProjectGenerationController() { new WebApplicationContextRunner().withConfiguration(BASIC_AUTO_CONFIGURATIONS) - .withUserConfiguration(CustomProjectRequestToDescriptionConverter.class).run((context) -> { - assertThat(context).hasSingleBean(ProjectGenerationInvoker.class); - assertThat(context.getBean(ProjectGenerationInvoker.class)).hasFieldOrPropertyWithValue( - "requestConverter", context.getBean("testProjectRequestToDescriptionConverter")); + .withUserConfiguration(CustomProjectGenerationController.class).run((context) -> { + assertThat(context).hasSingleBean(ProjectGenerationController.class); + assertThat(context.getBean(ProjectGenerationController.class)) + .isSameAs(context.getBean("testProjectGenerationController")); }); } @@ -223,11 +220,11 @@ class InitializrAutoConfigurationTests { } @Configuration - static class CustomProjectRequestToDescriptionConverter { + static class CustomProjectGenerationController { @Bean - ProjectRequestToDescriptionConverter testProjectRequestToDescriptionConverter() { - return mock(ProjectRequestToDescriptionConverter.class); + ProjectGenerationController testProjectGenerationController() { + return mock(ProjectGenerationController.class); } } diff --git a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectGenerationController.java b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectGenerationController.java index 01c9a88c..ba883c54 100644 --- a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectGenerationController.java +++ b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/CustomProjectGenerationController.java @@ -31,7 +31,7 @@ import io.spring.initializr.web.project.ProjectGenerationInvoker; class CustomProjectGenerationController extends ProjectGenerationController { CustomProjectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ProjectGenerationInvoker projectGenerationInvoker) { super(metadataProvider, projectGenerationInvoker); } diff --git a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java index 77472664..68c9b2c3 100644 --- a/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java +++ b/initializr-web/src/test/java/io/spring/initializr/web/controller/custom/ProjectGenerationControllerCustomRequestIntegrationTests.java @@ -25,10 +25,10 @@ import io.spring.initializr.web.controller.ProjectGenerationController; import io.spring.initializr.web.controller.custom.ProjectGenerationControllerCustomRequestIntegrationTests.CustomProjectGenerationConfiguration; import io.spring.initializr.web.project.DefaultProjectRequestToDescriptionConverter; import io.spring.initializr.web.project.ProjectGenerationInvoker; -import io.spring.initializr.web.project.ProjectRequest; import io.spring.initializr.web.project.ProjectRequestToDescriptionConverter; import org.junit.jupiter.api.Test; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -73,25 +73,22 @@ public class ProjectGenerationControllerCustomRequestIntegrationTests @Bean CustomProjectGenerationController customProjectGenerationController(InitializrMetadataProvider metadataProvider, - ProjectGenerationInvoker projectGenerationInvoker) { + ApplicationContext applicationContext) { + ProjectGenerationInvoker projectGenerationInvoker = new ProjectGenerationInvoker<>( + applicationContext, new CustomProjectRequestToDescriptionConverter()); return new CustomProjectGenerationController(metadataProvider, projectGenerationInvoker); } - @Bean - ProjectRequestToDescriptionConverter customProjectRequestToDescriptionConverter() { - return new CustomProjectRequestToDescriptionConverter(); - } - } - static class CustomProjectRequestToDescriptionConverter implements ProjectRequestToDescriptionConverter { + static class CustomProjectRequestToDescriptionConverter + implements ProjectRequestToDescriptionConverter { @Override - public ProjectDescription convert(ProjectRequest request, InitializrMetadata metadata) { - CustomProjectRequest customRequest = (CustomProjectRequest) request; + public ProjectDescription convert(CustomProjectRequest request, InitializrMetadata metadata) { CustomProjectDescription description = new CustomProjectDescription(); new DefaultProjectRequestToDescriptionConverter().convert(request, description, metadata); - description.setCustomFlag(customRequest.isCustomFlag()); + description.setCustomFlag(request.isCustomFlag()); // Override attributes for test purposes description.setPackageName("org.example.custom"); description.setApplicationName("CustomApp");