Polish documentation

This commit is contained in:
Stephane Nicoll 2019-08-07 18:32:21 +02:00
parent 98bdd29356
commit aaf44d1ec8
6 changed files with 286 additions and 58 deletions

View File

@ -180,7 +180,7 @@
<spring-boot-docs-version>${spring-boot.version}</spring-boot-docs-version>
<snippets>${snippets.location}</snippets>
<github-tag>${github-tag}</github-tag>
<sources-root>${project.basedir}/src/</sources-root>
<sources-root>${project.basedir}/src</sources-root>
</attributes>
</configuration>
<executions>

View File

@ -30,17 +30,34 @@ a little more detail.
[[initializr-generator]]
=== Initializr Generator
The `initializr-generator` module contains all the infrastructure necessary to generate projects.
The `ProjectGenerator` class is the main entry point for project generation.
The `initializr-generator` module contains the low-level infrastructure necessary to
generate JVM-based projects.
[[initializr-generator-project]]
==== Project Generator
The `ProjectGenerator` class is the main entry point for project generation. A
`ProjectGenerator` takes a `ProjectDescription` that defines a particular project to
generate as well as an implementation of `ProjectAssetGenerator` that is responsible to
generate assets based on available candidates.
A project is defined by `ProjectDescription` which consists of the following properties:
* Basic coordinates such as `groupId`, `artifactId`, `name`, `description`
* The `BuildSystem` and `Packaging`
* The JVM `Language`
* The requested dependencies, indexed by ID
* A platform `Version` used by the project. This can be used to tune available
dependencies according to the chosen generation.
* The name of the `application`
* The root package name
* The base directory for the project (if different from the root)
Project generation occurs in a dedicated application context (`ProjectGenerationContext`),
which means that for every project that is generated, the context only contains configuration and components
corresponding to that project. The components registered in a `ProjectGenerationContext` are decided based on
an immutable `ProjectDescription`.
Components for a `ProjectGenerationContext` are defined in `@ProjectGenerationConfiguration`-annotated
configuration classes. The `ProjectGenerationContext` imports `@ProjectGenerationConfiguration`-annotated
configuration classes that are registered in `META-INF/spring.factories`.
which means that for every project that is generated, the context only contains
configuration and components relevant to that particular project. Candidate components for
a `ProjectGenerationContext` are defined in `@ProjectGenerationConfiguration`-annotated
configuration classes. These configuration classes are imported automatically if they are
registered in `META-INF/spring.factories`, as shown in the following example:
[indent=0]
----
@ -49,32 +66,9 @@ com.example.acme.build.BuildProjectGenerationConfiguration,\
com.example.acme.code.SourceCodeProjectGenerationConfiguration
----
A project is defined by a `ProjectDescription` which consists of the following project properties:
* A platform `Version` used by the project. This can be used to tune available dependencies
according to the chosen generation.
* The `BuildSystem` and `Packaging`
* The JVM `Language`
* The requested dependencies, indexed by ID
* Basic coordinates such as `groupId`, `artifactId`, `name`, `description`
* The name of the `application`
* The root package name
* The base directory for the project
Before any project assets are generated, the `ProjectDescription` can be customized
using ``ProjectDescriptionCustomizer``s. ``ProjectDescriptionCustomizer``s are
beans in a `ProjectGenerationContext` and they can be ordered using Spring's `Ordered` interface.
Once the description has been customized based on the available ``ProjectDescriptionCustomizer``s,
the generator uses a `ProjectAssetGenerator` to generate the project assets. The default implementation
of `ProjectAssetGenerator` generates a directory structure using ``ProjectContributor``s available in the
`ProjectGenerationContext`. ``ProjectContributor``s can also be ordered using `Ordered`.
Components such as ``ProjectContributor``s and ``ProjectDescriptionCustomizer``s are made available in
a `ProjectGenerationContext` using conditions. Using conditions avoids exposing beans that have to
check if they have to do something and makes the declaration idiomatic.
Consider the following example:
Components that are added to the `ProjectGenerationContext` are generally made available
using conditions. Using conditions avoids exposing beans that have to check if they have
to do something and makes the declaration more idiomatic. Consider the following example:
[source,java,indent=0]
----
@ -86,30 +80,83 @@ Consider the following example:
}
----
This registers a bean only if the project to generate uses Gradle and `war` packaging.
Check the `io.spring.initializr.generator.condition` package for more details. Custom
conditions can easily be created by inheriting from `ProjectGenerationCondition`.
This registers a component that can customize a Gradle build only if the project to
generate uses the "Gradle" `BuildSystem` and "war" `Packaging`. Check the
`io.spring.initializr.generator.condition` package for more conditions. Also, custom
conditions can easily be created by inheriting from `ProjectGenerationCondition`. Finally,
any use of those conditions should be done on beans registered in the
`ProjectGenerationContext` (i.e. via a `@ProjectGenerationConfiguration` class).
Project generation may also rely on infrastructure that is not specific to a particular
project configuration and is usually configured in the main `ApplicationContext` to avoid
registering it every time a new request comes in. A common use case is to set the main
`ApplicationContext` as the parent of the `ProjectGenerationContext`, as shown in the
following example:
[source,java,indent=0,subs="verbatim,quotes,attributes"]
----
include::{code-examples}/doc/generator/project/ProjectGeneratorSetupExample.java[tag=code]
----
This creates a new `ProjectGenerator` that can use any bean of the application, register
all contributors found in `META-INF/spring.factories` and also registers an additional
`ProjectContributor` programmatically.
`ProjectContributor` is the highest level interface one can implement to contribute assets
to a a project. The `SampleContributor` registered above generates a `test.txt` file at
the root of the project structure, as shown below:
[source,java,indent=0,subs="verbatim,quotes,attributes"]
----
include::{code-examples}/doc/generator/project/SampleContributor.java[tag=code]
----
[[initializr-generator-project-lifecycle]]
==== Project Generation Lifecycle
When a `ProjectGenerator` is instructed to generate a project, the specified
`ProjectDescription` can be customized using available `ProjectDescriptionCustomizer`
beans and can be ordered using Spring's `Ordered` interface.
Once the description has been customized based on the available
``ProjectDescriptionCustomizer``s, the generator uses a `ProjectAssetGenerator` to
generate the project assets. The `initializr-generator` provides a default implementation
of this interface (``DefaultProjectAssetGenerator`) that generates a directory structure
using available `ProjectContributor` beans.
While the default `ProjectAssetGenerator` use the file system and invoke a particular set
of components, it is possible to use the same `ProjectGenerator` instance with a custom
implementation that focus on something else entirely.
[[initializr-generator-abstractions]]
==== Project Abstractions
This module also contains abstractions for various aspects of the project along with
some convenient implementations:
* A build system abstraction with Maven and Gradle implementations.
* A language abstraction with Java, Groovy and Kotlin implementations, including a SourceCodeWriter for each implementation
* A packaging abstraction with implementations for `jar` and `war`
* A language abstraction with Java, Groovy and Kotlin implementations, including a
`SourceCodeWriter` for each implementation.
* A packaging abstraction with implementations for `jar` and `war`.
Adding new implementations for these involves creating a `BuildSystemFactory`, `LanguageFactory`
and `PackagingFactory` and registering it in `META-INF/spring.factories` under
`io.spring.initializr.generator.buildsystem.BuildSystemFactory`, `io.spring.initializr.generator.language.LanguageFactory`
and `io.spring.initializr.generator.packaging.PackagingFactory` respectively.
Adding new implementations for these involves creating a `BuildSystemFactory`,
`LanguageFactory` and `PackagingFactory` and registering them in
`META-INF/spring.factories` under
`io.spring.initializr.generator.buildsystem.BuildSystemFactory`,
`io.spring.initializr.generator.language.LanguageFactory` and
`io.spring.initializr.generator.packaging.PackagingFactory` respectively.
A JVM project typically contains a build file which contains the build configuration
for the project. The `initializr-generator` module provides a model for `Build`
with implementations for `Maven` and `Gradle`. This model can be manipulated depending
on the conventions. The library also provides a `MavenBuildWriter` and `GradleBuildWriter`
that can convert a `Build` model to a build file.
A JVM project typically contains a build file which contains the build configuration for
the project. The `initializr-generator` module provides a model for `Build` with
implementations for `Maven` and `Gradle`. This model can be manipulated depending on the
conventions. The library also provides a `MavenBuildWriter` and `GradleBuildWriter` that
can convert a `Build` model to build file(s).
The next section about the <<initializr-generator-spring,`initializr-generator-spring`>> module showcases how the `Build`
can be manipulated before the build file is written using customizers.
The next section about the <<initializr-generator-spring,`initializr-generator-spring`>>
module showcases how the `Build` can be manipulated before the build file is written
using customizers.
@ -119,12 +166,13 @@ This is an optional module that defines the conventions that we think will be us
any Spring Boot project. You can include this jar in your project if your service is meant
for generating Spring Boot projects.
In the section above, we looked at how ``ProjectContributor``s can be used to contribute
assets to a project. This module contains concrete implementations of `ProjectContributor`
along with the ``@ProjectGenerationConfiguration``s that configure them. For example,
there is a `MavenBuildProjectContributor` which contributes the files for a Maven build,
such as `pom.xml`. This contributor is registered as a bean in a
`ProjectGenerationConfiguration` which is conditional on the build system being Maven.
In the <<initializr-generator-project,Project Generator section>>, we looked at how
``ProjectContributor``s can be used to contribute assets to a project. This module
contains concrete implementations of `ProjectContributor` along with the
``@ProjectGenerationConfiguration``s that configure them. For example, there is a
`MavenBuildProjectContributor` which contributes the files for a Maven build, such as
`pom.xml`. This contributor is registered as a bean in a `ProjectGenerationConfiguration`
which is conditional on the build system being Maven.
This module also introduces the concept of ``BuildCustomizer``s. ``BuildCustomizer``s are
used to customize a project's `Build` and are ordered. For instance, if your service
@ -132,6 +180,26 @@ requires you to add a certain plugin to the build, you can provide a `BuildCusto
that adds the plugin and the customizer will be called according to the order specified on
it.
[[initializr-generator-spring-requirements]]
==== Requirements
Contributors of this module expect the following beans to be available in the
`ProjectGenerationContext`:
* The `InitializrMetadata` instance to use
* Optionally, a `MetadataBuildItemResolver` that can resolve various build items (such as
dependencies and BOMs based on their id in the metadata
If you are using a parent context, it is advised to configure those there as you should
not register them every time a new project is generated:
* An `IndentingWriterFactory` that represents that indenting strategy to use.
* A `MustacheTemplateRenderer` using `classpath:/templates` as root location. Consider
registering such bean with a cache strategy to avoid resolving templates every time.
[[initializr-generator-spring-facets]]
==== Supported facets
The following facets are handled:

View File

@ -13,6 +13,7 @@ Stéphane Nicoll; Dave Syer; Madhura Bhave
:hide-uri-scheme:
:docinfo: shared,private
:code-examples: {sources-root}/main/java/io/spring/initializr
:test-examples: {sources-root}/test/java/io/spring/initializr
:initializr-repo: snapshot
:github-tag: master

View File

@ -0,0 +1,39 @@
/*
* 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.ProjectGenerator;
import org.springframework.context.ApplicationContext;
/**
* Setup a {@link ProjectGenerator} with a parent context and a custom contributor.
*
* @author Stephane Nicoll
*/
public class ProjectGeneratorSetupExample {
// tag::code[]
public ProjectGenerator createProjectGenerator(ApplicationContext appContext) {
return new ProjectGenerator((context) -> {
context.setParent(appContext);
context.registerBean(SampleContributor.class, SampleContributor::new);
});
}
// end::code[]
}

View File

@ -0,0 +1,44 @@
/*
* 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 java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import io.spring.initializr.generator.project.contributor.ProjectContributor;
/**
* A sample {@link ProjectContributor} that creates a {@code hello.txt} at the root of the
* project directory with content {@code Test}.
*
* @author Stephane Nicoll
*/
// tag::code[]
public class SampleContributor implements ProjectContributor {
@Override
public void contribute(Path projectRoot) throws IOException {
Path file = Files.createFile(projectRoot.resolve("hello.txt"));
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(file))) {
writer.println("Test");
}
}
}
// end::code[]

