Package Info Maven Plugin

Maven plugin that automatically generates package-info.java files based on a template.

License

License

Categories

Categories

Maven Build Tools
GroupId

GroupId

com.github.bohnman
ArtifactId

ArtifactId

package-info-maven-plugin
Last Version

Last Version

1.1.0
Release Date

Release Date

Type

Type

maven-plugin
Description

Description

Package Info Maven Plugin
Maven plugin that automatically generates package-info.java files based on a template.
Project URL

Project URL

https://github.com/bohnman/package-info-maven-plugin
Source Code Management

Source Code Management

https://github.com/bohnman/package-info-maven-plugin/tree/master

Download package-info-maven-plugin

How to add to project

<plugin>
    <groupId>com.github.bohnman</groupId>
    <artifactId>package-info-maven-plugin</artifactId>
    <version>1.1.0</version>
</plugin>

Dependencies

compile (2)

Group / Artifact Type Version
org.apache.maven : maven-plugin-api jar 3.3.9
org.sonatype.plexus : plexus-build-api jar 0.0.7

provided (1)

Group / Artifact Type Version
org.apache.maven.plugin-tools : maven-plugin-annotations jar 3.4

Project Modules

There are no modules declared in this project.

Package Info Maven Plugin

Contents

What is it?

The Package Info Maven Plugin generates package-info.java files based on a template file.

The main motivation for this plugin was to apply JSR305 Nullability annotations project wide in order to make libraries more Kotlin-friendly

Requirements

  • Java 8+

Installation

<build>
    <plugins>
        <plugin>
            <groupId>com.github.bohnman</groupId>
            <artifactId>package-info-maven-plugin</artifactId>
            <version>1.1.0</version>
           <configuration>
               <packages>
                   <package>
                       <pattern>**</pattern>
                       <template>${project.basedir}/src/main/java/your/base/package/package-info.java</template>
                   </package>
               </packages>
           </configuration>
           <executions>
               <execution>
                   <goals>
                       <goal>generate</goal>
                   </goals>
               </execution>
           </executions>
        </plugin>
    </plugins>
</build>

General Usage

mvn package-info:generate
mvn package-info:help

Also execute at the generate-sources phase by default if you have an execution configured.

Sample Project Layout

Let's use the following project layout

src/main/java/com/github/bohnman/example
|-- package1
|   |-- Foo.java
|-- package2
|   |-- package-info.java
|   |-- Bar.java
|-- package3
|   |-- package-info.java
|   |-- Baz.java 
|-- package4
|-- package5
    |-- Bear.java
|-- template1
    |-- package-info.java
|-- template2
    |-- package-info.java       

Contents of template1/package-info.java:

// Template 1
package com.github.bohnman.example.template1;

Contents of template2/package-info.java:

// Template 2
package com.github.bohnman.example.template2;

Contents of package2/package-info.java:

// Package2
package com.github.bohnman.example.package2;

Contents of package3/package-info.java:

// Package3 Before
// <replace>
package com.github.bohnman.example.package2;
// </replace>
// Package3 After

Basic Configuration

Based on project layout, let's look at a basic configuration for the plugin

<configuration>
  <packages>
    <package>
        <pattern>**</pattern>
        <template>${project.basedir}/src/main/java/com/github/bohnman/example/template1/package-info.java</template>        
    </package>
  </packages>
</configuration>

Now let's run the following on the command line:

mvn package-info:generate

Once this is run, you'll make several observations:

  1. Two new files are created:

    - generated/src/main/java/com/github/bohnman/example/package1/package-info.java
    - generated/src/main/java/com/github/bohnman/example/package5/package-info.java
    

    Contents of package1/package-info.java:

    // Template 1
    package com.github.bohnman.example.package1;

    Contents of package5/package-info.java:

    // Template 1
    package com.github.bohnman.example.package5;

    Both files we're generated with the correct template and the correct package path

  2. There was no generated package-info file for package2. This is because a package-info.java file already exists in the main source.

  3. There was no generate package-info file generated for package4. This is because there are no java files in that directory.

  4. The contents of src/main/java/com/github/bohnman/example/package3/package-info.java were changed to:

    // Package3 Before
    // <replace>
    // Template 1
    package com.github.bohnman.example.package3;
    // </replace>
    // Package3 After

    This is because the package-info.java contains special markers, which tells the plugin to replace the source file inline.

