Nepíšete javadoc komentáře? Díky Java 8 vězte, že dobře děláte!

Tomáš Zilvar

O autorovi: LinkedIn

Tomáš za dobu svého působení v oboru soudí, že programátor jest živoucím důkazem platnosti Paretova pravidla 80/20 - 20 % času programuje a 80 % času bádá, proč to nefunguje. Empiricky mezitím ověřil, že je to nezávislé na tom, zda programuje v korporaci nebo malé firmě, i na použité technologii.

Javu 8 na rozdíl od Javadoc komentářů (to jsou ty mezi /** a */;) nepoužívá úplně každý javista, ale jak se říká, jednou tam musíme všichni. Kvůli rozhodnutí Oracle přidat do Java 8 javadocu hodně striktní kontrolu obsahu javadoc komentářů, a to rovnou ve výchozím nastavení, může ale jejich kombinace přivodit i zásadní bolehlav způsobený nemožností releasovat starší projekty kvůli na první pohled absurdním chybám. Jak z toho ven, aniž bychom museli přepisovat několik let starou dokumentaci?

Pokrok nezastavíš

Předně mi nedá nepozastavit se nad rozhodnutím autorů Javy 8, kteří pro mě nepochopitelně zařadili tuto kontrolu jako default s možností vypnutí namísto volitelného zapnutí. Java tím po dvou dekádách naprosté benevolence úplně otočila přístup ke kontrole struktury javadoc komentářů. Dosud v nich mohlo být téměř cokoli a javadoc si maximálně postěžoval warningy, teď rovnou vrátí chybu a shodí tím celý build, často z malicherných důvodů (viz níže).

Šikovný nástroj DocLint a bohulibá snaha o zkvalitnění dokumentace se tak velice rychle stala noční můrou všech projektů s delší historií. Lze dohledat značné množství odkazů na téma doclint, obvykle ve spojení se slovy jako breaks, invalid a disable…

Velký DocLint tě vidí

Výsledek kontroly staršího projektu si dovedete představit, ani novější na tom ale nebududou lépe (uvádím jen unikátní chyby, skutečný počet vynásobte stem):

