November 17, 2008

ClearCase Trigger to Update TestTrack

Helix ALM

Works with TestTrack 2008.2.1 Works with ClearCase 7.0.1

This article outlines how to create a trigger in ClearCase, which calls a script that updates TestTrack. This was all developed and tested against TT 2008.2 and ClearCase 7.0.1.

You'll need:
  • The TestTrack SOAP SDK installed.
  • The path to your TestTrack SDK installation. If you're familiar with how web servers work and your IT environment, you can probably guess this. If not, ask the TestTrack administrator or IT staff for help.
  • ClearCase and the necessary permissions to create triggers.
You'll get:
  • A ClearCase trigger that automatically associates check ins with a defect in TestTrack.
What we'll do:
  • Review the script we're going to use to integrate with TestTrack.
  • Create a trigger to call the script.
  • Go through an example of using the new trigger.
[toc]

ClearCase Integration Script

The script we'll be using is written in Java. Java is good because it's used on a variety of platforms. I'm not going to review the actual source, but it's not overly complex if you understand a little bit of Java.

Script matters

This script does not take any parameters. The values it needs are either hard coded or gathered from ClearCase environment variables. The hard coded values are: These values may be dynamic if you pass them as arguments to the Java application. I was not successful with this, however. I did not find an example of how to pass parameters to the "execwin" option in the mktrtype command. Also, I tried to pass a batch file to the "execwin", which in turn called the jar file along with the parameters. This did not work either. Once called, the script does the following:
  • Sets its variables
  • Parses out the comment from the check in.
  • Looks for the following token in the comment: <:defect#:>.
  • If the token is found, parses out the defect number in the token.
  • It links the ClearCase file checked in to the SCC tab of the referenced defect.
For example, if you check in a file with <:56:> somewhere in the comment, this script will link the file to defect 56 with the revision and fixed date of the file. Other items of interest:
  • The current parse logic only looks for one defect number.
  • ClearCase documentation on environment variables did not provide one for the date of the check in. Hence, the script gets the current date and time to use in TestTrack. This may cause the fixed timestamp in TestTrack to be off by a couple of seconds from the timestamp in ClearCase.

Java Code

Below is the java code for the script. You will need to use the TestTrack SOAP classes available here. If you would like to generate your own TestTrack SOAP classes (if you are using a different version of TestTrack), follow this NetBeans Tutorial. If you do create your own classes, you may need to change the import of testtrack_Interface to the name of the package you create to hold the classes.
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package cctrigger;

import testtrack_Interface.*;//<---May need to be changed if you use your own TestTrack classes
import java.util.*;
import java.text.*;
import java.io.*;

