Overview
PME offers the ability to run arbitrary groovy scripts on the sources prior to running the build. This allows PME to be extensible by the user and to process other files not just Maven POMs.
Configuration
If the property -DgroovyScripts=<value>,....
is set, PME will load the remote Groovy script file.
The argument should a comma separated list of either:
- group:artifact:version (with optional type and classifiers).
- A http:// or https:// URL.
- A file:// URL
If using a GAVTC, the remote groovy file can be deployed by e.g.
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.5</version>
<inherited>false</inherited>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>Sample.groovy</file>
<type>groovy</type>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
The deployed file can then be used with e.g.
mvn -DgroovyScripts=org.commonjava.maven.ext:depMgmt1:groovy:1.0 clean install
Groovy Scripts
Each groovy script will be run on the execution root (i.e. where Maven is invoked).
NOTE : Prior to PME 3.5 groovy manipulator precedence was controlled via a flag ; by setting groovyManipulatorPrecedence to FIRST instead of the default LAST value. Further, annotation was different; the scripts used the BaseScript annotation e.g.
@BaseScript org.commonjava.maven.ext.core.groovy.BaseScript pme |
NOTE : As of PME 3.0 the API has changed: org.commonjava.maven.ext.core.groovy.BaseScript instead of org.commonjava.maven.ext.manip.groovy.BaseScript and org.commonjava.maven.ext.common.model.Project instead of org.commonjava.maven.ext.manip.model.Project |
Each script must use the following annotations:
NOTE : For PME versions from 3.8.1, the PMEInvocationPoint annotation has been deprecated and developers should use InvocationPoint in preference. From version 4.3, it is not supported.
|
import org.commonjava.maven.ext.core.groovy.BaseScript
import org.commonjava.maven.ext.core.groovy.InvocationStage
import org.commonjava.maven.ext.core.groovy.PMEBaseScript
import org.commonjava.maven.ext.core.groovy.InvocationPoint
...
@InvocationPoint(invocationPoint = InvocationStage.FIRST)
@PMEBaseScript BaseScript pme
The API can then be invoked by e.g.
pme.getBaseDir()
NOTE : Be careful not to use pme.getProperties()
or pme.getProject().getProperties()
as that actually calls the Groovy language API
Invocation stages
In the example script, we saw the use of the @InvocationPoint
annotation which controls when the script is run. It
takes a single argument, invocationPoint
, with the type InvocationStage
. The possible values for InvocationStage
are PREPARSE
, FIRST
, LAST
, and ALL
. These values are relative to when the manipulations to the POM files are
made. The table below provides a description of the invocation stages available for running a script.
Stage | Description | Since |
---|---|---|
PREPARSE |
Runs the script prior to parsing any POM files and before any manipulations have been performed, thus allowing the modification of POM files on disk before they are read into memory. | 4.6 |
FIRST |
Runs the script first after all POM files have been read into memory, but before any manipulations have been performed. | 3.5 |
LAST |
Runs the script last after all modifications to the in-memory POM files have been performed. | 3.5 |
BOTH |
Runs the script during stages FIRST and LAST . Note that as of version 4.6, BOTH has been replaced by ALL . |
[3.5, 4.5] |
ALL |
Runs the script during all possible stages: PREPARSE , FIRST , and LAST . The getInvocationStage() API can be used to determine in which stage the script is currently running. |
4.6 |
NOTE : It is safe to modify POM files on disk during the PREPARSE stage. However, if you modify
a POM file on disk during any other stage, the modifications will be overwritten when the in-memory POM file is written
back out to disk. To alter a POM file in memory, call Project.getModel() to retrieve the
org.apache.maven.model.Model instance and modify that instead, .e.g.,
pme.getProject().getModel().setVersion( "1.0.0" ) .
|
API
The following API is available:
Method | Description | Since | Valid in Stage |
---|---|---|---|
Translate the versions. Removed in 4.4; use lookupProjectVersions or lookupVersions instead. |
[1.7, 4.3] | FIRST , LAST |
|
File getBaseDir() | Get the working directory (the execution root). | 1.10 | FIRST , LAST |
ProjectVersionRef getGAV() | Get the GAV of the current project. | 1.10 | FIRST , LAST |
Project getProject() | Return the current Project. | 1.10 | FIRST , LAST |
List<Project> getProjects() | Returns the entire collection of Projects. | 1.10 | FIRST , LAST |
Properties getUserProperties() | Get the user properties. | 2.0 | ALL |
ModelIO getModelIO() | Return a ModelIO instance for artifact resolving. | 3.2 | ALL |
MavenSessionHandler getSession() | Return the current session handler. | 3.2 | ALL |
InvocationStage getInvocationStage() | Return the current stage of the groovy manipulation. | 3.5 | ALL |
void inlineProperty(Project, String) | Allows the specified property to be inlined in any dependencies/dependencyManagement. This is useful to split up properties that cover multiple separate projects. Note that as of version 3.8, the String argument changed from group:artifact to propertyKey . |
3.5 | FIRST , LAST |
void inlineProperty(Project, ProjectRef) | Allows the specified group:artifact property to be inlined in any dependencies/dependencyManagement. This is useful to split up properties that cover multiple separate projects. Supports * as a wildcard for artifactId. |
3.8 | FIRST , LAST |
void overrideProjectVersion(ProjectVersionRef) | The specified GAV will be queried from DA for its current suffix and that suffix be used in versionSuffix instead of any versionIncrementalSuffix. | 3.8 | FIRST , LAST |
void reinitialiseSessionStates() | This will re-initialise any State linked to this session. This is useful if the groovy scripts have altered the user properties. | 3.8 | ALL |
Logger getLogger() | This will return the current SLF4J Logger instance. | 3.8.2 | ALL |
FileIO getFileIO() | This will return a FileIO instance for remote File resolving. | 4.0 | ALL |
PomIO getPomIO() | This will return a PomIO instance for parsing POM models. | 4.0 | ALL |
Translator getRESTAPI() throws ManipulationException | Gets a configured VersionTranslator to make REST calls to DA. | 4.0 | ALL |
Map<ProjectVersionRef, String> lookupProjectVersions(List<Project>) throws RestException | Lookup versions (e.g. for a project) ignoring DA suffix priority schemes returning the latest version. | 4.4 | FIRST , LAST |
Map<ProjectVersionRef, String> lookupVersions(List<Project>) throws RestException | Lookup versions respecting DA suffix priority schemes which will return the best matched version. | 4.4 | FIRST , LAST |
Utility Functions
Currently, two main utility functions are provided:
inlineProperty
This function is typically used to deal with the problem where an upstream may use a single property for several distinct SCM Builds. This can lead to issues, so by ‘inlining’ the property it can avoid them.
NOTE : For PME versions after 3.8, the inlineProperty function that takes a ProjectRef will allow a wildcard (* ) for artifactId.
|
overrideProjectVersion
Occasionally upstream projects have circular dependencies in their build. This can typically happen if the first build creates a BOM that is used by multiple subsequent builds. If rebuilds (primarily with a suffix) occur then the versions can get out of sync and build errors arise. This function effectively ‘locks’ the subsequent builds to the suffix value of the first.
The above diagram shows two different SCM builds. The first has a sub-module that is a BOM that references the following SCM builds. This BOM is imported into the parent’s dependencyManagement
. The second SCM build inherits this parent. If the first has been rebuilt twice (hence the rebuild-2
suffix) then the BOM will refer to the incorrect version of SCM Build 2 (1.0.rebuild-2
instead of 1.0.rebuild-1
). By using overrideProjectVersion
it is possible to force SCM Build 2 to use suffix rebuild-2
thereby making it have the correct version.
Example
A typical groovy script that alters a JSON file on disk might be:
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import groovy.util.logging.Slf4j
import org.commonjava.maven.ext.core.groovy.BaseScript
import org.commonjava.maven.ext.core.groovy.InvocationStage
import org.commonjava.maven.ext.core.groovy.PMEBaseScript
import org.commonjava.maven.ext.core.groovy.InvocationPoint
@InvocationPoint(invocationPoint = InvocationStage.FIRST)
@PMEBaseScript BaseScript pme
@Slf4j
public class Processor
{
File basedir
private void processJson(Map n) {
...
}
def execute() {
log.info("Running ShrinkwrapProcessor...")
def shrinkwrap = new File(basedir.toString() + File.separator + "shrink.json")
log.info("shrinkwrap json is " + shrinkwrap)
if (shrinkwrap.exists()) {
log.info ("Found file {}", shrinkwrap)
LinkedHashMap json = new JsonSlurper().parseText(shrinkwrap.text)
processJson(json)
shrinkwrap.write(JsonOutput.prettyPrint(JsonOutput.toJson(json)))
}
}
}
def Processor sp = new Processor(basedir:pme.getBaseDir()))
sp.execute()
Developing Groovy Scripts
To make it easier to develop scripts for both PME (this project) and GME an example project has been set up. The manipulator-groovy-examples provides a framework to develop and test such scripts.