diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuild.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuild.java index 0a0a3901..a7f300d7 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuild.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuild.java @@ -27,6 +27,7 @@ import io.spring.initializr.generator.buildsystem.BuildItemResolver; * Maven build for a project. * * @author Andy Wilkinson + * @author Stephane Nicoll */ public class MavenBuild extends Build { @@ -42,6 +43,10 @@ public class MavenBuild extends Build { private final Map properties = new TreeMap<>(); + private final MavenResourceContainer resources = new MavenResourceContainer(); + + private final MavenResourceContainer testResources = new MavenResourceContainer(); + private MavenPluginContainer plugins = new MavenPluginContainer(); private String packaging; @@ -103,6 +108,14 @@ public class MavenBuild extends Build { this.testSourceDirectory = testSourceDirectory; } + public MavenResourceContainer resources() { + return this.resources; + } + + public MavenResourceContainer testResources() { + return this.testResources; + } + public MavenPluginContainer plugins() { return this.plugins; } diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriter.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriter.java index 0915027d..fe96b053 100644 --- a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriter.java +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriter.java @@ -240,18 +240,65 @@ public class MavenBuildWriter { } private void writeBuild(IndentingWriter writer, MavenBuild build) { - if (build.getSourceDirectory() == null && build.getTestSourceDirectory() == null && build.plugins().isEmpty()) { + if (build.getSourceDirectory() == null && build.getTestSourceDirectory() == null && build.resources().isEmpty() + && build.testResources().isEmpty() && build.plugins().isEmpty()) { return; } writer.println(); writeElement(writer, "build", () -> { writeSingleElement(writer, "sourceDirectory", build.getSourceDirectory()); writeSingleElement(writer, "testSourceDirectory", build.getTestSourceDirectory()); + writeResources(writer, build); writePlugins(writer, build); }); } + private void writeResources(IndentingWriter writer, MavenBuild build) { + if (!build.resources().isEmpty()) { + writeElement(writer, "resources", () -> writeCollection(writer, + build.resources().values().collect(Collectors.toList()), this::writeResource)); + } + if (!build.testResources().isEmpty()) { + writeElement(writer, "testResources", () -> writeCollection(writer, + build.testResources().values().collect(Collectors.toList()), this::writeTestResource)); + } + } + + private void writeResource(IndentingWriter writer, MavenResource resource) { + writeResource(writer, resource, "resource"); + } + + private void writeTestResource(IndentingWriter writer, MavenResource resource) { + writeResource(writer, resource, "testResource"); + } + + private void writeResource(IndentingWriter writer, MavenResource resource, String resourceName) { + writeElement(writer, resourceName, () -> { + writeSingleElement(writer, "directory", resource.getDirectory()); + writeSingleElement(writer, "targetPath", resource.getTargetPath()); + if (resource.isFiltering()) { + writeSingleElement(writer, "filtering", "true"); + } + if (!resource.getIncludes().isEmpty()) { + writeElement(writer, "includes", + () -> writeCollection(writer, resource.getIncludes(), this::writeResourceInclude)); + } + if (!resource.getExcludes().isEmpty()) { + writeElement(writer, "excludes", + () -> writeCollection(writer, resource.getExcludes(), this::writeResourceExclude)); + } + }); + } + + private void writeResourceInclude(IndentingWriter writer, String include) { + writeSingleElement(writer, "include", include); + } + + private void writeResourceExclude(IndentingWriter writer, String exclude) { + writeSingleElement(writer, "exclude", exclude); + } + private void writePlugins(IndentingWriter writer, MavenBuild build) { if (build.plugins().isEmpty()) { return; diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResource.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResource.java new file mode 100644 index 00000000..d09d62d7 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResource.java @@ -0,0 +1,138 @@ +/* + * 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.generator.buildsystem.maven; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A resource of a {@link MavenBuild}. + * + * @author Stephane Nicoll + */ +public class MavenResource { + + private final String directory; + + private final String targetPath; + + private final boolean filtering; + + private final List includes; + + private final List excludes; + + public MavenResource(Builder builder) { + this.directory = builder.directory; + this.targetPath = builder.targetPath; + this.filtering = builder.filtering; + this.includes = builder.includes; + this.excludes = builder.excludes; + } + + /** + * Return the directory where resources are to be found. Can use regular maven token + * such as {@code ${basedir}/src/main}. + * @return the resources directory + */ + public String getDirectory() { + return this.directory; + } + + /** + * Return the directory structure to place the set of resources from a build. Return + * {@code null} by default which represents the root directory. + * @return the target path or {@code null} + */ + public String getTargetPath() { + return this.targetPath; + } + + /** + * Specify if filtering is enabled when copying resources. + * @return {@code true} if filtering is enabled + */ + public boolean isFiltering() { + return this.filtering; + } + + /** + * Return files patterns which specify the files to include as resources under that + * specified directory. Can use {@code *} for all. + * @return the include patterns + */ + public List getIncludes() { + return this.includes; + } + + /** + * Return files patterns which specify the files to ignore as resources under that + * specified directory. In conflicts between {@code include} and {@code exclude}, + * {@code exclude} wins. + * @return the exclude patterns + */ + public List getExcludes() { + return this.excludes; + } + + /** + * Builder for a resource. + */ + public static class Builder { + + private final String directory; + + private String targetPath; + + private boolean filtering; + + private List includes = new ArrayList<>(); + + private List excludes = new ArrayList<>(); + + public Builder(String directory) { + this.directory = directory; + } + + public Builder targetPath(String targetPath) { + this.targetPath = targetPath; + return this; + } + + public Builder filtering(Boolean filtering) { + this.filtering = filtering; + return this; + } + + public Builder includes(String... includes) { + this.includes = Arrays.asList(includes); + return this; + } + + public Builder excludes(String... excludes) { + this.excludes = Arrays.asList(excludes); + return this; + } + + public MavenResource build() { + return new MavenResource(this); + } + + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainer.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainer.java new file mode 100644 index 00000000..d6673eaf --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainer.java @@ -0,0 +1,86 @@ +/* + * 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.generator.buildsystem.maven; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; + +/** + * A container for {@link MavenResource}s. + * + * @author Stephane Nicoll + */ +public class MavenResourceContainer { + + private final Map resources = new LinkedHashMap<>(); + + /** + * Specify if this container is empty. + * @return {@code true} if no {@link MavenResource} is added + */ + public boolean isEmpty() { + return this.resources.isEmpty(); + } + + /** + * Specify if this container has a resource the specified {@code directory}. + * @param directory the resource directory + * @return {@code true} if an item for the specified {@code directory} exists + */ + public boolean has(String directory) { + return this.resources.containsKey(directory); + } + + /** + * Returns a {@link Stream} of registered {@link MavenResource}s. + * @return a stream of {@link MavenResource}s + */ + public Stream values() { + return this.resources.values().stream().map(MavenResource.Builder::build); + } + + /** + * Add a resource with default settings for the specified {@code directory}. + * @param directory the directory to add + */ + public void add(String directory) { + this.resources.computeIfAbsent(directory, (key) -> new MavenResource.Builder(directory)); + } + + /** + * Add a resource with default settings for the specified {@code directory} and + * {@link Consumer} to customize the resource. If the resource has already been added, + * the consumer can be used to further tune the existing resource configuration. + * @param directory the directory to add + * @param resource a {@link Consumer} to customize the {@link MavenResource} + */ + public void add(String directory, Consumer resource) { + resource.accept(this.resources.computeIfAbsent(directory, (key) -> new MavenResource.Builder(directory))); + } + + /** + * Remove the resource with the specified {@code directory}. + * @param directory the directory to remove + * @return {@code true} if such a resource was registered, {@code false} otherwise + */ + public boolean remove(String directory) { + return this.resources.remove(directory) != null; + } + +} diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildTests.java index 2321bf83..45f8f665 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildTests.java @@ -28,6 +28,35 @@ import static org.assertj.core.api.Assertions.assertThat; */ class MavenBuildTests { + @Test + void mavenResourcesEmptyByDefault() { + MavenBuild build = new MavenBuild(); + assertThat(build.resources().isEmpty()).isTrue(); + assertThat(build.testResources().isEmpty()).isTrue(); + } + + @Test + void mavenResourcesCanBeConfigured() { + MavenBuild build = new MavenBuild(); + build.resources().add("src/main/custom", (resource) -> resource.filtering(true)); + assertThat(build.resources().values()).hasOnlyOneElementSatisfying((resource) -> { + assertThat(resource.getDirectory()).isEqualTo("src/main/custom"); + assertThat(resource.isFiltering()).isTrue(); + }); + assertThat(build.testResources().isEmpty()).isTrue(); + } + + @Test + void mavenTestResourcesCanBeConfigured() { + MavenBuild build = new MavenBuild(); + build.testResources().add("src/test/custom", (resource) -> resource.excludes("**/*.gen")); + assertThat(build.resources().isEmpty()).isTrue(); + assertThat(build.testResources().values()).hasOnlyOneElementSatisfying((resource) -> { + assertThat(resource.getDirectory()).isEqualTo("src/test/custom"); + assertThat(resource.getExcludes()).containsExactly("**/*.gen"); + }); + } + @Test void mavenPluginCanBeConfigured() { MavenBuild build = new MavenBuild(); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriterTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriterTests.java index c1fcb3e4..53109bb7 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriterTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenBuildWriterTests.java @@ -344,6 +344,38 @@ class MavenBuildWriterTests { assertThat(firstBom).textAtPath("scope").isEqualTo("import"); } + @Test + void pomWithResources() throws Exception { + MavenBuild build = new MavenBuild(); + build.resources().add("src/main/custom", (resource) -> resource.includes("**/*.properties")); + generatePom(build, (pom) -> { + assertThat(pom).textAtPath("/project/build/resources/resource/directory").isEqualTo("src/main/custom"); + assertThat(pom).textAtPath("/project/build/resources/resource/targetPath").isNullOrEmpty(); + assertThat(pom).textAtPath("/project/build/resources/resource/filtering").isNullOrEmpty(); + assertThat(pom).textAtPath("/project/build/resources/resource/includes/include") + .isEqualTo("**/*.properties"); + assertThat(pom).textAtPath("/project/build/resources/resource/excludes").isNullOrEmpty(); + assertThat(pom).textAtPath("/project/build/testResources").isNullOrEmpty(); + }); + } + + @Test + void pomWithTestResources() throws Exception { + MavenBuild build = new MavenBuild(); + build.testResources().add("src/test/custom", + (resource) -> resource.excludes("**/*.gen").filtering(true).targetPath("test")); + generatePom(build, (pom) -> { + assertThat(pom).textAtPath("/project/build/resources").isNullOrEmpty(); + assertThat(pom).textAtPath("/project/build/testResources/testResource/directory") + .isEqualTo("src/test/custom"); + assertThat(pom).textAtPath("/project/build/testResources/testResource/targetPath").isEqualTo("test"); + assertThat(pom).textAtPath("/project/build/testResources/testResource/filtering").isEqualTo("true"); + assertThat(pom).textAtPath("/project/build/testResources/testResource/includes").isNullOrEmpty(); + assertThat(pom).textAtPath("/project/build/testResources/testResource/excludes/exclude") + .isEqualTo("**/*.gen"); + }); + } + @Test void pomWithPlugin() throws Exception { MavenBuild build = new MavenBuild(); @@ -444,6 +476,14 @@ class MavenBuildWriterTests { }); } + @Test + void pomWithEmptyBuild() throws Exception { + MavenBuild build = new MavenBuild(); + build.setGroup("com.example.demo"); + build.setArtifact("demo"); + generatePom(build, (pom) -> assertThat(pom).textAtPath("/project/build/").isNullOrEmpty()); + } + @Test void pomWithMavenCentral() throws Exception { MavenBuild build = new MavenBuild(); diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainerTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainerTests.java new file mode 100644 index 00000000..acfe531b --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/maven/MavenResourceContainerTests.java @@ -0,0 +1,99 @@ +/* + * 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.generator.buildsystem.maven; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link MavenResourceContainer}. + * + * @author Stephane Nicoll + */ +class MavenResourceContainerTests { + + @Test + void mavenResourceCanBeConfigured() { + MavenResourceContainer container = new MavenResourceContainer(); + container.add("src/main/resources", (resource) -> { + resource.targetPath("targetPath"); + resource.filtering(true); + resource.includes("**/*.yml"); + resource.excludes("**/*.properties"); + }); + assertThat(container.values()).hasOnlyOneElementSatisfying((resource) -> { + assertThat(resource.getDirectory()).isEqualTo("src/main/resources"); + assertThat(resource.getTargetPath()).isEqualTo("targetPath"); + assertThat(resource.isFiltering()).isTrue(); + assertThat(resource.getIncludes()).containsExactly("**/*.yml"); + assertThat(resource.getExcludes()).containsExactly("**/*.properties"); + }); + } + + @Test + void mavenResourceCanBeAmended() { + MavenResourceContainer container = new MavenResourceContainer(); + container.add("src/main/resources", (resource) -> { + resource.filtering(true); + resource.includes("**/*.yml"); + }); + container.add("src/main/resources", (resource) -> { + resource.includes("**/*.yaml"); + resource.excludes("**/*.properties"); + }); + assertThat(container.values()).hasOnlyOneElementSatisfying((resource) -> { + assertThat(resource.getDirectory()).isEqualTo("src/main/resources"); + assertThat(resource.getTargetPath()).isNull(); + assertThat(resource.isFiltering()).isTrue(); + assertThat(resource.getIncludes()).containsExactly("**/*.yaml"); + assertThat(resource.getExcludes()).containsExactly("**/*.properties"); + }); + assertThat(container.isEmpty()).isFalse(); + } + + @Test + void mavenResourceDefaultValues() { + MavenResourceContainer container = new MavenResourceContainer(); + container.add("src/main/custom"); + assertThat(container.values()).hasOnlyOneElementSatisfying((resource) -> { + assertThat(resource.getDirectory()).isEqualTo("src/main/custom"); + assertThat(resource.getTargetPath()).isNull(); + assertThat(resource.isFiltering()).isFalse(); + assertThat(resource.getIncludes()).isEmpty(); + assertThat(resource.getExcludes()).isEmpty(); + }); + } + + @Test + void mavenResourceCanBeSearched() { + MavenResourceContainer container = new MavenResourceContainer(); + assertThat(container.has("src/main/test")).isFalse(); + container.add("src/main/test"); + assertThat(container.has("src/main/test")).isTrue(); + } + + @Test + void mavenResourceCanBeRemoved() { + MavenResourceContainer container = new MavenResourceContainer(); + container.add("src/main/test"); + assertThat(container.has("src/main/test")).isTrue(); + assertThat(container.remove("src/main/test")).isTrue(); + assertThat(container.has("src/main/test")).isFalse(); + } + +}