Contents


How to extend and customize Rational Team Concert for continuous integration

Create a new build participant for an Agile project

Comments

I began extending IBM® Rational Team Concert™ to improve the continuous delivery process for a client. The client needed a plugin that would recreate a package from a previous build with new environment build parameters in order to create a release for a higher environment. The plugin would replace a manual process that took up to 2 hours and was prone to mistakes.

The load workspace from label plugin I wrote ran as a pre-build participant. Pre-build, build, and post-build participants contain a Java class that is runnable by a Jazz Build Engine as part of a build process. Several build participants make up a build definition. The code for this exists as a plugin in the eclipse folder which is located within the Build Engines installation directory on the build server.

The functionality of the load workspace from label build participant is as follows:

  1. Takes the build definition and build label as user parameters from the Eclipse client UI.
  2. Validates that the inputs belong to a single successful build result
  3. Creates a new workspace from the associated build result's snapshot (referred to as a baseline set in the Rational Team Concert SDK).
  4. Updates the current build's working workspace with the new workspace.
  5. Executes before the jazzscm plugin, so that the jazzscm plugin continues as normal with the exception that it now loads the newly created workspace that contained the same source code as the original build associated with the user provided build definition id and build label.

The source code for the load workspace from label plugin is available on Github.

That solution's build participant code is excessive for this article so I have decided to walk through a simpler scenario and created the my build participant plugin. The functionality of the plugin described in this article is as follows:

  1. Takes user input in the Eclipse client
  2. Logs the user input to the build log
  3. Gets the name of the user who initiated the build and the workspace Universally Unique Identifier (UUID), and tags the build with the username and workspace UUID using Rational Team Concert APIs.

The source code for the my build participant plugin discussed in this article can be downloaded on Github.

Pre-requisites

To extend the Rational Team Concert plugin, knowledge of Eclipse Plugin Development and the Plugin Development Environment (PDE) will be valuable. I describe the full implementation in this article as a step-by-step guide and, for clarity, I have identified where the detail is for creating generic Eclipse plugins as opposed to being specific to Rational Team Concert plugin development.

Setting up the development environment

It is imperative that you download the correct SDK, Plain Java API, Jazz Build Engine, and Eclipse client plugin for your version of Rational Team Concert. For example, if you are using 4.0.0 of Rational Team Concert, then you cannot use the 4.0.6 SDK.

Local installation of Rational Team Concert and the SDK

A development environment to create a plugin is required. The Rational Team Concert 4.x Extensions Workshop on Jazz.net contains a step-by-step installation guide for setting up a development environment in Lab 1 - Setting up the IBM Rational Team Concert SDK. I have not yet tried this with version 5 so please add to the comments if this guide requires any changes. When installing Rational Team Concert, ensure that you select the option to also install the Jazz Build Engine.

The easiest way to install the Rational Team Concert Eclipse Client plugin is to import it as a p2 repository. Navigate to the releases page for your version of Rational Team Concert and click All Downloads. Find then download the RTC-Client-p2Repo-X.X.X.zip file. Install the plugin in Eclipse in Help > Install New Software, and add the p2 repo as an archive.

It will take at least an hour to get Rational Team Concert running locally. I recommend doing this because, as you will come to understand, developing plugins is an iterative process. No developer will thank you for this plugin if you accidentally broke every build in the process of creating it and made a mess of the build definitions.

Jazz Build Engine installation

The Jazz Build Engine initializes the process that runs the build script and collects the output. This runs as a Java process on your build server and waits for build instruction from the client.

Install the Jazz Build Engine locally. It is recommended that you copy this from your target build server so that you are 100% confident that you are working on the same version, however this can also be installed via the Rational Team Concert Setup in the Local Installation of Rational Team Concert and the SDK section.

In Linux, create an encrypted password file by issuing the command shown in Listing 1 and typing the password for the Rational Team Concert user who is the build user, created in the setup instructions previously.

Listing 1. bash command for creating an encrypted password file
 ${JBE_HOME}/jazz/buildsystem/buildengine/eclipse/jbe -createPasswordFile <output_password_file>

To start the Jazz Build Engine as a daemon in Linux, issue use the command in Listing 1. Make sure you replace the appropriate variable values.

