Friday, June 21, 2013

Effective Javadoc

Effective Javadoc Documentation Illustrated in Familiar Projects
Projects which provide good examples of effective Javadoc documentation practices

1. Advertising Ultimate Demise of Deprecated Method (Guava)

The current version of Guava (Release 10) provides some good examples of more informative statements of deprecation. The next example shows the @deprecated text for methods Files.deleteDirectoryContents(File) and Files.deleteRecursively(File). In both methods' cases, the documentation states why the method is deprecated and states when it is envisioned that the method will be removed (Release 11 in these cases). It is extremely good idea of stating in the deprecation statement when the deprecated thing is going away. It is easy to learn to ignore @deprecated and @Deprecated if one believes they are really never going to go away. Stating a planned removal version or date implies more urgency in not using deprecated features and provides fair warning to users.

deleteDirectoryContents

@Deprecated
public static void deleteDirectoryContents(File directory)
                                    throws IOException
Deprecated. 
Deprecated. This method suffers from poor symlink detection and race conditions. This functionality can be supported suitably only by shelling out to an operating system command such as rm -rf or del /s. This method is scheduled to be removed from Guava in Guava release 11.0.
Deletes all the files within a directory. Does not delete the directory itself.

If the file argument is a symbolic link or there is a symbolic link in the path leading to the directory, this method will do nothing. Symbolic links within the directory are not followed.

Parameters:
directory - the directory to delete the contents of
Throws:
IllegalArgumentException - if the argument is not a directory
IOException - if an I/O error occurs


deleteRecursively

@Deprecated
public static void deleteRecursively(File file)
                              throws IOException
Deprecated. 
Deprecated. This method suffers from poor symlink detection and race conditions. This functionality can be supported suitably only by shelling out to an operating system command such as rm -rf or del /s. This method is scheduled to be removed from Guava in Guava release 11.0.
Deletes a file or directory and all contents recursively.

If the file argument is a symbolic link the link will be deleted but not the target of the link. If the argument is a directory, symbolic links within the directory will not be followed.

Parameters:
file - the file to delete
Throws:
IOException - if an I/O error occurs

See more: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/index.html
Although the source code for each of these methods employs the @Deprecated annotation, the code from both cases does not specify this text with Javadoc's @deprecated, but instead simply specifies the deprecation details as part of the normal method description text with bold tags around the word "Deprecated."

2. Documenting Use of an API (Java SE, Java EE, Guava, Joda Time)

When learning how to use a new API, it is helpful when the Javadoc documentation provides examples of using that API. Good example of learning how to marshal and unmarshal JAXB objects by reading the Javadoc documentation for Marshaller and Unmarshaller respectively. Both of these classes take advantage of class-level documentation to describe how to use the class's APIs.

javax.xml.bind 
Interface Marshaller

All Known Implementing Classes:
AbstractMarshallerImpl
public interface Marshaller
The Marshaller class is responsible for governing the process of serializing Java content trees back into XML data. It provides the basic marshalling methods:

Assume the following setup code for all following code fragments:

       JAXBContext jc = JAXBContext.newInstance( "com.example.foo" );
       Unmarshaller u = jc.createUnmarshaller();
       Object element = u.unmarshal( new File( "foo.xml" ) );
       Marshaller m = jc.createMarshaller();
    
Marshalling to a File:

       OutputStream os = new FileOutputStream( "nosferatu.xml" );
       m.marshal( element, os );
    
Marshalling to a SAX ContentHandler:

       // assume MyContentHandler instanceof ContentHandler
       m.marshal( element, new MyContentHandler() );  
    
Marshalling to a DOM Node:

       DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
       dbf.setNamespaceAware(true);
       DocumentBuilder db = dbf.newDocumentBuilder();
       Document doc = db.newDocument();

       m.marshal( element, doc );
    
Marshalling to a java.io.OutputStream:

       m.marshal( element, System.out );
    
Marshalling to a java.io.Writer:

       m.marshal( element, new PrintWriter( System.out ) );
    
Marshalling to a javax.xml.transform.SAXResult:

       // assume MyContentHandler instanceof ContentHandler
       SAXResult result = new SAXResult( new MyContentHandler() );

       m.marshal( element, result );
    
