Yesterday I was asked to help a project that is incrementally migrating to Rational Team Concert with a small command line tool allowing to add comments to a work item. The team has switched to work items already and this tool is supposed to be used in some automation and called from another tool that is not yet replaced by RTC.
I had not yet worked with work item comments that much, except printing them and was interested in understanding how hard it would be to find the API involved. I made some nice experience doing so, which I would like to share.
Just Starting With Extending RTC?
If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.
You should be able to use the following code in this environment and get your own automation or extension working.
To keep it simple this example is as many others in this blog based on the Jazz Team Wiki entry on Programmatic Work Item Creation. The example shows client API.
License
As always, our lawyers, reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license, which basically means you can use it for internal usage, but not sell. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee.
Operation to Add a Comment
As usual I decided to go with a WorkItemOperation to add the comment and update the work item. To do that, I created an inner class WorkItemAddComment extending WorkItemOperation. Well, to tell the truth, I grabbed the whole initial code from another example like published in Upload Attachments To Work Items.
Then I looked into implementing the execute method, that is called when the operation is performed. Since I did not have code other than printing comments, I first used IComments comments = workItem.getComments() to get the comments.
Now I needed to find the API to create an IComment. So I used search for references as described here to look at references where IComment was created. Surprisingly I found the interface IComments provides the createComment() method.I could have found it right away if I had looked. But the learning here is, if you set up your environment as described here and here, and follow the tips to search, you can easily find it. Not much brainpower needed here.
The createComment() method needs an IContributor object for the person that is named as creator of the comment and an XMLString for the comment itself. I decided to change the constructor of the operation to pass an IContributor and a String and store them in fields, to have them available in the execute() method.
Finally it is necessary to add the new comment to the comments retrieved from the work item. All the save and update mechanism is handled in the WorkItemOperation.
To be able to update the work item in the execute() method, the constructor also needs to pass a load profile to the superclass. I started with using the full profile but optimized it later to load with a custom load profile based on the small profile with the Comments property as extension.
The resulting code is shown below.
private static class WorkItemAddComment extends WorkItemOperation {
private String fComment;
private IContributorHandle fCommenter;
public WorkItemAddComment(IContributorHandle commenter, String comment) {
super("Add Comment to Work Item", IWorkItem.SMALL_PROFILE.createExtension(Arrays.asList(new String[] { IWorkItem.COMMENTS_PROPERTY })));
fComment = comment;
fCommenter = commenter;
}
@Override
protected void execute(WorkItemWorkingCopy workingCopy,
IProgressMonitor monitor) throws TeamRepositoryException {
IWorkItem workItem = workingCopy.getWorkItem();
IComments comments = workItem.getComments();
IComment newComment = comments.createComment(fCommenter,
XMLString.createFromPlainText(fComment));
comments.append(newComment);
}
}
Call the new Operation
The new operation takes two parameters in the constructor.
- The IContributorHandle of the user to show as creator of the comment
- A string that is the text in the comment
The operation gets called and executed like below.
WorkItemAddComment operation = new WorkItemAddComment(commentUser,commentText);
operation.run(workItem, monitor);
System.out.println("Modified work item " + workItem.getId() + ".");
Get The Required Data
To call the code above, we need to get the user creating the comment and the work item to update. My final direction was, that I wanted to be able to just provide the repositoryURI, the credentials to log in, a work item ID and a comment string to be able to create the comment. In this scenario the user that runs the command line tool would be creator of the comment. As an enhancement I wanted to be able to pass the user ID of the creator of the comment.
The run method finally looks like this code:
private static boolean run(String[] args) throws TeamRepositoryException {
boolean result = false;
if (args.length 6) {
System.out
.println("Usage: AddComment []");
return result;
}
String repositoryURI = args[0];
String userId = args[1];
String password = args[2];
String idString = args[3];
String commentText = args[4];
String commenterID = null;
if (args.length == 6) {
commenterID = args[5];
}
IProgressMonitor monitor = new NullProgressMonitor();
ITeamRepository teamRepository = TeamPlatform
.getTeamRepositoryService().getTeamRepository(repositoryURI);
teamRepository.registerLoginHandler(new LoginHandler(userId, password));
teamRepository.login(monitor);
IContributor commentUser = null;
if (null != commenterID) {
try {
commentUser = teamRepository.contributorManager().fetchContributorByUserId(
commenterID, monitor);
} catch (ItemNotFoundException e) {
}
}
if (commentUser == null) {
commentUser = teamRepository.loggedInContributor();
}
// Use IWorkItemClient or IWorkItemCommon
IWorkItemCommon workItemCommon = (IWorkItemCommon) teamRepository
.getClientLibrary(IWorkItemCommon.class);
int id = new Integer(idString).intValue();
IWorkItem workItem = workItemCommon.findWorkItemById(id,
IWorkItem.SMALL_PROFILE, monitor);
if(null!=workItem){
WorkItemAddComment operation = new WorkItemAddComment(commentUser,
commentText);
operation.run(workItem, monitor);
System.out.println("Modified work item " + workItem.getId() + ".");
result=true;
} else{
System.out.println("Can not find work Item " + idString + ".");
}
teamRepository.logout();
return result;
}
What it does, is checking and getting the parameters first.
The code then logs into the repository. If a second user ID is available for the commenter, the code tries to get the related contributor. If this fails the code falls back to the automation user ID by getting the IContributor logged in.
Then the code gets the IWorkItemCommon client library to find the work item by the ID passed in the call. If all succeeds the operation is called to update the work item and we are done.
The Rest of The Code
You can grab the rest of the code from Upload Attachments To Work Items. The code is also available in the Jazz In Flight project at JazzHub in the project com.ibm.js.team.workitem.automation.examples in the Extension Development Stream in the RTC PlainJava Automation component. Once you have access you can get the code. You can also request to join the project and provide your own solutions there.
Calling The Code From The Command Line
The code can be called like below from the command line, provided JAVA_HOME is set and the Plain Java Client Libraries are installed in the second location and the code is compiled. The code below can be placed in a batch file which is called in the root of the eclipse project.
Set JAVA_HOME="C:\IBM\ibm-jdk\"
%JAVA_HOME%/jre/bin/java -Djava.ext.dirs=%JAVA_HOME%/jre/lib/ext;C:/RTC403Dev/installs/PlainJavaAPI -cp ./bin/ com.ibm.js.team.workitem.automation.examples.ModifyWorkItemAddCommentOperation "https://clm.example.com:9443/ccm" "ralph" "ralph" "54" "Add a comment"
See the README.TXT in the snippets folder of the Plain java Client Libraries for instructions for UNIX like operations systems.
Please Note: I had to explicitly point to the JRE, if the JDK did not provide an lib/ext folder.
Summary
As always I hope this code is useful to anyone. I also hope that the tips on searching the API helps others to explore the API as well. Please keep in mind, the code is not thoroughly tested and might need some polishing if you want to use it.