Listing 2. bash command for initializing a Jazz Build Engine as a daemon
  REPO="https://localhost:9443/ccm" # URL of your RTC Server  
  JAVA_HOME="${JBE_HOME}/jazz/buildsystem/buildengine/eclipse/jdk/bin/java"  
  ENGINE_ID="test-be" # matches the build engine name in RTC   
  ENGINE_USER="myadmin" # RTC user ID to use for the build engine  
  ENGINE_PWFILE="<output_password_file>" # Generated in Code Snippet 1  
  JBE_LOGFILE="/tmp/jazzbuildengine/jbe_test.log" # Location to write JBE log files   
  JBE_WORKSPACE="/tmp/plugin_engine_workspace" # JBEs Eclipse Workspace  
                               
  daemon "$JBE_HOME/jazz/buildsystem/buildengine/eclipse/jbe -clean -Xmx512m \  
     -data $JBE_WORKSPACE -vm $JAVA_HOME -repository $REPO -userId $ENGINE_USER \  
     -passwordFile $ENGINE_PWFILE -engineId $ENGINE_ID -sleeptime 10 -clean -debug \  
     >$JBE_LOGFILE 2>&1 &"

Your test-be build engine is now ready to start processing build requests.

Implementing the Rational Team Concert extension

In this example I describe how to create:

  • A custom editor in the Rational Team Concert client that accepts and validates user input.
  • A new build definition template including the build participant.
  • A build participant that logs the user input and tags the build with: the user id who triggered it and the workspace UUID.

This Rational Team Concert extension requires 3 separate plugins:

  • An engine plugin that is installed in the Jazz Build Engine's installation plugin directory.
  • A client plugin that is installed as a standalone Eclipse plugin imported as a p2 repository (the same process as the Rational Team Concert Client).
  • A common plugin. The common plugin is included in the p2 Repository and as a separate plugin to be installed in the Jazz Build Engine.

When developing Eclipse plugins, specify a target platform which provides the integrated development environment (IDE) with the context in which the application runs. For example, the plugins and libraries available at runtime. The problem here is that you are developing for two target platforms, the Jazz Build Engine and the Rational Team Concert Eclipse client. These contain different libraries and plugins, which is why you must create both an engine and a client plugin. The common plugin is required to ensure seamless integration between the two plugins.

In the next section I provide the method to create this and the Eclipse workspace setup required to develop it.

Eclipse workspace setup

At this point you are deep in the realms of Eclipse plugin development. For further clarity, refer to Eclipse Plugin tutorials for guides on each of the steps I mention. You can also refer to section 1.2 Setup for Development in the Rational Team Concert 4.x Extensions Workshop at Jazz.net.

Switch to the Plugin Development View in Eclipse. Create two workspaces, one for each target platform, named my_build_participant_client and my_build_participant_engine.

In the client workspace, create two projects, my_build_participant_client and my_build_participant_common. Set the target platform to the unzipped Rational Team Concert SDK you downloaded in the Setting up the development environment section. In the project settings for the client plugin, create a project dependency on the common plugin. You will also need to add the common plugin to the client plugin build manifest.

In the engine workspace, create the engine project named my_build_participant_engine and import the my_build_participant_common plugin. Set the target platform to ${JBE_HOME}/buildsystem/buildengine/eclipse/plugins. Create a project dependency on common and import the plugin as per the client workspace setup.

Logging and debugging

Now that the Jazz Build Engine is installed, there are 2 logs that are most useful to you when debugging a plugin for the Jazz Build Engine.

Jazz Build Engine plugin

The Jazz Build Engine log file, JBE_LOGFILE in Listing 2, is where information output by the Rational Team Concert logger is written. This information is also displayed at build time in the build log.

The -data parameter supplied to the build engine, JBE_WORKSPACE in Listing 2, is very important in debugging the Jazz Build Engine when it is initializing. All Eclipse errors on plugin initialization are identifiable in this log.

Rational Team Concert Eclipse client plugin

Now that you have setup your Eclipse workspace, it is possible to launch and debug the client plugin as an Eclipse application. Right-click on the client project and select Run As > Eclipse Application or Debug As > Eclipse Application.

Tips on debugging and logging can be found in many Eclipse plugin tutorials and on the Eclipse PDE site.

Developing the plugin

Plugin overview