Multiple Templates

Let's use the following configuration

<configuration>
  <packages>
    <package>
      <pattern>**.package5</pattern>
      <template>${project.basedir}/src/main/java/com/github/bohnman/example/template2/package-info.java</template>        
    </package>
    <package>
        <pattern>**</pattern>
        <template>${project.basedir}/src/main/java/com/github/bohnman/example/template1/package-info.java</template>        
    </package>
  </packages>
</configuration>

Now, the generated package-info.java for package5 will use template2 instead of template1.

A couple of things to note:

  • patterns are matching against the package name (i.e. foo.bar) and NOT the directory path.
  • patterns use Ant-style matching syntax (although regexes can be used). See here for more information
  • The first package declaration to match the package wins, so you'll want to put your more specific patterns first.

Kotlin Example

As stated previously, the main motivation for this project was to provide the ability to help with nullability JSR 305 annotations for library creators to provide better Java interoperability with Kotlin.

A current limitation with this support is that nullability cannot be defined at a project level. Instead, the least granular choice is at a package level. While this provides some help (as opposed to putting annotations on every class, method, etc.), it is still cumbersome for libraries with several packages. This plugin alleviates this burden, by automatically generating package-info.java files with any annotations that are desired.

Here is an example of how we would provide nullability annotations for our whole project.

First, we declare JSR305 as a dependency:

<dependency>
    <groupId>com.google.code.findbugs</groupId>
    <artifactId>jsr305</artifactId>
    <version>3.0.2</version>
</dependency>

Next, we create a NullableApi annotation that uses our JSR305 annotations;

package com.github.bohnman.example;

import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
import javax.annotation.meta.When;
import java.lang.annotation.ElementType;

@Nonnull(when = When.MAYBE)
@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})
public @interface NullableApi {
}

Then, we create a template package-info.java

@NullableApi
package com.github.bohnman.example;

Then, we configure the package import plugin to use the template package

<configuration>
  <packages>
    <package>
        <pattern>**</pattern>
        <template>${project.basedir}/src/main/java/com/github/bohnman/example/package-info.java</template>        
    </package>
  </packages>
</configuration>

Then, we create an example class

package com.github.bohnman.example;

import javax.annotation.Nonnull;

public class Util {
   
   public static Date getDate1() {
       return new Date();
   }
   
   @Nonnull
   public static Date getDate2() {
       return new Date();
   }
}

Finally, we package our library up and use it in our kotlin project

Note: you need to provide the kotlin compiler with the argument: -Xjsr305=strict

package com.github.bohnman.kotlin

import com.github.bohnman.example.Util

fun main(args: Array<String>) {
   println(Util.getDate1().getTime()) // error, because of NullableApi annotation, the type returned is actually Date?
   println(Util.getDate1()?.getTime()) // works because we use the safe navigation operator 
   println(Util.getDate2().getTime()) // works because we annotated the getDate2 method with @Nonnull
}

For more information Kotlin and Java and null safety, see here.

Configuration Options

Name Description
generate Perform generation of new package-info.java files? (Default: true)
inline Perform inline replacement of existing package-info.java (Default: true)
inlineReplacePattern Pattern to use for inline replacement (Default: (\s*//\s*<replace>\s*)(.*?)(\s*//\s*</replace>\s*))
outputDirectory Directory to place generated package-info.java files (Default: ${project.basedir}/generated/src/main/java)
packages Package pattern and template configuration (Required)
sourceDirectory Main source directory (Default: ${project.basedir}/src/main/java)

Individual Package Configuration

Name Description
caseSensitive Indicates whether the pattern matching is case sensitive (Default: true)
pattern Pattern to match the current package name against (Required)
template Path to template file (Required)

Versions

Version
1.1.0
1.0.1