Javadocs with UML diagrams
Pictures make people think you care about documentation!
What I’ve often noticed is that javadocs are kind of treated as a second class citizen when it comes a lot of opensource projects; not all projects, there are some with excellent javadocs; but, sadly, more often than not javadocs are an afterthought. Now, I’m as guilty as the next person for not having great javadocs; but you can spruce up your javadocs with some pretty UML diagrams courtesy of plantuml, graphviz and umldoclet…
Since gradle is now my build tool of choice all of this is gradle specific; I’ve done something similar with ant, and I’m sure you could do it using maven, but maven creates a different set of problems. These steps will build you javadocs with UML diagrams on any system that has graphviz installed. On windows you can easily install that using scoop.sh; on linux it’s going to be available via your preferred package manager; personally I haven’t had much luck with homebrew on the mac vis-a-vis graphviz, the dot executable fails to execute properly; I haven’t had the inclination to investigate since I don’t need it.
First of all figure out if you have graphviz
Look through the path, and see if dot(.exe) is available; don’t forget to add the import at the top of your build script.
import org.apache.tools.ant.taskdefs.condition.Os
ext.hasGraphViz = { ->
def app = "dot"
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
app = app + ".exe"
}
return System.getenv("PATH").split(File.pathSeparator).any{
java.nio.file.Paths.get("${it}").resolve(app).toFile().exists()
}
}
Next include a custom configuration for umldoclet
Add in a custom umlDoclet
configuration so that you can add the umldoclet jars to that configuration and use them in your javadoc task. For Java 9+ you can use umldoclet version 2+, 1.1.4 is appropriate for Java 8.
configurations {
umlDoclet {}
}
dependencies {
umlDoclet("nl.talsmasoftware:umldoclet:1.1.4")
}
Create UML javadocs if graphviz is available
We want the javadoc task to create some javadocs regardless of whether the developer actually has graphviz installed or not1; so we patch the bundled javadoc task so it doesn’t execute if graphviz is available, umlJavadoc executes instead when we run ./gradlew javadoc
.
javadoc {
onlyIf {
!hasGraphViz()
}
configure(options) {
options.tags('apiNote:a:API Note:', 'implSpec:a:Implementation Requirements:','implNote:a:Implementation Note:')
title= componentName
}
}
task umlJavadoc(type: Javadoc) {
group 'Documentation'
description 'Build javadocs using plantuml + graphviz + umldoclet, if dot is available'
onlyIf {
hasGraphViz()
}
source = sourceSets.main.extensions.delombokTask
classpath = project.sourceSets.main.compileClasspath
configure(options) {
options.tags('apiNote:a:API Note:', 'implSpec:a:Implementation Requirements:','implNote:a:Implementation Note:')
options.docletpath = configurations.umlDoclet.files.asType(List)
options.doclet = "nl.talsmasoftware.umldoclet.UMLDoclet"
options.addStringOption "umlBasePath", destinationDir.getCanonicalPath()
options.addStringOption "umlImageFormat", "SVG"
options.addStringOption "umlExcludedReferences", "java.lang.Exception,java.lang.Object,java.lang.Enum"
options.addStringOption "umlIncludePrivateClasses","false"
options.addStringOption "umlIncludePackagePrivateClasses","false"
options.addStringOption "umlIncludeProtectedClasses","false"
options.addStringOption "umlIncludeAbstractSuperclassMethods","false"
options.addStringOption "umlIncludeConstructors","false"
options.addStringOption "umlIncludePublicFields","false"
options.addStringOption "umlIncludePackagePrivateFields","false"
options.addStringOption "umlIncludeProtectedFields", "false"
options.addStringOption "umlIncludeDeprecatedClasses", "false"
options.addStringOption "umlIncludePrivateInnerClasses", "false"
options.addStringOption "umlIncludePackagePrivateInnerClasses", "false"
options.addStringOption "umlIncludeProtectedInnerClasses","false"
title= componentName
}
}
javadoc.dependsOn umlJavadoc
Modify your package-info.java
Depending on the version of umldoclet; you may have to modify your package-info.java to refer to your lovely pic. In later versions, it actually generates object
refs that do that for you in the package summary, so you don’t need to. If you do need to, then it’s as simple as
/**
* My awesome package.
*
* <img alt="UML" src="package.svg"/>
*/
package io.quotidianennui.awesomeness;
Lombok
If you’re using lombok, then you might notice that your UML javadocs don’t actually contain all the methods you might expect; that’s because in the example above we’re generating javadocs on the lombokified source code; what we need to do is to delombok first. If you’re using the freefair lombok gradle plugin then you need to change the source so that it is the source from the delombokTask…
source = sourceSets.main.extensions.delombokTask
CircleCI
If you’re using circleCI2 then you can make sure that graphviz is available during your publish, so that your javadocs are published with the UML built in.
publish:
docker:
- image: circleci/openjdk:8-jdk
working_directory: ~/project
environment:
JAVA_TOOL_OPTIONS: -Xmx2G -Djava.security.egd=file:/dev/./urandom
TERM: dumb
steps:
- checkout
- run:
name: Configure
command: |
sudo apt-get -y update
sudo apt-get -y install graphviz
mkdir -p ~/.gradle
echo "org.gradle.warning.mode=none" > ~/.gradle/gradle.properties
- restore_cache:
keys:
- dependencies
- run:
name: Publish
command: |
chmod +x ./gradlew
./gradlew test publish
- save_cache:
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
key: dependencies