diff --git a/buildSrc/src/main/java/org/springframework/boot/build/devtools/DocumentDevtoolsPropertyDefaults.java b/buildSrc/src/main/java/org/springframework/boot/build/devtools/DocumentDevtoolsPropertyDefaults.java new file mode 100644 index 000000000000..bb9ac2d52576 --- /dev/null +++ b/buildSrc/src/main/java/org/springframework/boot/build/devtools/DocumentDevtoolsPropertyDefaults.java @@ -0,0 +1,101 @@ +/* + * Copyright 2012-2022 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 org.springframework.boot.build.devtools; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +import org.gradle.api.DefaultTask; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.TaskAction; + +/** + * Task for documenting Devtools' property defaults. + * + * @author Andy Wilkinson + */ +public class DocumentDevtoolsPropertyDefaults extends DefaultTask { + + private Configuration devtools; + + private final RegularFileProperty outputFile; + + public DocumentDevtoolsPropertyDefaults() { + this.devtools = getProject().getConfigurations().create("devtools"); + this.outputFile = getProject().getObjects().fileProperty(); + this.outputFile.convention(getProject().getLayout().getBuildDirectory() + .file("docs/generated/using/devtools-property-defaults.adoc")); + Map dependency = new HashMap<>(); + dependency.put("path", ":spring-boot-project:spring-boot-devtools"); + dependency.put("configuration", "propertyDefaults"); + this.devtools.getDependencies().add(getProject().getDependencies().project(dependency)); + } + + @InputFiles + public FileCollection getDevtools() { + return this.devtools; + } + + @OutputFile + public RegularFileProperty getOutputFile() { + return this.outputFile; + } + + @TaskAction + void documentPropertyDefaults() throws IOException { + Map properties = loadProperties(); + documentProperties(properties); + } + + private Map loadProperties() throws IOException, FileNotFoundException { + Properties properties = new Properties(); + Map sortedProperties = new TreeMap<>(); + try (FileInputStream stream = new FileInputStream(this.devtools.getSingleFile())) { + properties.load(stream); + for (String name : properties.stringPropertyNames()) { + sortedProperties.put(name, properties.getProperty(name)); + } + } + return sortedProperties; + } + + private void documentProperties(Map properties) throws IOException { + try (PrintWriter writer = new PrintWriter(new FileWriter(this.outputFile.getAsFile().get()))) { + writer.println("[cols=\"3,1\"]"); + writer.println("|==="); + writer.println("| Name | Default Value"); + properties.forEach((name, value) -> { + writer.println(); + writer.printf("| `%s`%n", name); + writer.printf("| `%s`%n", value); + }); + writer.println("|==="); + } + } + +} diff --git a/spring-boot-project/spring-boot-devtools/build.gradle b/spring-boot-project/spring-boot-devtools/build.gradle index 883393cd532f..dd3e6b16ea5a 100644 --- a/spring-boot-project/spring-boot-devtools/build.gradle +++ b/spring-boot-project/spring-boot-devtools/build.gradle @@ -13,6 +13,13 @@ configurations { intTestDependencies { extendsFrom dependencyManagement } + propertyDefaults +} + +artifacts { + propertyDefaults(file("build/resources/main/org/springframework/boot/devtools/env/devtools-property-defaults.properties")) { + builtBy(processResources) + } } dependencies { diff --git a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java index bd99a3e21c2a..a55b6de3d62e 100755 --- a/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java +++ b/spring-boot-project/spring-boot-devtools/src/main/java/org/springframework/boot/devtools/env/DevToolsPropertyDefaultsPostProcessor.java @@ -16,9 +16,11 @@ package org.springframework.boot.devtools.env; -import java.util.Collections; +import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.Map; +import java.util.Properties; import org.apache.commons.logging.Log; @@ -60,23 +62,19 @@ public class DevToolsPropertyDefaultsPostProcessor implements EnvironmentPostPro private static final Map PROPERTIES; static { - Map properties = new HashMap<>(); - properties.put("spring.thymeleaf.cache", "false"); - properties.put("spring.freemarker.cache", "false"); - properties.put("spring.groovy.template.cache", "false"); - properties.put("spring.mustache.cache", "false"); - properties.put("server.servlet.session.persistent", "true"); - properties.put("spring.h2.console.enabled", "true"); - properties.put("spring.web.resources.cache.period", "0"); - properties.put("spring.web.resources.chain.cache", "false"); - properties.put("spring.template.provider.cache", "false"); - properties.put("spring.mvc.log-resolved-exception", "true"); - properties.put("server.error.include-binding-errors", "ALWAYS"); - properties.put("server.error.include-message", "ALWAYS"); - properties.put("server.error.include-stacktrace", "ALWAYS"); - properties.put("server.servlet.jsp.init-parameters.development", "true"); - properties.put("spring.reactor.debug", "true"); - PROPERTIES = Collections.unmodifiableMap(properties); + Properties properties = new Properties(); + try (InputStream stream = DevToolsPropertyDefaultsPostProcessor.class + .getResourceAsStream("devtools-property-defaults.properties")) { + properties.load(stream); + } + catch (IOException ex) { + throw new RuntimeException("Failed to load devtools-property-defaults.properties", ex); + } + Map map = new HashMap<>(); + for (String name : properties.stringPropertyNames()) { + map.put(name, properties.getProperty(name)); + } + PROPERTIES = map; } @Override diff --git a/spring-boot-project/spring-boot-devtools/src/main/resources/org/springframework/boot/devtools/env/devtools-property-defaults.properties b/spring-boot-project/spring-boot-devtools/src/main/resources/org/springframework/boot/devtools/env/devtools-property-defaults.properties new file mode 100644 index 000000000000..ea6141489d71 --- /dev/null +++ b/spring-boot-project/spring-boot-devtools/src/main/resources/org/springframework/boot/devtools/env/devtools-property-defaults.properties @@ -0,0 +1,15 @@ +server.error.include-binding-errors=always +server.error.include-message=always +server.error.include-stacktrace=always +server.servlet.jsp.init-parameters.development=true +server.servlet.session.persistent=true +spring.freemarker.cache=false +spring.groovy.template.cache=false +spring.h2.console.enabled=true +spring.mustache.cache=false +spring.mvc.log-resolved-exception=true +spring.reactor.debug=true +spring.template.provider.cache=false +spring.thymeleaf.cache=false +spring.web.resources.cache.period=0 +spring.web.resources.chain.cache=false \ No newline at end of file diff --git a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java index 6d470935a129..a5a0d62002b3 100644 --- a/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java +++ b/spring-boot-project/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/env/DevToolPropertiesIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2022 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. @@ -18,6 +18,7 @@ import java.net.URL; import java.util.Collections; +import java.util.Locale; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; @@ -107,9 +108,11 @@ void postProcessEnablesIncludeStackTraceProperty() throws Exception { this.context = getContext(application::run); ConfigurableEnvironment environment = this.context.getEnvironment(); String includeStackTrace = environment.getProperty("server.error.include-stacktrace"); - assertThat(includeStackTrace).isEqualTo(ErrorProperties.IncludeAttribute.ALWAYS.toString()); + assertThat(includeStackTrace) + .isEqualTo(ErrorProperties.IncludeAttribute.ALWAYS.toString().toLowerCase(Locale.ENGLISH)); String includeMessage = environment.getProperty("server.error.include-message"); - assertThat(includeMessage).isEqualTo(ErrorProperties.IncludeAttribute.ALWAYS.toString()); + assertThat(includeMessage) + .isEqualTo(ErrorProperties.IncludeAttribute.ALWAYS.toString().toLowerCase(Locale.ENGLISH)); } protected ConfigurableApplicationContext getContext(Supplier supplier) diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 4aa778fc41e2..657b4712aa63 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -249,6 +249,8 @@ task documentConfigurationProperties(type: org.springframework.boot.build.contex outputDir = file("${buildDir}/docs/generated/") } +task documentDevtoolsPropertyDefaults(type: org.springframework.boot.build.devtools.DocumentDevtoolsPropertyDefaults) {} + tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { dependsOn dependencyVersions asciidoctorj { @@ -309,6 +311,7 @@ syncDocumentationSourceForAsciidoctor { dependsOn documentDependencyVersions dependsOn documentVersionProperties dependsOn documentConfigurationProperties + dependsOn documentDevtoolsPropertyDefaults from("${buildDir}/docs/generated") { into "asciidoc" } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc index a44cd2a232b8..a870fe4498bf 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/using/devtools.adoc @@ -67,13 +67,15 @@ Cache options are usually configured by settings in your `application.properties For example, Thymeleaf offers the configprop:spring.thymeleaf.cache[] property. Rather than needing to set these properties manually, the `spring-boot-devtools` module automatically applies sensible development-time configuration. -Because you need more information about web requests while developing Spring MVC and Spring WebFlux applications, developer tools suggests you to enable `DEBUG` logging for the `web` logging group. -This will give you information about the incoming request, which handler is processing it, the response outcome, and other details. -If you wish to log all request details (including potentially sensitive information), you can turn on the configprop:spring.mvc.log-request-details[] or configprop:spring.codec.log-request-details[] configuration properties. +The following table lists all the properties that are applied: + +include::devtools-property-defaults.adoc[] NOTE: If you do not want property defaults to be applied you can set configprop:spring.devtools.add-properties[] to `false` in your `application.properties`. -TIP: For a complete list of the properties that are applied by the devtools, see {spring-boot-devtools-module-code}/env/DevToolsPropertyDefaultsPostProcessor.java[DevToolsPropertyDefaultsPostProcessor]. +Because you need more information about web requests while developing Spring MVC and Spring WebFlux applications, developer tools suggests you to enable `DEBUG` logging for the `web` logging group. +This will give you information about the incoming request, which handler is processing it, the response outcome, and other details. +If you wish to log all request details (including potentially sensitive information), you can turn on the configprop:spring.mvc.log-request-details[] or configprop:spring.codec.log-request-details[] configuration properties.