/**
 *
 * @author Cremerf
 */

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here

        //SOAP vars
        //hard coded
        String soapSrv = "http://localhost/scripts/ttsoapcgi.exe";
        String ttUser = "soap";
        String ttPass = "passoap";
        String ttProject = "Sample Project";

        //If you want to pass them as args, it would look like this.
        /*
         *  String soapSrv = args[0];
            String ttUser = args[1];
            String ttPass = args[2];
            String ttProject = args[3];
         *
         * You may also want to use an if statement to ensure the correct number of parameters
         * are passed.
         *
         * */

        //ClearCase env vars
        String sccFile = System.getenv("CLEARCASE_PN");
        String chkComments = System.getenv("CLEARCASE_COMMENT");
        String scmVersion = System.getenv("CLEARCASE_ID_STR");

        //Hard coded vars for testing outside of trigger environment (debugging).
        //String sccFile = "V:\file.test";
       // String chkComments = "<:7:>";
       // String scmVersion = "5";

        //Vars assigned during program execution
        long ttCookie = 0;
        String strDNum = "";
        long ttDNum = 0;

        //Get fixed date - not available as a ClearCase env var, so we'll have to use current date & time
        //All lines may not be needed, but this works, so why change it?
        Calendar cal=Calendar.getInstance();
        try
        {
             DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
             Date date = new Date();
             String d = dateFormat.format(date);
             cal.setTime(date);
        }
        catch (Exception e)
        {
           eLogger(e.getMessage());
        }
        try
        {
            //Parse the comments
            strDNum = parseComments(chkComments);
            ttDNum = Integer.parseInt(strDNum);
            //TestTrack SOAP interaction
            //Define soap object
            TtsoapcgiPortType_Stub ttSoap = (TtsoapcgiPortType_Stub)(new Ttsoapcgi_Impl().getTtsoapcgi());
            //Set the URL
            ttSoap._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY, soapSrv);
            //Get list of projects and select one we need
            CProject ttCProject = new CProject();
            CProject[] ttCProjects = ttSoap.getProjectList(ttUser, ttPass);
            for (int i=0;i < ttCProjects.length; i++)
            {
                if (ttCProjects[i].getDatabase().getName().equals(ttProject))
                {
                    ttCProject = ttCProjects[i];

                }
            }
            if (ttCProject != null)
            {
                //if we found project for user, lets log in
                //ttCookie = ttSoap.databaseLogon(ttProject, ttUser, ttPass); <---- Deprecated, but depending on your xml serializer
                //the projectLogon may return an error.  databaseLogon may work in that instance
                ttCookie = ttSoap.projectLogon(ttCProject,ttUser,ttPass);
                //Create a defect object and set it to the defect we are attaching to
                CDefect ttDefect = new CDefect();
                ttDefect = ttSoap.editDefect(ttCookie, ttDNum, "", false);
                //Get the list of SCC files the defect may have
                CSCCFileRecord[] ttSCCFiles = ttDefect.getPSCCFileList();
                int fileCount = 0;
                //If the list already contains files
                //If it does, we have to rebuild the array of SCCFiles objects and add our file
                //We can not just add our file to the end of the list.
                if (ttSCCFiles != null)
                {
                    //Get the number of files
                    fileCount= ttSCCFiles.length;
                    //check to see if file exists
                    boolean sccFileExists = false;
                    for (int i = 0;i < fileCount; i++)
                    {
                         if (ttSCCFiles[i].getMstrFileName().equals(sccFile))
                         {
                             //if the file has already been attached, we will just append the new version
                             ttSCCFiles[i].setMstrFixedRevision(ttSCCFiles[i].getMstrFixedRevision() + "," + scmVersion);
                             sccFileExists = true;
                         }
                     }
                     if (!sccFileExists)
                     {
                         CSCCFileRecord[] ttNSCCFiles = new CSCCFileRecord[fileCount + 1];
                         for (int i = 0; i < fileCount; i++)
                         {
                             ttNSCCFiles[i] = new CSCCFileRecord();
                             ttNSCCFiles[i].setMdateFixedTimestamp(ttSCCFiles[i].getMdateFixedTimestamp());
                             ttNSCCFiles[i].setMstrFileName(ttSCCFiles[i].getMstrFileName());
                             ttNSCCFiles[i].setMstrFixedRevision(ttSCCFiles[i].getMstrFixedRevision());

                         }
                         ttNSCCFiles[fileCount] = new CSCCFileRecord();
                         ttNSCCFiles[fileCount].setMstrFileName(sccFile);
                         ttNSCCFiles[fileCount].setMstrFixedRevision(scmVersion);
                         if (cal != null)
                         {
                            ttNSCCFiles[fileCount].setMdateFixedTimestamp(cal);
                         }
                         ttDefect.setPSCCFileList(ttNSCCFiles);

                     }
                }
                else
                {
                   CSCCFileRecord[] ttNSCCFiles = new CSCCFileRecord[fileCount + 1];
                   ttNSCCFiles[fileCount] = new CSCCFileRecord();
                   ttNSCCFiles[fileCount].setMstrFileName(sccFile);
                   ttNSCCFiles[fileCount].setMstrFixedRevision(scmVersion);
                   if (cal != null)
                   {
                        ttNSCCFiles[fileCount].setMdateFixedTimestamp(cal);
                   }
                   ttDefect.setPSCCFileList(ttNSCCFiles);  

                }
                ttSoap.saveDefect(ttCookie, ttDefect);
                ttSoap.databaseLogoff(ttCookie);

            }

        }
        catch(Exception fcc)
        {
             eLogger(fcc.getMessage());        

        }
    }
    //Method to log errors - change path of file to store it somewhere else
    private static void eLogger(String log)
    {
        try
             {
                 FileWriter outFile = new FileWriter("c:\logger.txt");
                 PrintWriter out = new PrintWriter(outFile);
                 out.append("The following error was encountered:");
                 out.append(log);

                 out.close();
              }
              catch (IOException ef)
              {
                 ef.printStackTrace();

              }

    }
    private static String parseComments(String scmComments)
    {
        String dNum = "";

        int startPos = scmComments.indexOf("<:");
        int endPos = scmComments.indexOf(":>");
        dNum = scmComments.substring(startPos + 2, endPos);
        return dNum;

    }

}

Create ClearCase Trigger

Now that we understand how to the script works, we can create a trigger in ClearCase.
  1. Open a command-prompt
  2. Change the directory (and maybe drive) to get to the VOB that we want this trigger to apply to.
  3. Using the ClearTool command, enter a command similar to the example below:
cleartool mktrtype -c "Attach to Defect" -element -all -postop checkin -execwin "C:Program FilesJavajre1.6.0_07binjava.exe -jar C:distCCTrigger.jar" TTATTACH
The example above will NOT work for you if:
  • You are not using Windows.
  • Your path to java.exe is different.
  • You path to the jar file is different.
Please refer to the ClearCase documentation for clear understanding on all of the options used and how to possibly change them to customize this for your own needs. After a successful check in, our script should be called. We're connecting to a project by the name of "Sample Project" as user "soap", with password of "passoap". We use the environment variables from ClearCase to get the rest of the information.

Debugging/Using the Trigger

To debug the code above, hard code the variables that depend on the environment variables. The sample code above already has this commented out for your use. To debug the code when it runs as a trigger, create a method similar to the eLogger() method to log messages throughout the code, these messages can help you see how far your code got before the error occurred. The reason I suggest using a separate method, is because the method overwrites the file, so the message you log will be overwritten by the message from the Exception. Here is a sample use case:
  1. Edit a file controlled by ClearCase in the VOB where you set up the trigger.
  2. Check-in the file
  3. Enter <:1:> This is my first attempt at using the trigger that will integrate to ClearCase!! as the check in comment.
Now you should have a file linked to defect #1 in your TestTrack project, on the SCC tab. If you've configure ClearCase integration within TestTrack, you should be able to perform ClearCase commands on that file from within TestTrack as well. Note: Seapine does not provide support for sample triggers.