Revapi Java Extension

Revapi extension to check API of java classes in jar archives.

In this example it will be shown how to extend the Revapi’s java API checking capabilities. To make it actually useful, this example will show how to automatically ignore addition of any new methods on the EJB interfaces. While a new method on an interface is generally an API breakage, because the implementations that were developed against the old version of the interface would no longer be valid, this change is actually OK on EJB interfaces, because these are not supposed to be implemented by "callers" - the implementations are in control of the library that defines the EJBs.

Project setup

First we need to set up our maven project. We will be extending Revapi’s Java extension that offers an SPI for doing so. In the pom.xml, we will specify that we want to use that SPI:

<project>
    <modelVersion>4.0.0</modelVersion>

    <groupId>my.group</groupId>
    <artifactId>my.extension</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>org.revapi</groupId>
            <artifactId>revapi-java-spi</artifactId>
            <version>{version}</version>
        </dependency>
    </dependencies>
</project>

Code

To ignore a found difference, we need to implement a difference transform.

package my.extension;

import java.io.Reader;
import java.util.regex.Pattern;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;

import org.revapi.AnalysisContext;
import org.revapi.Difference;
import org.revapi.DifferenceTransform;
import org.revapi.java.spi.JavaMethodElement;
import org.revapi.java.spi.Util;

public class IgnoreNewMethodsOnEJBInterfaces implements DifferenceTransform<JavaMethodElement> {
    @Override
    public Pattern[] getDifferenceCodePatterns() {
        return new Pattern[] { Pattern.compile("java\\.method\\.addedToInterface") };
    }

    @Override
    public Difference transform(JavaMethodElement oldElement, JavaMethodElement newElement,
        Difference difference) {

        // we know the element will be a JavaMethodElement. This is because we limit the
        // differences passed into this method.
        ExecutableElement method = newElement.getDeclaringElement();

        // ok, so we got a reference to the method that caused the difference. Now we need to
        // check whether the method was added to an EJB interface - we will just check whether
        // the interface was annotated with the @Local or @Remote annotations.
        for (AnnotationMirror annotation : method.getEnclosingElement().getAnnotationMirrors()) {
            // the Util class in the Java SPI provides a number of useful methods to ease the work
            // with the javax.lang.model objects.
            String annotationTypeName = Util.toHumanReadableString(annotation.getAnnotationType());
            if ("javax.ejb.Local".equals(annotationTypeName) ||
                "javax.ejb.Remote".equals(annotationTypeName)) {

                // ok, so we've found out that the type that declared the new method is indeed
                // an EJB interface. By returning null, we tell Revapi to remove this difference.
                return null;
            }
        }

        // ok, this is not an EJB interface, so we leave the difference alone
        return difference;
    }

    @Override
    public void close() throws Exception {
        // no resources to close...
    }

    @Override
    public String[] getConfigurationRootPaths() {
        // no configuration possible
        return null;
    }

    @Override
    public Reader getJSONSchema(String configurationRootPath) {
        // no configuration possible
        return null;
    }

    @Override
    public void initialize(AnalysisContext analysisContext) {
        // nothing needed here
    }
}

In addition to the code itself, the class needs to be registered as an Revapi extension. For that it needs to be made a java service. Create a file called src/main/resources/META-INF/services/org.revapi.DifferenceTransform and add a line to it with the fully qualified name of the above class, i.e my.extension.IgnoreNewMethodsOnEJBInterfaces.

Usage

Once installed into a maven repository (local or some public), our extension becomes useable by Revapi. Both the Revapi standalone and maven plugin support including new extensions by specifying their maven coordinates, see Getting Started for more details on that.

Back to top

Msb3 Maven skin by Marek Romanowski.