Revapi Maven Plugin

Maven integration for Revapi.

The maven plugin itself is configured using the properties of the goals. The configuration of the API analysis is a matter of configuring different Revapi extensions that are to be run. This is done using the analysisConfiguration element in the Maven plugin’s configuration. This element contains the configuration of the individual extensions in either XML or JSON.

Specifying The Analysis Configuration Using XML

As an example, let’s configure the java extension to ignore the classes it finds missing from the API rather than reporting them and also only include the archives with com.acme groupId in the analysis::

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  <configuration>
    <analysisConfiguration>
      <revapi.java>
        <missing-classes>
          <behavior>ignore</behavior>
        </missing-classes>
      </revapi.java>
      <revapi.filter>
        <archives>
          <include>
            <item>com\.acme:.*</item>
          </include>
        </archives>
      </revapi.filter>
    </analysisConfiguration>
  </configuration>

Each extension has a unique "extension id" which is used as the root tag for its configuration under the analysisConfiguration tag. Under the extension configuration’s root tag an XML representation of the configuration as specified by the extension documentation (and JSON schema - yes, the XML is validated against a JSON schema ;) ).

Multiple Configurations Per Extension

There can be multiple configurations for a single extension. Optionally, each extension configuration "instance" can be assigned an ID such that it can be effectively merged (see below).

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  ...
  <configuration>
    <analysisConfiguration>
      <revapi.reporter.text id="stdout">
        <output>out</output>
      </revapi.reporter.text>
      <revapi.reporter.text id="custom-report">
        <output>${project.build.directory}/revapi-custom-report.xml</output>
        <template>${custom-report.template.location}</template>
      </revapi.reporter.text>
    </analysisConfiguration>
  </configuration>

This configuration will cause the Revapi’s text reporter (if is included as a dependency of the plugin) to output the results of the analysis both to standard output and a custom file using a custom template.

Analysis Configuration And Maven Inheritance

Having the Revapi analysis configuration specified in XML enables Maven to apply its configuration inheritance logic to Revapi analysis configuration, too.

Here is a couple of tips on how to make the Maven configuration inheritance work nice with Revapi analysis configuration.

Tip
mvn help:effective-pom, combine.self and combine.children are your friends when inheriting more complex analysis configurations.

One Configuration Per Extension

Parent POM:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <configuration>
    <analysisConfiguration>
      <revapi.ignore>
        <item>
          <code>java.class.removed</code>
        </item>
        <item>
          <code>java.class.added</code>
        </item>
      </revapi.ignore>
    </analysisConfiguration>
  </configuration>
  ...

Child POM:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <configuration>
    <analysisConfiguration>
      <revapi.ignore>
        <item>
          <code>java.class.nowFinal</code>
        </item>
      </revapi.ignore>
    </analysisConfiguration>
  </configuration>
  ...

Effective Child POM:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <configuration>
    <analysisConfiguration>
      <revapi.ignore>
        <item>
          <code>java.class.nowFinal</code>
        </item>
      </revapi.ignore>
    </analysisConfiguration>
  </configuration>
  ...

Notice that revapi.ignore doesn’t contain the item`s defined in the parent POM. That is the default Maven behavior. To be able to inherit the configuration of the `revapi.ignore extension from the parent POM, you have to specify how to merge the `item`s in the child POM like so:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <configuration>
    <analysisConfiguration>
      <revapi.ignore combine.children="append">
        <item>
          <code>java.class.nowFinal</code>
        </item>
      </revapi.ignore>
    </analysisConfiguration>
  </configuration>
  ...

After that, the effective child POM will indeed contain configuration combined from both parent and child:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <configuration>
    <analysisConfiguration>
      <revapi.ignore>
        <item>
          <code>java.class.nowFinal</code>
        </item>
        <item>
          <code>java.class.removed</code>
        </item>
        <item>
          <code>java.class.added</code>
        </item>
      </revapi.ignore>
    </analysisConfiguration>
  </configuration>
  ...

Multiple Configurations Per Extension

As mentioned in the previous chapters, revapi supports multiple configurations per extension. This gets a little bit complicated in conjunction with inheritance. Let’s see an example.

Parent POM

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  ...
  <configuration>
    <analysisConfiguration>
      <revapi.reporter.text id="stdout">
        <output>out</output>
      </revapi.reporter.text>
      <revapi.reporter.text id="custom-report">
        <output>${project.build.directory}/revapi-custom-report.xml</output>
        <template>${custom-report.template.location}</template>
      </revapi.reporter.text>
    </analysisConfiguration>
  </configuration>

