Came across a surprising bug in JDK 5.0. today. “Surprising” because it’s so easy to reproduce, and the use case would seem to be pretty common. If you run javadoc over a codebase that uses custom annotations, and the annotation classes themselves are not in the runtime classpath, javadoc will throw ClassCastException
s and consequently fail to generate the indexed contents correctly. In my case, the annotation classes are the Hibernate annotations, and javadoc complains about not being able to find the EJB3 javax.persistence
classes in the classpath, along with the Hibernate classes. This is normally no problem, but if your annotation classes themselves are unavailable in the classpath, then javadoc will throw lots of exceptions.
The actual output looks like:
[javadoc] Standard Doclet version 1.5.0_03
[javadoc] Building tree for all the packages and classes…
[javadoc] java.lang.ClassCastException: com.sun.tools.javadoc.ClassDocImpl
[javadoc] at com.sun.tools.javadoc.AnnotationDescImpl.annotationType(AnnotationDescImpl.java:46)
[javadoc] at com.sun.tools.doclets.internal.toolkit.util.Util.isDeprecated(Util.java:804)
The culprit seems to be the annotationType()
method from AnnotationDescImpl
. If we look at that, we get:
/**
* Returns the annotation type of this annotation.
*/
public AnnotationTypeDoc annotationType() {
ClassSymbol atsym = (ClassSymbol)annotation.type.tsym;
return (AnnotationTypeDoc)env.getClassDoc(atsym);
}
The offending line is (AnnotationTypeDoc)env.getClassDoc(atsym)
. The getClassDoc()
method looks like:
/**
* Return the ClassDoc (or a subtype) of this class symbol.
*/
ClassDocImpl getClassDoc(ClassSymbol clazz) {
ClassDocImpl result = classMap.get(clazz);
if (result != null) return result;
if (isAnnotationType(clazz)) {
result = new AnnotationTypeDocImpl(this, clazz);
} else {
result = new ClassDocImpl(this, clazz);
}
classMap.put(clazz, result);
return result;
}
I have a feeling that the error may be due to Annotation types not being processed correctly when they are not in the classpath, and consequently not being initialized as ClassDoc instances. The easy fix/workaround is to pass the runtime classpath to the javadoc task.
Strangely enough, a very similar bug was posted and fixed for 1.4.