Skip to content

Implementing Custom Processing Steps

Markus Hoffrogge edited this page Sep 5, 2025 · 8 revisions

The implementation of custom processing steps can be useful if you require a certain functionality which serves a special task that must be performed often in your projects. You can add the jar with your steps to the plugin dependencies and include it into your custom workflow.

This possibility is no special feature of the unleash-maven-plugin but is part of its underlying base library Maven CDI Plugin Utils which enables CDI-based dependency injection as well as the workflow-based processing model for Maven plugins. There are also Wiki pages that explain Processing Steps and the Execution Context in detail. The following example implements a simple processing step that injects some basic data and processes some data retrieved from the execution context.

The Processing Step Implementation

package com.itemis.maven.plugins.unleash.steps.actions;

import jakarta.inject.Inject;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;

import com.itemis.maven.plugins.cdi.CDIMojoProcessingStep;
import com.itemis.maven.plugins.cdi.ExecutionContext;
import com.itemis.maven.plugins.cdi.annotations.ProcessingStep;
import com.itemis.maven.plugins.cdi.annotations.RollbackOnError;
import com.itemis.maven.plugins.cdi.logging.Logger;

@ProcessingStep(id = "sample", description = "A sample step that only prints out some data.")
public class SampleStep implements CDIMojoProcessingStep {
  @Inject
  private MavenProject project;
  @Inject
  private Logger log;

  private int state;

  @Override
  public void execute(ExecutionContext context) throws MojoExecutionException, MojoFailureException {
    this.log.info("This is the current project: " + this.project.getGroupId() + ':' + this.project.getArtifactId() + ':'
        + this.project.getVersion());

    if (context.hasUnmappedData()) {
      this.log.info("This step has unmapped execution data!");
      for (String date : context.getUnmappedData()) {
        this.log.debug("\t" + date);
      }
    }

    if (context.hasMappedData()) {
      this.log.info("This step has mapped execution data!");
      for (String key : context.getMappedDataKeys()) {
        this.log.debug("\t" + key + '=' + context.getMappedDate(key));
      }
    }

    // put something in the state for later rollback
    this.state = 42;
  }

  @RollbackOnError
  public void rollback(ExecutionContext context, Throwable t) {
    this.log.info("Rolling back sample step for project: " + this.project.getGroupId() + ':'
        + this.project.getArtifactId() + ':' + this.project.getVersion());

    this.log.info("Internal state is: " + this.state);

    if (context.hasUnmappedRollbackData()) {
      this.log.info("This step has unmapped rollback data!");
      for (String date : context.getUnmappedRollbackData()) {
        this.log.debug("\t" + date);
      }
    }

    if (context.hasMappedRollbackData()) {
      this.log.info("This step has mapped rollback data!");
      for (String key : context.getMappedRollbackDataKeys()) {
        this.log.debug("\t" + key + '=' + context.getMappedRollbackDate(key));
      }
    }
  }
}

Although this implementation should be pretty self-explanatory here are some basic notes about it:

  • The step must implement the interface CDIMojoProcessingStep and must carry the annotation ProcessingStep providing at least the id of the step.
  • You can use CDI injection to inject data into your step that is provided by the plugin but be careful since this this can restrict the portability of your step massively as different plugins and even different goals produce different data for injection. In order to stay generic you should only inject data produced by the CDI base library and use dynamic data provided by the execution context.
  • The step may keep state collected during the regular processing phase. This state can later be used if a rollback is necessary.
  • The execution of the step prints out all dynamic data provided by the execution context, mapped and unmapped data. For more information please refer to the Execution Context Wiki page of the base plugin.
  • The step also provides a rollback method which is triggered automatically if en exception is caught and the step has already been processed. There it prints all data specific to the rollback logic. Rollback methods can be customized to be triggered only under certain conditions. More about rollback is available here.

Inclusion Into The Standard Workflow

After implementing the step you will have to build a JAR and set it as a dependency to the list of plugin dependencies of the unleash-maven-plugin:

<project>
  ...
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>io.github.mavenplugins</groupId>
          <artifactId>unleash-maven-plugin</artifactId>
          <version>3.3.2</version>
          <dependencies>
            <dependency>
              <groupId>org.example</groupId>
              <artifactId>your-processing-hooks</artifactId>
              <version>1.0.0</version>
            </dependency>
          </dependencies>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

After that you can check if your step implementation is correct by listing all available processing steps:

mvn unleash:perform -DprintSteps

This prints a table with all available steps like this one:

┌────────────────────┬──────────────────────────────────────────────────┬──────────┐
│         ID         │                   DESCRIPTION                    │ REQUIRES │
│                    │                                                  │  ONLINE  │
╞════════════════════╪══════════════════════════════════════════════════╪══════════╡
│ ...                │ ...                                              │  ...     │
├────────────────────┼──────────────────────────────────────────────────┼──────────┤
│ sample             │ A sample step that only prints out some data.    │  true    │
├────────────────────┼──────────────────────────────────────────────────┼──────────┤
│ ...                │ ...                                              │  ...     │
└────────────────────┴──────────────────────────────────────────────────┴──────────┘

If your step is listed there you can integrate into your custom workflow. Your custom workflow should only modify the default workflow which can be retrieved by calling:

mvn unleash:perform -DprintWorkflow

Integrating the step into the workflow could look like this:

storeScmRevision
sample
checkProjectVersions
checkParentVersions
checkDependencies
checkPlugins
checkPluginDependencies
prepareVersions
checkAether
setReleaseVersions
addSpyPlugin
buildReleaseArtifacts
removeSpyPlugin
checkForScmChanges
tagScm
detectReleaseArtifacts
setDevVersion
installArtifacts
deployArtifacts

Using The Custom Processing Step

In order to use the our custom workflow which integrates our processing step implementation we will have to call the plugin goal and pass-in the workflow which overrides the default one:

mvn unleash:perform -Dworkflow=<path_to_workflow_file>

This will process the workflow with the sample step at second position. The output should be something like this:

...
[INFO] 08:46:17,146  This is the current project: org.example:test-project:0.0.1
...

If we now want to provide some additional data for the step we can do this using system properties:

mvn unleash:perform -Dworkflow=<path_to_workflow_file> -Dsample="unmapped-date 1,unmapped-date 2,key1=>mapped-date1,key2=>mapped-date2" -Dsample-rollback="rollback-data,rb-key1=>rollback-map-data1" -X

This will result in an output like this one:

...
[INFO] 08:46:17,146  This is the current project: org.example:test-project:0.0.1
[INFO] 08:46:17,148  This step has unmapped execution data!
[DEBUG] 08:46:17,149    unmapped-date 1
[DEBUG] 08:46:17,150    unmapped-date 2
[INFO] 08:46:17,152  This step has mapped execution data!
[DEBUG] 08:46:17,153    key1=mapped-date1
[DEBUG] 08:46:17,154    key2=mapped-date2
...
[INFO] 08:46:43,309  Rolling back sample step for project: org.example:test-project:0.0.1
[INFO] 08:46:43,310  Internal state is: 42
[INFO] 08:46:43,312  This step has unmapped rollback data!
[DEBUG] 08:46:43,313    rollback-data
[INFO] 08:46:43,315  This step has mapped rollback data!
[DEBUG] 08:46:43,316    rb-key1=rollback-map-data1
...

Clone this wiki locally