Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions initializr-docs/src/main/asciidoc/configuration-guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,20 @@ 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.

Similarly, an `ApplicationPropertiesCustomizer` can be used to contribute properties to
the application configuration file of the generated project, that is
`application.properties` or `application.yaml` depending on the requested
`ConfigurationFileFormat`. Properties added directly to the `ApplicationProperties`
argument are written to the configuration file of the main source set and the default
profile. Properties for another source set or Spring profile can be added to the matching
`section`, and a `src/{sourceSet}/resources/application[-{profile}]` file is generated
for every section that contains properties, as shown in the following example:

[source,java,indent=0]
----
include::{code-examples}/doc/generator/project/ProjectCustomizationExamples.java[tag=application-properties-customizer]
----



[[initializr-generator-spring-requirements]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

package io.spring.initializr.doc.generator.project;

import io.spring.initializr.generator.buildsystem.SourceSet;
import io.spring.initializr.generator.buildsystem.gradle.GradleBuild;
import io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystem;
import io.spring.initializr.generator.condition.ConditionalOnBuildSystem;
import io.spring.initializr.generator.condition.ConditionalOnPackaging;
import io.spring.initializr.generator.packaging.war.WarPackaging;
import io.spring.initializr.generator.spring.build.BuildCustomizer;
import io.spring.initializr.generator.spring.properties.ApplicationPropertiesCustomizer;

import org.springframework.context.annotation.Bean;

Expand All @@ -41,4 +43,18 @@ public BuildCustomizer<GradleBuild> warPluginContributor() {
}
// end::war-plugin-contributor[]

// tag::application-properties-customizer[]
@Bean
public ApplicationPropertiesCustomizer applicationPropertiesContributor() {
return (properties) -> {
// src/main/resources/application.properties (or .yaml)
properties.add("spring.application.name", "acme");
// src/test/resources/application.properties
properties.section(SourceSet.TEST).add("spring.datasource.url", "jdbc:h2:mem:acme");
// src/main/resources/application-dev.properties
properties.section(SourceSet.MAIN, "dev").add("logging.level.root", "DEBUG");
};
}
// end::application-properties-customizer[]

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2012 - present 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.spring.properties;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Map;

import io.spring.initializr.generator.buildsystem.SourceSet;
import io.spring.initializr.generator.project.contributor.ProjectContributor;
import io.spring.initializr.generator.spring.properties.ApplicationProperties.SectionKey;
import org.jspecify.annotations.Nullable;

/**
* Base {@link ProjectContributor} that contributes application configuration files to a
* project. A file is written per source set and Spring profile that has properties,
* following the {@code src/{sourceSet}/resources/application[-{profile}]} convention. The
* file for the main source set and the default profile is always written, even if it is
* empty.
*
* @author Denis A. Altoé Falqueto
* @see ApplicationProperties#section(SourceSet, String)
*/
public abstract class AbstractApplicationPropertiesContributor implements ProjectContributor {

private final ApplicationProperties properties;

private final String extension;

protected AbstractApplicationPropertiesContributor(ApplicationProperties properties, String extension) {
this.properties = properties;
this.extension = extension;
}

@Override
public void contribute(Path projectRoot) throws IOException {
writeSection(projectRoot, SourceSet.MAIN, null, this.properties);
for (Map.Entry<SectionKey, ApplicationProperties> entry : this.properties.getSections().entrySet()) {
ApplicationProperties section = entry.getValue();
if (!section.isEmpty()) {
writeSection(projectRoot, entry.getKey().sourceSet(), entry.getKey().profile(), section);
}
}
}

/**
* Writes the given properties using the given writer.
* @param properties the properties to write
* @param writer the writer to use
*/
protected abstract void write(ApplicationProperties properties, PrintWriter writer);

private void writeSection(Path projectRoot, SourceSet sourceSet, @Nullable String profile,
ApplicationProperties properties) throws IOException {
Path output = projectRoot.resolve(resolveFileName(sourceSet, profile));
if (!Files.exists(output)) {
Files.createDirectories(output.getParent());
Files.createFile(output);
}
try (PrintWriter writer = new PrintWriter(Files.newOutputStream(output, StandardOpenOption.APPEND), false,
StandardCharsets.UTF_8)) {
write(properties, writer);
}
}

private String resolveFileName(SourceSet sourceSet, @Nullable String profile) {
String profileSuffix = (profile != null) ? "-" + profile : "";
return "src/%s/resources/application%s.%s".formatted(sourceSet.getDirectoryName(), profileSuffix,
this.extension);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,24 @@

import java.io.PrintWriter;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import io.spring.initializr.generator.buildsystem.SourceSet;
import org.jspecify.annotations.Nullable;

import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

/**
* Application properties.
* <p>
* Properties added directly to this instance belong to the main source set and the
* default profile, and are written to {@code src/main/resources}. Properties for another
* source set or Spring profile can be added to the {@link #section(SourceSet, String)
* matching section}.
*
* @author Moritz Halbritter
* @author Rodrigo Mibielli Peixoto
Expand All @@ -38,6 +46,57 @@ public class ApplicationProperties {

private final Map<String, Object> properties = new HashMap<>();

private final Map<SectionKey, ApplicationProperties> sections = new LinkedHashMap<>();

private final boolean root;

/**
* Creates the application properties of the main source set and the default profile.
*/
public ApplicationProperties() {
this(true);
}

private ApplicationProperties(boolean root) {
this.root = root;
}

/**
* Returns the application properties for the given source set and profile, creating
* the section if necessary. Calling this method with {@link SourceSet#MAIN} and a
* {@code null} profile returns this instance.
* @param sourceSet the source set the properties belong to
* @param profile the Spring profile the properties belong to, or {@code null} for the
* default profile
* @return the application properties for the given source set and profile
* @throws IllegalStateException if called on a section rather than on the root
* application properties
*/
public ApplicationProperties section(SourceSet sourceSet, @Nullable String profile) {
Assert.notNull(sourceSet, "'sourceSet' must not be null");
Assert.state(this.root, "Sections cannot be nested");
if (profile != null) {
Assert.hasText(profile, "'profile' must not be empty");
}
if (sourceSet == SourceSet.MAIN && profile == null) {
return this;
}
return this.sections.computeIfAbsent(new SectionKey(sourceSet, profile),
(key) -> new ApplicationProperties(false));
}

/**
* Returns the application properties for the given source set and the default
* profile, creating the section if necessary.
* @param sourceSet the source set the properties belong to
* @return the application properties for the given source set
* @throws IllegalStateException if called on a section rather than on the root
* application properties
*/
public ApplicationProperties section(SourceSet sourceSet) {
return section(sourceSet, null);
}

/**
* Adds a new property.
* @param key the key of the property
Expand Down Expand Up @@ -198,4 +257,22 @@ private void add(String key, Object value) {
this.properties.put(key, value);
}

boolean isEmpty() {
return this.properties.isEmpty();
}

Map<SectionKey, ApplicationProperties> getSections() {
return Collections.unmodifiableMap(this.sections);
}

/**
* The source set and profile a section of application properties belongs to.
*
* @param sourceSet the source set the properties belong to
* @param profile the Spring profile the properties belong to, or {@code null} for the
* default profile
*/
record SectionKey(SourceSet sourceSet, @Nullable String profile) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,26 @@

package io.spring.initializr.generator.spring.properties;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import io.spring.initializr.generator.project.contributor.ProjectContributor;

/**
* A {@link ProjectContributor} that contributes a {@code application.properties} file to
* a project.
* A {@link ProjectContributor} that contributes {@code application.properties} files to a
* project, one per source set and Spring profile that has properties.
*
* @author Stephane Nicoll
* @author Moritz Halbritter
*/
public class ApplicationPropertiesContributor implements ProjectContributor {

private static final String FILE = "src/main/resources/application.properties";

private final ApplicationProperties properties;
public class ApplicationPropertiesContributor extends AbstractApplicationPropertiesContributor {

public ApplicationPropertiesContributor(ApplicationProperties properties) {
this.properties = properties;
super(properties, "properties");
}

@Override
public void contribute(Path projectRoot) throws IOException {
Path output = projectRoot.resolve(FILE);
if (!Files.exists(output)) {
Files.createDirectories(output.getParent());
Files.createFile(output);
}
try (PrintWriter writer = new PrintWriter(Files.newOutputStream(output, StandardOpenOption.APPEND), false,
StandardCharsets.UTF_8)) {
this.properties.writeProperties(writer);
}
protected void write(ApplicationProperties properties, PrintWriter writer) {
properties.writeProperties(writer);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,25 @@

package io.spring.initializr.generator.spring.properties;

import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import io.spring.initializr.generator.project.contributor.ProjectContributor;

/**
* A {@link ProjectContributor} that contributes a {@code application.yaml} file to a
* project.
* A {@link ProjectContributor} that contributes {@code application.yaml} files to a
* project, one per source set and Spring profile that has properties.
*
* @author Sijun Yang
*/
public class ApplicationYamlPropertiesContributor implements ProjectContributor {

private static final String FILE = "src/main/resources/application.yaml";

private final ApplicationProperties properties;
public class ApplicationYamlPropertiesContributor extends AbstractApplicationPropertiesContributor {

public ApplicationYamlPropertiesContributor(ApplicationProperties properties) {
this.properties = properties;
super(properties, "yaml");
}

@Override
public void contribute(Path projectRoot) throws IOException {
Path output = projectRoot.resolve(FILE);
if (!Files.exists(output)) {
Files.createDirectories(output.getParent());
Files.createFile(output);
}
try (PrintWriter writer = new PrintWriter(Files.newOutputStream(output, StandardOpenOption.APPEND), false,
StandardCharsets.UTF_8)) {
this.properties.writeYaml(writer);
}
protected void write(ApplicationProperties properties, PrintWriter writer) {
properties.writeYaml(writer);
}

}
Loading