APILyzer looks for deviations from the following rules.
APILyzer allows easy declaration of Public API Types and approved types. The public members of the public API types are analyzed to ensure only expected types are used. Public members include public methods, public fields, and public inner classes. Protected classes and members are treated as public during analysis.
This tool fills a niche not covered by other tools, like Animal Sniffer and checkstyle import control. A project that wants to ensure API stability would likely use these tools in addition to APILyzer. Import analysis differs because it’s ok for a public API class to import a non-public API class for use only in its implementation. Comparing new API changes to signatures of a previous API differs because it only validates that the old API is not broken.
To add this plugin to your project, configure the plugin similarly to:
<build> <plugins> <plugin> <groupId>net.revelc.code</groupId> <artifactId>apilyzer-maven-plugin</artifactId> <version>1.2.0</version> <executions> <execution> <id>apilyzer</id> <goals> <goal>analyze</goal> </goals> <configuration> <includes> <!--Specify one or more regular expressions that define the public API. Each regex is matched agains all fully qualified class names. Any class that matches (and is public) is added to the set of public API classes.--> <include>org[.]apache[.]accumulo[.]minicluster[.].*</include> </includes> <excludes> <!-- Specifiy zero or more regular expressions. Any regex that matches will exclude a prevously included class from the set of API classes --> <exclude>.*[.]impl[.].*</exclude> <exclude>.*Impl</exclude> </excludes> <allows> <!-- Specify zero or more regular expressions defining the set of non-API classes thats it ok for public API members to reference. These regular expressions are matched against fully qualified type names referenced by public API members. Conceptually, public API classes and Java classes are automatically added to this set, so there is no need to add those here. --> <allow>org[.]apache[.]accumulo[.]core[.]client[.].*</allow> <allow>org[.]apache[.]accumulo[.]core[.]data[.](Mutation|Key|Value|Condition|ConditionalMutation|Range|ByteSequence|PartialKey|Column)</allow> <allow>org[.]apache[.]accumulo[.]core[.]security[.](ColumnVisibility|Authorizations)</allow> </allows> </configuration> </execution> </executions> </plugin> </plugins> </build>
and build your project, similarly to (it runs at the verify phase by default):
mvn verify
Below is the output of running the above command.
Includes: [org[.]apache[.]accumulo[.]minicluster[.].*] Excludes: [.*[.]impl[.].*, .*Impl] Allowed: [org[.]apache[.]accumulo[.]core[.]client[.].*, org[.]apache[.]accumulo[.]core[.]data[.](Mutation|Key|Value|Condition|ConditionalMutation|Range|ByteSequence|PartialKey|Column), org[.]apache[.]accumulo[.]core[.]security[.](ColumnVisibility|Authorizations)] Public API: org.apache.accumulo.minicluster.MemoryUnit org.apache.accumulo.minicluster.MiniAccumuloCluster org.apache.accumulo.minicluster.MiniAccumuloConfig org.apache.accumulo.minicluster.MiniAccumuloInstance org.apache.accumulo.minicluster.MiniAccumuloRunner org.apache.accumulo.minicluster.MiniAccumuloRunner$Opts org.apache.accumulo.minicluster.MiniAccumuloRunner$PropertiesConverter org.apache.accumulo.minicluster.ServerType CONTEXT TYPE FIELD/METHOD NON-PUBLIC REFERENCE Method return org.apache.accumulo.minicluster.MiniAccumuloInstance getConfigProperties(...) org.apache.commons.configuration.PropertiesConfiguration Method param org.apache.accumulo.minicluster.MiniAccumuloInstance lookupInstanceName(...) org.apache.accumulo.fate.zookeeper.ZooCache
The output shows the 8 types that APILyzer determined to be public API types based on its configuration and the maven dependencies. These are the 8 types that APILyzer analyzed.
The output also shows two problems APILyzer found with these 8 types. Both problems are with public API methods. The first problem is MiniAccumuloInstance.getConfigProperties() returns PropertiesConfiguration which is not a public API type. The second problem is MiniAccumuloInstance.lookupInstanceName() takes a parameter of type ZooCache which is not a public API type.
Hadoop has two annotations that it uses to communicate who should use its APIs. First, the InterfaceAudience annotation has values of Public, LimitedPrivate, and Private. Second, the InterfaceStability annotation has values of Stable, Evolving, and Unstable. In Hadoop the convention is to mark each API with an audience and stability annotation. The following example finds types marked Public+Stable using types that are not Public+Stable.
When trying to analyze a classes annotations, the class must be loaded. In the example below some classes could not be loaded because their dependencies were not on the classpath. However these classes were not in a package we cared about. Excluding classes not in the org.apache.hadoop package fixed this problem.
<configuration> <!--Look for Public+Stable APIs using Types that are not Public+Stable--> <includes> <!-- This class has no annotation, but seems like it sould be in API --> <include>org[.]apache[.]hadoop[.]fs[.]RemoteIterator</include> </includes> <includeAnnotations> <include> [@]org[.]apache[.]hadoop[.]classification[.]InterfaceAudience[$]Public.* </include> </includeAnnotations> <excludeAnnotations> <exclude> [@]org[.]apache[.]hadoop[.]classification[.]InterfaceStability[$]Evolving.* </exclude> <exclude> [@]org[.]apache[.]hadoop[.]classification[.]InterfaceStability[$]Unstable.* </exclude> <!-- If a class is included in the API, then its public inner classes are also (unless excluded). This exclude is for the case where a type w/ Private annotation is an inner class of a class with a Public annotation --> <exclude> [@]org[.]apache[.]hadoop[.]classification[.]InterfaceAudience[$](Limited)?Private.* </exclude> </excludeAnnotations> <excludes> <!-- Exclude all classes not in the org.apache.hadoop package --> <exclude>(?!org[.]apache[.]hadoop.*).*</exclude> </excludes> </configuration>