Child POM

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  ...
  <configuration>
    <analysisConfiguration>
      <revapi.reporter.text id="stdout">
        <output>err</output>
      </revapi.reporter.text>
    </analysisConfiguration>
  </configuration>

I.e. the child POM wants to reconfigure the "stdout" configuration of revapi text reporter to report to standard error output instead of the standard output.

If we inspect the effective child POM, we’ll see this though:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  ...
  <configuration>
    <analysisConfiguration>
      <revapi.reporter.text id="stdout">
        <output>err</output>
      </revapi.reporter.text>
    </analysisConfiguration>
  </configuration>

I.e. the configuration for the custom output is lost in the child POM (again, this is standard Maven behavior. These are just examples to save you from ripping your hair out unnecessarily ;) ). To also inherit the other reporter configuration, you have to mention it like this in the child POM

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  ...
  <configuration>
    <analysisConfiguration>
      <revapi.reporter.text id="stdout">
        <output>err</output>
      </revapi.reporter.text>
      <revapi.reporter.text id="custom-report"/>
    </analysisConfiguration>
  </configuration>

Now the effective child POM contains the custom report configuration as well as the modified stdout configuration.

Specifying The Analysis Configuration Using JSON

Revapi has been around for a little bit and over the time it has evolved. Originally (up until Revapi API 0.8.0), each extension was instantiated exactly once and therefore also configured exactly once. Since Revapi API 0.8.0, supported by Revapi Maven plugin 0.9.0, there can be multiple configurations for each extension (and the extension can be therefore instantiated multiple times). This brings the ability to e.g. have 2 differently configured text reporter instances, each generating a different kind of output. Unfortunately, this complicates the configuration, because it is no longer possible to have a single "configuration tree" where extensions would read their configurations from their declared locations.

Therefore, since Revapi API 0.8.0 there is a new kind of JSON format for configuration (which in turn also enables maven plugin to support XML configuration btw). To ease the migration to the new versions, the old configuration format is still supported (but mixing the two formats can lead to unresolvable situations, see link:multi-file-configuration.html for more details).

The JSON Configuration Format

As explained above, each extension can be configured multiple times. To support this in JSON, the JSON configuration looks like this:

[
  {
    "extension": "revapi.reporter.text",
    "id": "optional-id",
    "configuration": {
      ... the actual configuration of the extension according to its schema ...
    }
  },
  {
    "extension": "revapi.reporter.text",
    "configuration": {
      ...
    }
  },
  {
    "extension": "revapi.ignore",
    "configuration": {
      ...
    }
  },
  ...
]

The configuration object is a list. The members of the list are individual configurations for the extensions. The extension being configured is specified by the extension key and the configuration (conforming to the schema specified by the extension) is present under the configuration key.

The optional id key is useful if there are multiple configuration sources (see multi file configuration for example) as it affects how the configurations from the different sources are merged together.

The Legacy JSON Configuration Format

Warning
This describes the obsolete JSON configuration format that cannot handle multiple configurations per extension. If you still use it, rest assured that it is still supported (with the exception of certain scenarios during merging of multiple configuration sources) but you are encouraged to start using the new configuration format.

The JSON data contains the configuration of all the extensions. Each of the extensions declares a "root" in the JSON data from which it reads its configuration (for example, ignoring specific problems found during the analysis can be done using the IgnoreDifferenceTransform extension from the basic features under the root revapi.ignore).

So, without further ado, let’s configure the java extension to report the classes it finds missing from the API rather than failing the analysis upon encountering them and also only include the archives with com.acme groupId in the analysis:

<plugin>
  <groupId>org.revapi</groupId>
  <artifactId>revapi-maven-plugin</artifactId>
  <version>...</version>
  <configuration>
    <analysisConfiguration><![CDATA[
      {
        "revapi": {
          "java": {
            "missing-classes": {
              "behavior": "report"
            }
          },
          "filter": {
            "archives": {
              "include": ["com\\.acme:.*"]
            }
          }
        }
      }
    ]]></analysisConfiguration>
  </configuration>
  <executions>
    <execution>
      <goals><goal>check</goal></goals>
    </execution>
  </executions>

The configuration options of the various extensions can be found in their respective docs: basic features documentation, java extension documentation.

Back to top

Msb3 Maven skin by Marek Romanowski.