Use offline javadoc links

Update docs build to use offline javadoc links based on the
contents of the javadoc jars. Since some libraries ship incorrect
Gradle `.module` files, new dependency variants have been added for
`implicitjavadoc` and `implicitsources`. These assume that javadoc
and source jars are shipped using Maven conventions. These variants
must be resolved using `lenient = true` since not all libraries ship
them.

The `element-list` files for `log4j` and `tomcat` have been copied
directly from their website since they are available online but no
javadoc jars are shipped.

The `aggregatedJavadoc` task will now fail if links are made to
unknown packages.

Closes gh-51456
This commit is contained in:
Phillip Webb
2026-09-17 16:47:01 -07:00
parent 59ec39fc50
commit f7c699ebdc
12 changed files with 773 additions and 177 deletions
@@ -389,19 +389,17 @@ class JavaConventions {
private void configureDependencyFixes(Project project) {
project.getDependencies().getComponents().all((details) -> {
// https://github.com/spring-projects/spring-framework/issues/37209
// https://github.com/spring-projects/spring-graphql/issues/1515
String group = details.getId().getGroup();
if ("org.springframework".equals(group) || "org.springframework.graphql".equals(group)) {
addDocumentationVariant(project, details, "javadocElements", DocsType.JAVADOC, "javadoc");
addDocumentationVariant(project, details, "sourcesElements", DocsType.SOURCES, "sources");
}
addDocumentationVariant(project, details, "implicitjavadoc", "implicitjavadoc", "javadoc");
addDocumentationVariant(project, details, "implicitsources", "implicitsources", "sources");
});
}
private void addDocumentationVariant(Project project, ComponentMetadataDetails details, String name,
String docsType, String classifier) {
ModuleVersionIdentifier id = details.getId();
if ("org.nodejs".equals(id.getGroup())) {
return;
}
String file = "%s-%s-%s.jar".formatted(id.getName(), id.getVersion(), classifier);
details.addVariant(name, (variant) -> {
variant.attributes((attributes) -> addDocumentationAttributes(project, attributes, docsType));
@@ -24,7 +24,10 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
@@ -43,13 +46,13 @@ import org.springframework.boot.build.bom.Library.Group;
import org.springframework.boot.build.bom.Library.ImportedBom;
import org.springframework.boot.build.bom.Library.Link;
import org.springframework.boot.build.bom.Library.LinkType;
import org.springframework.boot.build.bom.Library.LinkedVersion;
import org.springframework.boot.build.bom.Library.Module;
import org.springframework.boot.build.bom.ResolvedBom.Bom;
import org.springframework.boot.build.bom.ResolvedBom.Id;
import org.springframework.boot.build.bom.ResolvedBom.JavadocLink;
import org.springframework.boot.build.bom.ResolvedBom.Links;
import org.springframework.boot.build.bom.ResolvedBom.ResolvedLibrary;
import org.springframework.boot.build.xml.XmlDocument;
import org.springframework.util.Assert;
/**
* Creates a {@link ResolvedBom resolved bom}.
@@ -58,6 +61,8 @@ import org.springframework.boot.build.xml.XmlDocument;
*/
class BomResolver {
private static final Set<String> SKIPPED_OFFLINE_JAVADOC_LIBRARIES = Set.of("Spring Boot");
private final ConfigurationContainer configurations;
private final DependencyHandler dependencies;
@@ -70,51 +75,37 @@ class BomResolver {
this.documentBuilder = XmlDocument.builder();
}
ResolvedBom resolve(BomExtension bomExtension) {
List<ResolvedLibrary> libraries = new ArrayList<>();
for (Library library : bomExtension.getLibraries()) {
List<Id> managedDependencies = new ArrayList<>();
List<Bom> imports = new ArrayList<>();
for (Group group : library.getGroups()) {
for (Module module : group.getModules()) {
Id id = new Id(group.getId(), module.getName(), library.getVersion().toString());
managedDependencies.add(id);
}
for (ImportedBom imported : group.getBoms()) {
Bom bom = bomFrom(
resolveBom("%s:%s:%s".formatted(group.getId(), imported.name(), library.getVersion())));
imports.add(bom);
}
ResolvedBom resolve(BomExtension bom) {
Id id = Id.parse(bom.getId());
List<ResolvedLibrary> resolvedLibraries = bom.getLibraries().stream().map(this::resolve).toList();
Map<URI, List<String>> offlineJavadocLinks = getOfflineJavadocLinks(bom, resolvedLibraries);
return new ResolvedBom(id, resolvedLibraries, offlineJavadocLinks);
}
private ResolvedLibrary resolve(Library library) {
List<Id> managedDependencies = new ArrayList<>();
List<Bom> imports = new ArrayList<>();
for (Group group : library.getGroups()) {
for (Module module : group.getModules()) {
Id managedDependency = new Id(group.getId(), module.getName(), library.getVersion().toString());
managedDependencies.add(managedDependency);
}
for (ImportedBom importedBom : group.getBoms()) {
String importedId = "%s:%s:%s".formatted(group.getId(), importedBom.name(), library.getVersion());
imports.add(resolveBom(importedId));
}
List<JavadocLink> javadocLinks = javadocLinksOf(library).stream()
.map((link) -> new JavadocLink(URI.create(link.url(library.getVersion())), link.packages()))
.toList();
ResolvedLibrary resolvedLibrary = new ResolvedLibrary(library.getName(), library.getVersion().toString(),
library.getVersionProperty(), managedDependencies, imports, new Links(javadocLinks));
libraries.add(resolvedLibrary);
}
String[] idComponents = bomExtension.getId().split(":");
return new ResolvedBom(new Id(idComponents[0], idComponents[1], idComponents[2]), libraries);
return new ResolvedLibrary(library.getName(), library.getVersion().toString(), library.getVersionProperty(),
managedDependencies, imports);
}
private List<Link> javadocLinksOf(Library library) {
List<Link> javadocLinks = library.getLinks(LinkType.JAVADOC);
return (javadocLinks != null) ? javadocLinks : Collections.emptyList();
}
Bom resolveMavenBom(String coordinates) {
return bomFrom(resolveBom(coordinates));
}
private File resolveBom(String coordinates) {
private File resolveBomFile(String coordinates) {
Set<ResolvedArtifact> artifacts = this.configurations
.detachedConfiguration(this.dependencies.create(coordinates + "@pom"))
.getResolvedConfiguration()
.getResolvedArtifacts();
if (artifacts.size() != 1) {
throw new IllegalStateException("Expected a single artifact but '%s' resolved to %d artifacts"
.formatted(coordinates, artifacts.size()));
}
Assert.state(artifacts.size() == 1, "Expected a single artifact but '%s' resolved to %d artifacts"
.formatted(coordinates, artifacts.size()));
return artifacts.iterator().next().getFile();
}
@@ -122,52 +113,61 @@ class BomResolver {
try {
Node bom = nodeFrom(bomFile);
File parentBomFile = parentBomFile(bom);
Bom parent = null;
if (parentBomFile != null) {
parent = bomFrom(parentBomFile);
}
Bom parent = (parentBomFile != null) ? bomFrom(parentBomFile) : null;
Properties properties = Properties.from(bom, this::nodeFrom);
List<Node> dependencyNodes = bom.nodesAt("/project/dependencyManagement/dependencies/dependency");
List<Id> managedDependencies = new ArrayList<>();
List<Bom> imports = new ArrayList<>();
for (Node dependency : dependencyNodes) {
String groupId = properties.replace(dependency.textAt("groupId"));
String artifactId = properties.replace(dependency.textAt("artifactId"));
String version = properties.replace(dependency.textAt("version"));
String classifier = properties.replace(dependency.textAt("classifier"));
String scope = properties.replace(dependency.textAt("scope"));
Bom importedBom = null;
if ("import".equals(scope)) {
String type = properties.replace(dependency.textAt("type"));
if ("pom".equals(type)) {
importedBom = bomFrom(resolveBom(groupId + ":" + artifactId + ":" + version));
}
}
Id dependencyId = getDependencyId(dependency, properties);
Bom importedBom = (isPomImport(properties, dependency)) ? resolveBom(dependencyId.toString()) : null;
if (importedBom != null) {
imports.add(importedBom);
}
else {
managedDependencies.add(new Id(groupId, artifactId, version, classifier));
managedDependencies.add(dependencyId);
}
}
String groupId = bom.textAt("/project/groupId");
if ((groupId == null || groupId.isEmpty()) && parent != null) {
groupId = parent.id().groupId();
}
String artifactId = bom.textAt("/project/artifactId");
String version = bom.textAt("/project/version");
if ((version == null || version.isEmpty()) && parent != null) {
version = parent.id().version();
}
return new Bom(new Id(groupId, artifactId, version), parent, managedDependencies, imports);
Id bomId = getBomId(bom, parent);
return new Bom(bomId, parent, managedDependencies, imports);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private Id getDependencyId(Node dependency, Properties properties) {
String groupId = properties.replace(dependency.textAt("groupId"));
String artifactId = properties.replace(dependency.textAt("artifactId"));
String version = properties.replace(dependency.textAt("version"));
String classifier = properties.replace(dependency.textAt("classifier"));
return new Id(groupId, artifactId, version, classifier);
}
private boolean isPomImport(Properties properties, Node dependency) {
String scope = properties.replace(dependency.textAt("scope"));
return "import".equals(scope) && "pom".equals(properties.replace(dependency.textAt("type")));
}
private Id getBomId(Node bom, Bom parent) {
String groupId = bom.textAt("/project/groupId");
if ((groupId == null || groupId.isEmpty()) && parent != null) {
groupId = parent.id().groupId();
}
String artifactId = bom.textAt("/project/artifactId");
String version = bom.textAt("/project/version");
if ((version == null || version.isEmpty()) && parent != null) {
version = parent.id().version();
}
return new Id(groupId, artifactId, version);
}
Bom resolveBom(String coordinates) {
return bomFrom(resolveBomFile(coordinates));
}
private Node nodeFrom(String coordinates) {
return nodeFrom(resolveBom(coordinates));
return nodeFrom(resolveBomFile(coordinates));
}
private Node nodeFrom(File bomFile) {
@@ -186,11 +186,44 @@ class BomResolver {
String parentGroupId = parent.textAt("groupId");
String parentArtifactId = parent.textAt("artifactId");
String parentVersion = parent.textAt("version");
return resolveBom(parentGroupId + ":" + parentArtifactId + ":" + parentVersion);
return resolveBomFile(parentGroupId + ":" + parentArtifactId + ":" + parentVersion);
}
return null;
}
private Map<URI, List<String>> getOfflineJavadocLinks(BomExtension bom, List<ResolvedLibrary> resolvedLibraries) {
Map<URI, List<String>> offlineJavadocLinks = new TreeMap<>();
for (Library library : bom.getLibraries()) {
if (SKIPPED_OFFLINE_JAVADOC_LIBRARIES.contains(library.getName())) {
continue;
}
ResolvedLibrary resolvedLibrary = ResolvedLibrary.find(resolvedLibraries, library);
Set<Id> linkedDependencies = new TreeSet<>(resolvedLibrary.allDependencies().toList());
library.getModuleLinks().forEach((module, moduleLinks) -> {
moduleLinks.byType().getOrDefault(LinkType.JAVADOC, Collections.emptyList()).forEach((link) -> {
String version = resolvedLibrary.moduleVersion(module);
List<String> moduleNames = module.moduleNames();
addOfflineJavadocLink(offlineJavadocLinks, link, version, moduleNames.stream());
module.moduleNames()
.forEach((moduleName) -> linkedDependencies
.removeIf((candidate) -> candidate.artifactId().equals(moduleName)));
});
});
library.getLinks().byType().getOrDefault(LinkType.JAVADOC, Collections.emptyList()).forEach((link) -> {
Stream<String> modulesNames = linkedDependencies.stream().map(Id::artifactId);
addOfflineJavadocLink(offlineJavadocLinks, link, library.getVersion(), modulesNames);
});
}
return offlineJavadocLinks;
}
private void addOfflineJavadocLink(Map<URI, List<String>> offlineJavadocLinks, Link link, Object version,
Stream<String> moduleNames) {
URI uri = URI.create(link.url(new LinkedVersion(version)));
offlineJavadocLinks.computeIfAbsent(uri, (key) -> new ArrayList<>())
.addAll(moduleNames.map((name) -> "%s-%s-javadoc.jar".formatted(name, version)).toList());
}
private static final class Node {
private final XPath xpath;
@@ -295,8 +295,7 @@ public abstract class CheckBom extends DefaultTask {
List<String> errors = new ArrayList<>();
BomAlignment alignsWithBom = library.getAlignsWithBom();
if (alignsWithBom != null) {
Bom mavenBom = this.bomResolver
.resolveMavenBom(alignsWithBom.getCoordinates() + ":" + library.getVersion());
Bom mavenBom = this.bomResolver.resolveBom(alignsWithBom.getCoordinates() + ":" + library.getVersion());
checkDependencyManagementAlignment(resolvedLibrary, mavenBom, errors, alignsWithBom::exclude);
}
return errors;
@@ -41,8 +41,9 @@ import org.springframework.util.Assert;
* @author Andy Wilkinson
* @param id the ID of the resolved bom
* @param libraries the libraries declared in the bom
* @param offlineJavadocLinks offline javadoc links
*/
public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
public record ResolvedBom(Id id, List<ResolvedLibrary> libraries, Map<URI, List<String>> offlineJavadocLinks) {
private static final JsonMapper jsonMapper;
@@ -53,13 +54,7 @@ public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
}
public ResolvedLibrary library(Library library) {
String name = library.getName();
List<ResolvedLibrary> matching = libraries().stream()
.filter((candidate) -> candidate.name().equals(name))
.toList();
Assert.state(!matching.isEmpty(), () -> "No library found with name '%s'".formatted(name));
Assert.state(matching.size() == 1, () -> "Multiple libraries found with name '%s'".formatted(name));
return matching.get(0);
return ResolvedLibrary.find(libraries(), library);
}
public Map<String, String> dependencyVersions() {
@@ -71,6 +66,10 @@ public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
return libraries().stream().flatMap(ResolvedLibrary::allDependencies);
}
public void writeTo(Writer writer) {
jsonMapper.writeValue(writer, this);
}
public static ResolvedBom readFrom(File file) {
try (FileReader reader = new FileReader(file)) {
return jsonMapper.readValue(reader, ResolvedBom.class);
@@ -80,12 +79,8 @@ public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
}
}
public void writeTo(Writer writer) {
jsonMapper.writeValue(writer, this);
}
public record ResolvedLibrary(String name, String version, String versionProperty, List<Id> managedDependencies,
List<Bom> importedBoms, Links links) {
List<Bom> importedBoms) {
public String moduleVersion(LinkedModule linkedModule) {
Set<String> matching = allDependencies()
@@ -102,6 +97,16 @@ public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
return Stream.concat(managedDependencies().stream(), importedBoms().stream().flatMap(Bom::allDependencies));
}
public static ResolvedLibrary find(List<ResolvedLibrary> libraries, Library library) {
String name = library.getName();
List<ResolvedLibrary> matching = libraries.stream()
.filter((candidate) -> candidate.name().equals(name))
.toList();
Assert.state(!matching.isEmpty(), () -> "No library found with name '%s'".formatted(name));
Assert.state(matching.size() == 1, () -> "Multiple libraries found with name '%s'".formatted(name));
return matching.get(0);
}
}
public record Bom(Id id, Bom parent, List<Id> managedDependencies, List<Bom> importedBoms) {
@@ -153,13 +158,10 @@ public record ResolvedBom(Id id, List<ResolvedLibrary> libraries) {
return groupId() + ":" + artifactId();
}
}
public record Links(List<JavadocLink> javadoc) {
}
public record JavadocLink(URI uri, List<String> packages) {
public static Id parse(String value) {
String[] components = value.split(":");
return new Id(components[0], components[1], components[2]);
}
}
@@ -0,0 +1,161 @@
/*
* 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 org.springframework.boot.build.docs;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.gradle.api.UncheckedIOException;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.FileCollection;
import org.gradle.api.logging.LogLevel;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.external.javadoc.StandardJavadocDocletOptions;
import org.springframework.boot.build.bom.ResolvedBom;
import org.springframework.util.StringUtils;
/**
* Specialized {@link Javadoc} task for aggregated javadoc generation.
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
public abstract class AggregatedJavadoc extends Javadoc {
private static final Set<String> JAVADOC_LIST_FILES = Set.of("package-list", "element-list");
private static final Map<Pattern, String> BUNDLED_JAVADOC_LISTS;
static {
Map<Pattern, String> bundledJavadocLists = new LinkedHashMap<>();
bundledJavadocLists.put(Pattern.compile("^tomcat-embed-core-.*-javadoc\\.jar$"), "tomcat");
bundledJavadocLists.put(Pattern.compile("^log4j-api-.*-javadoc\\.jar$"), "log4j-api");
bundledJavadocLists.put(Pattern.compile("^log4j-core-.*-javadoc\\.jar$"), "log4j-core");
BUNDLED_JAVADOC_LISTS = Collections.unmodifiableMap(bundledJavadocLists);
}
private static final List<String> IGNORED_PACKAGES;
static {
List<String> ignoredPackages = new ArrayList<>();
ignoredPackages.add("com.datastax.oss.driver.api.core");
ignoredPackages.add("com.datastax.oss.driver.api.core.config");
ignoredPackages.add("kotlinx.serialization.json");
ignoredPackages.add("org.apache.activemq.artemis.core.server.embedded");
ignoredPackages.add("org.hibernate.engine.transaction.jta.platform.internal");
IGNORED_PACKAGES = Collections.unmodifiableList(ignoredPackages);
}
@Classpath
@InputFiles
public abstract ConfigurableFileCollection getResolvedBom();
@Classpath
@InputFiles
public abstract ConfigurableFileCollection getJavadocJars();
@Override
protected void generate() {
StandardJavadocDocletOptions options = (StandardJavadocDocletOptions) getOptions();
options.doclet("io.spring.javaformat.doclet.OfflineLinksDoclet");
options.addBooleanOption("quiet", true);
options.addBooleanOption("Xdoclint:all,-missing", true);
options.addBooleanOption("Werror", true);
options.links("https://docs.oracle.com/en/java/javase/17/docs/api/");
configureOfflineLinks(options);
super.generate();
}
private void configureOfflineLinks(StandardJavadocDocletOptions options) {
ResolvedBom resolvedBom = ResolvedBom.readFrom(getResolvedBom().getSingleFile());
File javadocListsDir = getProject().getLayout().getBuildDirectory().get().dir("docs/javadoclists").getAsFile();
copyElementListResourceIfMissing(javadocListsDir, "javase");
extractJavdocListFiles(javadocListsDir);
if (getProject().getGradle().getStartParameter().getLogLevel() == LogLevel.DEBUG) {
options.addBooleanOption("offlinelinks-debug", true);
}
options.addStringOption("-link-modularity-mismatch", "info");
options.addStringOption("offlinelinks-ignore-packages",
IGNORED_PACKAGES.stream().collect(Collectors.joining(",")));
options.addStringOption("offlinelinks-source", new File(javadocListsDir, "@name@").getAbsolutePath());
options.linksOffline("https://docs.oracle.com/en/java/javase/17/docs/api", "javase");
resolvedBom.offlineJavadocLinks()
.forEach((url, jars) -> addOfflineLinksOption(options, javadocListsDir, url, jars));
}
private void addOfflineLinksOption(StandardJavadocDocletOptions options, File javadocListsDir, URI url,
List<String> jars) {
for (Map.Entry<Pattern, String> bundled : BUNDLED_JAVADOC_LISTS.entrySet()) {
if (matchesBundled(bundled.getKey(), jars)) {
copyElementListResourceIfMissing(javadocListsDir, bundled.getValue());
options.linksOffline(url.toString(), bundled.getValue());
return;
}
}
String listOfJavadocJars = jars.stream()
.filter((jar) -> Files.isDirectory(javadocListsDir.toPath().resolve(jar)))
.collect(Collectors.joining(","));
if (StringUtils.hasLength(listOfJavadocJars)) {
options.linksOffline(url.toString(), listOfJavadocJars);
}
}
private boolean matchesBundled(Pattern pattern, List<String> jars) {
return jars.stream().anyMatch((jar) -> pattern.matcher(jar).matches());
}
private void copyElementListResourceIfMissing(File javadocListsDir, String name) {
try {
Path directory = javadocListsDir.toPath().resolve(name);
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
Path file = directory.resolve("element-list");
if (!Files.exists(file)) {
Files.copy(getClass().getResourceAsStream(name + "-element-list"), file);
}
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
private void extractJavdocListFiles(File javadocListsDir) {
getJavadocJars().forEach((javadocJar) -> {
FileCollection source = getProject().zipTree(javadocJar).filter(this::isJavadocListFile);
File destination = new File(javadocListsDir, javadocJar.getName());
getProject().copy((copy) -> copy.from(source).into(destination));
});
}
private boolean isJavadocListFile(File file) {
return JAVADOC_LIST_FILES.contains(file.getName());
}
}
@@ -1,72 +0,0 @@
/*
* 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 org.springframework.boot.build.docs;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.gradle.api.Action;
import org.gradle.api.file.FileCollection;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.external.javadoc.StandardJavadocDocletOptions;
import org.springframework.boot.build.bom.ResolvedBom;
import org.springframework.boot.build.bom.ResolvedBom.JavadocLink;
/**
* An {@link Action} to configure the links option of a {@link Javadoc} task.
*
* @author Andy Wilkinson
*/
public class ConfigureJavadocLinks implements Action<Javadoc> {
private final FileCollection resolvedBoms;
private final Collection<String> includedLibraries;
public ConfigureJavadocLinks(FileCollection resolvedBoms, Collection<String> includedLibraries) {
this.resolvedBoms = resolvedBoms;
this.includedLibraries = includedLibraries;
}
@Override
public void execute(Javadoc javadoc) {
javadoc.options((options) -> {
if (options instanceof StandardJavadocDocletOptions standardOptions) {
configureLinks(standardOptions);
}
});
}
private void configureLinks(StandardJavadocDocletOptions options) {
ResolvedBom resolvedBom = ResolvedBom.readFrom(this.resolvedBoms.getSingleFile());
List<String> links = new ArrayList<>();
links.add("https://docs.oracle.com/en/java/javase/17/docs/api/");
links.add("https://jakarta.ee/specifications/platform/11/apidocs/");
resolvedBom.libraries()
.stream()
.filter((candidate) -> this.includedLibraries.contains(candidate.name()))
.flatMap((library) -> library.links().javadoc().stream())
.map(JavadocLink::uri)
.map(URI::toString)
.forEach(links::add);
options.setLinks(links);
}
}
@@ -0,0 +1,284 @@
module:java.base
java.io
java.lang
java.lang.annotation
java.lang.constant
java.lang.invoke
java.lang.module
java.lang.ref
java.lang.reflect
java.lang.runtime
java.math
java.net
java.net.spi
java.nio
java.nio.channels
java.nio.channels.spi
java.nio.charset
java.nio.charset.spi
java.nio.file
java.nio.file.attribute
java.nio.file.spi
java.security
java.security.cert
java.security.interfaces
java.security.spec
java.text
java.text.spi
java.time
java.time.chrono
java.time.format
java.time.temporal
java.time.zone
java.util
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
java.util.function
java.util.jar
java.util.random
java.util.regex
java.util.spi
java.util.stream
java.util.zip
javax.crypto
javax.crypto.interfaces
javax.crypto.spec
javax.net
javax.net.ssl
javax.security.auth
javax.security.auth.callback
javax.security.auth.login
javax.security.auth.spi
javax.security.auth.x500
javax.security.cert
module:java.compiler
javax.annotation.processing
javax.lang.model
javax.lang.model.element
javax.lang.model.type
javax.lang.model.util
javax.tools
module:java.datatransfer
java.awt.datatransfer
module:java.desktop
java.applet
java.awt
java.awt.color
java.awt.desktop
java.awt.dnd
java.awt.event
java.awt.font
java.awt.geom
java.awt.im
java.awt.im.spi
java.awt.image
java.awt.image.renderable
java.awt.print
java.beans
java.beans.beancontext
javax.accessibility
javax.imageio
javax.imageio.event
javax.imageio.metadata
javax.imageio.plugins.bmp
javax.imageio.plugins.jpeg
javax.imageio.plugins.tiff
javax.imageio.spi
javax.imageio.stream
javax.print
javax.print.attribute
javax.print.attribute.standard
javax.print.event
javax.sound.midi
javax.sound.midi.spi
javax.sound.sampled
javax.sound.sampled.spi
javax.swing
javax.swing.border
javax.swing.colorchooser
javax.swing.event
javax.swing.filechooser
javax.swing.plaf
javax.swing.plaf.basic
javax.swing.plaf.metal
javax.swing.plaf.multi
javax.swing.plaf.nimbus
javax.swing.plaf.synth
javax.swing.table
javax.swing.text
javax.swing.text.html
javax.swing.text.html.parser
javax.swing.text.rtf
javax.swing.tree
javax.swing.undo
module:java.instrument
java.lang.instrument
module:java.logging
java.util.logging
module:java.management
java.lang.management
javax.management
javax.management.loading
javax.management.modelmbean
javax.management.monitor
javax.management.openmbean
javax.management.relation
javax.management.remote
javax.management.timer
module:java.management.rmi
javax.management.remote.rmi
module:java.naming
javax.naming
javax.naming.directory
javax.naming.event
javax.naming.ldap
javax.naming.ldap.spi
javax.naming.spi
module:java.net.http
java.net.http
module:java.prefs
java.util.prefs
module:java.rmi
java.rmi
java.rmi.dgc
java.rmi.registry
java.rmi.server
javax.rmi.ssl
module:java.scripting
javax.script
module:java.se
module:java.security.jgss
javax.security.auth.kerberos
org.ietf.jgss
module:java.security.sasl
javax.security.sasl
module:java.smartcardio
javax.smartcardio
module:java.sql
java.sql
javax.sql
module:java.sql.rowset
javax.sql.rowset
javax.sql.rowset.serial
javax.sql.rowset.spi
module:java.transaction.xa
javax.transaction.xa
module:java.xml
javax.xml
javax.xml.catalog
javax.xml.datatype
javax.xml.namespace
javax.xml.parsers
javax.xml.stream
javax.xml.stream.events
javax.xml.stream.util
javax.xml.transform
javax.xml.transform.dom
javax.xml.transform.sax
javax.xml.transform.stax
javax.xml.transform.stream
javax.xml.validation
javax.xml.xpath
org.w3c.dom
org.w3c.dom.bootstrap
org.w3c.dom.events
org.w3c.dom.ls
org.w3c.dom.ranges
org.w3c.dom.traversal
org.w3c.dom.views
org.xml.sax
org.xml.sax.ext
org.xml.sax.helpers
module:java.xml.crypto
javax.xml.crypto
javax.xml.crypto.dom
javax.xml.crypto.dsig
javax.xml.crypto.dsig.dom
javax.xml.crypto.dsig.keyinfo
javax.xml.crypto.dsig.spec
module:jdk.accessibility
com.sun.java.accessibility.util
module:jdk.attach
com.sun.tools.attach
com.sun.tools.attach.spi
module:jdk.charsets
module:jdk.compiler
com.sun.source.doctree
com.sun.source.tree
com.sun.source.util
com.sun.tools.javac
module:jdk.crypto.cryptoki
module:jdk.crypto.ec
module:jdk.dynalink
jdk.dynalink
jdk.dynalink.beans
jdk.dynalink.linker
jdk.dynalink.linker.support
jdk.dynalink.support
module:jdk.editpad
module:jdk.hotspot.agent
module:jdk.httpserver
com.sun.net.httpserver
com.sun.net.httpserver.spi
module:jdk.incubator.foreign
jdk.incubator.foreign
module:jdk.incubator.vector
jdk.incubator.vector
module:jdk.jartool
com.sun.jarsigner
jdk.security.jarsigner
module:jdk.javadoc
jdk.javadoc.doclet
module:jdk.jcmd
module:jdk.jconsole
com.sun.tools.jconsole
module:jdk.jdeps
module:jdk.jdi
com.sun.jdi
com.sun.jdi.connect
com.sun.jdi.connect.spi
com.sun.jdi.event
com.sun.jdi.request
module:jdk.jdwp.agent
module:jdk.jfr
jdk.jfr
jdk.jfr.consumer
module:jdk.jlink
module:jdk.jpackage
module:jdk.jshell
jdk.jshell
jdk.jshell.execution
jdk.jshell.spi
jdk.jshell.tool
module:jdk.jsobject
netscape.javascript
module:jdk.jstatd
module:jdk.localedata
module:jdk.management
com.sun.management
module:jdk.management.agent
module:jdk.management.jfr
jdk.management.jfr
module:jdk.naming.dns
module:jdk.naming.rmi
module:jdk.net
jdk.net
jdk.nio
module:jdk.nio.mapmode
jdk.nio.mapmode
module:jdk.sctp
com.sun.nio.sctp
module:jdk.security.auth
com.sun.security.auth
com.sun.security.auth.callback
com.sun.security.auth.login
com.sun.security.auth.module
module:jdk.security.jgss
com.sun.security.jgss
module:jdk.xml.dom
org.w3c.dom.css
org.w3c.dom.html
org.w3c.dom.stylesheets
org.w3c.dom.xpath
module:jdk.zipfs
@@ -0,0 +1,7 @@
org.apache.logging.log4j
org.apache.logging.log4j.message
org.apache.logging.log4j.simple
org.apache.logging.log4j.spi
org.apache.logging.log4j.status
org.apache.logging.log4j.util
org.apache.logging.log4j.util.internal
@@ -0,0 +1,50 @@
org.apache.logging.log4j.core
org.apache.logging.log4j.core.appender
org.apache.logging.log4j.core.appender.db
org.apache.logging.log4j.core.appender.db.jdbc
org.apache.logging.log4j.core.appender.mom
org.apache.logging.log4j.core.appender.mom.jeromq
org.apache.logging.log4j.core.appender.mom.kafka
org.apache.logging.log4j.core.appender.nosql
org.apache.logging.log4j.core.appender.rewrite
org.apache.logging.log4j.core.appender.rolling
org.apache.logging.log4j.core.appender.rolling.action
org.apache.logging.log4j.core.appender.routing
org.apache.logging.log4j.core.async
org.apache.logging.log4j.core.config
org.apache.logging.log4j.core.config.arbiters
org.apache.logging.log4j.core.config.builder.api
org.apache.logging.log4j.core.config.builder.impl
org.apache.logging.log4j.core.config.composite
org.apache.logging.log4j.core.config.json
org.apache.logging.log4j.core.config.plugins
org.apache.logging.log4j.core.config.plugins.convert
org.apache.logging.log4j.core.config.plugins.processor
org.apache.logging.log4j.core.config.plugins.util
org.apache.logging.log4j.core.config.plugins.validation
org.apache.logging.log4j.core.config.plugins.validation.constraints
org.apache.logging.log4j.core.config.plugins.validation.validators
org.apache.logging.log4j.core.config.plugins.visitors
org.apache.logging.log4j.core.config.properties
org.apache.logging.log4j.core.config.status
org.apache.logging.log4j.core.config.xml
org.apache.logging.log4j.core.config.yaml
org.apache.logging.log4j.core.filter
org.apache.logging.log4j.core.filter.mutable
org.apache.logging.log4j.core.impl
org.apache.logging.log4j.core.jackson
org.apache.logging.log4j.core.jmx
org.apache.logging.log4j.core.layout
org.apache.logging.log4j.core.lookup
org.apache.logging.log4j.core.net
org.apache.logging.log4j.core.net.ssl
org.apache.logging.log4j.core.osgi
org.apache.logging.log4j.core.parser
org.apache.logging.log4j.core.pattern
org.apache.logging.log4j.core.script
org.apache.logging.log4j.core.selector
org.apache.logging.log4j.core.time
org.apache.logging.log4j.core.tools
org.apache.logging.log4j.core.util
org.apache.logging.log4j.core.util.datetime
org.apache.logging.log4j.core.util.internal.instant
@@ -0,0 +1,121 @@
org.apache.catalina
org.apache.catalina.ant
org.apache.catalina.ant.jmx
org.apache.catalina.authenticator
org.apache.catalina.authenticator.jaspic
org.apache.catalina.connector
org.apache.catalina.core
org.apache.catalina.deploy
org.apache.catalina.filters
org.apache.catalina.ha
org.apache.catalina.ha.authenticator
org.apache.catalina.ha.backend
org.apache.catalina.ha.context
org.apache.catalina.ha.deploy
org.apache.catalina.ha.session
org.apache.catalina.ha.tcp
org.apache.catalina.loader
org.apache.catalina.manager
org.apache.catalina.manager.host
org.apache.catalina.manager.util
org.apache.catalina.mapper
org.apache.catalina.mbeans
org.apache.catalina.realm
org.apache.catalina.security
org.apache.catalina.servlets
org.apache.catalina.session
org.apache.catalina.ssi
org.apache.catalina.startup
org.apache.catalina.storeconfig
org.apache.catalina.tribes
org.apache.catalina.tribes.group
org.apache.catalina.tribes.group.interceptors
org.apache.catalina.tribes.io
org.apache.catalina.tribes.jmx
org.apache.catalina.tribes.membership
org.apache.catalina.tribes.membership.cloud
org.apache.catalina.tribes.tipis
org.apache.catalina.tribes.transport
org.apache.catalina.tribes.transport.nio
org.apache.catalina.tribes.util
org.apache.catalina.users
org.apache.catalina.util
org.apache.catalina.valves
org.apache.catalina.valves.rewrite
org.apache.catalina.webresources
org.apache.catalina.webresources.war
org.apache.coyote
org.apache.coyote.ajp
org.apache.coyote.http11
org.apache.coyote.http11.filters
org.apache.coyote.http11.upgrade
org.apache.coyote.http2
org.apache.el
org.apache.el.lang
org.apache.el.stream
org.apache.el.util
org.apache.jasper
org.apache.jasper.compiler
org.apache.jasper.compiler.tagplugin
org.apache.jasper.el
org.apache.jasper.optimizations
org.apache.jasper.runtime
org.apache.jasper.servlet
org.apache.jasper.tagplugins.jstl
org.apache.jasper.tagplugins.jstl.core
org.apache.jasper.util
org.apache.juli
org.apache.juli.logging
org.apache.naming
org.apache.naming.factory
org.apache.naming.java
org.apache.tomcat
org.apache.tomcat.buildutil
org.apache.tomcat.buildutil.translate
org.apache.tomcat.dbcp.dbcp2
org.apache.tomcat.dbcp.dbcp2.cpdsadapter
org.apache.tomcat.dbcp.dbcp2.datasources
org.apache.tomcat.dbcp.dbcp2.managed
org.apache.tomcat.dbcp.pool2
org.apache.tomcat.dbcp.pool2.impl
org.apache.tomcat.jdbc.naming
org.apache.tomcat.jdbc.pool
org.apache.tomcat.jdbc.pool.interceptor
org.apache.tomcat.jdbc.pool.jmx
org.apache.tomcat.jni
org.apache.tomcat.util
org.apache.tomcat.util.bcel
org.apache.tomcat.util.bcel.classfile
org.apache.tomcat.util.buf
org.apache.tomcat.util.collections
org.apache.tomcat.util.compat
org.apache.tomcat.util.concurrent
org.apache.tomcat.util.descriptor
org.apache.tomcat.util.descriptor.tagplugin
org.apache.tomcat.util.descriptor.tld
org.apache.tomcat.util.descriptor.web
org.apache.tomcat.util.digester
org.apache.tomcat.util.file
org.apache.tomcat.util.http
org.apache.tomcat.util.http.fileupload
org.apache.tomcat.util.http.fileupload.disk
org.apache.tomcat.util.http.fileupload.impl
org.apache.tomcat.util.http.fileupload.servlet
org.apache.tomcat.util.http.fileupload.util
org.apache.tomcat.util.http.fileupload.util.mime
org.apache.tomcat.util.http.parser
org.apache.tomcat.util.log
org.apache.tomcat.util.modeler
org.apache.tomcat.util.modeler.modules
org.apache.tomcat.util.net
org.apache.tomcat.util.net.jsse
org.apache.tomcat.util.net.openssl
org.apache.tomcat.util.net.openssl.ciphers
org.apache.tomcat.util.res
org.apache.tomcat.util.scan
org.apache.tomcat.util.security
org.apache.tomcat.util.threads
org.apache.tomcat.util.xreflection
org.apache.tomcat.websocket
org.apache.tomcat.websocket.pojo
org.apache.tomcat.websocket.server
+19 -6
View File
@@ -17,7 +17,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.springframework.boot.build.docs.ConfigureJavadocLinks
import org.springframework.boot.build.docs.AggregatedJavadoc
import org.springframework.boot.build.optional.OptionalDependenciesPlugin
plugins {
@@ -34,6 +34,7 @@ plugins {
description = "Spring Boot Docs"
configurations {
doclet
remoteSpringApplicationExample
resolvedBom
springApplicationExample
@@ -83,6 +84,8 @@ plugins.withType(EclipsePlugin) {
dependencies {
compileOnly("org.hamcrest:hamcrest-core")
doclet("io.spring.javaformat:spring-javaformat-doclet:${javaFormatVersion}")
dokka(project(":core:spring-boot"))
dokka(project(":module:spring-boot-resttestclient"))
@@ -142,7 +145,7 @@ configurations {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
}
extendsFrom configurations.javadoc
extendsFrom configurations.runtimeClasspath
}
configureEach {
resolutionStrategy {
@@ -160,13 +163,24 @@ configurations {
}
}
def aggregatedJavadoc = tasks.register('aggregatedJavadoc', Javadoc) {
dependsOn configurations.resolvedBom
def aggregatedJavadoc = tasks.register('aggregatedJavadoc', AggregatedJavadoc) {
resolvedBom.from(configurations.resolvedBom)
javadocJars.from(configurations.runtimeClasspath.incoming.artifactView {
withVariantReselection()
attributes {
lenient = true
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION));
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL));
attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, "implicitjavadoc"));
}
}.files)
destinationDir = project.file(project.layout.buildDirectory.dir("docs/javadoc"))
source = configurations.javadocSource
classpath = configurations.javadocClasspath
classpath = configurations.runtimeClasspath
include("**/*.java")
options {
docletpath = configurations.doclet.files.toList()
author = true
docTitle = "Spring Boot ${project.version} API"
memberLevel = "protected"
@@ -175,7 +189,6 @@ def aggregatedJavadoc = tasks.register('aggregatedJavadoc', Javadoc) {
use = true
windowTitle = "Spring Boot ${project.version} API"
}
doFirst(new ConfigureJavadocLinks(configurations.resolvedBom, ["Spring Framework", "Spring Security", "Tomcat"]))
}
project.rootProject.gradle.projectsEvaluated {
@@ -2583,7 +2583,7 @@ bom {
.formatted(version.forAntora()))
releaseNotes("https://github.com/spring-projects/spring-data-elasticsearch/releases/tag/{version}")
}
module("spring-data-jpa") {
module("spring-data-jpa", ["spring-data-jpa", "spring-data-envers"]) {
site("https://spring.io/projects/spring-data-jpa")
github("https://github.com/spring-projects/spring-data-jpa")
javadoc("https://docs.spring.io/spring-data/jpa/reference/{version}/api/java",
@@ -2619,7 +2619,7 @@ bom {
.formatted(version.forAntora()))
releaseNotes("https://github.com/spring-projects/spring-data-neo4j/releases/tag/{version}")
}
module("spring-data-relational") {
module("spring-data-relational", ["spring-data-relational", "spring-data-jdbc", "spring-data-r2dbc"]) {
site("https://spring.io/projects/spring-data-relational")
github("https://github.com/spring-projects/spring-data-relational")
javadoc("https://docs.spring.io/spring-data/relational/reference/{version}/api/java",