Plug-in tasks

Plug-in steps can interact with the plug-in tasks that are used in deployment plans.

Plug-in steps can respond to notification-type events in external tools and provide data from those tools to IBM® UrbanCode™ Release plug-in tasks. For example, you might create plug-ins that integrate with ticketing systems and email servers, and then have the external tools provide data to plug-in tasks in deployment plans. To enable a plug-in to work with plug-in tasks, you modify the steps in the plugin.xml file. To work with plug-in tasks, a plug-in step extends the PLUGIN_TASK class.

The plug-in step is responsible to set the task status after execution. The step can wait for the process execution to complete or provide a call-back URL in order for the external tool to update the task status later. The plug-in step can add information about the task execution to the task's Comments tab.

Example of a plug-in that can be used with plug-in tasks

The following example of a plugin.xml file illustrates how to create plug-in steps that can be used by plug-in tasks. The step extends PLUGIN_TASK and defines several properties. Users can set the property values in the plug-in task. The step sets the task's status in the post-processing element.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<plugin xmlns="" xmlns:server="" xmlns:xsi="">
    <identifier id="com.urbancode.plugin.demo" name="Demo Plugin" version="1"/>

      Demo Plugin
  <!-- extends="PLUGIN_TASK" will register that step as a task when creating an integration provider with that plugin-->
  <step-type name="ExecuteTask" displayed="true" extends="PLUGIN_TASK">
    <description>Execute Task</description>
      <property name="property1" required="true">
        <property-ui type="textBox" description="Property 1" label="Plugin Property 1" />
      <property name="property2" required="true">
        <property-ui type="textBox" description="Property 2" label="Plugin Property 2" />
      <property name="property3" required="true">
        <property-ui type="textAreaBox" description="Property 3" label="Plugin Property 3"/>
        if (properties.get("exitCode") != 0) {
            properties.put(new java.lang.String("Status"), new java.lang.String("Failure"));
        else {
            properties.put("Status", "Success");

    <command program="${GROOVY_HOME}/bin/groovy">
      <arg value="-cp"/>
      <!-- ucr-plugin-util.jar is the plugin client for consuming UCR rest api-->
      <arg path="classes:lib/commons-codec.jar:lib/CommonsUtil.jar:lib/ucr-plugin-util.jar:lib/commons-lang3.jar:lib/commons-lang.jar:lib/jettison-1.1.jar"/>
      <arg file="ExecuteTask.groovy"/>
      <arg file="${PLUGIN_INPUT_PROPS}"/>
      <arg file="${PLUGIN_OUTPUT_PROPS}"/>

Groovy file example

The plug-in framework passes the entire context as a JsonObject, extraProperties, that can be parsed into an object. After the plug-in step parses the JsonObject, it has access to every element of the plug-in task, such as callBackUrl, taskUrl, and UserId.

The following code example, ExecuteTask.groovy, illustrates how to parse the extraProperties object, and add comments to a plug-in task.

#!/usr/bin/env groovy
 * Licensed Materials - Property of IBM Corp.
 * IBM UrbanCode Release
 * (c) Copyright IBM Corporation 2014. All Rights Reserved.
 * U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
 * GSA ADP Schedule Contract with IBM Corp.

import static;
import static;
import static;
import static;
import com.urbancode.air.*
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;


final def workDir = new File('.').canonicalFile

def apTool = new AirPluginTool(this.args[0], this.args[1]);
def props = apTool.getStepProperties();

//We launch the integration here
def executor = new TaskExecutor (props)
//We authenticate first
//We then execute the task ()

public class TaskExecutor {
    //Used to parse JsonObjects
    def slurper;
    def integrationProviderId

    //Used for authentication
    def releaseToken;
    def serverUrl;
    //JsonObject that will contain context properties
    def extraProperties;
    //Properties defined in the UI for the integration provider
    def uiProperty1 = "";
    def uiProperty2 = "";
    def uiProperty3 = "";

    //The task object
    def task;

    //Main constructor
    TaskExecutor (props) {
        this.integrationProviderId = props['releaseIntegrationProvider'];
        this.releaseToken = props['releaseToken'];
        this.serverUrl = props['releaseServerUrl'];
        //Context properties
        this.extraProperties = props['extraProperties'];
        //UI Integration properties
        this.uiProperty1 = props['property1'];
        this.uiProperty2 = props['property2'];
        this.uiProperty3 = props['property3'];
        this.extraProperties = props['extraProperties'];
        //We initialize the Json parser
        this.slurper = new groovy.json.JsonSlurper()
    //Authentication with Release
    def releaseAuthentication () {
        Clients.loginWithToken(serverUrl, releaseToken);
    def run () {
         //Lets display the UI input properties
         addLogSection ("Global Properties set on the integration provider");
         log ("Property 1: "+this.uiProperty1);
         log ("Property 2: "+this.uiProperty2);
         log ("Property 3: "+this.uiProperty3);
         //We load the context properties
         //If ran from the integration provider page and not a scheduled deployment the extraProperties object my be null
         if (extraProperties) {
            log ("Task ran from a scheduled deployment");
            def contextProperties = slurper.parseText(extraProperties)
            //The context properties will provide the task object as well as the plugin  properties set on it in the plan
            addLogSection ("Task information");
            log ("Call Back URL: "+contextProperties.callBackUrl);
            //Direct link in UCR for that task
            log ("Direct link to that task: "+contextProperties.taskUrl);
            //User who executed the task
            log ("User Id (Admin for automated tasks): "+contextProperties.userId);
            //Task properties
            if (contextProperties.task != null) {
                //We need the ucr task id so we can handle that UCR object later
                def urcTaskId =
                task = new TaskExecution()
                log ("Task Id: "+urcTaskId);
                log ("Task Name: ";
                log ("Task Description: "+contextProperties.task.description);
                log ("Scheduled Deployment Id: "+contextProperties.task.scheduledDeploymentId);
                log ("Segment Id: ";
                log ("Segment Name: ";
                log ("Only Changed Versions: "+contextProperties.task.onlyChangedVersions);
                addLogSection ("Application Info");
                if (contextProperties.task.application != null) {
                    log ("Application UCR Id: ";
                    log ("Application External Id: "+contextProperties.task.application.externalId);
                    log ("Application Name: ";
                    addLogSection ("Version Info");
                    if (contextProperties.task.version != null) {
                        log ("Version UCR Id: ";
                        log ("Version External Id: "+contextProperties.task.version.externalId);
                        log ("Version Name: ";
                        //Version Statuses
                        if (contextProperties.task.version.versionStatuses != null) {
                            contextProperties.task.version.versionStatuses.each {
                                versionStatus -> log ("Status: "" added by "+versionStatus.createdByUser.actualName+" on "+new Date(versionStatus.dateCreated));
                addLogSection ("Target Info");
                if (contextProperties.task.targets != null) {
                    contextProperties.task.targets.each {
                       target ->  log ("Target Name: ";
                       log ("Target UCR Id: ";
                       log ("Target External Id: "+target.externalId);
                addLogSection ("Approval information");
                //To get more about the deployment we need to load the object
                def sdClient = new ScheduledDeployment();
                def sd =;
                def approvalSet = sd.approval;
                approvalSet.tasks.each {
                     approvalItem ->  log ("Approval: "" by "" User: "+ approvalItem.userName);
                     log ("Approval ID: ";
            addLogSection ("Task plugin properties");
            if (contextProperties.task.pluginProperties != null) {
               log ("Plugin properties input from the task UI "+contextProperties.task.pluginProperties);
            //Comments can be added to the task
            new Comment().userId(contextProperties.userId).task(task).comment("Here is a comment posted during the execution of the task").post();

            //Once done the status of the tasks should be updated
            //task.complete() or
        else {
            log ("Task ran from the integration provider page");
    def log (logText) {
        println (logText)
    def addLogSection (sectionTitle) {
        println ("")
        println ("------------------------------------------------------------------")
        println ("<b><u>"+sectionTitle+"</u></b>")
        println ("")