View File

@ -0,0 +1,76 @@
/*
* 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 java.nio.file.Path;
import io.spring.initializr.generator.buildsystem.BuildSystem;
import io.spring.initializr.generator.buildsystem.maven.MavenBuildSystem;
import io.spring.initializr.generator.io.IndentingWriterFactory;
import io.spring.initializr.generator.io.template.MustacheTemplateRenderer;
import io.spring.initializr.generator.language.Language;
import io.spring.initializr.generator.language.java.JavaLanguage;
import io.spring.initializr.generator.project.DefaultProjectAssetGenerator;
import io.spring.initializr.generator.project.ProjectDescription;
import io.spring.initializr.generator.project.ProjectGenerator;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataBuilder;
import io.spring.initializr.metadata.InitializrProperties;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.context.support.StaticApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ProjectGeneratorSetupExample}.
*
* @author Stephane Nicoll
*/
class ProjectGeneratorSetupExampleTests {
@Test
void generateSimpleProjectStructure(@TempDir Path tempDir) {
StaticApplicationContext context = new StaticApplicationContext();
context.registerBean(InitializrMetadata.class,
() -> InitializrMetadataBuilder.fromInitializrProperties(new InitializrProperties()).build());
context.registerBean(IndentingWriterFactory.class, IndentingWriterFactory::withDefaultSettings);
context.registerBean(MustacheTemplateRenderer.class,
() -> new MustacheTemplateRenderer("classpath:/templates"));
context.refresh();
DefaultProjectAssetGenerator assetGenerator = new DefaultProjectAssetGenerator((description) -> tempDir);
ProjectGenerator projectGenerator = new ProjectGeneratorSetupExample().createProjectGenerator(context);
Path directory = projectGenerator.generate(createProjectDescription(), assetGenerator);
assertThat(directory).isSameAs(tempDir);
Path helloFile = directory.resolve("hello.txt");
assertThat(helloFile).exists().isRegularFile().hasContent("Test");
}
private ProjectDescription createProjectDescription() {
ProjectDescription description = new ProjectDescription();
description.setGroupId("com.example");
description.setArtifactId("demo");
description.setApplicationName("DemoApplication");
description.setPlatformVersion(Version.parse("1.0.0.RELEASE"));
description.setLanguage(Language.forId(JavaLanguage.ID, "11"));
description.setBuildSystem(BuildSystem.forId(MavenBuildSystem.ID));
return description;
}
}