The Rational Team Concert extension is split into the following three plugins.

  • com.ibm.rtcexts.mybuildparticipant.common
  • com.ibm.rtcexts.mybuildparticipant.client
  • com.ibm.rtcexts.mybuildparticipant.engine

There are two additional plugins that are required for releasing the client component as a p2 repository as part of the client plugins build process, described in the Build and distribution section below.

  • com.ibm.rtcexts.mybuildparticipant.feature
  • com.ibm.rtcexts.mybuildparticipant.updatesite

The engine and common plugins exist within the my_build_participant_engine workspace, and the client, common, feature and updatesite plugins exist within the my_build_participant_client workspace.

Like many Eclipse plugins, the engine and client plugins require a plugin.xml, a plugin manifest and an activator class to be initialized. Although the common component is also packaged as an Eclipse plugin, the activator is not required because it is never intended for initialization as a standalone plugin, and is instead only referenced by the engine and client plugins. In the following sections I describe the implementation of each Eclipse plugin in detail.

Common plugin

The common plugin binds the plugin that runs in the Jazz Build Engine and the plugin that runs in the Eclipse client. The common plugin is only used by extension of the engine and client plugins, therefore no activator is required. More information about activators is available below.

The most important part of the common plugin is in the plugin.xml.

The Plugin XML

Listing 3. plugin.xml for the common plugin
<plugin>
    <!--buildConfigurationElement-->
    <extension point="com.ibm.team.build.common.buildConfigurationElements">
        <buildConfigurationElement
            id="com.ibm.rtcexts.mybuildparticipant.buildconfigelement"
            name="My build configuration element"
            description="my new element that is to be used by my build participant"
            buildPhase="PRE_BUILD" >

            <genericProperty
                genericEditAllowed="false"
                name="com.ibm.rtcexts.mybuildparticipant.common.buildConfigProperty1"
                description="A build property."
                required="true" />
            <genericProperty
                genericEditAllowed="false"
                name="com.ibm.rtcexts.mybuildparticipant.common.buildConfigProperty2"
                description="Another build property."
                required="true"
                />
        </buildConfigurationElement>
    </extension>

<!--buildDefinitionTemplate -->
<extension point="com.ibm.team.build.common.buildDefinitionTemplates">
    <buildDefinitionTemplate
        id="com.ibm.rtcexts.mybuildparticipant.common.myTemplate"
        description="My new template containing my new build configuration element."
        name="my new build definition" >

        <buildConfigurationElement id="com.ibm.team.build.properties"/>
        <buildConfigurationElement id="com.ibm.team.build.general"/>
        <buildConfigurationElement
            id="com.ibm.rtcexts.mybuildparticipant.buildconfigelement" />

        <buildConfigurationElement id="com.ibm.team.build.jazzscm"/>
        <buildConfigurationElement id="com.ibm.team.build.cmdline"/>
        <buildConfigurationElement id="com.ibm.team.build.ant"/>
        <buildConfigurationElement id="com.ibm.team.build.email"/>
        </buildDefinitionTemplate>
    </extension>
</plugin>

The first extension point is to extend the buildConfigurationElements by adding a new build configuration element. You can read more about what an extension point is in this article on Eclipse Plugin Architecture. This is the most important part of all the plugin.xml information because it is the object that binds everything together and therefore should exist within a common plugin. The id is how the element is referenced by both the client and engine plugins. The buildPhase scopes the configuration element to a given build phase, which must have the same phase as the AbstractBuildEngineParticipant class described in the engine plugin below. The generic properties are used to bind data to the Eclipse form written in the client plugin. The build engine can then access the buildConfigProperty1 and buildConfigProperty2 values via the com.ibm.rtcexts.mybuildparticipant build configuration element object.

The last extension point for the common plugin is to the Rational Team Concert buildDefinitionTemplate. This extension point describes an additional build definition template. The template dictates the order in which your build participant is executed in the build lifecycle, therefore it is required by the engine plugin. Later you will see that selecting the buildDefinitionTemplate in the Eclipse client when creating a build definition results in the UI tabs for the associated build participants' configuration being made available.

For the load workspace from label build participant that I developed on my previous project, the order in which the new build configuration element runs is important to its functionality. The com.ibm.rtcexts.mybuildparticipant must run before the Jazz SCM plugin so that the source code retrieved by the build can be changed by my plugin. To demonstrate this here, the my build participant exists earlier in the list. Both of these plugins run in the pre-build phase as they are pre-build participants. You'll find more information about this in the Engine plugin section.