Marshalling to a javax.xml.transform.DOMResult:

       DOMResult result = new DOMResult();
       
       m.marshal( element, result );
    
Marshalling to a javax.xml.transform.StreamResult:

       StreamResult result = new StreamResult( System.out );
 
       m.marshal( element, result );
    
Marshalling to a javax.xml.stream.XMLStreamWriter:

       XMLStreamWriter xmlStreamWriter = 
           XMLOutputFactory.newInstance().createXMLStreamWriter( ... );
 
       m.marshal( element, xmlStreamWriter );
    
Marshalling to a javax.xml.stream.XMLEventWriter:

       XMLEventWriter xmlEventWriter = 
           XMLOutputFactory.newInstance().createXMLEventWriter( ... );
 
       m.marshal( element, xmlEventWriter );
    
Marshalling content tree rooted by a JAXB element
The first parameter of the overloaded Marshaller.marshal(java.lang.Object, ...) methods must be a JAXB element as computed by JAXBIntrospector#isElement(java.lang.Object); otherwise, a Marshaller.marshal method must throw a MarshalException. There exist two mechanisms to enable marshalling an instance that is not a JAXB element. One method is to wrap the instance as a value of a JAXBElement, and pass the wrapper element as the first parameter to a Marshaller.marshal method. For java to schema binding, it is also possible to simply annotate the instance's class with @XmlRootElement.
Encoding
By default, the Marshaller will use UTF-8 encoding when generating XML data to a java.io.OutputStream, or a java.io.Writer. Use the setProperty API to change the output encoding used during these marshal operations. Client applications are expected to supply a valid character encoding name as defined in the W3C XML 1.0 Recommendation and supported by your Java Platform.
Validation and Well-Formedness
Client applications are not required to validate the Java content tree prior to calling any of the marshal API's. Furthermore, there is no requirement that the Java content tree be valid with respect to its original schema in order to marshal it back into XML data. Different JAXB Providers will support marshalling invalid Java content trees at varying levels, however all JAXB Providers must be able to marshal a valid content tree back to XML data. A JAXB Provider must throw a MarshalException when it is unable to complete the marshal operation due to invalid content. Some JAXB Providers will fully allow marshalling invalid content, others will fail on the first validation error.

Even when schema validation is not explictly enabled for the marshal operation, it is possible that certain types of validation events will be detected during the operation. Validation events will be reported to the registered event handler. If the client application has not registered an event handler prior to invoking one of the marshal API's, then events will be delivered to a default event handler which will terminate the marshal operation after encountering the first error or fatal error. Note that for JAXB 2.0 and later versions, DefaultValidationEventHandler is no longer used.
[...]

 See more: http://docs.oracle.com/javaee/6/api/
Guava's class-level description for Stopwatch shows how to use most of that class's features in a concise and easily understandable class usage description.

com.google.common.base
Class Stopwatch
 java.lang.Object

 com.google.common.base.Stopwatch
________________________________________

@Beta
@GwtCompatible(emulated=true)
public final class Stopwatch
extends Object
An object that measures elapsed time in nanoseconds. It is useful to measure elapsed time using this class instead of direct calls toSystem.nanoTime() for a few reasons:
• An alternate time source can be substituted, for testing or performance reasons.
• As documented by nanoTime, the value returned has no absolute meaning, and can only be interpreted as relative to another timestamp returned by nanoTime at a different time. Stopwatch is a more effective abstraction because it exposes only these relative values, not the absolute ones.
Basic usage:
   Stopwatch stopwatch = new Stopwatch().start();
   doSomething();
   stopwatch.stop(); // optional

   long millis = stopwatch.elapsed(MILLISECONDS);

   log.info("that took: " + stopwatch); // formatted string like "12.3 ms"
 
Stopwatch methods are not idempotent; it is an error to start or stop a stopwatch that is already in the desired state.
When testing code that uses this class, use the alternate constructor to supply a fake or mock ticker. This allows you to simulate any valid behavior of the stopwatch.
Note: This class is not thread-safe.
Since:
10.0
Author:
Kevin Bourrillion

 See more: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/index.html
Use of an API can be documented at the method level as well as at the class level. Examples of this are Guava's Throwables.propagateIfInstanceOf method and the overloaded Throwables.propagateIfPossible methods . The Javadoc documentation for these methods shows "example usage" for each.


