Aggregating Javadocs from Dependency Sources

Since: 2.7

Consider the following project dependency graph:

org.test:project-distro:0.1
  |
  |--> org.test.dep:project-A:1.0
  |    |
  |    |--> commons-lang:commons-lang:1.0
  |    |
  |    `--> commons-io:commons-io:1.0
  |
  |--> org.test.dep:project-B:1.1
  |    |
  |    `--> commons-io:commons-io:1.0
  |
  |--> org.test.dep:project-C:1.0
  |
  `--> commons-cli:commons-cli:1.0

Suppose you maintain a series of projects with separate release cycles, along with another project which serves to integrate everything together to provide a tested platform, with its dependencies coordinated for compatibility. In order to produce the project-distro distribution, you'll want to produce a comprehensive set of javadocs containing not only what little code is in the distro project itself - which isn't likely to be very enlightening - but also the javadocs for your other projects, which are dependencies of the distribution.

Since version 2.7 of the maven-javadoc-plugin, you can do this using the includeDependencySources flag and its associated configuration options. Using this flag, it's possible to aggregate sources from other modules in a multi-module build or use the sources and test-sources jars published alongside the main artifacts for your projects. It's also possible to fine-tune which dependencies' sources should be aggregated, and which ignored. Last but not least, it's possible to include non-source javadoc configurations and resources from your dependencies by publishing javadoc-resources and test-javadoc-resources bundle artifacts alongside your dependencies' main artifacts.

To get started, let's look at how to lay the ground work for dependency-driven javadoc aggregation.

Preparing Dependencies for Aggregation

Dependency-driven javadoc aggregation works by resolving the sources for included dependencies, then including these sources in the javadoc production process for your project. As is common practice with Maven, dependency sources will be resolved from other modules in the current reactor if they are available there. Otherwise, the plugin will attempt to resolve the appropriate sources artifact for the dependency: sources for main javadocs, test-sources for test javadocs. NOTE: If your configuration includes a dependency but that dependency's source artifact is unavailable, the javadoc plugin will fail.

The recommended method for producing these source artifacts is the maven-source-plugin. The following simple example shows how to produce source artifacts:

<project>
  [...]
  <groupId>org.test.dep</groupId>
  <artifactId>project-A</artifactId>
  [...]
  <build>
    <plugins>
      [...]
      <plugin>
        <artifactId>maven-source-plugin</artifactId>
        <version>2.1.1</version>
        <executions>
          <execution>
            <id>bundle-sources</id>
            <phase>package</phase>
            <goals>
              <!-- produce source artifact for main project sources -->
              <goal>jar-no-fork</goal>
              
              <!-- produce source artifact for project test sources -->
              <goal>test-jar-no-fork</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

NOTE: If you don't intend to generate test javadocs that include dependency sources, you can omit the test-jar-no-fork goal above.

At this point, your project is ready to produce the artifacts necessary to support dependency-driven javadoc aggregation. The next time you install or deploy the project, the appropriate artifacts will be available for your distribution project to consume.

Fine-Tuning Included Dependencies

Unifying the javadocs of your project and its dependencies is fine, but let's not go too far. Along with your own upstream projects, your distribution may include dependencies on external projects. It may not be appropriate to include javadocs for these projects in your distribution; linking to them is usually better. In any case, you may not have enough influence over these external projects to get them to produce source artifacts like the ones discussed above. In our original example, take a look at that last direct dependency:

  
  `--> commons-cli:commons-cli:1.0

This is a pretty common direct dependency for projects that provide command-line execution options. But consider what happens if commons-cli doesn't provide a sources jar: dependency-driven javadoc aggregation would fail for any project that contained commons-cli as a direct dependency. To avoid this case, you have the option to fine-tune which dependencies get included in the javadoc aggregation process. In your distribution project, add configuraiton for the maven-javadoc-plugin similar to the following:

<project>
  [...]
  <groupId>org.test</groupId>
  <artifactId>project-distro</artifactId>
  [...]
  <build>
    <plugins>
      [...]
      <plugin>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.6.3</version>
        <executions>
          <execution>
            <id>javadoc-jar</id>
            <phase>package</phase>
            <goals>
              <goal>jar</goal>
            </goals>
            <configuration>
              <!-- switch on dependency-driven aggregation -->
              <includeDependencySources>true</includeDependencySources>
              
              <dependencySourceExcludes>
                <!-- exclude ONLY commons-cli artifacts -->
                <dependencySourceExclude>commons-cli:*</dependencySourceExclude>
              </dependencySourceExcludes>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

NOTE: The above configuration excludes only commons-cli. If you have multiple external dependencies, it may be easier to configure which dependencies are included, as follows:

  [...]
            <configuration>
              <!-- switch on dependency-driven aggregation -->
              <includeDependencySources>true</includeDependencySources>

              <dependencySourceIncludes>
                <!-- include ONLY dependencies I control -->
                <dependencySourceInclude>org.test.dep:*</dependencySourceInclude>
              </dependencySourceIncludes>
            </configuration>
  [...]

Including Javadoc Resources from Dependencies

Many projects choose customize their javadoc configuration beyond the defaults.

If you have customized configuration for generating the javadocs in these dependency projects, you may wish to propagate these customizations to the distribution project itself. To do this, use the resource-bundle and test-resource-bundle goals, new in version 2.7 of the javadoc plugin. These will create new artifacts that contain the javadoc configuration options plus the contents of ${javadocDirectory} (which defaults to src/main/javadoc), or ${tetsJavadocDirectory} (which defaults to src/test/javadoc) depending on the bundle goal. If these artifacts are available, the dependency-driven aggregation process will include the content and configuration they contain when generating the javadocs for your distribution. The following example shows how to produce these artifacts:

<project>
  [...]
  <groupId>org.test.dep</groupId>
  <artifactId>project-A</artifactId>
  <version>1.0</version>
  [...]
  <build>
    <plugins>
      [...]
      <plugin>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.6.3</version>
        <executions>
          <execution>
            <id>resource-bundles</id>
            <phase>package</phase>
            <goals>
              <!-- produce source artifact for main project sources -->
              <goal>resource-bundle</goal>

              <!-- produce source artifact for project test sources -->
              <goal>test-resource-bundle</goal>
            </goals>
            <configuration>
              <detectOfflineLinks>false</detectOfflineLinks>
              [...]
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  [...]
</project>

NOTE: Again, if you don't need to generate test javadocs, you can omit the test-resource-bundle goal above.

NOTE 2: The configuration option detectOfflineLinks is provided as an example only. It is not required.