Finally in the common plugin, I wrote a simple class for storing constants that is used by both the engine and client plugins.

The Implementation

The common plugin doesn't provide functionality as such, however it is a good place to store common properties across the engine and client plugins. For example, Listing 4 describes properties on the Build Configuration Element.

Listing 4. Configuration Element Class
public abstract interface MyBuildParticipantsConfigurationElement {

    public static final BuildPhase BUILD_PHASE = BuildPhase.PRE_BUILD;
    public static final String NAME = "My new build participant";
    public static final String ELEMENT_ID = "com.ibm.rtcexts.mybuildparticipant." +
        "buildconfigelement";
    public static final String PROPERTY_1 = "com.ibm.rtcexts.mybuildparticipant." +
        "common.buildConfigProperty1";
    public static final String PROPERTY_2 = "com.ibm.rtcexts.mybuildparticipant." +
        "common.buildConfigProperty2";

}

With just the build definition template and build configuration element in place, you already have enough to see the template appear in the Available build templates list when creating a new build definition as shown in Figure 1.

Figure 1. Screenshot showing my new build definition as an Available build template
new build Definition Template available in Eclipse
new build Definition Template available in Eclipse

Client plugin

The client plugin contains the code for the Eclipse forms that capture the properties for my build configuration element created above.

The activator

The activator class is required for eclipse to initialize the client plugin.

Listing 5. Activator class for the client plugin
public class MyBuildParticipantClientPlugin extends AbstractUIPlugin {

    // The plug-in ID
    public static final String PLUGIN_ID = "com.ibm.rtcexts.mybuildparticipant.client";

    // The shared instance
    private static MyBuildParticipantClientPlugin plugin;

    //The constructor
    public MyBuildParticipantClientPlugin() {
    }

    public void start(BundleContext context) throws Exception {
    super.start(context);
    plugin = this;
    }

    public void stop(BundleContext context) throws Exception {
        plugin = null;
        super.stop(context);
    }

    // Returns the shared instance
    public static MyBuildParticipantClientPlugin getDefault() {
        return plugin;
    }

    // Returns an image descriptor for the image file at the given
    public static ImageDescriptor getImageDescriptor(String path) {
        return imageDescriptorFromPlugin(PLUGIN_ID, path);
    }
}

The activator class is similar to one that you will find in an Eclipse Plugin Development hello world example. Refer to the Eclipse Plugin Development documentation and tutorials for more detail on this class.

The manifest

The Bundle-Activator is then specified in the manifest.

Listing 6. Manifest for the client plugin
Manifest-Version : 1.0
Bundle-ManifestVersion : 2
Bundle-Name : com.ibm.rtcexts.mybuildparticipant.client
Bundle-SymbolicName : com.ibm.rtcexts.mybuildparticipant.client;singleton:=true
Bundle-Version : 1.0.1
Bundle-Vendor : IBM
Require-Bundle : org.eclipse.ui,
    org.eclipse.core.runtime,
    com.ibm.team.build.ui,
    org.eclipse.ui.forms,
    com.ibm.team.build.common,
    com.ibm.team.build.client,
    com.ibm.team.repository.client,
    com.ibm.team.repository.common,
    com.ibm.team.jface,
    com.ibm.rtcexts.mybuildparticipant.common
Bundle-RequiredExecutionEnvironment : JavaSE-1.6
Bundle-Activator : com.ibm.rtcexts.mybuildparticipant.client.MyBuildParticipantClientPlugin
Bundle-ActivationPolicy : lazy

The plugin XML

The plugin.xml for client plugin is basic, and only describes the build configuration element editor itself, which is what we would expect from a client.

Listing 7. plugin.xml for the client plugin
<plugin>
    <!--The editor tab in the build configuration-->
    <extension point="com.ibm.team.build.ui.buildConfigurationElementEditors">
        <buildConfigurationElementEditor
            configurationElementId="com.ibm.rtcexts.mybuildparticipant.buildconfigelement"
                name="my new element editor's name"
                class="com.ibm.rtcexts.mybuildparticipant.client.ConfigurationElementEditorFactory"
                />
    </extension>