propagateIfInstanceOf
public static <X extends Throwable> void propagateIfInstanceOf(@Nullable
                                               Throwable throwable,
                                               Class<X> declaredType)
                                  throws X extends Throwable
Propagates throwable exactly as-is, if and only if it is an instance of declaredType. Example usage:
   try {
     someMethodThatCouldThrowAnything();
   } catch (IKnowWhatToDoWithThisException e) {
     handle(e);
   } catch (Throwable t) {
     Throwables.propagateIfInstanceOf(t, IOException.class);
     Throwables.propagateIfInstanceOf(t, SQLException.class);
     throw Throwables.propagate(t);
   }
 
Throws:
X extends Throwable
propagateIfPossible
public static void propagateIfPossible(@Nullable
                       Throwable throwable)
Propagates throwable exactly as-is, if and only if it is an instance of RuntimeException or Error. Example usage:
   try {
     someMethodThatCouldThrowAnything();
   } catch (IKnowWhatToDoWithThisException e) {
     handle(e);
   } catch (Throwable t) {
     Throwables.propagateIfPossible(t);
     throw new RuntimeException("unexpected", t);
   }
 
propagateIfPossible
public static <X extends Throwable> void propagateIfPossible(@Nullable
                                             Throwable throwable,
                                             Class<X> declaredType)
                                throws X extends Throwable
Propagates throwable exactly as-is, if and only if it is an instance of RuntimeException, Error, or declaredType. Example usage:
   try {
     someMethodThatCouldThrowAnything();
   } catch (IKnowWhatToDoWithThisException e) {
     handle(e);
   } catch (Throwable t) {
     Throwables.propagateIfPossible(t, OtherException.class);
     throw new RuntimeException("unexpected", t);
   }
 
Parameters:
throwable - the Throwable to possibly propagate
declaredType - the single checked exception type declared by the calling method
Throws:
X extends Throwable

 See more: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/index.html