user@hostname:~/www/project/myproject$ mvn javadoc:jar
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.9.1:jar (default-cli) on project myproject: MavenReportException: Error while creating archive:
[ERROR] /home/user/www/project/myproject/library/src/main/java/cz/sys/tis/AccidentList.java:181: error: bad use of '>'
[ERROR] * </element>
[ERROR] ^
[ERROR] /home/user/www/project/myproject/library/src/main/java/cz/sys/tis/ObjectFactory.java:75: warning: no @return
[ERROR] public AccidentList.Accident.ImpactDescriptionList createAccidentListAccidentImpactDescriptionList() {
[ERROR] ^
[ERROR] /home/user/www/project/myproject/library/src/main/java/cz/sys/tis/ObjectFactory.java:83: warning: no @return
[ERROR] public AccidentList.Accident.AlternateSupplyList.AlternateSupplyItem createAccidentListAccidentAlternateSupplyListAlternateSupplyItem() {
[ERROR] /home/user/www/project/myproject/library/src/main/java/com/fg/client/filter/CityOwnerRightsToDocumentFilter.java:125: warning - Tag @see:illegal character: "64" in "{@link CityOwnerRightsToDocumentFilter#getCityCode(String)}"
[ERROR] /home/user/www/project/myproject/library/src/main/java/com/fg/client/model/price_list/PriceList.java:118: warning: no description for @return
[ERROR] * @return
[ERROR] ^
[ERROR] /home/user/www/project/myproject/library/src/main/java/com/fg/client/web/frontend/model/MapMarkerSet.java:35: warning: no description for @param
[ERROR] * @param geolocated
[ERROR] ^

Pokud vám přijde, že trocha kontroly neuškodí, vězte, že se jedná o kontrolu místy až absurdně přísnou. Je založena na HTML 4.01 specifikaci, která se kontroluje tak, jak ji chápe Oracle, tedy nikoli validací oproti DTD nebo XSD. Pokud se k tomu přičte to, že i oficiální Java nástroje generují neplatné javadoc komentáře (wsimport, xjc), a fakt, že nikde nejsou zdokumentována kontrolovaná pravidla (maximálně se dají vyčíst zezdrojového kódu kontrolující třídy a něco-jako-DSL zápisu pravidel pro HTML tagy), dává to dohromady zaručený recept na katastrofu. Z pravidel, která jsem našel, jsou to například:

  • Zakázaný znak <, ale co víc, i > (který je jinak povolený!) - potěší zvláště ty, kteří jsou zvyklí každou třídu podepisovat jako @author Franta Brabec <fbr@fg.cz>
  • Chybějící atribut summary nebo element caption u tabulky table - přiznám se bez mučení, že o summary slyším poprvé a caption jsem snad nikdy nikam nepsal
  • Chybějící nebo nepopsaný (!) @return / @throws u funkcí s návratovou hodnotou nebo kontrolovanou výjimkou.

a další více či méně závažné prohřešky proti specifikaci, které zmiňuje v blog postu o vypnutí doclintu Stephen Colebourne:

With JDK 8, you are unable to get Javadoc unless your tool meets the standards of doclint. Some of its rules are:

  • no self-closed HTML tags, such as <br /> or <a id="x" />
  • no unclosed HTML tags, such as <ul> without matching </ul>
  • no invalid HTML end tags, such as </br>
  • no invalid HTML attributes, based on doclint's interpretation of W3C HTML 4.01
  • no duplicate HTML id attribute
  • no empty HTML href attribute
  • no incorrectly nested headers, such as class documentation must have <h3>, not <h4>
  • no invalid HTML tags, such as List<String> (where you forgot to escape using &lt;)
  • no broken @link references
  • no broken @param references, they must match the actual parameter name
  • no broken @throws references, the first word must be a class name

Note that these are errors, not warnings. Break the rules and you get no Javadoc output.

Nic proti kvalitní dokumentaci, ale tepat autora Java kódu, o který snad jde stále v první řadě, za chyby v generovaném HTML výstupu určeném pro zobrazení ve velmi benevolentních prohlížečích, a ještě o tom nikam nenapsat, to se nám opravdu, ale opravdu nezdá. Místo toho, aby autor byl motivován k tomu, aby javadoc komentářích bylo alespoň něco, vyhrávají totiž ti, kteří tam nenapíšou vůbec nic. Jak z toho tedy ven, aniž by bylo nutné přepisovat několik let staré komentáře?

Vítězství sestává se ze samých průs…švihů

Nebyla by to přísná kontrola, kdyby se nedala jednoduše obejít. Javadocu stačí předat parametr -Xdoclint:none. Není to ale zase tak jednoduché, jak by se na první pohled zdálo:

  • Parametr je totiž zpětně nekompatibilní, jeho použití ve starších verzích javy vede k chybě o neznámém parametru a tedy neúspěchu kompilace
  • Různé buildovací frameworky (Maven, Gradle, Ant) používají javadoc automaticky v rámci pluginů nebo podobných mechanismů a někdy je potíž mu parametr podstrčit
  • V Mavenu nastává problém pouze při release nebo javadoc pluginu (nikoli při install nebo deploy).
  • V FG Forrest releasujeme často z lokálních strojů, kde není jednoduché předem určit, jaká Java bude použita, maven ji detekuje (alespoň pro mě) nevyzpytatelně.

Podařilo se mi najít řešení, které umožní build ve všech verzích Javy, aniž by bylo nutné nějak hýbat s konfigurací. Využívá profily a silent parametry pluginů. Stačí v pom.xml v sekci project/profile uvést profil, který se aktivuje pro Java 8 (a vyšší) a definuje property.


<profile>
 <id>java8-disable-strict-javadoc</id>
 <activation>
 <jdk>[1.8,)</jdk>
 </activation>
 <properties>
 <javadoc.doclint.none>-Xdoclint:none</javadoc.doclint.none>
 </properties>
</profile>

Potom v konfiguraci pluginu maven-javadoc-plugin, který javadoc v rámci releasu spouští, předat tento parametr s quiet=true, aby si build nestěžoval, pokud ho v nižších verzích Javy nebude mít.

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-javadoc-plugin</artifactId>
   <configuration>
      <additionalparam>${javadoc.doclint.none}</additionalparam>
      <quiet>true</quiet>
   </configuration>
</plugin>

Celá konfigurace tedy může vypadat takto:

<profiles >
     <profile >
     <id >basic </id >
     <build >
       <plugins >
         <plugin >
         <groupId >org.apache.maven.plugins </groupId >
         <artifactId >maven-javadoc-plugin </artifactId >
         <configuration >
         <additionalparam >${javadoc.doclint.none} </additionalparam >
         <quiet >true </quiet >
         </configuration >
         </plugin >
       </plugins >
     </build >
     </profile >
     <profile >
     <id >java8-disable-strict-javadoc </id >
     <activation >
       <jdk >[1.8,) </jdk >
     </activation >
     <properties >
       <javadoc.doclint.none >-Xdoclint:none </javadoc.doclint.none >
     </properties >
     </profile >
 </profiles>

TL;DR - vypnout a psát, jako by to bylo zapnuté

Jediné rozumné řešení je myslím doclint vypnout. Přepisovat nic nemá smysl a generované věci teprve ne. Ne všechna pravidla jsou ale k ničemu - v zájmu budoucích generací i v zájmu kvality dokumentace na ně pokud možno pamatujte a řiďte se jimi, alespoň těmi smysluplnými. Nikdy totiž nevíme dne ani hodiny, kdy Oracle odstraní i možnost vypnutí kontroly.