</plugin>

The extension point is to the buildConfigurationElementEditors. A build configuration element editor, has a configurationElementId that points to the build configuration element from the common plugin we created before. The class points to the fully qualified class name of a class that extends an AbstractConfigurationElementEditor.

Implementation

We make use of Eclipse forms and create a class that extends the AbstractConfigurationElementEditor to create the UI for editing the build configuration element properties. I made use of the BasicConfigurationElementEditor within the com.ibm.team.build.client for this.

Listing 8. Client code for the configuration element editor
public class BuildIdAndLabelPreConfigurationElementEditor extends
        BasicConfigurationElementEditor {

    private static final String ERROR_PROPERTY_REQUIRED = "Property is required";

    private static final String PROPERTY_1_AND_2_CONFIGURATION_SECTION_LABEL = "Build 
        Definition ID and Label";
    private static final String PROPERTY_1_CONFIG_LABEL = "property 1";
    private static final String PROPERTY_2_CONFIG_LABEL = "property 2";

    private static final String PROPERTY_1_AND_2_SECTION_DESCRIPTION = "The section 
        containing my properties 1 and 2";
    private static final String GENERIC_PROPERTY_DESCRIPTION = "A property required for 
        build.";

    protected Text fBuildDefinitionIdText;
    protected Text fBuildLabelText;

    //editor constructor
    public BuildIdAndLabelPreConfigurationElementEditor(String elementId, String title) {
        super(elementId, title);
    }

    @Override
    public void createContent(Composite parent, FormToolkit toolkit) {
        parent.setLayout(new TableWrapLayout());
        Section section = createSection(parent, PROPERTY_1_AND_2_CONFIGURATION_SECTION_LABEL,
            PROPERTY_1_AND_2_SECTION_DESCRIPTION, false);
        Composite composite = (Composite) section.getClient();
        createBuildDefinitionIdWidgets(composite);
        createBuildLabelWidgets(composite);
    }

    private void createBuildDefinitionIdWidgets(Composite parent) {
        fBuildDefinitionIdText = createConfigPropertyTextField(parent,
            MyBuildParticipantsConfigurationElement.PROPERTY_1,PROPERTY_1_CONFIG_LABEL,
            GENERIC_PROPERTY_DESCRIPTION, false);
    }

    private void createBuildLabelWidgets(Composite parent) {
        createSpacer(parent, FIELD_SPACING, 2);
        fBuildLabelText = createConfigPropertyTextField(parent,
            MyBuildParticipantsConfigurationElement.PROPERTY_2,PROPERTY_2_CONFIG_LABEL,
            GENERIC_PROPERTY_DESCRIPTION, false);
    }

    @Override
    public boolean validate() {
        boolean isValid = true;

        if (fBuildDefinitionIdText.getText().trim().equals("")) {
            addErrorMessageForRequiredField(fBuildDefinitionIdText,ERROR_PROPERTY_REQUIRED,
                fBuildDefinitionIdText);
            isValid = false;
        } else {
            removeMessage(fBuildDefinitionIdText, fBuildDefinitionIdText);
        }
        if (fBuildLabelText.getText().trim().equals("")) {
            addErrorMessageForRequiredField(fBuildLabelText,ERROR_PROPERTY_REQUIRED,
                fBuildLabelText);
            isValid = false;
        } else {
            removeMessage(fBuildLabelText, fBuildLabelText);
        }
        setPageStatusIndicator(!isValid, false);
        return isValid;
    }

    @Override
    public Control getFocusControl() {
        return fBuildDefinitionIdText;
    }

}

In its simplicity, this class overrides two important methods. The first, createContent, is to create the form on load, and the other, validate, is to validate the user's input. The data is bound to the build configuration element via the createConfigPropertyTextField method that is called on the BasicConfigurationElementEditor class.

At this point, enough code exists to actually view the configuration element editor for the associated build definition template, as shown in Figure 2.

Figure 2. My new build configuration element editor associated to the build definition template
Build configuration element editor tab in Eclipse
Build configuration element editor tab in Eclipse

Engine plugin

The engine plugin contains the code that actually runs as part of the build process.

The activator

Remembering that the Jazz Build Engine is an Eclipse-based application, you need to extend it via an Eclipse plugin. An activator is required to initialize the plugin.

