ZetCode

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.

textkit/src/main/java/com/example/textkit/Slugs.java
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.

textkit/src/main/java/com/example/textkit/package-info.java
/**
 * 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.

textkit/src/test/java/com/example/textkit/SlugsTest.java
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.

textkit/pom.xml
<?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.

Slugs.java (changed tag)
* @see #isSlugs(String)

The @see tag refers to a method that does not exist.

Slugs.java (new method)
    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.

pom.xml (Javadoc plugin)
    <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.

pom.xml
<?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.

src/site/markdown/index.md
# 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

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.