japicmp

repository·master·Indexed 20 days ago

https://github.com/siom79/japicmp

A tool and library for comparing two versions of a Java JAR archive to detect API changes. It identifies source, binary, and serialization compatibility differences and can be used as a standalone CLI tool, a Java library via JarArchiveComparator, a Maven plugin (japicmp-maven-plugin), or an Ant task. Key features include dependency-free comparison using javassist, annotation tracking, Semantic Versioning suggestions, and reports exportable to Markdown, XML, or HTML.

Tokens
42.5K
Snippets
61
Records
137
Agent score
72%

What's inside japicmp

  1. Configure oldVersion and newVersion for comparison

    master

    To perform a comparison, you must specify which two versions are being compared using the <oldVersion> and <newVersion> elements. Both elements support two types of identification:

    1. <dependency>: Specifies an existing Maven dependency. You can optionally include a <classifier> inside the <dependency> element.
    2. <file>: Specifies a direct path to a file (e.g., a JAR file in the build directory).

    Example of using a dependency for the old version and a local file for the new version:

    <oldVersion>
        <dependency>
            <groupId>japicmp</groupId>
            <artifactId>japicmp-test-v1</artifactId>
            <version>0.26.1</version>
            <type>jar</type>
        </dependency>
    </oldVersion>
    <newVersion>
        <file>
            <path>${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}</path>
        </file>
    </newVersion>
  2. Configure error exit codes for CI/CD

    master

    To use japicmp in automated pipelines (like CI/CD), you can force the tool to exit with an error code if specific types of incompatibilities are detected. This allows you to break a build if breaking changes are introduced.

    • --error-on-binary-incompatibility: Exit if binary incompatibility is found.
    • --error-on-source-incompatibility: Exit if source incompatibility is found.
    • --error-on-modifications: Exit if any change (even non-breaking) is detected.
    • --error-on-semantic-incompatibility: Exit if changes violate Semantic Versioning rules (requires versions in Major.Minor.Patch format).

    Note on Exclusions: If you use --no-error-on-exclusion-incompatibility, the tool will not trigger an error if an incompatibility is caused by a class or member you have explicitly excluded (e.g., an interface was removed from a class, but that interface was excluded from the check).

  3. Understand the Compatibility Report format

    master

    The Compatibility Report is a tool used to identify breaking changes between two versions of a library (e.g., Guava 18.0 vs 19.0). It categorizes changes based on SemVer (Semantic Versioning) principles, specifically highlighting MAJOR incompatible changes.

    When reviewing a report, you can expand the options used to see the specific configuration applied during the check, such as:

    • Access modifier filter: Limits the scope of the check (e.g., PROTECTED).
    • Report only changes: If enabled, the report focuses on what changed rather than a full summary.
    • Evaluate annotations: Determines if annotation changes are considered part of the compatibility check.
    • Classpath mode: Defines how the tool handles classpaths (e.g., ONE_COMMON_CLASSPATH).
    • Treat changes as errors: Configures whether specific types of changes (binary, source, or semantic incompatibilities) should trigger error states.
  4. Key features of japicmp

    master

    japicmp provides several advanced capabilities for Java API comparison:

    • Dependency-free comparison: Uses the javassist library to inspect class files, meaning you don't need to add all dependencies of the JARs to the classpath.
    • Compatibility detection: Differentiates between source compatible and binary compatible changes.
    • Annotation tracking: Compares changes in annotations, making it suitable for annotation-based APIs like JAXB, JPA, and JAX-RS.
    • Flexible output: Differences can be printed to the command line or exported as Markdown, XML, or HTML files.
    • Granular control:
      • Control access modifier visibility (public, protected, package, private).
      • Include/exclude specific packages, classes, methods, or fields.
      • Include synthetic classes/members using --include-synthetic.
    • Semantic Versioning: The --semantic-versioning option suggests the appropriate version increment (Major/Minor/Patch) based on changes.
    • Serialization awareness: Evaluates changes regarding the Java Object Serialization Specification if a class is serializable.
  5. Understand japicmp output notation

    master

    When comparing Java APIs, japicmp uses specific symbols in its command-line output to indicate the nature of changes:

    • ! (Exclamation mark): Indicates binary incompatible changes.
    • * (Trailing star): Indicates source incompatible changes.

    Example of command-line output showing a modified interface and a new method:

    **** MODIFIED INTERFACE: PUBLIC ABSTRACT com.google.common.collect.RangeMap  (not serializable)
    	+++* NEW METHOD: PUBLIC(+) ABSTRACT(+) java.util.Map asDescendingMapOfRanges()
  6. Configure element inclusion and exclusion

    master

    You can control which classes, members, or packages are analyzed using the --include and --exclude options.

    • Format: Use a semicolon-separated list of elements in the form package.Class#classMember.
    • Wildcards: Use * as a wildcard.
    • Annotations: Use the Fully Qualified Name (FQN) starting with @ (e.g., @my.Annotation).
    • Exclusion Modes:
      • --exclude-exclusively: Only excludes the specific packages provided, while still including their sub-packages.
      • --include-exclusively: Only includes the specific packages provided, excluding all sub-packages.

    Examples:

    • mypackage (includes a whole package)
    • my.Class (includes a specific class)
    • other.Class#method(int,long) (includes a specific method with signature)
    • foo.Class#field (includes a specific field)
    • @my.Annotation (includes an annotation)
  7. Review Guava compatibility reports

    master

    The japicmp project generates compatibility reports for libraries like Guava. These reports indicate whether changes between versions affect Binary compatibility (compiled code), Source compatibility (source code compilation), or Serialization compatibility (object persistence).

    Common status indicators in the reports include:

    • Added: New methods or classes have been introduced.
    • Modified: Existing methods or classes have changed (e.g., signature changes or annotation additions).
    • Removed: Methods or classes have been deleted.
    • Source-incompatible: Changes that will break source code compilation (e.g., changing generic type bounds).
    • Serialization-incompatible: Changes that break serialization (e.g., changing serialVersionUID).
  8. Run japicmp via CLI

    master

    You can use japicmp as a standalone command-line tool to compare two JAR archives. Use the -n flag to specify the new version and the -o flag to specify the old version.

    java -jar japicmp-0.0-jar-with-dependencies.jar -n new-version.jar -o old-version.jar
  9. Skip japicmp execution in Maven

    master

    You can skip the execution of the japicmp-maven-plugin using the following methods:

    1. Maven Property: Set -Djapicmp.skip=true in your Maven command line.
    2. Plugin Configuration: Use the <skip>true</skip> parameter within the plugin configuration in your pom.xml.
    <!-- Maven plugin configuration -->
    <configuration>
      <skip>true</skip>
    </configuration>
  10. Integrate japicmp-maven-plugin into Maven builds

    master

    The japicmp-maven-plugin allows you to automate API compatibility checks during your Maven build lifecycle (e.g., during the verify phase). You can define the oldVersion as a dependency and the newVersion as a local file.

    <plugin>
    	<groupId>com.github.siom79.japicmp</groupId>
    	<artifactId>japicmp-maven-plugin</artifactId>
    	<version>0.26.1</version>
    	<configuration>
    		oldVersion>
    			<dependency>
    				<groupId>japicmp</groupId>
    				<artifactId>japicmp-test-v1</artifactId>
    				<version>${oldversion}</version>
    				<type>jar</type>
    			</dependency>
    		</oldVersion>
    		<newVersion>
    			<file>
    				<path>${project.build.directory}/${project.artifactId}-${project.version}.${project.packaging}</path>
    			</file>
    		</newVersion>
    		<parameter>
    			<!-- see documentation -->
    		</parameter>
    	</configuration>
    	<executions>
    		execution>
    			<phase>verify</phase>
    			<goals>
    				<goal>cmp</goal>
    			</goals>
    		</execution>
    	</executions>
    </plugin>
  11. Use Groovy scripts for post-analysis in the Maven Plugin

    master

    The <postAnalysisScript> parameter allows you to execute a Groovy script after the API analysis is complete but before the output is written. This is useful for:

    1. Custom Filtering: Removing specific classes or methods from the report (e.g., filtering out annotations or getter/setter methods).
    2. Custom Build Rules: Breaking the build by throwing an exception if certain API change conditions are met (e.g., ensuring specific packages remain UNCHANGED).

    Important: The script must return a list of JApiClass objects. If it does not, the Maven plugin will report an error.

    // Example: Filtering out classes in a specific package and specific method patterns
    def it = jApiClasses.iterator()
    while (it.hasNext()) {
    	def jApiClass = it.next()
    	def fqn = jApiClass.getFullyQualifiedName()
    	if (fqn.startsWith("japicmp.test.annotation")) {
    		it.remove()
    	}
    	def methodIt = jApiClass.getMethods().iterator()
    	while (methodIt.hasNext()) {
    		def method = methodIt.next()
    		if (method.getName().startsWith("get") || method.getName().startsWith("set")) {
    			methodIt.remove()
    		}
    	}
    }
    return jApiClasses
  12. Load post-analysis scripts from an external artifact

    master

    If your Groovy post-analysis script is stored within another artifact, you can load it from the classpath by adding that artifact as a <dependency> to the japicmp-maven-plugin configuration. This allows you to manage complex analysis logic separately from your main project POM.

    <plugin>
    	<groupId>com.github.siom79.japicmp</groupId>
    	<artifactId>japicmp-maven-plugin</artifactId>
    	<version>${project.version}</version>
    	<configuration>
    		<parameter>
    			<!-- The post-analysis script is contained in "post-analysis-script-artifact" -->
    			<postAnalysisScript>post-analysis-script.groovy</postAnalysisScript>
    		</parameter>
    	</configuration>
    	<dependencies>
    		<dependency>
    			<groupId>com.github.siom79.japicmp</groupId>
    			<artifactId>post-analysis-script-artifact</artifactId>
    			<version>${project.version}</version>
    		</dependency>
    	</dependencies>
    </plugin>