Listing 9. Engine plugin's activator class
public class MyBuildParticipantEnginePlugin extends Plugin {

    // The plug-in ID
    public static final String PLUGIN_ID = "com.ibm.rtcexts.mybuildparticipant.engine";

    // The shared instance
    private static MyBuildParticipantEnginePlugin plugin;

    //The constructor
    public MyBuildParticipantEnginePlugin() {
    }

    public void start(BundleContext context) throws Exception {
        super.start(context);
        @SuppressWarnings("unused")
        Plugin plugin = this;
    }

    public void stop(BundleContext context) throws Exception {
        plugin = null;
        super.stop(context);
    }

    //Returns the shared instance
    public static MyBuildParticipantEnginePlugin getDefault() {
        return plugin;
    }
}

The activator class only differs from the client plugin as it has no UI elements, and is therefore simpler.

The manifest

The Bundle-Activator property in the manifest is again set to the value of the PLUGIN_ID from the activator class.

Listing 10. Engine's plugin manifest
Manifest-Version : 1.0
Bundle-ManifestVersion : 2
Bundle-Name : com.ibm.rtcexts.mybuildparticipant.engine
Bundle-SymbolicName : com.ibm.rtcexts.mybuildparticipant.engine;singleton:=true
Bundle-Version : 1.0.2
Bundle-RequiredExecutionEnvironment : JavaSE-1.6
Require-Bundle : com.ibm.team.build.common,
    org.eclipse.core.runtime,
    com.ibm.team.build.engine,
    com.ibm.team.repository.client,
    com.ibm.team.repository.common,
    com.ibm.team.build.client,
    com.ibm.rtcexts.mybuildparticipant.common;bundle-version="1.0.2",
    com.ibm.team.build.toolkit,
    com.ibm.team.scm.common,
    com.ibm.team.scm.client
Bundle-ClassPath : .
Bundle-Activator : com.ibm.rtcexts.mybuildparticipant.engine.MyPreBuildParticipantPlugin
Bundle-ActivationPolicy : lazy

Aside from the Bundle-Activator, there are three other notable parts of the manifest.

  • The Bundle-ClassPath should be set from the plugin base directory.
  • The Bundle-ActivationPolicy should be set to lazy, ensuring it is only instantiated upon use, making for much easier debugging.
  • Notice that I removed the versions of the plugins in the Require-Bundle so that the plugin is more portable and less susceptible to minor upgrades. I left the version in for the common plugin, in case I forget to remove the old version when upgrading the plugin.

The plugin XML

The engine's plugin.xml just contains the build engine participant.

Listing 11. plugin.xml for the engine plugin
<plugin>
    <extension point="com.ibm.team.build.engine.buildEngineParticipants">
        <buildEngineParticipant
            id="com.ibm.rtcexts.mybuildparticipant.engine.MyPreBuildParticipant"
            class="com.ibm.rtcexts.mybuildparticipant.engine.MyPreBuildParticipant"
            buildPhase="PRE_BUILD"
            configurationElementId="com.ibm.rtcexts.mybuildparticipant.buildconfigelement"
        />
    </extension>
</plugin>

The extension point extends the buildEngineParticipants. The buildPhase can either be PRE_BUILD, BUILD or POST_BUILD. The configurationElementId is the ID for the build configuration element that we defined earlier in the common plugin. The class must point to the fully-qualified class name that you create and should extend an AbstractBuildEngineParticipant. It is good practice for the ID to be the same as the build participant class name.

The implementation

In this example, you will create a pre-build participant to extend the AbstractPreBuildParticipant class in Listing 12.

Listing 12. Pre-build participant Java class
public class MyPreBuildParticipant extends AbstractPreBuildParticipant {

    private String property1;

    private String property2;

