Maven Javadoc and site
last modified November 13, 2026
In this article we generate API documentation with the Maven Javadoc Plugin and build a project website with the Maven Site Plugin. We check Javadoc comments for errors, package the documentation into a JAR, and add project, Javadoc, and test reports to the site. The examples use Maven 3.10, Javadoc plugin 3.12.0, Site plugin 3.22.0, and Java 25.
Javadoc is the documentation tool of the JDK. It reads the
/** ... */ comments in the source code and generates HTML
pages that describe packages, classes, and methods. The Javadoc plugin runs
the javadoc tool with the sources and the classpath of the
project.
The project
Our library creates URL slugs from titles. A slug is the readable part of
a URL, such as maven-site-plugin.
package com.example.textkit;
import java.text.Normalizer;
import java.util.Locale;
/**
* Creates URL slugs from titles.
*
* <p>A slug is the part of a URL that identifies a page in a readable form,
* for example {@code maven-site-plugin} for the title
* <i>Maven Site Plugin</i>.
*
* @author Jan Bodnar
* @since 1.0
*/
public final class Slugs {
/** The default maximum length of a slug. */
public static final int MAX_LENGTH = 60;
private Slugs() {
}
/**
* Converts a title to a slug.
*
* <p>Accented letters are replaced with their base letters, every run of
* other characters becomes a single hyphen, and the result is cut to
* {@link #MAX_LENGTH} characters.
*
* @param title the title to convert
* @return the slug in lower case
* @throws IllegalArgumentException if the title has no letters or digits
* @see #isSlug(String)
*/
public static String toSlug(String title) {
String plain = Normalizer.normalize(title, Normalizer.Form.NFD)
.replaceAll("\\p{M}", "");
String slug = plain.toLowerCase(Locale.ROOT)
.replaceAll("[^a-z0-9]+", "-")
.replaceAll("^-|-$", "");
if (slug.isEmpty()) {
throw new IllegalArgumentException("No letters or digits: " + title);
}
return slug.length() > MAX_LENGTH ? slug.substring(0, MAX_LENGTH) : slug;
}
/**
* Tells whether a text is a valid slug.
*
* @param text the text to test
* @return {@code true} if the text contains only lower-case letters,
* digits, and single hyphens between them
*/
public static boolean isSlug(String text) {
return text.matches("[a-z0-9]+(-[a-z0-9]+)*");
}
}
Every public element has a Javadoc comment. The first sentence of a
comment is the summary shown in tables; the block tags, such as
@param, @return, and @throws,
describe the parameters, the return value, and the exceptions. The
{@link} and {@code} inline tags create a link and
format code.
/**
* Small utilities for working with text.
*
* <p>The main entry point is the {@link com.example.textkit.Slugs} class.
*/
package com.example.textkit;
The package-info.java file holds the documentation of the
package.
package com.example.textkit;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
class SlugsTest {
@Test
void convertsTitle() {
assertEquals("maven-site-plugin", Slugs.toSlug("Maven Site Plugin"));
}
@Test
void removesAccents() {
assertEquals("zlta-ruza", Slugs.toSlug("Žltá ruža"));
}
@Test
void producesValidSlug() {
assertTrue(Slugs.isSlug(Slugs.toSlug(" Hello, World! ")));
}
@Test
void rejectsTitleWithoutLetters() {
assertThrows(IllegalArgumentException.class, () -> Slugs.toSlug("!?"));
}
}
A few JUnit tests check the conversion. Their results will appear in the site report.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>textkit</artifactId>
<version>1.0.0</version>
<name>TextKit</name>
<description>Small utilities for working with text</description>
<properties>
<maven.compiler.release>25</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<configuration>
<links>
<link>https://docs.oracle.com/en/java/javase/25/docs/api/</link>
</links>
</configuration>
</plugin>
</plugins>
</build>
</project>
The name and description elements are used as
titles in the generated documentation.
<links>
<link>https://docs.oracle.com/en/java/javase/25/docs/api/</link>
</links>
The links option links classes from other libraries to their
online documentation. Here we link JDK classes, such as
String, to the Java 25 API documentation.
Generating Javadoc
The javadoc:javadoc goal generates the documentation into the
target/reports/apidocs directory. Open its
index.html file in a browser.
The directory contains the pages of the documentation, the search index,
and the element-list file, which lists our packages so that
other projects can link to them. The links to the JDK classes in the page
of our class point to the Oracle documentation.
$ mvn javadoc:javadoc --- javadoc:3.12.0:javadoc (default-cli) @ textkit --- BUILD SUCCESS $ ls target/reports/apidocs allclasses-index.html allpackages-index.html com constant-values.html element-list help-doc.html index-all.html index.html legal member-search-index.js module-search-index.js overview-tree.html package-search-index.js resource-files script-files search.html tag-search-index.js type-search-index.js $ grep -o 'href="https://docs.oracle.com[^"#]*"' target/reports/apidocs/com/example/textkit/Slugs.html | sort -u href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/IllegalArgumentException.html" href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Object.html" href="https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/String.html"
Errors and warnings
The javadoc tool checks the comments with doclint. We break
the documentation of our class.
* @see #isSlugs(String)
The @see tag refers to a method that does not exist.
public static String join(String first, String second) {
return toSlug(first + " " + second);
}
}
The new public method has no comment.
The broken reference is an error, which fails the build. The missing comment is only a warning.
The plugin skips the generation when it thinks that nothing has changed.
It compares the options and the timestamp of the source directory, saved
in target/maven-javadoc-plugin-stale-data.txt, but not the
timestamps of the files in packages. After a change to a single file,
javadoc:javadoc may finish without generating anything; run
mvn clean javadoc:javadoc to be sure.
$ mvn javadoc:javadoc
BUILD FAILURE
Exit code: 1
Slugs.java:34: error: reference not found
* @see #isSlugs(String)
^
Slugs.java:59: warning: no comment
public static String join(String first, String second) {
^
1 error
1 warning
Strict checks
We fix the reference and keep the method without a comment. The
failOnWarnings option, set here with the
maven.javadoc.failOnWarnings property, makes warnings fail the
build too.
The doclint option chooses the doclint groups. The
all,-missing value enables all checks except the check for
missing comments. Both options can be set in the plugin configuration as
<failOnWarnings> and <doclint>.
The first run fails on the missing comment; the second run passes, because missing comments are no longer reported.
$ mvn javadoc:javadoc -Dmaven.javadoc.failOnWarnings=true Slugs.java:59: warning: no comment 1 warning BUILD FAILURE $ mvn clean javadoc:javadoc -Dmaven.javadoc.failOnWarnings=true -Ddoclint=all,-missing BUILD SUCCESS
Javadoc JAR
Libraries published to Maven Central must include a JAR with the Javadoc documentation. IDEs download it to show documentation of library classes.
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<configuration>
<links>
<link>https://docs.oracle.com/en/java/javase/25/docs/api/</link>
</links>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
The jar goal is bound to the package phase. It
generates the documentation and packages it into a JAR with the
javadoc classifier, which is installed and deployed with the
main JAR.
The javadoc:jar goal runs after the main JAR is built. The
JAR contains the HTML pages.
$ mvn package --- jar:3.5.1:jar (default-jar) @ textkit --- Building jar: /home/user/project/textkit/target/textkit-1.0.0.jar --- javadoc:3.12.0:jar (attach-javadocs) @ textkit --- Building jar: /home/user/project/textkit/target/textkit-1.0.0-javadoc.jar BUILD SUCCESS $ jar tf target/textkit-1.0.0-javadoc.jar | grep textkit com/example/textkit/ com/example/textkit/class-use/ com/example/textkit/Slugs.html com/example/textkit/class-use/Slugs.html com/example/textkit/package-summary.html com/example/textkit/package-tree.html com/example/textkit/package-use.html
Project site
The Site plugin generates a website for the project. The site consists of our own pages and of reports generated by report plugins from the POM, the sources, and the build results.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>textkit</artifactId>
<version>1.0.0</version>
<name>TextKit</name>
<description>Small utilities for working with text</description>
<url>https://example.com/textkit/</url>
<properties>
<maven.compiler.release>25</maven.compiler.release>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>6.1.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<version>3.22.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<configuration>
<links>
<link>https://docs.oracle.com/en/java/javase/25/docs/api/</link>
</links>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.9.0</version>
<reportSets>
<reportSet>
<reports>
<report>index</report>
<report>summary</report>
<report>dependencies</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.12.0</version>
<reportSets>
<reportSet>
<reports>
<report>javadoc</report>
</reports>
</reportSet>
</reportSets>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>3.6.1</version>
<reportSets>
<reportSet>
<reports>
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>
The Site plugin is pinned in the build section like any
other plugin.
<url>https://example.com/textkit/</url>
The url element is the address of the published site. The
plugin uses it to make links relative; without it, it prints a warning.
<reporting>
The plugins that generate reports are listed in the reporting
section. The reportSets element chooses the reports of a
plugin; without it, a plugin generates all its reports. The Project Info
Reports plugin creates pages from the POM, such as the summary and the
list of dependencies; it has more than twenty reports. The Javadoc plugin
adds the API documentation, and the Surefire Report plugin a report of the
test results.
# TextKit
TextKit creates URL slugs from titles.
```java
String slug = Slugs.toSlug("Maven Site Plugin");
```
See the [API documentation](apidocs/index.html).
Our own pages live in the src/site directory, in a
subdirectory named by the format. The Site plugin supports Markdown, the
Doxia XDoc format, and others. The index.md file becomes the
home page of the site.
The mvn site command runs the site lifecycle.
Some reports need parts of the build: the Javadoc report runs the
generate-sources phase, the Surefire report runs the tests. The
index report is skipped because our Markdown page already
creates index.html. The site uses the Fluido skin. The result
is in target/site.
The site:run goal serves the site from an embedded Jetty
server, on port 8080 by default; we choose another port with the
port property. To
publish the site, mvn site:deploy copies it to the location
given in the distributionManagement section.
$ mvn site --- site:3.22.0:site (default-site) @ textkit --- Rendering site for default locale Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.058 s -- in com.example.textkit.SlugsTest Tests run: 4, Failures: 0, Errors: 0, Skipped: 0 Skipped "About" report (maven-project-info-reports-plugin:3.9.0:index), file "index.html" already exists. Rendering content with org.apache.maven.skins:maven-fluido-skin:jar:2.0.0-M9 skin Rendering 1 Doxia document: 1 markdown Rendering 6 report documents Generating "Summary" report --- maven-project-info-reports-plugin:3.9.0:summary Generating "Dependencies" report --- maven-project-info-reports-plugin:3.9.0:dependencies Generating "Javadoc" report --- maven-javadoc-plugin:3.12.0:javadoc Generating "Surefire" report --- maven-surefire-report-plugin:3.6.1:report Generating "Project Information" report --- maven-site-plugin:3.22.0:project-info Generating "Generated Reports" report --- maven-site-plugin:3.22.0:project-reports BUILD SUCCESS $ ls target/site apidocs css dependencies.html fonts images img index.html js project-info.html project-reports.html summary.html surefire.html $ mvn site:run -Dport=8086 --- site:3.22.0:run (default-cli) @ textkit --- Started Jetty on http://localhost:8086/ $ curl -s localhost:8086/summary.html | grep -o '<title>.*</title>' <title>Project Summary – TextKit</title>
Source
Apache Maven Javadoc Plugin, Apache Maven Site Plugin
In this article we have generated and checked Javadoc documentation, packaged it into a JAR, and built a project site with reports.
Author
List all Java tutorials.