mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-17 12:09:16 +00:00
Check javadoc macros in adoc files
Closes gh-48298
This commit is contained in:
@@ -40,12 +40,16 @@ import org.gradle.api.file.Directory;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.logging.LogLevel;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.provider.Provider;
|
||||
import org.gradle.api.tasks.Copy;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskContainer;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
import org.springframework.boot.build.antora.AntoraAsciidocAttributes;
|
||||
import org.springframework.boot.build.antora.CheckJavadocMacros;
|
||||
import org.springframework.boot.build.antora.GenerateAntoraPlaybook;
|
||||
import org.springframework.boot.build.bom.BomExtension;
|
||||
import org.springframework.boot.build.bom.ResolvedBom;
|
||||
@@ -97,6 +101,27 @@ public class AntoraConventions {
|
||||
(antoraTask) -> configureAntoraTask(project, antoraTask, npmInstallTask, generateAntoraPlaybookTask));
|
||||
project.getExtensions()
|
||||
.configure(NodeExtension.class, (nodeExtension) -> configureNodeExtension(project, nodeExtension));
|
||||
TaskProvider<CheckJavadocMacros> checkAntoraJavadocMacros = tasks.register("checkAntoraJavadocMacros",
|
||||
CheckJavadocMacros.class, (task) -> {
|
||||
task.setSource(project.files(ANTORA_SOURCE_DIR));
|
||||
task.getOutputDirectory().set(project.getLayout().getBuildDirectory().dir(task.getName()));
|
||||
});
|
||||
project.getPlugins().withType(JavaPlugin.class, (java) -> {
|
||||
String runtimeClasspathConfigurationName = project.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME)
|
||||
.getRuntimeClasspathConfigurationName();
|
||||
Configuration javadocMacros = project.getConfigurations().create("javadocMacros", (configuration) -> {
|
||||
configuration.extendsFrom(project.getConfigurations().getByName(runtimeClasspathConfigurationName));
|
||||
configuration.setDescription(
|
||||
"Dependencies referenced in javadoc macros. Extends from " + runtimeClasspathConfigurationName);
|
||||
configuration.setCanBeResolved(true);
|
||||
configuration.setCanBeDeclared(true);
|
||||
configuration.setCanBeConsumed(false);
|
||||
});
|
||||
checkAntoraJavadocMacros.configure((macrosTask) -> macrosTask.setClasspath(javadocMacros));
|
||||
});
|
||||
}
|
||||
|
||||
private void configureGenerateAntoraPlaybookTask(Project project,
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* Copyright 2025 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.antora;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.VerificationException;
|
||||
|
||||
import org.springframework.asm.ClassReader;
|
||||
import org.springframework.asm.ClassVisitor;
|
||||
import org.springframework.asm.FieldVisitor;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.SpringAsmInfo;
|
||||
import org.springframework.asm.Type;
|
||||
import org.springframework.util.function.ThrowingConsumer;
|
||||
|
||||
/**
|
||||
* A task to check {@code javadoc:[]} macros in Antora source files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class CheckJavadocMacros extends DefaultTask {
|
||||
|
||||
private static final Pattern JAVADOC_MACRO_PATTERN = Pattern.compile("javadoc:(.*?)\\[(.*?)\\]");
|
||||
|
||||
private final Path projectRoot;
|
||||
|
||||
private FileCollection source;
|
||||
|
||||
private FileCollection classpath;
|
||||
|
||||
public CheckJavadocMacros() {
|
||||
this.projectRoot = getProject().getRootDir().toPath();
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
public FileCollection getSource() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public void setSource(FileCollection source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Optional
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
public void setClasspath(FileCollection classpath) {
|
||||
this.classpath = classpath;
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getOutputDirectory();
|
||||
|
||||
@TaskAction
|
||||
void checkJavadocMacros() {
|
||||
Set<String> availableClasses = indexClasspath();
|
||||
List<String> problems = new ArrayList<>();
|
||||
this.source.getAsFileTree()
|
||||
.filter((file) -> file.getName().endsWith(".adoc"))
|
||||
.forEach((file) -> problems.addAll(checkJavadocMacros(file, availableClasses)));
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
writeReport(problems, outputFile);
|
||||
if (!problems.isEmpty()) {
|
||||
throw new VerificationException("Javadoc macro check failed. See '%s' for details".formatted(outputFile));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> indexClasspath() {
|
||||
Set<String> availableClasses = StreamSupport.stream(this.classpath.spliterator(), false).flatMap((root) -> {
|
||||
if (root.isFile()) {
|
||||
try (JarFile jar = new JarFile(root)) {
|
||||
return jar.stream()
|
||||
.map(JarEntry::getName)
|
||||
.filter((entryName) -> entryName.endsWith(".class"))
|
||||
.map((className) -> {
|
||||
if (className.startsWith("META-INF/versions/")) {
|
||||
className = className.substring("META-INF/versions/".length());
|
||||
}
|
||||
className = className.substring(0, className.length() - ".class".length());
|
||||
className = className.replace('/', '.');
|
||||
return className;
|
||||
})
|
||||
.toList()
|
||||
.stream();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
return Stream.empty();
|
||||
}).collect(Collectors.toSet());
|
||||
return availableClasses;
|
||||
}
|
||||
|
||||
private List<String> checkJavadocMacros(File adocFile, Set<String> availableClasses) {
|
||||
List<String> problems = new ArrayList<>();
|
||||
List<JavadocMacro> macros = JavadocMacro.parse(adocFile);
|
||||
for (JavadocMacro macro : macros) {
|
||||
if (!classIsAvailable(macro.className.name, availableClasses)) {
|
||||
problems.add(this.projectRoot.relativize(macro.className.origin.file.toPath()) + ":"
|
||||
+ macro.className.origin.line + ":" + macro.className.origin.column + ": class "
|
||||
+ macro.className.name + " does not exist.");
|
||||
}
|
||||
else {
|
||||
JavadocAnchor anchor = macro.anchor;
|
||||
if (anchor != null) {
|
||||
if (anchor instanceof MethodAnchor methodAnchor) {
|
||||
MethodMatcher methodMatcher = new MethodMatcher(methodAnchor);
|
||||
inputStreamOf(macro.className.name, (stream) -> {
|
||||
ClassReader reader = new ClassReader(stream);
|
||||
reader.accept(methodMatcher, 0);
|
||||
});
|
||||
if (!methodMatcher.matched) {
|
||||
problems.add(this.projectRoot.relativize(macro.anchor.origin.file.toPath()) + ":"
|
||||
+ macro.anchor.origin.line + ":" + methodAnchor.origin().column + ": method "
|
||||
+ methodAnchor + " does not exist");
|
||||
}
|
||||
}
|
||||
else if (anchor instanceof FieldAnchor fieldAnchor) {
|
||||
FieldMatcher fieldMatcher = new FieldMatcher(fieldAnchor);
|
||||
inputStreamOf(macro.className.name, (stream) -> {
|
||||
ClassReader reader = new ClassReader(stream);
|
||||
reader.accept(fieldMatcher, 0);
|
||||
});
|
||||
if (!fieldMatcher.matched) {
|
||||
problems.add(this.projectRoot.relativize(macro.anchor.origin.file.toPath()) + ":"
|
||||
+ macro.anchor.origin.line + ":" + fieldAnchor.origin().column + ": field "
|
||||
+ fieldAnchor.name + " does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return problems;
|
||||
}
|
||||
|
||||
private boolean classIsAvailable(String className, Set<String> availableClasses) {
|
||||
if (availableClasses.contains(className)) {
|
||||
return true;
|
||||
}
|
||||
if (className.startsWith("java.") || className.startsWith("javax.")) {
|
||||
return jdkResourceForClass(className) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private URL jdkResourceForClass(String className) {
|
||||
return getClass().getClassLoader().getResource(className.replace(".", "/") + ".class");
|
||||
}
|
||||
|
||||
private void inputStreamOf(String className, ThrowingConsumer<InputStream> streamHandler) {
|
||||
for (File root : this.classpath) {
|
||||
if (root.isFile()) {
|
||||
try (JarFile jar = new JarFile(root)) {
|
||||
ZipEntry entry = jar.getEntry(className.replace(".", "/") + ".class");
|
||||
if (entry != null) {
|
||||
try (InputStream stream = jar.getInputStream(entry)) {
|
||||
streamHandler.accept(stream);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
URL resource = jdkResourceForClass(className);
|
||||
if (resource != null) {
|
||||
try (InputStream stream = resource.openStream()) {
|
||||
streamHandler.accept(stream);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeReport(List<String> problems, File outputFile) {
|
||||
outputFile.getParentFile().mkdirs();
|
||||
StringBuilder report = new StringBuilder();
|
||||
if (!problems.isEmpty()) {
|
||||
if (problems.size() == 1) {
|
||||
report.append("Found 1 javadoc macro problem:%n".formatted());
|
||||
}
|
||||
else {
|
||||
report.append("Found %d javadoc macro problems:%n".formatted(problems.size()));
|
||||
}
|
||||
problems.forEach((problem) -> report.append("%s%n".formatted(problem)));
|
||||
}
|
||||
try {
|
||||
Files.writeString(outputFile.toPath(), report.toString(), StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class JavadocMacro {
|
||||
|
||||
private final ClassName className;
|
||||
|
||||
private final JavadocAnchor anchor;
|
||||
|
||||
private JavadocMacro(ClassName className, JavadocAnchor anchor) {
|
||||
this.className = className;
|
||||
this.anchor = anchor;
|
||||
}
|
||||
|
||||
private static List<JavadocMacro> parse(File adocFile) {
|
||||
List<JavadocMacro> macros = new ArrayList<>();
|
||||
try {
|
||||
Path adocFilePath = adocFile.toPath();
|
||||
List<String> lines = Files.readAllLines(adocFilePath);
|
||||
for (int i = 0; i < lines.size(); i++) {
|
||||
Matcher matcher = JAVADOC_MACRO_PATTERN.matcher(lines.get(i));
|
||||
while (matcher.find()) {
|
||||
Origin classNameOrigin = new Origin(adocFile, i + 1, matcher.start(1) + 1);
|
||||
String target = matcher.group(1);
|
||||
String className = target;
|
||||
int endOfUrlAttribute = className.indexOf("}/");
|
||||
if (endOfUrlAttribute != -1) {
|
||||
className = className.substring(endOfUrlAttribute + 2);
|
||||
}
|
||||
JavadocAnchor anchor = null;
|
||||
int anchorIndex = className.indexOf("#");
|
||||
if (anchorIndex != -1) {
|
||||
anchor = JavadocAnchor.of(className.substring(anchorIndex + 1), new Origin(adocFile,
|
||||
classNameOrigin.line(), classNameOrigin.column + anchorIndex + 1));
|
||||
className = className.substring(0, anchorIndex);
|
||||
}
|
||||
macros.add(new JavadocMacro(new ClassName(classNameOrigin, className), anchor));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
return macros;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class ClassName {
|
||||
|
||||
private final Origin origin;
|
||||
|
||||
private final String name;
|
||||
|
||||
private ClassName(Origin origin, String name) {
|
||||
this.origin = origin;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private record Origin(File file, int line, int column) {
|
||||
|
||||
}
|
||||
|
||||
private abstract static class JavadocAnchor {
|
||||
|
||||
private final Origin origin;
|
||||
|
||||
protected JavadocAnchor(Origin origin) {
|
||||
this.origin = origin;
|
||||
}
|
||||
|
||||
Origin origin() {
|
||||
return this.origin;
|
||||
}
|
||||
|
||||
private static JavadocAnchor of(String anchor, Origin origin) {
|
||||
JavadocAnchor javadocAnchor = WellKnownAnchor.of(anchor, origin);
|
||||
if (javadocAnchor == null) {
|
||||
javadocAnchor = MethodAnchor.of(anchor, origin);
|
||||
}
|
||||
if (javadocAnchor == null) {
|
||||
javadocAnchor = FieldAnchor.of(anchor, origin);
|
||||
}
|
||||
return javadocAnchor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class WellKnownAnchor extends JavadocAnchor {
|
||||
|
||||
private WellKnownAnchor(Origin origin) {
|
||||
super(origin);
|
||||
}
|
||||
|
||||
private static WellKnownAnchor of(String anchor, Origin origin) {
|
||||
if (anchor.equals("enum-constant-summary")) {
|
||||
return new WellKnownAnchor(origin);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class MethodAnchor extends JavadocAnchor {
|
||||
|
||||
private final String name;
|
||||
|
||||
private final List<String> arguments;
|
||||
|
||||
private MethodAnchor(String name, List<String> arguments, Origin origin) {
|
||||
super(origin);
|
||||
this.name = name;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name + "(" + String.join(", ", this.arguments + ")");
|
||||
}
|
||||
|
||||
static MethodAnchor of(String anchor, Origin origin) {
|
||||
if (!anchor.contains("(")) {
|
||||
return null;
|
||||
}
|
||||
int openingIndex = anchor.indexOf('(');
|
||||
String name = anchor.substring(0, openingIndex);
|
||||
List<String> arguments = Stream.of(anchor.substring(openingIndex + 1, anchor.length() - 1).split(","))
|
||||
.map(String::trim)
|
||||
.map((argument) -> argument.endsWith("...") ? argument.replace("...", "[]") : argument)
|
||||
.toList();
|
||||
return new MethodAnchor(name, arguments, origin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class FieldAnchor extends JavadocAnchor {
|
||||
|
||||
private final String name;
|
||||
|
||||
private FieldAnchor(String name, Origin origin) {
|
||||
super(origin);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
static FieldAnchor of(String anchor, Origin origin) {
|
||||
return new FieldAnchor(anchor, origin);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class MethodMatcher extends ClassVisitor {
|
||||
|
||||
private final MethodAnchor methodAnchor;
|
||||
|
||||
private boolean matched = false;
|
||||
|
||||
private MethodMatcher(MethodAnchor methodAnchor) {
|
||||
super(SpringAsmInfo.ASM_VERSION);
|
||||
this.methodAnchor = methodAnchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
|
||||
String[] exceptions) {
|
||||
if (!this.matched && name.equals(this.methodAnchor.name)) {
|
||||
Type type = Type.getType(descriptor);
|
||||
if (type.getArgumentCount() == this.methodAnchor.arguments.size()) {
|
||||
List<String> argumentTypeNames = Arrays.asList(type.getArgumentTypes())
|
||||
.stream()
|
||||
.map(Type::getClassName)
|
||||
.toList();
|
||||
if (argumentTypeNames.equals(this.methodAnchor.arguments)) {
|
||||
this.matched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class FieldMatcher extends ClassVisitor {
|
||||
|
||||
private final FieldAnchor fieldAnchor;
|
||||
|
||||
private boolean matched = false;
|
||||
|
||||
private FieldMatcher(FieldAnchor fieldAnchor) {
|
||||
super(SpringAsmInfo.ASM_VERSION);
|
||||
this.fieldAnchor = fieldAnchor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||
if (!this.matched && name.equals(this.fieldAnchor.name)) {
|
||||
this.matched = true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -181,6 +181,68 @@ dependencies {
|
||||
implementation("org.junit.jupiter:junit-jupiter")
|
||||
implementation("org.yaml:snakeyaml")
|
||||
|
||||
javadocMacros(project(":spring-boot-project:spring-boot-tools:spring-boot-loader"))
|
||||
javadocMacros("com.fasterxml.jackson.dataformat:jackson-dataformat-xml")
|
||||
javadocMacros("com.google.code.gson:gson")
|
||||
javadocMacros("com.h2database:h2")
|
||||
javadocMacros("com.hazelcast:hazelcast")
|
||||
javadocMacros("com.hazelcast:hazelcast-spring")
|
||||
javadocMacros("com.redis:testcontainers-redis")
|
||||
javadocMacros("io.lettuce:lettuce-core")
|
||||
javadocMacros("io.micrometer:micrometer-registry-new-relic")
|
||||
javadocMacros("io.opentelemetry:opentelemetry-sdk-logs")
|
||||
javadocMacros("io.opentelemetry:opentelemetry-sdk-metrics")
|
||||
javadocMacros("io.opentelemetry:opentelemetry-sdk-trace")
|
||||
javadocMacros("io.prometheus:prometheus-metrics-tracer-common")
|
||||
javadocMacros("io.prometheus:simpleclient_tracer_common")
|
||||
javadocMacros("io.rsocket:rsocket-core")
|
||||
javadocMacros("jakarta.json.bind:jakarta.json.bind-api")
|
||||
javadocMacros("jakarta.mail:jakarta.mail-api")
|
||||
javadocMacros("jakarta.websocket:jakarta.websocket-api")
|
||||
javadocMacros("org.apache.logging.log4j:log4j-core")
|
||||
javadocMacros("org.apache.activemq:artemis-jakarta-server")
|
||||
javadocMacros("org.eclipse.jetty:jetty-client")
|
||||
javadocMacros("org.eclipse.jetty:jetty-server")
|
||||
javadocMacros("org.elasticsearch.client:elasticsearch-rest-client-sniffer")
|
||||
javadocMacros("org.flywaydb:flyway-core")
|
||||
javadocMacros("org.infinispan:infinispan-core")
|
||||
javadocMacros("org.liquibase:liquibase-core")
|
||||
javadocMacros("org.messaginghub:pooled-jms")
|
||||
javadocMacros("org.postgresql:postgresql")
|
||||
javadocMacros("org.projectlombok:lombok")
|
||||
javadocMacros("org.seleniumhq.selenium:selenium-api")
|
||||
javadocMacros("org.springframework.amqp:spring-rabbit-stream")
|
||||
javadocMacros("org.springframework.data:spring-data-jdbc")
|
||||
javadocMacros("org.springframework.data:spring-data-rest-webmvc")
|
||||
javadocMacros("org.springframework.hateoas:spring-hateoas")
|
||||
javadocMacros("org.springframework.integration:spring-integration-core")
|
||||
javadocMacros("org.springframework.integration:spring-integration-rsocket")
|
||||
javadocMacros("org.springframework.security:spring-security-oauth2-authorization-server")
|
||||
javadocMacros("org.springframework.security:spring-security-oauth2-jose")
|
||||
javadocMacros("org.springframework.security:spring-security-oauth2-resource-server")
|
||||
javadocMacros("org.springframework.security:spring-security-saml2-service-provider") {
|
||||
exclude group: "org.opensaml"
|
||||
}
|
||||
javadocMacros("org.springframework.session:spring-session-core")
|
||||
javadocMacros("org.testcontainers:activemq")
|
||||
javadocMacros("org.testcontainers:cassandra")
|
||||
javadocMacros("org.testcontainers:clickhouse")
|
||||
javadocMacros("org.testcontainers:couchbase")
|
||||
javadocMacros("org.testcontainers:elasticsearch")
|
||||
javadocMacros("org.testcontainers:grafana")
|
||||
javadocMacros("org.testcontainers:jdbc")
|
||||
javadocMacros("org.testcontainers:kafka")
|
||||
javadocMacros("org.testcontainers:mariadb")
|
||||
javadocMacros("org.testcontainers:mssqlserver")
|
||||
javadocMacros("org.testcontainers:mysql")
|
||||
javadocMacros("org.testcontainers:oracle-free")
|
||||
javadocMacros("org.testcontainers:oracle-xe")
|
||||
javadocMacros("org.testcontainers:postgresql")
|
||||
javadocMacros("org.testcontainers:pulsar")
|
||||
javadocMacros("org.testcontainers:rabbitmq")
|
||||
javadocMacros("org.testcontainers:redpanda")
|
||||
javadocMacros("org.thymeleaf:thymeleaf-spring6")
|
||||
|
||||
remoteSpringApplicationExample(platform(project(":spring-boot-project:spring-boot-dependencies")))
|
||||
remoteSpringApplicationExample(project(":spring-boot-project:spring-boot-devtools"))
|
||||
remoteSpringApplicationExample(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-logging"))
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ This can be useful when you want to re-order or remove some of the converters th
|
||||
=== MessageCodesResolver
|
||||
|
||||
Spring MVC has a strategy for generating error codes for rendering error messages from binding errors: javadoc:org.springframework.validation.MessageCodesResolver[].
|
||||
If you set the configprop:spring.mvc.message-codes-resolver-format[] property `PREFIX_ERROR_CODE` or `POSTFIX_ERROR_CODE`, Spring Boot creates one for you (see the enumeration in javadoc:org.springframework.validation.DefaultMessageCodesResolver#Format[]).
|
||||
If you set the configprop:spring.mvc.message-codes-resolver-format[] property `PREFIX_ERROR_CODE` or `POSTFIX_ERROR_CODE`, Spring Boot creates one for you (see the enumeration in javadoc:org.springframework.validation.DefaultMessageCodesResolver$Format[]).
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-1
@@ -61,8 +61,9 @@ javadoc:org.springframework.boot.autoconfigure.security.servlet.PathRequest[] ca
|
||||
|
||||
Similar to Spring MVC applications, you can secure your WebFlux applications by adding the `spring-boot-starter-security` dependency.
|
||||
The default security configuration is implemented in javadoc:org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration[] and javadoc:org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration[].
|
||||
javadoc:org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration[] imports `WebFluxSecurityConfiguration` for web security and javadoc:org.springframework.boot.autoconfigure.security.reactive.UserDetailsServiceAutoConfiguration[] for authentication.
|
||||
In addition to reactive web applications, the latter is also auto-configured when RSocket is in use.
|
||||
javadoc:org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration[] imports `WebFluxSecurityConfiguration` for web security.
|
||||
javadoc:org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration[] auto-configures authentication.
|
||||
|
||||
To completely switch off the default web application security configuration, including Actuator security, add a bean of type javadoc:org.springframework.security.web.server.WebFilterChainProxy[] (doing so does not disable the javadoc:org.springframework.security.core.userdetails.ReactiveUserDetailsService[] configuration).
|
||||
To also switch off the javadoc:org.springframework.security.core.userdetails.ReactiveUserDetailsService[] configuration, add a bean of type javadoc:org.springframework.security.core.userdetails.ReactiveUserDetailsService[] or javadoc:org.springframework.security.authentication.ReactiveAuthenticationManager[].
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ Refer to documentation of the builder being used to determine the image OS and a
|
||||
|
||||
| `imageName`
|
||||
| `--imageName`
|
||||
| javadoc:org.springframework.boot.buildpack.platform.docker.type.ImageReference#of-java.lang.String-[Image name] for the generated image.
|
||||
| javadoc:org.springframework.boot.buildpack.platform.docker.type.ImageReference#of(java.lang.String)[Image name] for the generated image.
|
||||
| `docker.io/library/${project.name}:${project.version}`
|
||||
|
||||
| `pullPolicy`
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ Refer to documentation of the builder being used to determine the image OS and a
|
||||
|
||||
| `name` +
|
||||
(`spring-boot.build-image.imageName`)
|
||||
| javadoc:org.springframework.boot.buildpack.platform.docker.type.ImageName#of-java.lang.String-[Image name] for the generated image.
|
||||
| javadoc:org.springframework.boot.buildpack.platform.docker.type.ImageName#of(java.lang.String)[Image name] for the generated image.
|
||||
| `docker.io/library/` +
|
||||
`${project.artifactId}:${project.version}`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user