    @Override
    public BuildStatus preBuild(IProgressMonitor monitor) throws Exception {

        IBuildDefinitionInstance buildDefinitionInstance = getBuildRequest().
            getBuildDefinitionInstance();

        IBuildConfigurationElement element = buildDefinitionInstance.getConfigurationElement(
            MyBuildParticipantsConfigurationElement.ELEMENT_ID);

        property1 = element.getConfigurationProperty(
            MyBuildParticipantsConfigurationElement.PROPERTY_1).getValue();
        property2 = element.getConfigurationProperty(
            MyBuildParticipantsConfigurationElement.PROPERTY_2).getValue();
        if (property1 == null || property1.length() < 1) {
            throw new MissingPropertyException("property 1 is not valid!!");
        }
        if (property2 == null || property2.length() < 1) {
            throw new MissingPropertyException("property 2 is not valid!!");
        }
        BuildParticipantLogger.info(getBuildLog(), "user set property1 to \"" + property1 +
            "\"");
        BuildParticipantLogger.info(getBuildLog(), "user set property2 to \"" + property2 +
            "\"");

    ITeamRepository repo = getTeamRepository();
    String buildRequesterUserId = repo.getUserId();

    String currentWorkspaceUUID = buildDefinitionInstance.getProperty(
        IJazzScmConfigurationElement.PROPERTY_WORKSPACE_UUID).getValue();

    String tag = (buildRequesterUserId.isEmpty() || currentWorkspaceUUID.isEmpty()) ?
        "" : buildRequesterUserId + "_" + currentWorkspaceUUID;

    if (tag!= null && !tag.isEmpty()) {

        IBuildResult buildResult = (IBuildResult) repo.itemManager().fetchCompleteItem(
            getBuildRequest().getBuildResult(), IItemManager.REFRESH, monitor);
        IBuildResult buildResultWorkingCopy = (IBuildResult) buildResult.getWorkingCopy();
        String existingTags = buildResultWorkingCopy.getTags();

        String tagStrippedInvalidChars = tag.trim().replace(' ', '_').replace(',', '_');
        String fullTag = (existingTags != null && existingTags.isEmpty()) ?
            existingTags + "," + tagStrippedInvalidChars : tagStrippedInvalidChars;

        BuildParticipantLogger.info(getBuildLog(), "Tagging current build with tag: "
            + tagStrippedInvalidChars);
        buildResultWorkingCopy.setTags(fullTag + tagStrippedInvalidChars);
        ITeamBuildClient buildClient = (ITeamBuildClient) repo.getClientLibrary(
            ITeamBuildClient.class);
        buildClient.save(buildResultWorkingCopy, monitor);
}
return BuildStatus.OK;
}

Depending on which AbstractBuildEngineParticipant class is being extended you should override either of the preBuild, build or postBuild methods. Here we are extending the AbstractPreBuildParticipant so the preBuild method is overridden. It is the preBuild method that is called when your build participant is executed by the Jazz Build Engine.

The first thing of note that the build participant does is to get the buildDefinitionInstance, which is an immutable object as opposed to the buildDefinition itself, which is subject to change. The build definition is used to access the build configuration element id, which is described in the common's plugin.xml. The generic properties also specified in the common's plugin.xml are then available to the build participant via the build configuration element. Here, these are retrieved by the build participant and output to the Jazz Build Engine log via a custom logger. We also make use of the configuration element class in the common plugin to get the ELEMENT_ID and PROPERTY_X IDs.

The participant continues to get the workspace UUID via the build definition instance, and then the Rational Team Concert user name of the user who initiated the build, via the Jazz Team Repository API. The build participant then gets a local working copy of the build result for the current build, and determines if there are any existing tags. The participant finally creates the tag string and sets the tag on the local working copy of the build result, and saves it via the build client.

Result

Figure 3 shows the output when the build participant is run.

Figure 3. Build results showing the successful use of my new build participant plugin
Successful build result for new build template
Successful build result for new build template

Notice the build tagged with the username and the workspace. Also notice that the user properties on our build configuration element have been successfully logged. Finally, it is of interest that the plugin did indeed run in the expected order, i.e. before the jazzscm plugin.

Now that you have seen how to extend Rational Team Concert with a new build participant, and how to divide the responsibilities between the client and the engine plugins, I will finish the article with some useful tips for automating the build and deployment of the plugin.

Build and distribution

When automating the build, test, package and deployment steps, I chose to use Ant. You may chose to go down the Maven route and build your project with Tycho. There is a good tutorial for this on Vogella's website, creators of Tycho. I got results that suited my plugin requirements better using customized Ant Scripts.

To create the Ant scripts, right-click the plugin.xml for each of the three plugins, and select PDE Tools > Create Ant Build File. The target build.update.jar creates the plugin as a jar file in the build directory. I made many modifications to these scripts, in order to better consolidate properties such as the plugins name and version. These are too large to include in this article. You can extract and modify these from the "my-build-participant" project on Github.

Engine plugin build

To deploy to the Jazz Build Engine, build the common plugin, then the engine plugin using the build.update.jar Ant Targets for each. Then copy the jars to the Jazz Build Engine's plugins directory: ${JBE_HOME}/jazz/buildsystem/buildengine/eclipse/plugins. Now restart the Jazz Build Engine daemon that you initiated in the Install Jazz Build Engine section.

In the build-engine.xml and build-common.xml, from the attached project, you can simply run the deploy.to.build.engine target, which removes existing plugins and deploys the new ones. You still need to manually restart the Jazz Build Engine.

If you are using the included project, be sure to make use of the /config folder. This folder contains your plugins properties. The properties in /config/host/local/<hostname>-build.properties take priority, followed by the /config/plugin-build-defaults.properties.

The engine and common jars are now ready to be deployed to any compatible Jazz Build Engine.

Client plugin build

To launch the client plugin, right-click on the com.ibm.rtcexts.mybuildparticipant.client project and select Run As > Eclipse Application. This opens a new Eclipse instance with the plugin loaded.

Client package and release

To distribute the client plugin, create a p2 repository. To do this, there are some requirements of the Eclipse framework. I recommend using the client workspace in the load workspace from build label project, because much of this is already done. Creating a p2 repository is purely an Eclipse Plugin Development task. I have written a brief guide for those unfamiliar with Eclipse Plugin Development, as I found it a difficult task to accomplish.

Create a p2 repository via an update site project. In order to actually expose the plugins via the p2 repository, use an Eclipse feature. Create an Eclipse Feature Project, by selecting File > New > Other.

Enter the properties shown in Figure 4.