API documentation is not limited to the class level or method level. The javax.management package-level documentation provides a nice overview of Java Management Extensions (JMX). The first sentence of the package description (which is what's always shown at top) is simple enough: "Provides the core classes for the Java Management Extensions." However, there are far more details in the rest of the package description. The next example shows a small portion of that package documentation.

Package javax.management Description

Provides the core classes for the Java Management Extensions.

The Java Management Extensions (JMXTM) API is a standard API for management and monitoring. Typical uses include:

consulting and changing application configuration
accumulating statistics about application behavior and making them available
notifying of state changes and erroneous conditions.
The JMX API can also be used as part of a solution for managing systems, networks, and so on.

The API includes remote access, so a remote management program can interact with a running application for these purposes.

MBeans

The fundamental notion of the JMX API is the MBean. An MBean is a named managed object representing a resource. It has a management interface consisting of:

named and typed attributes that can be read and/or written
named and typed operations that can be invoked
typed notifications that can be emitted by the MBean.
For example, an MBean representing an application's configuration could have attributes representing the different configuration items. Reading the CacheSize attribute would return the current value of that item. Writing it would update the item, potentially changing the behavior of the running application. An operation such as save could store the current configuration persistently. A notification such as ConfigurationChangedNotification could be sent every time the configuration is changed.

In the standard usage of the JMX API, MBeans are implemented as Java objects. However, as explained below, these objects are not usually referenced directly.

Standard MBeans

To make MBean implementation simple, the JMX API includes the notion of Standard MBeans. A Standard MBean is one whose attributes and operations are deduced from a Java interface using certain naming patterns, similar to those used by JavaBeansTM. For example, consider an interface like this:

    public interface ConfigurationMBean {
         public int getCacheSize();
         public void setCacheSize(int size);
         public long getLastChangedTime();
         public void save();
    }
[...]


Another example of a useful package-level description is the package description for Joda Time package org.joda.time. This core package describes many of the concepts applicable to the entire project in one location.

Package org.joda.time Description

Provides support for dates, times, time zones, durations, intervals, and partials. This package aims to fully replace the Java Date, Calendar, and TimeZone classes. This implementation covers both the Gregorian/Julian calendar system and the ISO8601 standard. Additional calendar systems and extensions can be created as well.

The ISO8601 standard is the international standard for dates, times, durations, and intervals. It defines text representations, the first day of the week as Monday, and the first week in a year as having a Thursday in it. This standard is being increasingly used in computer interchange and is the agreed format for XML. For most uses, the ISO standard is the same as Gregorian, and is thus the preferred format.

Interfaces

The main API concepts are defined by interfaces:

ReadableInstant - an instant in time
ReadableDateTime - an instant in time with field accessors such as dayOfWeek
ReadablePartial - a definition for local times that are not defined to the millisecond, such as the time of day
ReadableDuration - a duration defined in milliseconds
ReadablePeriod - a time period defined in fields such as hours and minutes
ReadableInterval - a period of time between two instants
ReadWritableInstant - an instant that can be modified
ReadWritableDateTime - a datetime that can be modified
ReadWritablePeriod - a time period that can be modified
ReadWritableInterval - an interval that can be modified
These define the public interface to dates, times, periods, intervals and durations. As with java.util.Date and Calendar, the design is millisecond based with an epoch of 1970-01-01. This should enable easy conversions.

Implementations

The basic implementation of the ReadableInstant interface is Instant. This is a simple immutable class that stores the millisecond value and integrates with Java Date and Calendar. The class follows the definition of the millisecond instant fully, thus it references the ISO-8601 calendar system and UTC time zone. If you are dealing with an instant in time but do not know, or do not want to specify, which calendar system it refers to, then you should use this class.

The main implementation class for datetimes is the DateTime class. This implements the ReadableDateTime interface, providing convenient methods to access the fields of the datetime. Conversion methods allow integration with the Java Date and Calendar classes.


3. Explicitly Declaring Throws Clause for Unchecked Exceptions (Guava)

Iit is best to "document all thrown exceptions" whether they are checked or unchecked. Guava's InetAddresses.forString(String) method's documentation does this, specifying that it throws the runtime exception IllegalArgumentException.

forString
public static InetAddress forString(String ipString)
Returns the InetAddress having the given string representation.
This deliberately avoids all nameservice lookups (e.g. no DNS).

Parameters:
ipString - String containing an IPv4 or IPv6 string literal, e.g. "192.168.0.1" or "2001:db8::1"
Returns:
InetAddress representing the argument
Throws:
IllegalArgumentException - if the argument is not a valid IP string literal


4. Using -linksource (JFreeChart, Guava)

For an open source project, a nice benefit that can be provided to developers using that project is to allow linking of Javadoc documentation to underlying source code. There are two examples below with the first one showing the Javadoc with link to source code annotated and the second showing the source code displayed when the class name is clicked on in the Javadoc.


com.google.common.base
Class Strings

java.lang.Object
com.google.common.base.Strings

@GwtCompatible
public final class Strings
extends Object
Static utility methods pertaining to String or CharSequence instances.
Since:
3.0
Author:
Kevin Bourrillion



037public final class Strings {
038  private Strings() {}
039
040  /**
041   * Returns the given string if it is non-null; the empty string otherwise.
042   *
043   * @param string the string to test and possibly return
044   * @return {@code string} itself if it is non-null; {@code ""} if it is null
045   */
046  public static String nullToEmpty(@Nullable String string) {
047    return (string == null) ? "" : string;
048  }
049
050  /**
051   * Returns the given string if it is nonempty; {@code null} otherwise.
052   *
053   * @param string the string to test and possibly return
054   * @return {@code string} itself if it is nonempty; {@code null} if it is
055   *     empty or null
056   */
057  public static @Nullable String emptyToNull(@Nullable String string) {
058    return isNullOrEmpty(string) ? null : string;
059  }


It is very convenient to be able to move easily between the Javadoc documentation and the source code. Of course, this can also be done in an IDE that supports Javadoc presentation in conjunction with code.

Ultimate example

 The ultimate example of JavaDoc is Mockito, where the whole documentation is concisely embedded.

Conclusion

This post has highlighted several projects who Javadoc documentation provides examples of more effective Javadoc-based documentation.


No comments:

Post a Comment

Web 3 - blockchain layers

Layers from a blockchain perspective. My plan is to write 5 articles:  1 Intro: Web 1.. 2.. 3.. 2 Layers in crypto.  [this one] 3 Applicatio...