Authoring Extensions
Extensions are a collection of ExtensionPoint
classes. Each extension point takes care of a particular feature.
Follow these steps to create your own extensions.
1. Build Tool
Pick your Java build tool of choice (Maven, Gradle, JBang, etc). Declare the following dependency for compilation only, for example:
<dependency>
<groupId>org.jreleaser</groupId>
<artifactId>jreleaser-model-api</artifactId>
<version>1.21.0-SNAPSHOT</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
dependencies {
compileOnly 'org.jreleaser:jreleaser-model-api:1.21.0-SNAPSHOT'
}
//DEPS org.jreleaser:jreleaser-model-api:1.21.0-SNAPSHOT
2. Extension Class
Create a class that implements the org.jreleaser.extensions.api.Extension
interface. This class must be
registered as a service provider. Feel free to use Jipsy,
AutoService, or any other annotation processor that can
keep the service file up to date. Alternatively you must register the extension class in a resource file such as
META-INF/services/org.jreleaser.extensions.api.Extension
.
JReleaser does not yet support the Java Modules system. You can not use a full module descriptor with service providers. |
The org.jreleaser.extensions.api.Extension
is defined as follows:
package org.jreleaser.extensions.api;
import java.util.Set;
/**
* Provides a collection of {@code ExtensionPoints}.
* <p>
* Every extension must define a unique name.
* <p>
* Extensions are loaded using the standard ServiceProvider mechanism,
* (see <a href="https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html">
* https://docs.oracle.com/javase/tutorial/sound/SPI-intro.html</a>
* for more details).
*
* @author Andres Almiray
* @since 1.3.0
*/
public interface Extension {
/**
* The given name of the extension.
*
* @return a non {@code null} String.
*/
String getName();
/**
* A collection of {@code ExtensionPoint} instances.
* <p>
*
* @return a non {@code null} collection.
*/
Set<ExtensionPoint> provides();
}
Your custom extension class is responsible for providing extensions points, such as:
package com.acme;
import org.jreleaser.extensions.api.Extension;
import org.jreleaser.extensions.api.ExtensionPoint;
import org.kordamp.jipsy.annotations.ServiceProviderFor;
import java.util.Collections;
import java.util.Set;
@ServiceProviderFor(Extension.class)
public final class MyExtension implements Extension {
@Override
public String getName() {
// provide a name, used for matching the name in the configuration DSL
return "my-extension";
}
@Override
public Set<ExtensionPoint> provides() {
return Collections.singleton(new MyExtensionPoint());
}
}
3. Extension Points
An extension point class is an implementation of a given org.jreleaser.extensions.api.ExtensionPoint
interface.
Review the list of available extension points to find what behavior you may
extend.
The org.jreleaser.extensions.api.ExtensionPoint
is defined as follows:
package org.jreleaser.extensions.api;
import org.jreleaser.model.api.JReleaserContext;
import java.util.Map;
/**
* Defines an extension point for a given feature.
*
* @author Andres Almiray
* @since 1.3.0
*/
public interface ExtensionPoint {
/**
* Initializes the extension point with values defined in the configuration DSL.
*
* @param context the current execution context.
* @param properties a {@code Map} of key/value pairs.
*/
default void init(JReleaserContext context, Map<String, Object> properties) {
// noop
}
}
The init()
method should be used to configure the extension point given key/value pairs defined in the
extensions block found in the configuration DSL. Specific extension points
may offer additional methods that require implementing. Consult their respective documentation to find out more.
4. Packaging
Extensions are packaged as JARs. Use the standard packaging mechanism from your build tool of choice. You have the following options depending on the extensions requirements:
-
No additional dependencies. Package as a single JAR.
-
Additional dependencies. Package as an uberjar. Use the Maven shade plugin or the Gradle shadow plugin.
-
Additional dependencies. Package as a single JAR.
Make sure that no JReleaser dependencies are found as part of the set of JARs or transitive closure. These JARs will be provided by JReleaser when loading the extension.
5. Publishing
You have the choice to publish the extension to a Maven compatible repository (such as Maven Central) or your local Artifact Repository Manager. Consumers of your extension may configure GAV coordinates to let JReleaser download the extension JARs plus any additional transitive dependencies it may require, or they can manually download the JARs and place them inside the default location or a custom location of their own. Review the extensions DSL for more information on these options.
6. JBang Scripts
As an alternative to traditional JAR packaging, you can create extensions using JBang scripts. This approach allows you to write extensions as single Java files that are automatically compiled and packaged by JBang when JReleaser loads them.
Creating a JBang Extension
Create a Java file with the following structure:
//DEPS org.jreleaser:jreleaser-model-api:1.21.0-SNAPSHOT
//DEPS org.kordamp.jipsy:jipsy-annotations:1.2.0
//DEPS org.kordamp.jipsy:jipsy-processor:1.2.0
package com.acme;
import org.jreleaser.extensions.api.Extension;
import org.jreleaser.extensions.api.ExtensionPoint;
import org.kordamp.jipsy.annotations.ServiceProviderFor;
import java.util.Collections;
import java.util.Set;
@ServiceProviderFor(Extension.class)
public final class MyJReleaserExtension implements Extension {
@Override
public String getName() {
return "my-jbang-extension";
}
@Override
public Set<ExtensionPoint> provides() {
return Collections.singleton(new MyExtensionPoint());
}
}
The script above demonstrates the basic requirements for a JReleaser extension:
-
Use
//DEPS
directives to declare dependencies -
Include the
jreleaser-model-api
dependency with the appropriate version -
Implement the
Extension
interface as shown above -
Register the extension using
@ServiceProviderFor(Extension.class)
-
Finally, you must provide an implementation of an ExtensionPoint.
Using JBang Extensions
Consumers can reference your JBang script in their configuration using the jbang
property:
extensions:
my-jbang-extension:
jbang: src/jreleaser-ext/MyJReleaserExtension.java
The script can be referenced as a local file path, remote URL, or JBang catalog reference. JBang will automatically handle compilation and dependency resolution when JReleaser loads the extension.
Since JBang compiles and packages the script as a JAR file, you can leverage any JBang feature to extend JReleaser.
For example, JBang scripts can span multiple files using //SOURCES */.java
to include all Java files in the current
directory and subdirectories, or explicitly list specific files using //SOURCES file1.java, file2.java
.