  1. In the Project name field, enter com.ibm.rtcexts.mybuildparticipant.feature.
  2. Select the Use default location checkbox.
  3. In the Feature ID field, enter com.ibm.rtcexts.mybuildparticipant.feature.
  4. In the Feature Name field, enter com.ibm.rtcexts.mybuildparticipant.feature.
  5. In the Feature Version field, enter 1.0.0.
  6. In the Feature Provider field, enter IBM.
  7. Leave the Install Handler Library field empty.
Figure 4. My Build Participant's Eclipse Feature Project properties
Build Participant's Eclipse Feature properties
Build Participant's Eclipse Feature properties

On the next page, select the client and common plugins and click the Finish button.

Right-click on the feature.xml and select PDE Tools > Create Ant File.

Now create the update site project under File > New > Other and give it the name com.ibm.rtxexts.mybuildparticipant.updatesite. Open the site.xml within the updatesite project, and click Add Feature which selects the feature project created previously.

When building the plugins with Ant, you need to modify the Ant run configuration. Right-click the desired target and select Run As > Ant build. Edit the Ant config, by selecting the JRE tab and select Run in the same JRE as the workspace checkbox under Runtime JRE. If you do not do this you may find that you get taskdef not found errors on Eclipse Ant tasks.

Finally, you can build the client plugin. Run the build.update.jar Ant task for each of the projects in the following order:

build-common.xml > build-client.xml > build-feature.xml

Now open the site.xml within the update site project. Highlight the feature in the Site Map and click Build.

Zip the files generated in the root of the updatesite project.

Listing 13. Bash command for zipping the p2 repository
zip -rp ~/load.workspace.from.label.client.p2Repo_1.0.1.zip features plugins artifacts.jar content.jar

That's it! You can now distribute this p2repository to fellow Rational Team Concert users, who can install the client plugin in Eclipse by selecting it as an Archive file in Help > Install New Software.

Figure 5. Installing my new build participant client plugin, as a p2 repository in Eclipse
Installing my new build participant client plugin
Installing my new build participant client plugin

A final note on packaging the client. I have not managed to build the update site automatically using the Eclipse API, to generate a valid p2 repository. However, I have left my attempt within the build.xml file in the update site project within the load workspace from label build example. Please add to the comments if you are successful.


Downloadable resources


Related topic


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=DevOps, Rational
ArticleID=995507
ArticleTitle=How to extend and customize Rational Team Concert for continuous integration
publish-date=01202015