Work Item Command Line 4.1


I recently had a chat with a colleague who is trying to automate RTC Work Item creation. As a casual user without administration experience they where wondering how to find the IDs for the work item types to be able to create work items of the types in WCL. Today you must know the ID of the work item and look them up in the administration UI.

I always wanted to add the ability to be able to list the available work item types in WCL, but for whatever reason I never got to searching the API and add it to WCL.  Having some time last week I took the opportunity to finally implement it. This closes a last big gap that WCL left open. Users can now print the work item types and with that information print the attributes available on the work item type.

A small description how to use WCL can be found here. WCL prints a help if the command is omitted. The help information is very long. Make sure to redirect it into a file or increase the shell buffer size.

This post provides the code for the latest version of WCL and also a description how to install WCL.

License

WCL is released under the MIT License. See the License.txt and the license headers in the individual files.

Compatibility

This code has been used with RTC 4.x, 5.x  and 6.x with no or minimal changes and it is pretty safe to assume, that the code will work with newer versions of RTC to come. The code requires the RTC Plain Java Client Libraries. The Export and import commands require additional external libraries that need to be downloaded and installed separately. See the document below.

Download

Source Code for Download

The code is available in the Jazz Community. WCL 4.1 is available in this release.

Project

Additional Download

You can also download the latest version 4.1 here:

Changes

Added a command printtypes to list the work item types available for a project area.

wcl -printtypes repository="https://clm.example.com:9443/ccm" user=ralph password=****** projectArea="JKE Banking (Change Management)"

lists the work item types. The output will look like below:

printtypes

The Type ID is the work item type ID required to set and get work item information. The type category is an additional information to understand if work item types have the same workflow.

Download the Plain Java Client Libraries

WCL requires at least the RTC Plain Java Client Libraries for your version of RTC. To download that, open the All Downloads tab of the RTC version you are interested in. For example https://jazz.net/downloads/rational-team-concert/releases/6.0.5?p=allDownloads and scroll down to the Plain .zip Files section.

PlainJava

Download the RTC Plain Java Client Libraries file.

Use 7Zip and unzip the RTC Plain Java client Libraries download file (for example named RTC-Client-plainJavaLib-6.0.5.zip). Use 7Zips Extract Files command and provide the extraction Path for example C:\RTCWCL\PlainJavaAPI. Remember the location for later.

Optional: Download the Plain Java Client Libraries API documentation and extract it to the same folder you extracted the RTC Plain Java client Libraries download.

How to Install WCL

There are several ways how the WCL can be set up and run. The two main methods are:

  1. As pre-compiled Java e.g in a jar file using a batch file
  2. Using launch files to run or debug in Eclipse

The following sections explain the first method.

Create Packaged Version

To package your own version of WCL for release from the source code, setup your development environment as described in the next section. Then follow the document ReadMe – HowToRelease.txt in the eclipse project com.ibm.js.team.workitem.commandline you can find in the source code to package WCL.

Install Packaged Version

If you download the packaged, executable application latest version 4.1, the download is already packaged and prepared. The downloaded file is compressed and will be named like WCL_V4.1_20180323.zip. Extract the WCL package file e.g. using 7Zip to a folder, for example C:\RTCWCL.

The folders structure should look as below.

WCLCommandLine

Check and Adjust the Script File

To work, the WCL needs a Java JRE or JDK. Open the file wcl.bat or wcl.sh. Provide a JRE or JDK. I Usually use a JRE that comes with the CCM server or a JDK that comes with the Eclipse client. However any compatible JRE should do.

Change the JAVA_HOME variable to point to a location containing a JRE. If needed, remove the path section  JRE from the final call.

BatchFile

If you installed the Plain Java Client Libraries API into a different location, set the variable PLAIN_JAVA to that folder location.

Make sure the files are executable and test WCL e.g. by calling wcl and running one of the commands. For example use the new prittypes command

wcl -printtypes repository="https://clm.example.com:9443/ccm" user=ralph password=****** projectArea="JKE Banking (Change Management)"

 

Provide Additional Libraries

The export and the import commands of WCL need two libraries that are not shipped with the downloads.

If you use the packaged WCL and want to use the export/import capability follow the steps below to add the required libraries to the folder lib in the folder lib in the WCL folder.

If you use the Eclipse project for WCL and want to use the export/import capability follow the steps below to add the required libraries to the folder lib in the Eclipse project com.ibm.js.team.workitem.commandline.

The export and the import commands of WCL use the Open CSV Library. I had issues with the newer versions of Open CSV that I could not resolve, so this code assumes the version 3.7. Download the version 3.7 from here. Uncompress and untar the the file opencsv-3.7-src-with-libs.tar.gz you downloaded. Look for the folder opencsv-3.7 deploy\ copy the JAR file opencsv-3.7.jar and put it into the lib folder of your version of WCL.

It is possible to use other such libraries like SuperCSV with minor changes to the source code as well.

The import commands of WCL can only provide the capability to use a mapping file by using a JAR file that only ships with the RTC Eclipse client and the SDK. The classes used for the mapping file capability are located in the library com.ibm.team.workitem.rcp.core.  Open the Install location of the RTC Eclipse client and search for com.ibm.team.workitem.rcp.core*. You should find a file names similar to this one: com.ibm.team.workitem.rcp.core_3.1.900.v20141010_0124.jar. The version numbers at the end could be different. Copy the JAR file into the into the lib folder of your version of WCL.

Installing the Source Code

The best approach to develop for the RTC Java APIs is, to setup the development environment based on the RTC SDK . This provides with the source code for the API and allows to search examples. This saves so much time in the long run that saving the effort of setting the SDK makes no sense. So follow the instructions in the next section.

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.

Importing the Source Code

Get the source code from the Jazz Community. Use Git to clone the project and import the source project into Eclipse. You can also download the source as a zip file and import the project with the other Eclipse Import wizards. Switch to the Java perspective or the Plug in Development perspective.

The project should look as below

SorceProject

The folders contain the information to build the jar file, to run the and debug the code, folder for additional libraries and the scripts to run the Java application from a shell.

The Eclipse project is a Plug-in development project. This trick provides the access to the SDK and the API source code.

Install The Plain Java Client Libraries

The project references the Plain Java Client Libraries and needs them to run.

BuildPath

So the minimum additional step needed to be able to run the WCL for development in Eclipse is to install the RTC Plain Java Client Libraries as a user library.  The Plain Java Client Libraries have already been downloaded and installed. The same folder with the install can be used in the next steps.

Open Windows>Preferences and type “User lib” into the search window.

BuildPath_2

In the Java>Build Path User Libraries click the New… button. Type the name that is alreay referenced: PlainJavaAp. Click OK.

Select the new User Library and click Add External JARs…

BuildPath_3

Browse to the folder containing the Plain Java Client Libraries C:\RTCWCL\PlainJavaAPI in this example. Select all JAR files and click open.

BuildPath_4

The User Library should now contain the libraries.

BuildPath_5

Go back to the section “Provide Additional Libraries” and add the libraries to the folder lib. Make sure the libraries are available. Remove and re add the jar files to the build path if needed.

You should now be able to run or debug the WCL from within Eclipse. The debug configurations shipped with the source code in the Launches folder are now available under the Debug>Debug Configuration section.

Launches

Change the configurations as needed.

To package your own version of WCL for release from the source code, follow the document ReadMe – HowToRelease.txt in the eclipse project com.ibm.js.team.workitem.commandline you can find in the source code to package WCL.

Summary

You should now be able to start working on WCL and enhance it if needed. I hope this document helps the many users out there.

 

 

 

 

 

Advertisements

Query Models or how to find stuff with the RTC Java API


Although I have done my share using and blogging about the API there are still a lot of uncharted areas. How can I use the RTC API to find a user by the name? This is a question that came up recently in the forum and is one of many questions, I did not have a good answer until today.

While writing my last post, for whatever reason I started thinking about this question. I decided to have a quick peek and try to find out. As a result this blog post describes how questions like that can be approached with the RTC Java API.

The Problem

The API provided for objects such as contributors, build results and a lot more model elements used in the RTC application is not necessarily the API a human would expect. This is because the API is written to make it easy to develop the tool and not to make it easy for a human to access data. So a question “How do I find a user by the name” is not necessarily something the RTC API would be optimized for.

If a user logs into RTC, they provide the ID and not the name. After login the user ID is available in the API and that is the glue used for almost all internal computation. The user name is usually nothing of interest to the API. However there are cases, for example integration scenarios, where questions like this might be of interest. So how does RTC solve this under the covers?

Work Item Queries

Please don’t confuse the Query Models in this post with work item queries and work item expressions. To search for work items see

License

The post contains published code, so 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. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee. Enjoy!

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.

Compatibility

This code has been used with RTC 6.0 and is prepared to be used with RTC 6.0.x with no changes and it is pretty safe to assume, that the code will work with newer versions of RTC. It should however run with any version of RTC that has the specific API already implemented. The code shown below should work with almost all versions of RTC.

The post shows client, common and server API that are available in the RTC Server SDK and the RTC Plain Java Client Libraries.

Download

You can download the Eclipse project with the examples.  Please note, there might be restrictions to access Dropbox and therefore the code in your company or download location.

Solution

RTC works against a database. Some domains such as work items and SCM provide higher level query mechanisms to support user configurable queries in the UI to find stuff. Other domains do not.  But under the cover, the RTC API provides query mechanisms to query the database for data and to manage result sets.

The RTC API provides a common query service and  query models to be able to define and run queries for a wide variety of RTC objects.  The query service and the query models are common API and can be used in Java client applications, Eclipse client extensions as well as in RTC Server extensions.

Although there are questions and examples in the forums and there are some examples like this wiki page and somewhat hidden in this article (section Querying for Items), there is no good description how this works on a broader level. Needless to say that there is no description how to find the entry points into the API either. This post tries to help as good as possible.

How to find the query models

The best way to approach this is to setup a RTC Development environment by following the getting started post. Thsi means follow Setting up Rational Team Concert for API Development and the Extensions workshop and perform at least lab 1. Now you have an Eclipse with a RTC SDK set up that provides you with searchable example code. Without this environment, it is pretty pointless to try to approach this API.

You can use the naming conventions used while developing RTC to search for the query models. In the Plug-in Development Perspective select the menu Search>Java. Type *QueryModel as search string. Select declarations and the other choices shown below, then click Search.

searchquerymodel

Be patient while the SDK is searched. Dependent on the version of RTC you should finally see a search view similar to the one below.

searchquerymodelresult

Note that more than 960 query modes are found. There might be some duplicates and some might be totally uninteresting but there is obviously an enormous potential to access data in RTC.

If you know the model element interface you are interested in, for example an IBuildResult,you can use these approaches to find the related query model.

You can try to use the Eclipse content assist capability of the Plugin Development Environment (PDE) to find the related query model. Type in the name of the model element without the leading I and append QueryModel to it. To find the query model for IBuildResult, type BuildResultQueryModel. While you type use Ctrl+Space for content assist. The PDE should find the query model class.

contentassistpde

You can also use the search approach from earlier to search for the specific class BuildResultQueryModel with or without using the asterisk.

specificsearch

That way the Eclipse client, if set up correctly, allows to find the query model for the model element interfaces you are interested in.

What is provided by the query models?

The query models allow to define queries, to find model elements related to the query model. The query model provides comparisons, Boolean operations on properties specific to the model elements, sort and filter operations. The queries can be constructed and called with parameters. The queries can then be run using a query service and return a result set that can be further processed.

Example 1: Find the build results related to a specific build definition that are tagged with a specific tag.

Example 2: Find a Contributor by user name.

The entry point of a query model is the ROOT entry of the query model. It allows to instantiate queries against the query model and defines the interfaces available for the specific query model. These interfaces also specify which properties an object has, how to access them and the Boolean and other operations available to operate on this model element.

The image below shows the query model root BuildResultQueryModel.ROOT. It returns an implementation class. The BuildResultQueryModel interface also extends interfaces BaseBuildResultQueryModel and ISingleItemQueryModel.

querymodelroot

The interface BaseBuildResultQueryModel defines which properties the model element IBuildResult exposes in queries.

buildresultquerymodel

ISingleItemQueryModel defines operators such as equals or contained in providing a IPredicate interface.

isingleitemquerymodel

The IPredicate interface provides the interfaces to create the Boolean operations and, or, not.

ipredicate

Given this pattern, it is possible to create complex expressions.

Using the query models

Using the Query mechanism typically works in the following steps.
Create a query for the QueryModel for the model element (example [ModelElementName]=BuildResult.

IItemQuery query = IItemQuery.FACTORY.newInstance([ModelElementName]QueryModel.ROOT);

Create a predicate to filter the results based on some properties. This specific example uses a parameter of type string that gets a value passed. Instead of the paramter, it would also be possible to hard code a string here.

IPredicate predicate = [ModelElementName]QueryModel.ROOT.property()._eq(query.newStringArg());

Use the predicate from the step before as filter for the query.

IItemQuery filtered = (IItemQuery) query.filter(predicate);

Finally use the query service to run the query. Here a parameter of type string is passed to the query. Result sets can be big, so the last parameter is used to pass how many results should be retrieved.

IItemQueryPage page = queryService.queryItems(filtered, new Object[] { "Jerry Jazz"}, 1 );

All this can be as compact as in the following example.

IItemQueryPage page = queryService.queryItems(IItemQuery.FACTORY.newInstance(IterationPlanRecordQueryModel.ROOT), IQueryService.EMPTY_PARAMETERS, IQueryService.DATA_QUERY_MAX_PAGE_SIZE);

Get contributor by user name

Lets look at how the code for this forum question.: “How can I get the contributor for a user if I have the user name and not the ID?”. The code is inspired by very similar code that is used for the RTC Jabber integration.

We are looking for an IContributor. So the looking for the query model seems to be ContributorQueryModel.

First the code creates the query for the ContributorQueryModel. Then it creates a predicate to filter out a contributor with a specified name. The predicate uses an argument for the user name instead of providing the user name already here as string. The predicate is set as filter.

This is plain java client library code. There is no direct access to the com.ibm.team.repository.common.service.IQueryService. To get the IQueryService the code uses a trick. the IQueryService is available from the Implementation Class for ITeamRepository.  The teamRepository object is casted to com.ibm.team.repository.client.internal.TeamRepository. This makes the usage of QueryModels unsupported due to using unsupported internal code.

The IQueryService is indirectly available in some client libraries as well.

ITeamBuildClient buildClient = (ITeamBuildClient) teamRepository.getClientLibrary(ITeamBuildClient.class);
IItemQueryPage queryPage = buildClient.queryItems(query, parameters, IQueryService.ITEM_QUERY_MAX_PAGE_SIZE, monitor);

Once the QueryService is available the query is executed. In the example the user name is passed as a new parameter to the query. The result expects no more than one result.

The paged result is retrieved as list of handles and further processed.

// Create a query for the ContributorQueryModel
final IItemQuery query = IItemQuery.FACTORY.newInstance(ContributorQueryModel.ROOT);
// Create a predicate with a parameter to search for name property  
final IPredicate predicate = ContributorQueryModel.ROOT.name()._eq(query.newStringArg());
// Use the predicate as query filter 
final IItemQuery filtered = (IItemQuery) query.filter(predicate);
// Get the query service. This is a cast to an internal class. Note TeamRepository and not ITeamRepository is casted.
final IQueryService qs = ((TeamRepository) teamRepository).getQueryService();
// Run this ItemQuery. Note, there are also other types of queries qs.queryData(dataQuery, parameters, pageSize)
final IItemQueryPage page = qs.queryItems(filtered, new Object[] { findUserByName }, 1 /* IQueryService.DATA_QUERY_MAX_PAGE_SIZE */);
// Get the item handles if any
final List<?> handles = page.getItemHandles();
System.out.println("Hits: " + handles.size());
if (!handles.isEmpty()) {
	System.out.println("Found user.");
	// Resolve and print the information to the contributor object.
	final IContributorHandle handle = (IContributorHandle) handles.get(0);

Please note that there are various operations available to create the predicate and that the predicate can actually more complex. The code above uses the method

_eq()

to check for an equal string. In this case the match has to be exact.

Another option would be to search with ignore case option. This can be done using the following predicate definition:

final IPredicate predicate = ContributorQueryModel.ROOT.name()._ignoreCaseLike(query.newStringArg());

The complete code can be found below and is available for download. The downloadable code contains the example to search for the user by exact (case sensitive) user name and an example to search for the contributor by e-mai with a case insensitive match.

If the QueryUserByName example class is called with the required parameters like.

"https://clm.example.com:9443/ccm" "myadmin" "myadmin" "John Doe"

the result looks like this, provided the user exists, of course.

executionresult

Differences in the Server API

Server extensions must extend com.ibm.team.repository.service.AbstractService. This allows to use com.ibm.team.repository.service.AbstractService.getService(Class) to get the IQueryService in a server Extension like this:

IQueryService queryService = this.getService(IQueryService.class);

The rest of the API is as described above.

Dynamic Query  Model

There is also a dynamic query model based on the IItemType. It can be created as shown below.

IDynamicItemQueryModel dynamicQueryModel = IBuildResult.ITEM_TYPE.getQueryModel();

From the documentation in the code:

Generally, static query models should be used whenever they are visible and the types/properties are known at compile time. Note also that there are no API contracts regarding dynamic APIs – model objects may change shape, queryable properties, etc.”

So use the static version as described above.

Interesting examples in the SDK

Search for “Owned By” is in com.ibm.team.repository.client.tests.query.ExistsPredicateQueryTests.testExistsPredicateUsingLinks()

The full example code

Please find below the complete code for the RTC Plain Java Client Library version to find a contributor by its user name.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2017. All Rights Reserved. 
 *
 * Note to U.S. Government Users Restricted Rights:  Use, duplication or 
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.team.workitem.ide.ui.example;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;

import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.client.ITeamRepository.ILoginHandler;
import com.ibm.team.repository.client.ITeamRepository.ILoginHandler.ILoginInfo;
import com.ibm.team.repository.client.TeamPlatform;
import com.ibm.team.repository.client.internal.TeamRepository;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.common.model.query.BaseContributorQueryModel.ContributorQueryModel;
import com.ibm.team.repository.common.query.IItemQuery;
import com.ibm.team.repository.common.query.IItemQueryPage;
import com.ibm.team.repository.common.query.ast.IPredicate;
import com.ibm.team.repository.common.service.IQueryService;

/**
 * Uses the ContributorQueryModel to search for a user by the user name 
 * and not the ID.
 * 
 * 
 * Example code, see
 * https://jazz.net/wiki/bin/view/Main/ProgrammaticWorkItemCreation.
 */
public class QueryUserByName {

	private static class LoginHandler implements ILoginHandler, ILoginInfo {

		private String fUserId;
		private String fPassword;

		private LoginHandler(String userId, String password) {
			fUserId = userId;
			fPassword = password;
		}

		public String getUserId() {
			return fUserId;
		}

		public String getPassword() {
			return fPassword;
		}

		public ILoginInfo challenge(ITeamRepository repository) {
			return this;
		}
	}

	public static void main(String[] args) {

		boolean result;
		TeamPlatform.startup();
		try {
			result = run(args);
		} catch (TeamRepositoryException x) {
			x.printStackTrace();
			result = false;
		} finally {
			TeamPlatform.shutdown();
		}

		if (!result)
			System.exit(1);
	}

	private static boolean run(String[] args) throws TeamRepositoryException {

		if (args.length != 4) {
			System.out
					.println("Usage: QueryContributorByName [repositoryURI] [userId] [password] [NameOfUserToSearch]");
			return false;
		}

		IProgressMonitor monitor = new NullProgressMonitor();
		final String repositoryURI = args[0];
		final String userId = args[1];
		final String password = args[2];
		final String findUserByName = args[3];
		ITeamRepository teamRepository = TeamPlatform
				.getTeamRepositoryService().getTeamRepository(repositoryURI);
		teamRepository.registerLoginHandler(new LoginHandler(userId, password));
		teamRepository.login(monitor);

		/***
		 * There is a wide variety of query models available for several domains that allow to query 
		 * the elements and filter the results.
		 *
		 * For some examples on the topic
		 * @see https://jazz.net/wiki/bin/view/Main/QueryDevGuide#ExampleOne
		 * @see https://jazz.net/library/article/1229
		 */

		// Create a query for the ContributorQueryModel
		final IItemQuery query = IItemQuery.FACTORY.newInstance(ContributorQueryModel.ROOT);
		// Create a predicate with a parameter to search for name property  
		final IPredicate predicate = ContributorQueryModel.ROOT.name()._eq(query.newStringArg());
		// Use the predicate as query filter 
		final IItemQuery filtered = (IItemQuery) query.filter(predicate);
		// Get the query service. This is a cast to an internal class. Note TeamRepository and not ITeamRepository is casted.
		final IQueryService qs = ((TeamRepository) teamRepository).getQueryService();
		// Run this ItemQuery. Note, there are also other types of queries qs.queryData(dataQuery, parameters, pageSize)
		final IItemQueryPage page = qs.queryItems(filtered, new Object[] { findUserByName }, 1 /* IQueryService.DATA_QUERY_MAX_PAGE_SIZE */);
		// Get the item handles if any
		final List<?> handles = page.getItemHandles();
		System.out.println("Hits: " + handles.size());
		if (!handles.isEmpty()) {
			System.out.println("Found user.");
			// Resolve and print the information to the contributor object.
			final IContributorHandle handle = (IContributorHandle) handles.get(0);
			IContributor foundContributor = (IContributor) teamRepository.itemManager().fetchCompleteItem(handle, IItemManager.DEFAULT, monitor);
			System.out.println("UUID: " + foundContributor.getItemId());
			System.out.println("ID: " + foundContributor.getUserId());
			System.out.println("Name: " + foundContributor.getName());
			System.out.println("E-Mail: " + foundContributor.getEmailAddress());
			System.out.println("Archived: " + foundContributor.isArchived());
		}			
		teamRepository.logout();
		return true;
	}
}

Additional Examples

Summary

As always I hope this helps someone out there with running RTC. Please keep in mind that this is as usual a collection of very basic examples with no or very limited testing and error handling. Please see the links in the getting started section for more examples, especially if you are just getting started with the RTC APIs.

RTC Process Customization – What you can and cannot do


Rational Team Concert Process Customization – What you can and cannot do, that is the title of the webinar I presented two days ago.

If you are interested in my view on this, you can find the replay of the webinar here in the Rational Team Concert Enlightenment Series.

The slides are shared here.

Also see What API’s are Available for RTC and What Can You Extend? for more links.

 

Learning To Fly: Getting Started with the RTC Java API’s


I intend this to become the beginners guide for the RTC java API’s. The reason is, I spend far too much time on the forums trying to answer beginner questions on how to get started over and over again. I intend this post to be my link to answer these questions without having to repeat everything all over.

Where to Start?

You first have to understand what API’s are available and what you can extend. If you don’t understand these basics, you can’t decide on how to proceed. It is also a good idea to understand RTC Process Customization and what you can and cannot do.

Setting up Your Development Environment

If you determined you are interested in one of the Java API’s, you need to set up your environment for Java API development. It is extremely important to perform the steps in that post to have an environment, that has the RTC SDK installed as well as the Plain Java Client Libraries. Do the whole workshop even if you just want to use the Plain Java Client Libraries to code up some automation. This familiarizes you with the concepts of plug-ins and give you some first glimpse on the API.

Even if you intent to develop only Plain Java Client Libraries, you should use a plug-in project to develop your code, because that allows to trick the Eclipse Plugin Development Environment (PDE) into showing you the whole source code of the RTC SDK. It requires to set up your environment as explained in Setting up Rational Team Concert for API Development.

This allows you to

  • See the comments on interfaces, classes and on methods
  • Search interfaces, classes and methods even with using an asterisk if you are not sure about the names and packages
  • Search for references to interfaces, classes, methods, extension points to go looking for inspiration and example code

Search The APIGetting started with the RTC Plain Java Client Libraries

To understand how that works read the post Understanding and Using the RTC Java Client API. Especially read the sections

  • Getting Started With Developing for the Plain Java Client Libraries
  • Debugging Client Code and Finding Information about the API
  • Prepare Your Project to Allow Debugging
  • Finding Information About the API

You should carefully read the whole post Understanding and Using the RTC Java Client API, as it explains how the basic mechanisms in the API work. This is also interesting if you intent to write server extensions.

Getting started with Eclipse Client and Server Extensions

If you are not (yet) interested in this section skip to the next sections and go to “Where can I find Examples and Example Code?“.

The best place to start is following the Rational Team Concert Extensions Workshop. This provides you with a fully set up environment for debugging and guides you through all the important steps that are necessary to develop client and server extensions. You might also want to look at Bartosz Chrabskis post How to create Rational Team Concert – Advisor & Participant Extension (step by step) and the related video for creating a participant and the video for creating an advisor.

RTC provides a lot of ways to extend the Eclipse based clients and the server. The most important in general are

  • Pre-conditions which are also referred to as Advisors, because they drive the process advisor behavior of operations
  • Follow-up action which are also referred to as participants, because they participate in the process behavior of operations
  • Attribute Customization which are also referred to as providers, because most of them provide values for work item attributes; please  find examples here
  • Components which are used to group extensions

The wiki topic Team Process Developer Guide provides a great overview about what you can do and what the rules are.

On the RTC Server side it is also possible to create asynchronous tasks, that can perform operations such as event generation and mail notification. An example with code and explanation can be found in the post Due Date Notifier – an Asynchronous Task Example.

There are additional extension points available on the client and on the server that can be used. Some examples are

  • Eclipse Client UI Extension points
  • RTC Web UI Extension points

There are still more, that I haven’t even looked at yet.

The most important part is to be able to develop and debug your server extensions using Jetty as explained in the Setting up Rational Team Concert for API Development. If you can’t do that, it is just a waste of time and I would consider not trying it out at all. You also always want to have a dedicated server for extensions development, deployment testing and functional testing.

The code used in advisors and participants is very similar except that the interface used is different and the result that is returned is also different.

Advisors and participants always run in the context of the user that calls the operation. This means they can only access data the user is able to access and perform operations the user is permitted to.

In advisors it is not permitted to modify the element for which the advisor is triggered. In participants this is allowed.

Participants that modify or create elements have to perform an additional save for the objects modified. Please note that this can trigger the same or another participant to be executed. This can result in a server crash, if it causes a recursive descent for a recursion that does not stop. To prevent that, it is possible to pass additional arguments in the save that the sub-sequentially called  participant can use to prevent running into a recursion. See for example this post for how that can be used.

The Extension Points and Operation ID’s to assign advisors and participants to the specific operation to work for can be found here.

In general it is the best approach to extend the RTC Server if at all possible. The reason is that this makes deployment easier. Deployment basically has to only happen on the server and deploying on all the clients can be avoided.

Deploying Extensions

If, and only if, your extension works in the Jetty based development and debug environment, you can deploy it on a real server. If you have not successfully tried your extension on Jetty as explained in the Rational Team Concert Extensions Workshop, it does not make any sense to try deploying it on a test server, let alone on a production server.

The Rational Team Concert Extensions Workshop handles deploying extensions very manual. The post A Custom Condition to Make Attributes Required or Read-Only by Role explains how this can be made more automatic in the section Deploying the Extension.

If the extension works on Jetty, the worst that can go wrong deploying it on a real server is that the dependencies in the extension include libraries that are not available on the server.

Other issues could be missing or not satisfiable dependencies, version issues, missing provision profiles or typos in the profile or corrupt files. This would show in the log file during server start up. Check the RTC Server log (i.e. server/logs/ccm.log).

Please note, a deployment error, a typo in the provision profile or missing or corrupted files in the sites folder can lead to the server crashing / not successfully starting up. The server start up can be interrupted and the server may not be reachable. See the server log for hints what might be the problem.

Please refer to the post Is The Extension Deployed? How Can I Redeploy? for checking if the deployment actually worked and for maintaining and upgrading server extensions.

Client extensions are required if the data that it works with is only available on the client, or if the extension is needed on client and server anyway e.g. for Java based attribute customization provider.

Where can I find Examples and Example Code?

There are countless examples out there, here on Jazz.net, on Stackoverflow and on many other blogs and sites. But apparently this is not enough or too hard to find. So I will try to put guidance on how to get started here.

The SDK

As already explained you can search for code that ships with the RTC SDK. This requires a working development environment as described above and here.

This Blog

You can find examples and example code in this blog. The easiest way to do so is to search the blog. This is really easy. Type the interface, method or any other clue you search for in the search window on the top right section underneath the banner and then use the “Search” button.

Search the BlogThen go through the list of posts that appear.

Most posts have working code attached for download as well.

There is also a page with Interesting Links that I maintain and add stuff that I come across. There are many links to examples for API and other interesting sources.

The Internet

We live in the times of search engines. In the past, like 25 years ago, you had to go to a public library and try to find relevant information or books that most likely were not there. Today you can use your preferred search engine and, with very few effort, find all you can ask for.  It is of course always easier to ask someone else to “Google that for me”, but it is way better to skill up to do it yourself in these Darwin times.

A good approach in this case is to just come up with some keywords, interface or class name and run the search engine on it. In my experience some words what you are looking for and RTC usually is enough. I find the interesting information usually on the first two pages. If not, I try to change and refine my query. Examples for typical search paattern are

  • “create work item java API RTC”
  • “access work item attribute java API RTC”
  • “RTC API IIteration”

If the results on page one are just too many and not promising it is possible – for Google at least – to limit the scope of the search to specific sites.

Using Search EnginesAn example for this pattern would be

  • “create work item java API RTC site:jazz.net”

This is most useful if you know that there has to be something there or recall something was there and you can’t remember where. I find this even more effective than using the search engine available on Jazz.net, because it usually shows the information I am looking for higher in the ranking.

Good sites are

Summary

If you want to get started with the RTC Java API’s, work through this blog post and the resources linked to it. I will try to extend this post with more detailed links for specific topics in the future.

As always I hope that helps users out there.

The Work Item Time Tracking API


Recently I was contacted by a colleague about how to work with work item time tracking, available in the Formal Project Management Process. This is an area of the work item API I had not touched so far. So I decided to have a look at it and share the result.

License

The post contains published code, so 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. I found a section relevant to source code at the and of the license.

Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee.

On the other hand, you have the code and are able to add your own code to it. It would be nice to know what you did and how, if you do so.

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 code attached to this post in the development environment you set up in the Rational Team Concert Extensions Workshop and get your own extensions or automation working there as well.

Download

The code is available for download here.

Work Item Time Tracking

Time Tracking can be enabled in projects based on the Formal Project Management Process. If enabled the user can no longer edit the time spent attribute of work items. Instead the user adds time sheet entries with hours worked on specific time codes on a special tab of the work item editor. The information will be summed up and the total work time will be automatically set in the work item’s time spent attribute.

The image below shows the time tracking data.

Time TrackingTab

The time spent attribute is read-only and calculated from the data above.

Time TrackingTime Spent

In certain circumstances it would be desirable to be able to access the data using the API. The question is, how is the data stored? How can it be read and, most important, how can it be updated using the API?

The Time Tracking API

There is a short explanation in the wiki about how the time tracking API works in general. However, this leaves out some very important details.

Time sheets are stored in RTC as special objects of type ITimeSheetEntry. These objects are associated to the work item they belong to using a special link type using the end point WorkItemEndPoints.WORK_TIME.

It is easy enough to follow the Wiki Entry and create a new time sheet entry.

However, I found out the hard way that it is not as easy to update an ITimeSheetEntry.

The reason is, that the work item API does not detect an update to a time sheet entry alone. So just updating the entry and then saving the work item will not work. The work item API will detect that no change has been done to the work item itself and not perform a save operation. Since the work item save also triggers the save of the dependent work item data, the save for the time sheet entry is also not performed. Adding a new relationship will also not work as you will end up with multiple relationships to the same item.

The only way to update the time sheet data is to force an update to the work item by setting the duration value. This way the API detects that it has to save the work item as well as the time sheet entries as dependent items.

The code I finally ended up with looks like below.

public void updateOrCreateTimeSheetEntry(WorkItemWorkingCopy workingCopy,
		ITimeCode timeCode, Timestamp startDateTimeStamp,
		Duration workDuration, Identifier workType,
		IProgressMonitor monitor) throws TeamRepositoryException {

	// set the active work item from the working copy
	setWorkItem(workingCopy.getWorkItem());

	// Find a matching time sheet if it exists.
	ITimeSheetEntry timeSheet = findTimeSheetEntry(timeCode,
			startDateTimeStamp, monitor);
	if (timeSheet == null) {
		// There is no time sheet for this entry
		// Create a new one and create the link
		timeSheet = createTimeSheet();
		workingCopy.getReferences()
				.add(WorkItemEndPoints.WORK_TIME,
						IReferenceFactory.INSTANCE
								.createReferenceToItem(timeSheet));
		// Add the entry to the map to hold the data
		addEntry(timeSheet, monitor);
	} else {
		// There is a time sheet, we need to update it
		// Get the workingCopy of the time sheet
		timeSheet = (ITimeSheetEntry) timeSheet.getWorkingCopy();
		// remove the time spent from current time
		setTotalDuration(new Duration(getTotalDuration().longValue()
				- timeSheet.getTimeSpent().longValue()));
	}

	// Set the new data
	timeSheet.setStartDate(startDateTimeStamp);
	timeSheet.setTimeCodeId(timeCode.getTimeCodeId());
	// TODO: If I leave this out it fails....
	timeSheet.setTimeCode(timeCode.getTimeCodeLabel());
	timeSheet.setTimeSpent(workDuration);
	timeSheet.setWorkType(workType);
	// add the new time back
	setTotalDuration(getTotalDuration().add(workDuration));
	// Update the value
	// Note: it is important to set the duration value, of the work item
	// otherwise the work item is not marked as dirty and in need to update
	// in the repository and the save process will not save the time sheet
	getWorkItem().setDuration(getTotalDuration().longValue());
	workingCopy.getDependentItems().add(timeSheet);
}

How does the code above work?

First the code tries to find a time sheet entry for the time code and the date that is given.

If it can not find a time sheet entry, a new entry is created and the required reference is also created and added to the work item references. This sets the work item to changed already.

If a time sheet entry can be found, the code gets a working copy that can be modified. The total duration of all the time spent on time sheets is reduced by the amount of time spent on that time sheet (as if the time sheet duration was set to zero).

The data on the tine sheet is then finally set to the current data. In addition the new over all duration is calculated and the result set to the work item’s duration.

Finally the time sheet is added to the work item’s dependent items. Updating the work items duration and adding the time sheet as dependent results in both being saved if the work item is saved.

A few remarks on the code.

  • I did this with RTC 4.0.1
  • For whatever reason the method setTimeCode() is deprecated. However, if I run the code against a 5.0.2 server and don’t use the method, the entry does not show up
  • To create the start date I use SimpleDateFornat and provide the data only down to the year, month and date, this way the entry is automatically created with a timezone and time that works for me
  • The work type is either provided and then used; otherwise the work item type is used as work type

If the start date is created with a wrong timezone and time, the entry might not show up in the UI. To fix that, look at the data used in your repository and change the creation of the start data accordingly.

The TimeTrackingHelper

To make it easier to use the code above, it is part of a class called TimeTrackingHelper. This helper class implements loading existing time sheet entries into a hash map structure that makes searching for the entry easier. The hash map should allow to iteratively use the helper on the same class if required. If the work item changes, the hash map is built up again.

The time tracking helper has code to convert text/string values to the elements needed for a time sheet. It also implements a pattern that allows to iterate the hash map and run an interface on each entry to print the data or do something else with it.

The example below shows how to use the helper in a WorkItemOperation to add a time sheet entry.

// TimeTracking
private static class ModifyTimeTracking extends WorkItemOperation {

	private String fTimeCode = null;
	private String fStartDate = null;
	private String fWorkHours = null;
	private String fWorkType;

	public ModifyTimeTracking(String timeCode, String startDate,
			String workHours, String workType) {
		super("Modify TimeTracking", IWorkItem.FULL_PROFILE);
		this.fTimeCode = timeCode;
		this.fStartDate = startDate;
		this.fWorkHours = workHours;
		this.fWorkType = workType;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		TimeTrackingHelper helper = new TimeTrackingHelper();
		helper.updateTimeTrackingInfo(workingCopy, fTimeCode, fStartDate,
				fWorkHours, fWorkType, monitor);
		helper.printTimeSheets(workingCopy.getWorkItem(), monitor);
	}
}

The interesting part is in the method execute(), where the helper is instantiated first and then used to update (or create) a time sheet.

The code below shows how the helper can be used to just print (or access) the time sheet data.

	int id = new Integer(idString).intValue();

	IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
			.getClientLibrary(IWorkItemClient.class);
	IWorkItem workItem = workItemClient.findWorkItemById(id,
			IWorkItem.FULL_PROFILE, monitor);
	System.out.println("Accessing work item: " + workItem.getId() + ".");
	TimeTrackingHelper helper = new TimeTrackingHelper();
	helper.printTimeSheets(workItem, monitor);
	System.out.println("Accessed work item: " + workItem.getId() + ".");

The code for the TimeTrackingHelper the example class to modify the time tracking data of a work item called ModifyWorkItemTimeTracking as well as the class to just print the data AccessTimeTracking is shipped with the download.

Summary

This post shows how time tracking data is managed using the API. As always I hope this helps someone out there to get their job done more efficient.

Understanding and Using the RTC Java Client API


A lot of users have expressed the need to better understand how to set up and develop against the Plain Java Client Libraries and the client API in general. This post continues my series of best practices around extending and goes deeper into the mysteries of the client API.

There was a nice Jazz.net article posted recently that is also a good source of information about the client API. However, there can never be enough examples, so I think this is useful. Also check the many links to other sources in the Interesting Links page.

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.

Additional RTC Work Item API Examples

See additional links at the end of the article.

The post A RTC WorkItem Command Line Version 2 contains downloadable code that performs most of the activities required for reading and modifying work items. The interesting code can be found in the com.ibm.js.team.workitem.commandline.helper package in the class WorkItemHelper. All techniques described below are used there. You can familiarize yourself with the concepts in this post and then look into that project for how it is used.

Getting Started With Developing for the Plain Java Client Libraries

If you want to develop for the Plain Java Client Libraries, I would suggest to follow this post and set up your development environment with the SDK and the Plain Java Client Libraries. A complete guidance is in Learning To Fly: Getting Started with the RTC Java API’s and the linked resources.

You should be able to use the following code in this environment and get your own automation or extension working.

Debugging Client Code and Finding Information about the API

It is absolutely critical that you have set up your development environment as described in Setting up Rational Team Concert for API Development. If you have not done that, you are severely limited in what you can see and do. You can not debug your code and therefore will be almost unable to do anything.

Prepare Your Project to Allow Debugging

As described in Setting up Rational Team Concert for API Development in the section Setting Up Your Workspace for Plain Java Development and Make the SDK Source Code Available for the Plain Java Clients Libraries, the Eclipse Plugin Development Environment together with the RTC SDK can help you with developing Plain Java Client Library and Client SDK code.

If you create a new project for new code, create a Plug-In Project as described and add the main plugins that contain the API you want to use. The following dependencies are a good starting point.

DependenciesThe version numbers will depend on your SDK. Keep in mind you really just use the plugin to make searching the API easier by tricking the PDE into believing you want to develop a plug in. Your code will later run as a plain Java class.

In addition to these plug-ins, make sure the PlainJavaApi is in the build path of the project. Add the library to the project. In the package explorer right click the project, select Build Path>Add Libraries… 

AddLibrary

In the wizard select User Library, click Next. Select PlainJavaApi and click Finish.

Select_PlainJava

Make sure the PlainJavaApi library is ordered as described in Setting up Rational Team Concert for API Development in the section Make the SDK Source Code Available for the Plain Java Clients Libraries.

OrderLibaries

Create a main class able to access the API and login

Then you create your main class, that provides the main() method to be called later.

You can just create a Java Class and let the wizard add the main() method for you. I usually use the same package structure I use to name my plugins. E.g com.ibm.js.team. and call my class according to its usage.

I usually start with code that I grabbed from the CreateWorkItem class presented in this Jazz.net wiki page. You can download the example code from the Wiki Page. A typical main() method in a standalone client API tool looks like this code.

public static void main(String[] args) {
    boolean result;
    TeamPlatform.startup();
    try {
        result = run(args);
    } catch (TeamRepositoryException x) {
        x.printStackTrace();
        result = false;
    } finally {
        TeamPlatform.shutdown();
    }
    if (!result)
       System.exit(1);
}

The code starts the TeamPlatforn and if that worked passes the parameters. I usually again use the example code mentioned above and call the run() method passing the arguments for processing. The run method usually starts like the code below:

private static boolean run(String[] args) throws TeamRepositoryException {
	if (args.length != 6) {
		System.out.println("Usage: CreateWorkItemApproval [repositoryURI] [userId] [password] [workItemID] [approverUserID] [approvalName]");
		System.out.println(" [approverUserID] for example 'bob' ");
		System.out.println(" [approvalName] for example 'Code Review' ");
		return false;
	}

	String repositoryURI = args[0];
	String userId = args[1];
	String password = args[2];
	String workItemID = args[3];
	String approverUserID = args[4];
	String approvalName = args[5];

The code gets the required information such as the repository URI, the user id and the password to log in from the arguments. Then it uses this information to log into the repository. Typical code for this looks like below.

	ITeamRepository teamRepository = TeamPlatform
		.getTeamRepositoryService().getTeamRepository(repositoryURI);
	teamRepository.registerLoginHandler(new LoginHandler(userId, password));
	teamRepository.login(monitor);

Once you have successfully logged into the repository, you need the API entry points to actually do something with objects accessible to the API.

The LoginHandler looks as follows:

private static class LoginHandler implements ILoginHandler, ILoginInfo {

	private String fUserId;
	private String fPassword;

	private LoginHandler(String userId, String password) {
		fUserId = userId;
		fPassword = password;
	}

	public String getUserId() {
		return fUserId;
	}

	public String getPassword() {
		return fPassword;
	}

	public ILoginInfo challenge(ITeamRepository repository) {
		return this;
	}
}

Getting Required Client Libraries

You need Client Libraries to access any interesting client API in RTC. In the client API you use the ITeamRepository.getClientLibrary() call to get the client libraries that you need. You can find some examples in the code below. Getting a Client Library uses singletons so the operation should be fast.

	IProcessClientService processClient = (IProcessClientService) teamRepository
			.getClientLibrary(IProcessClientService.class);
	IAuditableClient auditableClient = (IAuditableClient) teamRepository
			.getClientLibrary(IAuditableClient.class);
	IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
			.getClientLibrary(IWorkItemClient.class);

Common API

There is some common API available that can be used in the client and the server. This API usually has “Common” in the name. Examples are IAuditableCommon, IWorkItemCommon, IProcessCommon and some other classes. The namespace or package name always contains “common” as well, for example com.ibm.team.process.common.IProjectArea. It is a good idea to look into these first, because code created with them can be reused on the server as well.

Finding Information About the API

The next question is probably, how do you know which Client Libraries are available and what they are for.

Lets look at what the ones above do first. If you have set up your development environment according to the previous post and got the order of the plugin dependencies and the Plain Java Api library right, you can simply position the mouse pointer over one of the interfaces and browse the information available like below.

BrowseDocumentation

Please note: if you don’t see information, documentation or code while you are following this, make sure that the dependency for the package is added to the plugin.xml as described above. If it is and you still don’t get the source, make sure that the Plug-in Dependencies are above the user Library for the Plain Java Client Libraries PlainJavaApi in the list on the Order and Export tab of the build path as described at the end of the Plain Java Client Libraries section of this post. There are some few classes, where even this might not work, but it is typically nothing you would be interested in seeing anyway.

You can focus on the hover to browse more or follow the links. If you are interested in the methods available on the class, you can just point your mouse pointer on the class or interface and open its Declaration as shown below. You can also open the declaration from the hover, if you point to a class, new actions become available at the window.

OpenDeclaration

You can open the Type Hierarchy and even more. If you are interested in how that class or interface is used by the clients, just  browse the references in the Workspace.

BrowseReferencesIf you do this, the PDE searches the SDK for where the class or interface is being used. This will take a moment, so be patient. You are rewarded with a search window that shows locations where whatever you search for is being used.

BrowseTheSearchHitsYou can use the up and down arrow to navigate. The Eclipse Editor will show you the file and the code around the places the class of interest is used.

Try that with the IWorkItemClient. Notice that it extends IWorkItemCommon and open the declaration of IWorkItemCommon. Search for references and browse some of the code. Locate the findWorkItemById() method in the declaration and double click on it. The code and documentation is opened in a Java editor. You can use the same technique to find references as you used before.

Often searching for all the references just provides too many hits. It is easy to narrow down your search. Just select the item of interest such as the findWorkItemById() method and click on the Search menu item in the Main Menu.

BetterSearchSelect the menu item Java…

JavaSearch

You can search for what you are interested in, methods, types, match locations. You can also type some text for example *License, if you are looking for a Type and search the SDK for it. This is interesting if you don’t know what is there, but think it is related to, in this example License management.

If you search for types the match location really comes into play. You can narrow down the search for what you are interested in. As an example mark the parameter type ItemProfile in the method above. Open the search window for Java Search for it (use copy/paste if you want and paste the text into the search field, if you like).

TypeArgumentSearch

Select the Type Arguments for example or anything you are interested to look for, hit OK and search. Now you have way less hits. This way you can find any information about the API that you are interested in.

Finding the Client Libraries

At this point you should be able to answer the question how you can find the available client libraries. Just click on the getClientLibrary() method of ITeamRepository and search for the references and you get many hits that you can easily browse. Build up a list and try to understand the name patterns.

You can also look into the Plain Java Client Libraries JavaDoc for the supported API. If you search the SDK, you will find a lot more client libraries – which you can use, even if they are not supported.

Getting at Data in the API

If you just start with the API, the next issue is how to get at data in the API and how to work with it. The data is not just there, you have to find it based on some criteria and get your hands on it.

Lets assume a simple example. You call your new automation Plain Java Client Library Class and provide the ID of a work item to modify somehow, in addition to the user ID, Password and whatever other information you need to pass it.

Now you need to find the work item based on the ID, which you get as string. The code below shows how that looks like:

IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
	.getClientLibrary(IWorkItemClient.class);

int id = new Integer(idString).intValue();	
IWorkItem workItem = workItemClient.findWorkItemById(id, IWorkItem.FULL_PROFILE, monitor);

What does the code do? First it gets a client library of Type IWorkItemClient that provides a method to find work items by ID.  Then it converts the string with the ID into an integer that can be used to be passed to it. It then calls the find Method and provides additional parameters. Finally it gets the work item.

Different Client Libraries are responsible for different parts of the API. They typically provide methods to locate and find items in the API that are related to their domain. The trick is to find them. Well, consider the previous sections and search for them. You will quickly understand the patterns.

This is pretty typical and actually rather simple code to get at data in the API. It is simple because you often don’t get the real object, but a handle. We will talk about how to work with those handles later.

Item Profiles for Loading

Because the data is stored in a database, you need to use client libraries to retrieve the data. Since the data for things like work items can be of a considerable size and you often don’t need all the data, you provide an ItemProfile for the thing you are trying to load. The ItemProfile determines the properties you are interested in and that need to be retrieved from the persistency layer.

IWorkItem provides several ItemProfiles that determine, which properties are provided for the Item when loaded. IWorkItem.FULL_PROFILE loads everything. If you are only interested in the summary, you might want to limit that. IWorkItem.SMALL_PROFILE only loads the basic properties. You can use several of the provided item profiles. If you look up the code for the item profiles, you can create load profiles yourself.

The code below creates a new ItemProfile for a work item based on the small profile and requests to also load the “Planned For” attribute.

ItemProfile loadProfile = IWorkItem.SMALL_PROFILE.createExtension(
	Arrays.asList(new String[] { IWorkItem.TARGET_PROPERTY }));

IWorkItem also provides a list of definitions for the built in properties (attributes and other things) of a work item.

Not all items provide you with predefined ItemProfiles to load data as easy as workitems do. Sometimes you have to create your own item profile. Any IItem type has a static ITEM_TYPE defined in its interface. You can use that to create an Item Profile. The code below shows how to create an ItemProfile from a type.

ItemProfile.createFullProfile(IProjectArea.ITEM_TYPE)

Expressions and Queries to find WorkItems

You can use queries that are already available or use the API to create expressions to find work items by special criteria.

Progress Monitors

Some API operations are long operations. They require an IProgressMonitor that is used in the UI to show progress to the user (and to cancel operations). In some contexts you are provided with a monitor by the UI. In others like plain Java Client Libraries classes called from the command line you don’t have one. You can typically pass a null, but I find it cleaner to use org.eclipse.core.runtime.NullProgressMonitor instead.

	IProgressMonitor monitor = new NullProgressMonitor();

The Plain Java Client Libraries define a SysoutProgressMonitor you can also use. If you are smart, you hook up a logging library with your own implementation.

Resolving Item Handles

As mentioned above you don’t always get the item, but a handle of type IItemHandle to an item or even a list of handles. The code below is an example.

URI uri = URI.create(projectAreaName.replaceAll(" ", "%20"));
IProjectArea projectArea = (IProjectArea) processClient
	.findProcessArea(uri, null, monitor);
IContributorHandle[] contributors = projectArea.getMembers();

Now you have your handle you need to get at the data. Regardless of what IItemHandle you have retrieved, in the Client API you can typically get the IItemManager implementation from the ITeamRepository class. You can use the IItemManager to resolve or fetch an item. The code below shows an example.

ITeamArea teamArea = (ITeamArea) teamRepository.itemManager()
	.fetchCompleteItem(handle, IItemManager.REFRESH, monitor);

The client API uses caching to optimize speed. You have to tell the ItemManager if you want to resolve using the cached data (if available), or if you explicitly want to refresh the cache, because the item might be modified elsewhere.

IItemManager.DEFAULT and IItemManager.REFRESH are the flags provided that I usually use.

After fetching the Item, you can cast it to the Type you wanted to resolve.

Casting

Regardless whether you read a value or write a value, you usually can’t just use the value. You need to cast the object you get to something you can use. A String, an Int, an enumeration literal, or for more complex itens to an ItemHandle and resolve the handle to be able to access the data.

If you want to set a value, you have to make sure that the Object you pass conforms to the attribute type as well.

It is very typical that you get an object back and have to cast it to something you can use. It is easy enough to use the debugger to find out what the object really is, if in doubt. I usually use instanceof to be sure.

if (workItem.hasCustomAttribute(customString)){
	Object value = workItem.getValue(customString);
	if (value instanceof String) {
		String attributeValue = (String) value;
		// do something with the value
	}
}

ClassCast Exceptions

If your data does not conform to the value that is returned or expected, your code will throw a ClassCastException. Use the debugging capabilities to understand what the Object is that you get back.

IllegalArgument Exception

This usually happens similar to ClassCastException if you pass a wrong class or object to a method. You don’t get a ClassCastException because the code has some built in protection and does not try to blindly cast.

In every case I have seen so far, it was always my fault. Check what type the method wants and try to figure out how to get it. Prominent examples are just passing strings with iteration, timeline, project area, contributor and other objects names. This will not work. In most of the cases you have to use a service or client library to look up these objects by their name or ID or whatever and resolve the result to pass it along.

Get the TeamRepository from an Item

Sometimes you receive IItems and need to understand where they came from, or you just don’t have an ITeamRepository object and need it to do additional work. IItems in the client API provide you with the method getOrigin() that allows you to access the TeamRepository of the Item. The code below shows how that allows you to get at that information.

ITeamRepository repo = (ITeamRepository) teamArea.getOrigin();

Also see the next section how to get the team repository if you only have a UUID.

UUID’s

Internally items in the repository are represented with unique UUID’s. In some cases it is easy to get the UUID. E.g. you can get the UUID from the item handle.

UUID itemUuid = itemHandle.IItemHandle.getItemId();

It is easy to get the teamRepository from the UUID

ITeamRepository repo = TeamPlatform.getTeamRepositoryService().getTeamRepository(itemUuid);

It is also desirable to get the Item from it. The code below helps here:

IItemHandle handle = [itemtype].ITEM_TYPE.createItemHandle(UUID.valueOf(uuid_string), null);

Where [itemtype] is one of the existing IAuditable classes such as IWorkItem, IQueryDescriptor, IBuildResult and so forth. The uuid_string would be the string representing the UUID. Once the handle is retrieved it can be resolved as described above.

This can be done if the type is known. One example is in build automation, where you would have the ID of a build result as text. The example below shows how to get the handle from a string with the UUID.

IBuildResultHandle resultHandle = (IBuildResultHandle) IBuildResult.ITEM_TYPE
			.createItemHandle(UUID.valueOf(buildResultID), null);

You can find the code for the full example here: Automated Build Output Management Using the Plain Java Client Libraries.

Getting the API in an Eclipse Plugin

If you are extending the Eclipse based clients you might be logged in already. So you don’t want to have to login again, but use the user context available.

If you have an IItem for example from a ISelection, you can use getOrigin() as desccribed above to get the TeamRepository.

Otherwise you can just use the TeamPlatform to get an entry to get at data, because it should be available and you should be logged in. The code below shows an example.

	// Find the repositories
	ITeamRepository[] repos = TeamPlatform.getTeamRepositoryService().getTeamRepositories();
	for (ITeamRepository iTeamRepository : repos) {
		String user = iTeamRepository.getUserId();
		System.out.println("User: " + user);
	}

Creating and Modifying Items

Update: See the next topic about a better method for work items.

If you have an item, retrieved as described above, that does not mean you can actually modify it. So far you can only read its data and act upon it. If you want to modify data you might need to get a special object that allows you to modify the item. It is typically called a WorkingCopy. For work items for example, you need a WorkItemWorkingCopy. The code below shows how to get one and how to save the change.

IWorkItemWorkingCopyManager copyManager = workItemClient.getWorkItemWorkingCopyManager();

try {
	copyManager.connect(wi, IWorkItem.FULL_PROFILE, monitor);
	WorkItemWorkingCopy wc = copyManager.getWorkingCopy(wi);

	// TODO: Do something to the work item.

	status = wc.save(monitor);
	if (status.getCode() != org.eclipse.core.runtime.IStatus.OK) {
		return "Error: " + status.getDetails();
	}
} catch (Exception e) {
	return "Unexpected error: " + e.getMessage();
} finally {
	copyManager.disconnect(wi);
}

How do you know if you need a WorkingCopy? There does not seem to be a rule about this. It depends on the part of the API. Try it out, debug it and look for exceptions. If you run into the situation where you seem to be unable to modify the data you want, search the SDK for examples. There are different ways to create modify and save items, dependent on the type. You might have to search the SDK to find a method for the particular object you are dealing with.

Alternative to Create and Modify Work Items

I rarely use a WorkitemWorkingCopyManager these days, instead I wrap my changes to work items into a class implementing com.ibm.team.workitem.client.WorkItemOperation the WorkItemOperation as described in this Wiki page about programmatic work item creation and this and other posts on this blog. I found this the best way to create and modify work items in the client API.

You can use a WorkItemOperation for both creating and updating work items. You pass the parameters to the constructor and override the execute method. If you want to use the operation to update an existing work item make sure to provide an Item Profile in the super() call in the constructor.

The code below modifies a work item and adds an approval.

private static class WorkItemCreateApproval extends WorkItemOperation {

	private String fContributorUserID;
	private String fApprovalName;
	public WorkItemCreateApproval( String contributorUserID, String approvalName) {
		super("Create Approval",IWorkItem.FULL_PROFILE);
		fContributorUserID=contributorUserID;
		fApprovalName=approvalName;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		IWorkItem workItem = workingCopy.getWorkItem();
		// TODO: Do something with the work item here
		ITeamRepository repo = (ITeamRepository)workItem.getOrigin();
            // Find a contributor based on the ID
            IContributor aUser = repo.contributorManager()
                    .fetchContributorByUserId(fContributorUserID, null);
            // Find a contributor based on the login information
            IContributor loggedIn = repo.loggedInContributor();
            ArrayList reviewers = new ArrayList();
            reviewers.add(loggedIn);
            reviewers.add(aUser);

            // Create a new approval and add the approvers
            IApprovals approvals = workItem.getApprovals();
            IApprovalDescriptor descriptor = approvals.createDescriptor(
                    WorkItemApprovals.REVIEW_TYPE.getIdentifier(),
                    fApprovalName);
            for (IContributorHandle reviewer : reviewers) {
                IApproval approval = approvals.createApproval(descriptor,
                        reviewer);
                // Approve @See https://jazz.net/library/article/1118/
                approvals.add(approval);
	}
}

If you use a WorkItemOperation, you can later call the operation like in the code below:

WorkItemCreateApproval operation = new WorkItemCreateApproval(approverUserID, approvalName);
operation.run(workItem, monitor);

The code below creates a work item and sets some values and is basically grabbed from the Wiki page about programmatic work item creation.

private static class WorkItemInitialization extends WorkItemOperation {

	private String fSummary;
	private ICategoryHandle fCategory;

	public WorkItemInitialization(String summary, ICategoryHandle category) {
		super("Initializing Work Item");
		fSummary = summary;
		fCategory = category;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		IWorkItem workItem = workingCopy.getWorkItem();
		workItem.setHTMLSummary(XMLString.createFromPlainText(fSummary));
		workItem.setHTMLDescription(XMLString.createFromPlainText(fSummary));
		workItem.setCategory(fCategory);
			
		List tags = workItem.getTags2();
		tags.add("NewTag");
		workItem.setTags2(tags);
	}
}

The code gets called as shown below

WorkItemInitialization operation = new WorkItemInitialization(summary+" No: "+i, category);
IWorkItemHandle handle = operation.run(workItemType, monitor);

The difference between modifying and creating is in the call to the operation.

  • To modify the work item you pass the item or its handle
  • To create a work item you pass the type of the work item to be created

In both cases you can provide a load profile in the call to super(). If you modify the work item you must provide a load profile.

Working With Work Items Attributes

Please see Working with Work Item Attributes for more examples and how to find even more.

Accessing Work Item Attributes

If you use an Item Profile to load a work item, you typically intent to access its attributes. Please be aware that the work item itself has only very few methods to access the basic data of the work item. To access most of the attributes you need an IAttribute interface first. There are several ways to achieve that. You can look them up if you have the ID.

If you know the work item ID, it is possible to find the work item form the string value.

IWorkItemCommon workItemCommon = (IWorkItemCommon ) fTeamRepository.getClientLibrary(IWorkItemCommon.class);
IAttribute attribute = workItemCommon.findAttribute(projectArea, attributeID,
				getMonitor());
if(attribute != null){
	// Do something with it.
}

Or you can get a list of all the attributes that are built in.

List builtIn = workItemClient.findBuiltInAttributes(projectArea, monitor);

You can get the custom attributes from the work item itself.

List attribs = workitem.getCustomAttributes();

Please be aware it is a good idea to look for the ID’s if you look up attributes, if you do fuzzy matches by name, you might end up using the wrong attribute.

You can find the atribute ID in the Web Project Area Administration UI in work item types and attributes.

WebUI_AttribID2016-04-25_16-41-46

Once you have the IAttribute, you can use that to get the value from the work item. See Working with Work Item Attributes to get more information. You have to cast to the correct types to use the values and when you set values, you have to provide the right types too. Here in the Jazz.net Forum is a nice example that outputs all data of a work item provided as an answer by Sam.

Here one example:

if (workItem.hasCustomAttribute(customString)){
	Object value = workItem.getValue(customString);
	if (value instanceof String) {
		String attributeValue = (String) value;
		// do something with the value
	}
}

Enumerations and some other built in attribute types are a bit special. See the post about working with enumerations, the post about working with iterations and the post about working with work flow states and actions for more details.

WorkItem Data not Based on Attributes

Not all work item data is based on attributes. Most popular are subscriptions, attachments, approvals and Links.

Links between work items are special. See this post with some code that can be used to link work items and other elements. Also see the server link API post for some special cases you want to consider in the client too.

Stale Data Exceptions

I saved a work item and now I have a stale data exception. Why? Well, the reason for that is that someone changed the work item after you resolved it. It is always a good idea to resolve a work item short before you want to do an update. We will see this again in the server API.

ClassCast Exceptions

Regardless whether you read a value or write a value, you usually can’t just use that value. You need to cast the object you get to something you can use. A String, an Int, an enumeration literal, or for more complex itens to an IItemHandle and resolve the handle to be able to access the data.

If you want to set a value, you have to make sure that the Object you pass conforms to the parameter type as well.

If your data does not conform to the value that is returned or expected, your code will throw a ClassCastException.

ImmutableProperty Exceptions

As mentioned above, many objects can not just be changed. You need a WorkingCopy to do so. There are different ways how to get a working copy for different objects.

Null Pointer and Other Exceptions

I get exceptions, the API is broken!

My experience so far is, that it was usually my fault. I didn’t understand the API, failed to resolve another item or made wrong assumptions that led to exceptions. The best approach is to really look into all parameters you pass. Also check if the client libraries you intent to get using the getClientLibraries() really return. One example where you won’t get it that way is the is the IContributorManager, which you don’t get using this call, but using ITeamRepository.getContributorManager(). If you try to get the Client Library it will return null.

Permissions and Read Access

In the last releases more and more API is added to handle read restrictions to items. Make sure that you use the permission aware API like in this blog post or in this Jazz.net article. If you don’t you might have no access to data and even not recognize that this is the fact. See some permission aware API from the Defect178748Snippet.java that ships with the plain Java client libraries below.

private static void processSuspectWorkItems(ArrayList suspectWorkItems, boolean [] hasWarnings, ITeamRepository repo, IProgressMonitor monitor)
       throws TeamRepositoryException {
   IFetchResult result = repo.itemManager().fetchCompleteItemsPermissionAware(suspectWorkItems, IItemManager.DEFAULT, monitor);
   for (Object o : result.getRetrievedItems()) {
       IWorkItem wi = (IWorkItem)o;
       System.out.println("Found suspect workitem " + wi.getId() + ": " + wi.getHTMLSummary().getPlainText());
   }
   if (!result.getNotFoundItems().isEmpty()) {
       hasWarnings[0] = true;
   }
   if (!result.getPermissionDeniedItems().isEmpty()) {
       hasWarnings[1] = true;
   }
}

The permission aware API returns an IFetchResult that allows you to access enough data to decide if you didn’t have access to some items.

List retrieved = result.getRetrievedItems();
List permissionDenied = result.getPermissionDeniedItems();
List missingItems = result.getMissingItems();
List notFoundItems = result.getNotFoundItems();
// Do something with each list.

Operational Behavior, Permissions and Write Access

Please be aware that operational behavior permissions and write access also work in the client API. If you are not allowed to save a work item in a state, because there are required attributes, you will not be able to do so using the API either. So you have to take the permissions and other restrictions into consideration. Potentially you need to create a special role for an automation user, to override preconditions and give the correct permission.

Currently some modification that are only governed by presentations, for example work item editor presentations might be possible with the API and not throw errors. Don’t count on this not going to change.

Debugging and Calling your Code

I find it essential to run and debug the code I write often, especially if I don’t know the area of the API I am dealing with by heart. Since I don’t know any area by heart, I debug early and often 8-). You should do that too.

You should set a break-point into your code at the beginning of an interesting section.

There are some tricks, that can make it easier for you. If you use a main operation like above, you can create a debug configuration and store it in your project along with the client you are writing.

First create a folder called Launches in the project you are developing. This folder will contain your debug and run configurations. You can put it under SCM and share it with others.

Then go into the debugging perspective and open the Debug Configurations editor for example using the Run menu item.

The Debug Configurations editor should come up and allow you to create a configuration like below. Well, yours is probably empty at the beginning.

DebugConfigurationsAdd a new configuration. If you develop a stand alone command line client add a new launch for a Java Application. If you develop an Eclipse Client extension I would encourage you to start with a main method and a java Application Launch too, if at all possible. It saves you to create an Eclipse^2 launch and launching a debug Eclipse every time. You can wrap your code into an extension later. In any case the trick with saving the launch works for all launches.

First configure your project and main class.

Launch_1Next provide the arguments on the Arguments tab.

Launch_2Last step is to go to the Common tab. Select Shared file  and select your Launches folder to store the launch.

Launch_3

Push the Apply button and you saved the launch. Press the Debug button to start debugging.

You can find your Launch as file with the extension .launch in your launches folder. If you forgot to save it that way in the past, you can still change that later, by following the description for the Common tab.

The Launches will show up in the Debug Configuration section as soon as you have a project open with launches. so you can always manage and use your launches on different machines if they are available in their folder in a project. If you Use SCM you will automatically get them with the code for your project.

Run From Command Line

You can run from a command line as described in the README.TXT file you can find in the snippets folder in the Plain Java Client Libraries. What works for me is the command line call as follows:

rem 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"

This is for Windows and assumes you have JAVA_HOME set and the Plain Java Client Libraries installed at the given location. It also assumes your source code is in a sub folder src/ and the binaries are located in a sub folder bin/ as they are in Eclipse projects. It also assumes the Java code is compiled. Please make sure there is a lib/ext folder available in the JRE.

If you create a jar file call as follows:

rem 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 ./lib/;C:/RTC403Dev/installs/PlainJavaAPI -jar ModifyWorkItemAddCommentOperation.jar "https://clm.example.com:9443/ccm" "ralph" "ralph" "54" "Add a comment"

It is easy enough to modify this for Unix like operating systems by replacing ; by : in the -Djava.ext.dirs statement, fixing the usage of the environment variable and the paths.

Have a Test Repository

Did I mention, you should have a test repository to run your tests against? You should in any case test against some local test repository.

Download the Code

Some example code can be downloaded from DropBox. Please note, there might be restrictions to access DropBox and the code in your company or download location.

Related Posts

Performance

Please look at the post Boost Your Automation Performance Using an Automation Server for considerations if you run the automation a lot.

Summary

You should now be up and running for developing Eclipse Client or Plain Java Client Libraries standalone applications. You should know where and how to find the API and have the minimum set of information needed to get started without stalling on the second line of code. As always, I hope this post saves users out there some time – or is at least fun to read.

What API’s are Available for RTC and What Can You Extend?


This post is about what is available as API for RTC. I will try to provide you with some information about the available API’s. It was confusing to me in the beginning, so I assume others face the same problem.

This post assumes you already read the post about Setting up Rational Team Concert for API Development. Learning to Fly contains references to the most important posts for beginners. Please also consider to look into the Interesting Links page for more examples and downloads.You should understand the RTC Process Fundamentals if you want to do any extension or automation.

The post RTC Process Customization – What you can and cannot do is also a great resource to understand the basics.

The first API’s are only mentioned for completeness. They are not covered in this series of posts, but might be in the future.

REST and OSLC APIs

RTC provides some REST API’s that can be used to access and manipulate data. It also provides OSLC based APIs to access some of its data. See Consuming Rational Team Concert’s OSLC Change Management V2 Services to get started. See the Open Services for Lifecycle Collaboration Workshop and Using Perl to access the JAZZ REST API as an entry point. See Lyo OSLC 4J as a potential framework to access OSLC and REST APIs. This blog post provides you with a different browser extension to use for creating REST and OSLC URL’s.

JavaScript API

RTC provides a limited JavaScript API that can be used for Attribute Customization. The JavaScript API is very limited and allows only to access attribute data in the context of the work item the attribute customization runs. See this blog post or go directly to the Process Enactment Workshop for the Rational solution for Collaborative Lifecycle Management 2012 for some examples.

Relevant links

Available Java APIs

There are really two Java API’s available in RTC, and although they have much in common, there are differences that are important to understand if you want to use them. The following API’s are available

  • The Server API that is used in the server part of RTC (and JTS)
  • The Client API that is used by the Eclipse Client and the Plain Java Client Libraries

RTC Server API

The RTC Server API is used in extensions to RTC that are deployed on the server. Examples for these kind of extensions are Operation Advisors, Operation Participants, Server Tasks and other extensions that are run on the server. The RTC Server API is shipped with the RTC SDK that you set up to have an environment for development. This API is typically found in packages called com.ibm.team.*.service and the interfaces are usually named *Service or *Server for example IWorkItemServer.

This blog has several examples for how to extend RTC using the server API. For example

RTC Common API

Parts of the API are available on the client and on the server. The way you get the API depends on if you have a client or a server extension, however, the services are available as client libraries as well as as services. This API is typically found in packages called com.ibm.team.*.common and the interfaces are usually named *Common for example IWorkItemCommon. In some cases the required services are provided by the extension mechanism. This allows to write some extensions that run on the server, as well as on the client. A good example are Attribute Customization providers. You can find examples in these posts

RTC Client API

The RTC Client API is used in Extensions to any of the Eclipse based RTC clients as well as in the RTC Plain Java Client Libraries. This API is typically found in packages called com.ibm.team.*.client and the interfaces are usually named *Client for example IWorkItemClient. The RTC Client API is shipped with the RTC SDK and partially shipped with the Plain Java Client Libraries.

What? The Client API is shipped twice? Why would anyone do that?

RTC Client API

The reason for shipping the client API in the RTC SDK and the Plain Java Client Libraries is really simple. You need it in the SDK to develop extensions for the Eclipse based RTC clients. The SDK provides you with the source code and allows to search the code in the Eclipse Plugin Development Environment PDE. Strictly speaking, you would not need the source code to develop the extensions, because the Eclipse client has the relevant parts of the API already installed as plug in’s and that is enough to be able to write extensions. However, it makes development so much easier. The RTC (client) SDK also ships some internal classes and tests that you can use to find examples for the API usage.

Plain Java Client Libraries

If you just want to run a small Java application to do some automation outside of the Eclipse client, you would need only the JAR files for the API, and some JAR files that it depends upon. It would be quite some effort to identify the JAR files in the Eclipse client you need. So the Plain Java Client Libraries  has the required JAR files bundled for you to use. This makes setting up a Plain Java Client application you developed much easier.

The Plain Java Client Libraries ship only the documented and public API. They are missing a lot of RTC Eclipse client specific code.

The post A RTC WorkItem Command Line Version 2 provides an extensive example for using the API to create and modify work items.

Extending Eclipse based Clients

When mentioning you can develop Extensions to the Eclipse based RTC Clients, that was no typo. The Jazz SCM Command Line as well as the Jazz Build Engine and Toolkit are also Eclipse based clients that can be extended. You can find an example for extending the Jazz Build  Toolkit in this post from Robin. The Eclipse Extension mechanism works for all Eclipse based clients.

There are several posts on this blog that talk about the client API. For Example

What is the Difference Between Client And Server API

Although you can often reuse the client API some things are different in the client and the server API so you need to be careful. The Services and Client Libraries have differences in how they are called and what they provide. Creating, getting and manipulating elements is sometimes slightly different in both API’s and you might need data on the server that you don’t have on the client and the other way round.

What can you Extend and Access?

The Server API allows to extend

  • The RTC Server with operational behavior, event handling, jobs and the like.

The Client API allows to extend

  • The Jazz  Build Engine and the Build System Toolkit
  • The SCM Command Line
  • The RTC Eclipse client
  • Potentially any other Eclipse based client

The Client API allows you to access

  • The RTC server and the data in its repository
  • Parts of the JTS server, for example the User and License management

What can’t you extend?

As far as I know you can not extend the Visual Studio Client, nor the Windows Shell, because both are not Eclipse based.

The available Extension Points and Operation ID’s are explained here.

Why is the API Designed the Way it is?

If you start working against the API, you will realize that, from a human perspective, sometimes things appear overly complicated. For example if you work with enumerations, you need to look up all the literals to find one that has the same Label value as your string you want to use to set it.

My personal view on this is, the API is a byproduct of what needed to be developed to design clients and servers. In the client for example, you will typically have views, that need to show more than one element. Or you have drop down boxes to select enumeration values. This just requires to get at all the data such as literals, project areas etc. needed to be displayed in that context. There is just no need to be able to provide a method that takes some string and looks up an item that has a display value that matches that string.

If you want it to be easier, you can always wrap pieces of code up to better fulfill your requirements and reuse that code.

Don’t Mix Client and Server API

You need to be really careful, to understand what client API is and what server API. Client API usually has client in the package name and you get the services with getClientLibrary() calls. Server code usually has server in the package name and you get services using gerService() from the AbstractService type you extend.

There are some packages with common in the package name, and contain common code that can be used on client and server. Still, you would get the common classes using getService() on the Server and the analogous Client Library with getClientLibrary() on the client.

If you mix the Client and the Server API in a server extension, you might find it works in Jetty – because that potentially has the client and the server part available in the SDK. But once you deploy the  extension on the server it will not load because it can’t find the required bundles. The same applies for client extensions.

The next post will go into the details of the client API.

Setting up Rational Team Concert for API Development


There seem to be still a lot of questions around how to develop against the RTC API. I will try to cover the best practices I developed over the years in some posts on this Blog over the next few months. I will try to cover at least the following topics over time:

This post will provide some guidance on how to set up your development environment for SDK and Plain Java development. But before we look into that, some information on the general challenges involved with doing this kind of development.

If you want to skip the valuable introduction that follows, skip to “Getting Started”.

Preface

“All hope abandon ye who enter here”

Well, it is not that bad, really. Developing automation and extensions can be loads of fun and a very rewarding task. However, I have learned the hard way, that it can also be very challenging and I would like to use this section to set, or correct, some expectations and make some general statements about the domain you are going to enter, should you decide to continue your quest.

The SDK and the Plain Java Client Libraries are an Eclipse Framework based Java API. To work against it, will require Java skills. That should not stop you from trying, if you are only a Java beginner, but you should be aware that you might have to dig into how to do certain things in Java, which will cost time.

Because RTC and Jazz is based on Eclipse, even on the server, you will be exposed to a lot of the Eclipse extensibility and programming model. A lot of advanced frameworks and APIs are used underneath the API you will use. These frameworks, EMF as an example, are very performant and versatile. On the other hand they are also using advanced programming patterns and models. Therefore they are often very abstract and it is sometimes hard to understand the relationships of objects.

Especially EMF is far more abstract as PoJo’s. EMF provides capabilities to generate code for domain models from abstract models such as UML. The input model allows to describe objects, their attributes and the relationship between these objects. The code generated is very efficient and clearly separates model and implementation. It uses generated interfaces to represent model objects and generated classes that represent the implementation of the objects. Using Interfaces allows implementing multiple interfaces and multiple inheritance. This makes it sometimes hard to understand the structure and the purpose of objects, how they relate and how to get from one to another. Knowledge about EMF is not required, but will help to deal with finding around in the API.

In addition to using EMF, Jazz/RTC also uses a database to store its information. This means you have to look up data using specific API. To increase performance it does also not always provide you with all the related data, but with handles that you have to resolve. One example is work item attributes. A work item needs to be found and resolved from a handle. It provides you with only very few methods to access its basic data such as its ID. If you want to access more attributes, you have to look up the attribute interface for the specific attribute ID first and then use the information found to access the value on the work item. This means you will have to find and understand a process with several steps to get at the data and the services needed to get it.

If you intent to create client or server extensions, you should have at least a basic understanding of the how Eclipse Extension Points and Extensions work. It is possible to to do this without any knowledge, but it is not easy. If you don’t have experience with Eclipse extensions, I would suggest to get a book about how that works and how to develop them. There are several on the market and they will make things easier for you over time. Eclipse extensions use a declarative approach as well as Java code. Without any background it is easy to get into a situation where things don’t work and the reason is just a mystery. This is especially true when working with the plug-in XML and during deployment of plug ins.

RTC covers a lot of ground. It supports managing Work Items, planning with them, provides SCM, Build and process enactment. The process component allows modeling projects, teams, timelines, roles and ties the other capabilities together. This means a lot of capabilities and a lot of API. Even if there are areas where there is no official API yet, like for planning.

If you get started, it will feel basically like being stuck in your village 1000 or more years ago, with no map and the task to explore and conquer the world. At least that is how it felt (and still feels) to me. So the best you can do in the beginning, is to look at your task and find some information on how to get started.

Other than 1000 years before, nowadays we have a pretty global village and ways to get at information that were not available even half a century ago. So you can search the internet for examples that have an overlap with what you are up to. You can ask in the Forum and build up your network. If you ask in the Forum make sure to tag your question with the tag extending. If you are lucky and find information, you will have to find the missing pieces and discover new areas. Sometimes you will have to cross seas or chasms to explore what lies beyond. Over time you will create yourself a map of the world that is more or less detailed and more or less accurate. It will most likely have white areas and places described with “Here be Dragons”. So prepare yourself for exploring new areas every time you want to do something different. Just be aware you probably haven’t been everywhere yet and you might not know the customs of the other areas. Exploration is hard and sometimes dangerous. Keep reminding you of this fact. Be prepared for a rough ride with great rewarding discoveries on the way.

If there would be a complete map of this world, I would be happy to share it. As it is, the best I can do is share what I, and others, have explored. I and your other fellow explorers would be really happy if you share information on areas you discovered to make it easier for the ones that follow in our steps. This blog provides you with examples and links to blogs of other explorers in this realm that are sharing pieces of the map discovered so far. I will try to maintain a link page with pieces of the map other explorers have found.

Getting Started

The natural starting point is how to set up your environment for development. Being able to set up a development environment for the RTC SDK and the Plain Java Client Libraries still seems to be a hurdle when starting developing against the RTC API. So lets have a look how that works. For the books, I always wanted to share this information, but there were always more pressing matters. My apologies.

Setting up the RTC SDK

Regardless if you want to develop client or server extensions or if you just want to work against the Plain Java Client Libraries, from my experience you should first set up a RTC SDK based development environment. As you will see later on, what you will gain is worth the investment.

Follow the Rational Team Concert Extensions Workshop Lab 1 (See the beginning of the post for newer versions) as described below, to perform this task. I don’t intent to rewrite the workshop. This section will only try to provide you with some more context and additional information that makes it easier to understand which steps are important and which have to be done (or can be left out) to set up the development environment.

If you intent to develop Eclipse extensions for the Eclipse Client or the RTC Server, you should run this RTC Extensions workshop at least once. It provides you with a step-by-step description you can follow. It also provides you with useful example code that touches some areas of the RTC API.

* Update * there was a major overhaul of the RTC Extensions Workshop. The text below has been updated to reflect the new workshop.

* Update * there are some problems with the SDK when Running The RTC 4.x Extensions Workshop With RTC 5.0.x. Check the post for how to solve them or use 4.x for development – the APIs are mostly unchanged.

* Update * See also Running the RTC Extensions Workshop With RTC 6.0

When can I skip doing the Extensions workshop?

As described below, there are cases where you may be able to skip all or part of the Extensions workshop in order to only setup the development environment. When can you skip doing the workshop and when should you do it?

If you want to develop extensions for the server, you can skip the workshop if

  • You have done the workshop already
  • You are a Jazz developer that develops with the server API every day
  • You are are a subject matter expert that has already successfully developed a bunch of custom follow up actions and successfully deployed them.

If you only want to code against the Plain Java Client Libraries you can also only do Lab 1 and skip the rest of the workshop.

In all other cases you should definitely take the time to do the RTC Extensions workshop.

Minimal Setup Steps to Setup Your Development Environment

If you only want to code against the Plain Java Client Libraries, or if you want to just set up the SDK, the part of the workshop you should perform is Lab 1. At least run through the sections 1.1 Download and Unzip the Required Files, 1.2 Setup for Development. If you just want to do Plain Java Client Libraries you can skip all the following labs. You can basically scroll down to the section Setting Up The Plain Java Client Libraries below.

If you want to setup only the SDK you don’t have to run 1.3 Setup the RTC Tomcat Server,  to setup the development RTC server and you don’t have to run the WorkshopSetup tool. You don’t have to connect the Eclipse client to the Tomcat RTC workshop server at https://localhost:9443/ccm/ if you skip 1.3, instead you would likely connect to your team’s SCM system to version your code.

When setting up the SDK 1.4 Complete Setup of Your RTC Eclipse Client is only necessary if you want to use the SDK to develop client and server extensions. In this case I strongly recommend to run all the labs. If you want to test the extensions on a Tomcat based server you have to at least  run 1.3_12 in 1.3 Setup the RTC Tomcat Server, to be able to connect to that server.

Please follow the advice in the workshop for setting up the Eclipse workspace and use a folder named /workspaces/Dev1/WS . A second workspace would be named /workspaces/Dev2/WS . This greatly simplifies workspace management later.

You should use different folders to develop for different Versions of RTC. I usually follow the pattern /RTCDev, where version is the version of the release. For example for 4.0.1 /RTC401Dev/ as root. see the image blow. It is a best practice to keep the file name short, especially on Windows, because that OS struggles with long path names you tend to get with Eclipse.

Development FoldersIf you want to develop client or server extensions, I strongly recommend to set up a test server based on Tomcat and Derby for deployment tests. I use the JKE Banking aka Money that Matters example for all my initial tests. If necessary, I deploy custom templates and create customized project areas on my test server. It is possible to backup the test server to be able to repeat deployment tests without always having to set up a new test server. With Tomcat and Derby you can zip the complete install folder for backup and unzip it to restore. This can be improved if you just backup and zip the data required. You can find some more details about what is required for backing up here.

As a general best practice, at least on Windows, use 7Zip to zip and unzip files related to Jazz and RTC. The zip tool built into Windows has problems with long path names and just does not work reliable. See the article Workaround: Rational Team Concert SDK .zip fails to extract on Windows because of long file path for details.

To run your own Tomcat test server for debugging, you have to modify the server.startup.bat file of this test server as described in the beginning of 1.3 Setup the Tomcat Server. If you do, run the rest of Lab 1 and make sure you can do the basic debugging steps as described in the last sections of the lab with your own test server.

If you run the test server on your development machine, make sure to avoid using conflicting ports as described in the RTC Extensions Workshop.

In my experience it is not necessary to run the workshop on the exact version you finally want to develop for. You can run the workshop on any supported version. Later you can set up the SDK for the version you want to use, as described here and bring the source code over. As an alternative, if there are issues with the versions (which should no longer be the case) you can set up the workshop on a supported version, upgrade the repository to the version you want and run it. Because the code did not change since 3.x, I don’t think it is worth the effort.

Running the Workshop – Additional Information

Please Note: If you run the workshop, make sure to follow it closely for the parts you are doing. This workshop has been run many times in classes. The experience is, it works really well if you pay attention to the details. This holds especially true for Lab 1. If you miss to check a check-box, or push the wrong button, or import the wrong file the wrong way with wrong selections, you will have issues. In a case like that, the only chance you have is to delete the wrong import, or whatever it was, and do the step again. The trouble is to understand the issue from the symptoms later, if you don’t have the experience. In this case you might have to start all over, delete what you did so far and repeat all steps.

You should be aware that the workshop uses two different RTC/CLM servers. The server you set up in 1.3 Set Up The RTC Tomcat Server https://localhost:9443/ccm/ as displayed below, is an external workshop SCM and Test repository simulating a production environment. It is used to get the source code of the workshop example. The code is stored in the repository in several states of development each marked by a baseline. In addition this workshop server is used as deployment target in the final lab.

ExtensionWorkshop Development RepositoryThe second server that you use in the workshop is the Jetty based Test Server you run in debug mode with the public URI https://localhost:7443/jazz/.

ExtensionWorkshop topologyJettyIt is a best practice to use a Jetty based test server to develop and test your code as described in the workshop, before attempting to deploy on a test system and finally in production. The reason is simple, it is much faster to develop this way. You can get your extension stable and you will be confident it works. In the final deployment tests on Tomcat or WAS you will likely only have to deal with deployment problems.

In a real production environment, you would connect to your development repository RTC server and use its SCM system to store your code. You would have your own test RTC server, as suggested above, that you use for your final deployment tests. This test server needs to be enabled for debugging, if you want to use remote debugging as mentioned above. In the workshop the Tomcat based RTC server is used for both purposes, like in a simulation.

Working With Your Development Workspace

If you followed the steps above, you have now a working Eclipse workspace as a development environment set up with the SDK. If you develop Extensions you can use this workspace to debug your extension, search for code in the SDK and do all kinds of cool things. Some of these capabilities are used in the Extensions Workshop. The key capability is, the SDK provides you with source code for many of the API classes which allows searching for the API.

It is a best practice to create a copy of your Eclipse workspace with the SDK set up and all its settings by just duplicating /workspaces/Dev1/ and naming the duplicate for example /workspaces/Dev2/. This duplicates all settings, including launches and jetty test servers. You can also use 7Zip to zip the folder /workspaces/Dev1/ and all its content as a backup.

You can also share the projects and files you imported into your workspace in the Jazz SCM. I would suggest to use a dedicated component. Please be aware that you still have to setup the SDK target platform and have to hook up the SDK source code if you start with a fresh workspace. Again, you can copy or zip/unzip your workspace that has been set up to sped up things and then load the component with the imported files.

Launches and Jetty Server Configuration

If you have run the Rational Team Concert Extensions Workshop and want to use the Feature Based Launches for a different RTC Version or Workspace, you can copy them over into the desired location. You can find these files in the Eclipse project RTC Extension Workshop Configuration.

Configuration and Launches

Use File>Import, select Existing Projects into Workspace. Then browse to the workspace that contains the project. Select the project to import and check the check box for the Copy projects into workspace option.

You can rename or remove launches for the client and the server and organize them as needed.

Make sure to remove the XML files in the conf folder and replace them with the ones for your current version.

Development, Debugging, Deployment of Server Extensions

You should use the techniques described in the Rational Team Concert Extensions Workshop to develop, debug and deploy your extensions.

Always use Jetty to test if server extensions solution can be deployed and work there first. After that is proven, you can be reasonably sure that it can be deployed on a real application server.

You can only be reasonably sure, because your extensions dependencies might still contain dependencies to code that is not available where you deploy it, or might experience problems with the class loaders of the application server.

However, your extension will then likely deploy and, in case there are unsatisfied dependencies or other issues, at least create an error message.

Use https://localhost:9443/ccm/admin?internal=true to see the internal tools that can help to request a server reset, see the provisioning status and the component status of your extension.

Internal ToolsYou can use

If you deploy and the deployment fails catastrophically, you can see this in the server logs e.g. as exception. If you fail to deploy correctly and, as an example, the ini file for the update site points to a non existent folder, you server might be unable to start and the links above won’t work.

Debugging Enterprise Extensions with Jetty

The Enterprise Extensions are not shipped with the SDK. If you need them for debugging in Jetty follow this answer on Jazz.net.

Setting Up The Plain Java Client Libraries

If you want to develop client extensions or Plain Java Client Library based automation it is recommended to set up the Eclipse workspace to use the Plain Java Client Libraries. You can use most of the code developed with Plain Java Client Libraries also for Eclipse client extensions and debugging of Plain Java Client Library code is faster and easier than debugging in a dedicated debug Eclipse client.

If you followed the Rational Team Concert Extensions Workshop Lab 1 you have already downloaded and installed the Plain Java Client Libraries.

If you haven’t, here is what is needed to download and extract them. Open the All Downloads tab of the RTC version you are interested in. For example https://jazz.net/downloads/rational-team-concert/releases/4.0.1?p=allDownloads and scroll down to the Plain .zip Files section. Look for the Plain Java Client Libraries downloads. There are the libraries as well as the JavaDoc.

PlainJavaDownloadDownload both files into a temporary folder.

Use 7Zip as explained in the workshop and unzip the Plain Java client Libraries download file (for example named RTC-Client-plainJavaLib-4.0.1.zip) using 7Zips Extract Files and provide the extraction Path /Installs/PlainJavaAPI .

ExtractLibrary

Now do the same with the JavaDoc for the Plain Java Client Libraries e.g. a file named RTC-Client-plainJavaLib-API-javadoc-4.0.1.zip. As extraction path provide /Installs/PlainJavaAPI/ as path again.

You should end up with this directory structure.

PlainJavaInstalled

Now you have installed the Plain Java Client Libraries. Familiarize yourself with the content of the folder PlainJavaApi. It contains the Jar files you need if you run Java code that uses the Plain Java Client Libraries. The doc folder contains the JavaDoc that you can open using the index.html file in that folder. The license folder contains the license files for it.

The snippets folder contains example code and a README.TXT file with instructions how to run the snippets. Basically you have to provide reasonable defaults for the parameters, compile the snippet code and run the snippets against a test repository. The snippets require the parameters repositoryAddress and snippetUserAndPassword. You can either set them as environment variables, or you can change the parameter default in the source code of the snippets displayed below.

SnippetParameters

You can edit the source with any editor, compile it with a JDK and run it from a shell. However, it is hard to debug this and it is impossible to search the API that way. Lets change that.

Setting Up Your Workspace for Plain Java Development

It would be great if we could run this in Eclipse and look for the API classes, similar to creating Eclipse extensions. The next sections show how that can be done, if you have setup your Eclipse with the SDK as described in the Extensions Workshop as described above.

Start your Eclipse client with the workspace set up for the SDK. You should see a few projects already that appeared during the setup for the SDK. Now we want the Plain Java snipptes to be usable in Eclipse for debugging.

Create a new Project using File>New>Project. In the wizard type Java in the filter or select Java>Java Project. Click Next. Name the Project PlainJavaSnippets and click Finish. Let Eclipse change to the Java Perspective. Now unfold the project, click on the src folder and create a new package. Name it snippets. Right click on the new package and select Import in the context menu. In the wizard select General>File System.

Click Next. Browse for /Installs/PlainJavaAPI/snippets. Select the folder snippets. In the Into Folder selection field, make sure it shows your package src/snippets in your PlainJavaSnippets project as presented below, then click Finish.

ImportSnippets

Now you have the code imported in your project and it shows errors, rats! But that is just because it is missing all the classes from the Plain Java client Libraries.

ImportedAndErrorsSo lets fix the errors first. right-click on the project and select Build Path>Configure Build Path.

AddPlainJava API LibrariesOpen the Libraries tab in the properties editor, then click Add Library. In the Add Library dialog select User Library and click Next. There is no User Library yet. Click on the button User Libraries… On the next dialog click New.

NewUserLibrary

Name the Library PlainJavaApi and click OK.

AddLibraryPlainJavaApi

On the next dialog click Add JARs… In the following dialog browse to /Installs/PlainJavaAPI/. Select all JAR files in the folder and click Open.

AddJARs

Your library should show a lot of JAR’s now. Click OK.

Jar's Added

On the next dialog make sure your new User Library PlainJavaApi is selected and press Finish. The Library should now show up in the build path. Click OK to close the Build Path Editor.

All the compiler errors should be gone after a moment. You can now run and debug your Plain Java Client Library snippets in Eclipse. Next time you want to create a new project, you can just re-use the library you have just created.

If you start new Plain Java Client Library based projects, just add this library to the build path and you should be ready to go.

Open Snippet1.java. and look into the main method. right click at TeamRepositoryException and select Open Declaration.

BrowseDeclaration

You will see a Class File Editor and, unless you installed a Java Decompiler, no code. Dang, we had hoped for more. And, we can actually achieve more, just using a little dirty trick described in the next section.

Make the SDK Source Code Available for the Plain Java Clients Libraries

This only works if you have installed the RTC SDK in your Eclipse following the Extensions Workshop. If you did not and just wanted to shortcut, this a bit, consider following the original advice.

The reason why we can see the code in plug-in development for the SDK is the Eclipse PDE – the Plugin Development Environment. Unfortunately that works only for plug-ins. We don’t have a Plugin – yet. We will have one in a minute and trick the PDE into showing us what we want.

Right click on the PlainJavaSnippets project and select Configure>Convert to Plug-in Projects…

ConvertToPlugin

In the wizard make sure your project is selected and click Finish.

Your project is now a plug in project. It will still not show any code. You have to tell it where to look first.

Unfold the new folder META-INF and double click at the file MANIFEST.MF.

AddDependenciesA plug in editor opens. Open the Dependencies tab of the editor and click Add…

AddDependencies2

In the Plugin Selection enter org.eclipse.core.runtime and add the dependency by pressing OK.

AddRuntime

The same way enter

  • com.ibm.team.repository.client
  • com.ibm.team.repository.common

Basically look into the import section of your snippet and add every dependency using the package name.

Dependencies

Save the changes to the plug in manifest.

Now we should have everything done to get the source code. However, the order of things to look up might cause an issue. Open the Configure Build Path dialog again as described above. Open the Order and Export tab and make sure the PlainJavaApi library is below the Plug-in Dependencies entry. Use the Up and Down button to achieve this, then click OK.

OrderLibaries

Now open your snippet again and use Open Declaration on TeamRepositoryExceptioen in the main method, as you did above. You should now be able to see the code that was imported in the SDK.This works, because the SDK and the Plain Java Client Libraries are basically the same code, but packaged in a different way.

This makes life a lot easier, because you can now use debugging and search and all other means available in the PDE to find the API you need. We will explore what we can do in future posts. See Understanding and Using the RTC Java Client API for more details on the client libraries.

Please Note: You can add more dependencies to the manifest, one for each package you need to look at. You should just make sure it is client API that is available in the Plain Java Client Libraries. You should be able to use Eclipse code completion with CTRL-Space for all the classes in the Plain Java Client Libraries. If you find a class and don’t see the code, try to add the package to the manifest. Please be aware that the SDK does not package the complete code. There are classes that don’t provide the source code.

It is a best practice to create a copy of your Eclipse workspace with the SDK and the Plain Java Client Libraries set up and all its settings by just duplicating /workspaces/Dev1/ and naming the duplicate for example /workspaces/Dev2/. This duplicates all settings, including launches and jetty test servers. You can also use 7Zip to zip the folder /workspaces/Dev1/ and all its content as a backup. This can save you a lot of time if you want to set up a fresh workspace later.

Summary

Setting up the SDK and development for the Plain Java Client Libraries helps you to explore and use the unknown lands of the RTC API and enables you to develop much more efficient than before. You should always invest the little effort to set up your environment this way.

I hope sharing this will help users out there to understand and develop the RTC API more efficient and provide more automation and extensions in less time.

Working with Work Item Attributes


When I started with customizing RTC, the biggest challenge was how to get started on work item attributes. Since getting work item attributes is also one of the most popular search terms on this blog, I think a small summary would be useful for new users of the RTC Java API’s.

*Update* The post The RTC Work Item Command Line on Bluemix contains downloadable code that performs most of the activities required for reading and modifying work items, their attributes, and all kinds of links. This includes reading and writing work item attribute of all kinds, including list attribute types. The interesting code can be found in the com.ibm.js.team.workitem.commandline.helper package in the class WorkItemHelper. For reading values look at the class ExportWorkItemsCommand and look at ExportWorkItemsCommand.getStringRepresentation(IWorkItem, ParameterValue) and the subsequently called methods. All techniques described below are used there. You can familiarize yourself with the concepts in this post and then look into that project for how it is used.

License and how to get started with the RTC API’S

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. Enjoy!

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 and the Plain Java Client Library Snippets. The example in this blog shows RTC Client API.

Download Example Code

Download Examples from Dropbox here. Please note, that Dropbox might be blocked and unavailable in some companies.

Update: the The RTC Work Item Command Line on Bluemix has example code for an almost complete set of attribute types.

To keep it simple this example is again based on the Jazz Team Wiki entry on Programmatic Work Item Creation. If you are just starting with extending Rational Team Concert, start reading this and the linked posts to get some guidance on how to set up your environment.

Which API?

If you look at the examples below, you will note that the code below uses the interfaces

com.ibm.team.workitem.common.IWorkItemCommon

or

com.ibm.team.workitem.client.IWorkItemClient

to access work item data. Always look at the interface com.ibm.team.workitem.common.IWorkItemCommon first and use it if the method you need is available. The common API is usable in the client and the server API and is the preferred API to use. Only use specific client or server API if the common API does not have this API(the namespace is like com.ibm.*.domain.client for client API or  com.ibm.*.domain.server for server API).

Most of the usage of the methods available on IWorkItemClient are inherited from IWorkItemCommon and it is better to replace usage of IWorkItemClient with usage of IWorkItemCommon whereever possible.

Why did I use IWorkItemClient? I was inexperienced and found the client API first in examples and It only later occurred to me that it is better to search the common API first.

Getting the Work Item’s Attribute ID’s in the UI

You can find the work item attributes and their ID’s in the project area administration in the section Work Items, subsection Types and Attributes. select the work item type you are interested in. The attribute IDs for the built in and the custom attributes for the selected work item type can be found in the ID column.

AttributeIDs

RTC Web Administration UI shows valid attribute ID’s

Please note, do not use the attribute ID’s you find in the project area administration in the RTC Eclipse client. The RTC Eclipse client shows attribute ID’s with a prefix com.ibm.team.workitem.attribute such as such as com.ibm.team.workitem.attribute.category. This attribute ID is not the one that works with the work item API. I am not sure what they really do, but they don’t work to get attribute values. See the Enhancement Request 219100 RTC Eclipse Client shows wrong IDs for internal Work Item Attributes for Java API for details.

WrongAttributeID's

RTC Eclipse Client shows wrong attribute ID’s for internal attributes

Getting the Work Item’s Attributes in the API

You can also find the attribute ID’s in the API. The Work Item Command Line uses this for the command printtypeattributes.

If you have a work item, an object of type IWorkItem in the API, you will realize that you have only a very limited set of getters and setters to access information for the work item. You have access to the Summary, Owner, and some other built in attributes of the work item. If you look at a RTC work item, you realize there is much more information in it. The question is how to access this information?

The RTC data meta-model provides a special interface that is used to access arbitrary attributes of a work item. The interface is com.ibm.team.workitem.common.model.IAttribute. To access an arbitrary Attribute at a work item, you need to get the IAttribute first.

There are several ways how this can be done. Please note that most of the functionality used below is available in the interface com.ibm.team.workitem.common.IWorkItemCommon. This interface can be used in both the client and the server API. In the client API com.ibm.team.workitem.client.IWorkItemClient inherits the IWorkItemCommon interface. in the server API com.ibm.team.workitem.service.IWorkItemServer also inherits the IWorkItemCommon interface. Use IWorkItemCommon where possible.

If you have the Attribute ID available as a string, you can use this code to get the attribute.

IWorkItemClient workItemClient = (IWorkItemClient) fTeamRepository.getClientLibrary(IWorkItemClient.class);
IAttribute someAttribute= workItemClient.findAttribute(fProjectArea, "some_attribute_ID", monitor);

Another example if you know the work item ID, it is possible to find the work item form the string value.

IWorkItemCommon workItemCommon = (IWorkItemCommon ) fTeamRepository.getClientLibrary(IWorkItemCommon.class);
IAttribute attribute = workItemCommon.findAttribute(projectArea, attributeID,
				getMonitor());
if(attribute != null){
	// Do something with it.
}

RTC defines some built-in attributes that are typical for all kinds of work items. You can get the ID’s for these attributes using the interface com.ibm.team.workitem.common.model.IWorkItem. The interface defines several properties containing the ID’s for built-in the work item attributes.

This code for example gets the attribute for the built-in duration.

IAttribute built_in_duration = workItemCommon.findAttribute(fProjectArea, IWorkItem.DURATION_PROPERTY, monitor);

If this is not feasible, because you do some automation and don’t want to hard code it, the code below gets all the built in attributes of a project area.

List builtInAttributeHandles = workItemCommon.findBuiltInAttributes(fProjectArea, monitor);

If you are looking for all attributes, this method below retrieves the list.

List allAttributeHandles = workItemCommon.findAttributes(fProjectArea, monitor);

There is no method that returns only the custom attributes from the project area. You can however get the custom attributes directly from a work item using the code below:

List custAttributeHandles = workItem.getCustomAttributes();

The code to get the attributes, returns only a handle. You have to resolve the handle to the full object. The code below shows how that can be done.

for (Iterator iterator = builtInAttributeHandles .iterator(); iterator.hasNext();) {
    IAttributeHandle iAttributeHandle = (IAttributeHandle) iterator.next();
    IAttribute iAttribute = (IAttribute) fTeamRepository
        .itemManager().fetchCompleteItem(
        iAttributeHandle, IItemManager.DEFAULT ,monitor);
}

This code uses the permission aware API instead, to get the built in attributes.

List builtInAttributeHandles = workItemClient.findBuiltInAttributes(fProjectArea, monitor);
IFetchResult builtIn = getTeamRepository().itemManager().fetchCompleteItemsPermissionAware(built_in_attribs,
    IItemManager.DEFAULT, monitor);

Get a Work Item Attribute

Once you have the IAttribute, you can get the value of the attribute using the method IWorkItem.getValue(). However, as described in the post Using an Expression to Synchronize Attributes for Work Items of a Specific Type, work items do not necessarily have all attributes already. This especially holds true for custom attributes. You either need to check for a null value, or you can check if the work item actually has the attribute using code like below.

if (workItem.hasAttribute(iAttribute)) {
	Object value = workItem.getValue(iAttribute);
}

The call returns a plain object. You have to cast the returned value to the expected type, to use it. Examples for basic objects are String, Integer, Boolean. You can use these to access the data. The API has many more attribute types that you can also use to cast and access the data.

Here an example how to access a attribute of type String:

if (workItem.hasCustomAttribute(customString)){
	Object value = workItem.getValue(customString);
	if (value instanceof String) {
		String attributeValue = (String) value;
		// do something with the value
	}
}

It is necessary to understand the type that is returned. The information is can easily be picked up during debugging.

Typical Attribute Types

The available attribute types can be found in the process configuration in the Web UI (or the Eclipse UI). To get the valid information for built in and custom attributes, the Web UI works best. The Type column shows the type, the ID column shows the ID of the attribute. Note, the Eclipse Client shows an external ID for built in attributes, that does not work.

AttributeTypes_2017-05-15_10-40-15

Please find below some examples how to access work item attributes of different types. The examples are taken from the Work Item Command Line which has examples for almost every attribute type.You can download the code from this post. All the examples expect the object returned by

Object value = workItem.getValue(customString);

as parameter. Some need the IAttribute in addition to that.

For a complete list of attribute values and their conversion to text see the WorkItem Command Line and the class ExportWorkItemsCommand and look at ExportWorkItemsCommand.getStringRepresentation(IWorkItem, ParameterValue) and the subsequently called methods to convert the attributes to strings.

The most important attribute types are shown below.

Category

The attribute “Filed Against” returns a category. The conversion to user readable text looks like this:

/**
 * Compute the string representation for a category
 * 
 * @param value
 * @return
 * @throws TeamRepositoryException
 */
private String calculateCategoryAsString(Object value)
		throws TeamRepositoryException {
	if (value != null) {
		if (value instanceof ICategoryHandle) {
			return getWorkItemCommon().resolveHierarchicalName(
				(ICategoryHandle) value, getMonitor());
		}
		throw new WorkItemCommandLineException(
				"Convert Category - Incompatible Type Exception: "
						+ value.toString());
	}
	return CONSTANT_NO_VALUE;
}

Number Types

There are several number types supported. Here the conversion to text values.

/**
 * Compute the string representation for a number
 * 
 * @param value
 * @param format
 * @return
 */
private String calculateNumberAsString(Object value, String format) {
	if (value == null) {
		return CONSTANT_NO_VALUE;
	}
	if (value instanceof Integer) {
		return ((Integer) value).toString();
	}
	if (value instanceof Long) {
		return ((Long) value).toString();
	}
	if (value instanceof Float) {
		return ((Float) value).toString();
	}
	if (value instanceof BigDecimal) {
		return ((BigDecimal) value).toString();
	}
	throw new WorkItemCommandLineException(
			"Calculate number - Incompatible Type Exception: "
					+ value.toString());
}

Enumeration Type Attributes

Typical custom attribute types are enumerations. This code shows how to access the values. Also see the post Manipulating Work Item Enumeration Values.

/**
 * Compute a string representation for one enumeration literal
 * 
 * @param value
 * @param attribute
 * @return
 * @throws TeamRepositoryException
 */
private String calculateEnumerationLiteralAsString(Object value,
		IAttribute attribute) throws TeamRepositoryException {
	if (value == null) {
		return CONSTANT_NO_VALUE;
	}
	if (!(value instanceof Identifier)) {
		return "Value not an enumeration literal";
	}
	IEnumeration enumeration = getWorkItemCommon()
			.resolveEnumeration(attribute, getMonitor());
	@SuppressWarnings("unchecked")
	Identifier currentIdentifier = (Identifier) value;
	ILiteral literal = enumeration
			.findEnumerationLiteral(currentIdentifier);
	return literal.getName();
}

Comments

Comments are not an attribute, but a collection that can be accessed. Here an example how to get at the information.

IComments comments = workItem.getComments();
IComment[] theComments = comments.getContents();
List commentText = new ArrayList(theComments.length);
int i = 1;
for (IComment aComment : theComments) {
	if (i > 1) {
		commentText.add("\r");
	}
	commentText.add(i + ". " + aComment.getHTMLContent().getPlainText());
	i++;
}

Contributor Type Attributes

User references are stored as com.ibm.team.repository.common.IContributorHandle as handles for IContributors. To access the contributor information it is necessary to get the handle and resolve the contributor. This is shown below.

/**
 * Compute the string representation for a contributor/user
 * 
 * @param value
 * @return
 * @throws TeamRepositoryException
 */
private String calculateContributorAsString(Object value)
		throws TeamRepositoryException {
	if (value == null) {
		return CONSTANT_NO_VALUE;
	}
	if ((value instanceof IContributorHandle)) {
		IContributor contributor = (IContributor) getTeamRepository()
				.itemManager().fetchCompleteItem(
						(IContributorHandle) value, IItemManager.DEFAULT,
						getMonitor());
		return contributor.getName();
	}
	throw new WorkItemCommandLineException(
			"Convert Contributor - Incompatible Type Exception: "
					+ value.toString());
}

Timestamps

Timestamps  are stored as java.sql.Timestamp. The code below shows how to get the data.

/**
 * Compute the string representation for a timestamp
 * 
 * 
 * @param value
 * @return
 */
private String calculateTimestampAsString(Object value) {
	if (value != null) {
		if (value instanceof Timestamp) {
			Timestamp timestamp = (Timestamp) value;
			return SimpleDateFormatUtil.getDate(timestamp,
					getSimpleDateTimeFormatPattern());
		}
		throw new WorkItemCommandLineException(
				"Convert timestamp - Incompatible Type Exception: "
						+ value.toString());
	}
	return CONSTANT_NO_VALUE;
}

The class com.ibm.js.team.workitem.commandline.utils.SimpleDateFormatUtil is part of the WorkItem Command Line and uses java.text.SimpleDateFormat to convert the value to a string.

Durations

Durations such as “Time Spent” are stored as long. The code below converts the value.

/**
 * Compute the string representation for a duration
 * 
 * @param value
 * @param attribType
 * @return
 */
private String calculateDurationAsString(Object value, String attribType) {
	if (value != null) {
		if (value instanceof Long) {
			Long milliseconds = (Long) value;
			return SimpleDateFormatUtil.convertToTimeSpent(milliseconds);
		}
		throw new WorkItemCommandLineException(
				"Calculate Duration - Incompatible Type Exception: "
						+ value.toString());
	}
	return CONSTANT_NO_VALUE;
}

The class com.ibm.js.team.workitem.commandline.utils.SimpleDateFormatUtil is part of the WorkItem Command Line and uses java.text.SimpleDateFormat to convert the value to a string.

Iterations

Iterations are returned as IIterations. To resolve the iteration see the code below. The WorkItem Command Line contains the class com.ibm.js.team.workitem.commandline.helper.DevelopmentLineHelper to convert Iterations and Timelines to user readable text.

/**
 * Get the IIteration object from a handle
 * 
 * @param handle
 * @return
 * @throws TeamRepositoryException
 */
public IIteration resolveIteration(IIterationHandle handle)
		throws TeamRepositoryException {
	if (handle instanceof IIteration) {
		return (IIteration) handle;
	}
	IIteration iteration = (IIteration) fTeamRepository.itemManager()
			.fetchCompleteItem((IIterationHandle) handle,
					IItemManager.DEFAULT, fMonitor);
	return iteration;
}

Work Item State

To get the work item state see the code blow.

/**
 * Convert a work item state to a string
 * 
 * @param workItem
 * @return
 * @throws TeamRepositoryException
 */
private String calculateStateAsString(IWorkItem workItem)
		throws TeamRepositoryException {
	Identifier state = workItem.getState2();
	IWorkflowInfo wfInfo = getWorkItemCommon().findWorkflowInfo(workItem,getMonitor());
	String stateName = wfInfo.getStateName(state);
	if (stateName == null) {
		return "";
	}
	return stateName;
}

Subscriptions

Subscriptions is another pseudo attribute that really is a collection.

ISubscriptions subscriptions = workItem.getSubscriptions();
IContributorHandle[] contributors = subscriptions.getContents();
List subscribers = Arrays.asList(contributors);

Compute the contributors from the list as shown above. Also see Subscribing To a Work Item Using the Java API.

Approvals

Approvals is also a pseudo attribute and is accessed using a collection. The conversion is quite sophisticated as the approval information is composed of several descriptors and approval records. Please see the WorkItem Command Line and the ExportWorkItemsCommand.calculateApprovalsAsString(IWorkItem) for the code.

Also see Adding Approvals to Work Items Using the Plain Java Client Libraries.

Other Attribute Types or Pseudo Attributes

Please see the WorkItem Command Line especially the class ExportWorkItemsCommand for how to access the values. Also see

List Attributes

Work Item Attributes have a type for example smallString, mediumString, myCustomEnumeration and many more. Since RTC 4.x it is possible to have attributes defined that can contain a list of attribute values. The attributes are defined by picking the attribute type with the post-fix list in the attribute type selection for example string list, myCustomEnumeration list, Work Item list, Team Area list etc.

Basically the returned object for attribute types is a list object an instance of:

List

to get at the contained values, cast to a list. Then iterate the list and work on the returned object.

/**
 * Compute the string representation for a string list object
 * 
 * @param value
 * @return
 */
private String calculateStringListAsString(Object value) {
	if (value == null) {
		return CONSTANT_NO_VALUE;
	}
	List resultList = new ArrayList();
	if (value instanceof List) {
		List items = (List) value;
		for (Object object : items) {
			resultList.add(calculateString(object));
		}
	}
	return StringUtil.listToString(resultList, SEPERATOR_NEWLINE);
}

For the objects returned cast them to the correct instance and then use the values as fits. Her an example for a string type.

/**
 * Compute the string representation for a string object
 * 
 * @param value
 * @return
 */
private String calculateString(Object value) {
	if (value != null) {
		if (value instanceof String) {
			return (String) value;
		}
		throw new WorkItemCommandLineException(
				"Convert string - Incompatible Type Exception: "
						+ value.toString());
	}
	return CONSTANT_NO_VALUE;
}

See the WorkItem Command Line and ExportWorkItemsCommand.getStringRepresentation(IWorkItem, ParameterValue) for how to analyze the attribute types and call the conversions.

Set the Work Item Attribute

Very similar to getting the work item Attribute, you can set the value of an attribute. You need to have a WorkItemWorkingCopy of the work item to be able to set an work item attribute value. See the Jazz Team Wiki or for example this post for how to retrieve a working copy. This post shows some of the code for the server. The code below shows this for a custom integer attribute. First it checks if the work item attribute is available and then sets the value. The IWorkItem.setValue() method assumes it is able to cast the value in order to be able to set it. You need to provide a value of a matching type to set the attribute.

IWorkItem workItem = IWorkItem workItem = workingCopy.getWorkItem();
Integer value = new Integer("4");
if (workItem.hasCustomAttribute(customInteger))
	workItem.setValue(customInteger, value);
}

The various types that can be set are explained above. The data has to be provided as retrieved above. Typical types are String, Integer, Long, BigDecimal, enumerations, Timestamps.

For List attributes you have to construct a list of attributes and set the list. e.g. A very simple example would be for a custom enumeration. Construct a list of attribute values (e.g. the Identifier for the enumeration literal) and use the list to set the values:

List values = new ArrayList();
values.add(getLiteralEqualsString("foo", customEnumerationList));
values.add(getLiteralEqualsString("bar", customEnumerationList));
workItem.setValue(customEnumerationList,values);

With getLiteralEqualsString returning the enumeration value identifier as explained in Manipulating Work Item Enumeration Values.

private static Identifier getLiteralEqualsString(String name, IAttributeHandle ia) throws TeamRepositoryException {
.
.
.
}

See the work item commandline especially the WorkItemUpdateHelper for methods ending with List for example:

com.ibm.js.team.workitem.commandline.helper.WorkItemUpdateHelper.calculateEnumerationLiteralList(ParameterValue, List)

ClassCast Exceptions

Regardless whether you read a value or write a value, you usually can’t just use the value. As described in Understanding and Using the RTC Java Client API you need to cast the object you get to something you can use. A String, an Int, an enumeration literal, or for more complex itens to an ItemHandle and resolve the handle to be able to access the data.If you want to set a value, you have to make sure that the Object you pass conforms to the attribute type as well.If your data does not conform to the value that is returned or expected, your code will throw a ClassCastException.My experience so far is, that such exceptions usually were my fault and not the fault of the API. So in case of exceptions first look at your code before posting the API is broken in the forum.

Examples for setting work item attributes

The Work Item Command Line has examples for a complete set of work item attribute types. The class   com.ibm.js.team.workitem.commandline.helper.WorkItemUpdateHelper provides examples for how to find the work item attributes from an ID, get the attribute type and set a value from a string representation. The entry points are

  • com.ibm.js.team.workitem.commandline.helper.WorkItemUpdateHelper.updateProperty() – the method tries to find the attribute and calculate the attribute type
  • com.ibm.js.team.workitem.commandline.helper.WorkItemUpdateHelper.getRepresentation() – the method takes the calculated attribute type IAttribute and the string representation and tries to get an object that represents the value in the repository; the code tries to find a contributor from the ID, tries to find an iteration or a category from a text description.

Various methods with the prefix calculate, find, get, create, update are provided that do the work to convert or lookup the data.

Related posts for setting attributes

Saving the work item

As described in Understanding and Using the RTC Java Client API you can not just do a setValue() and expect the work item to change. You need a WorkItemWorkingCopy to actually change the value. The best way to do this in the client API is described in Understanding and Using the RTC Java Client API. Use a WorkItemOperation to wrap your code. This post has examples for how to work in the server API.

Summary

This is a short overview about the basics you need to know to access and modify work item attribute values. Other posts in this blog and several other examples on Jazz.net and IBM Developerworks provide more information on the API.

Related Posts

The RTC Work Item Command Line on BluemixA RTC WorkItem Command Line Version 3RTC Update Parent Duration Estimation and Effort ParticipantManipulating Work Item Enumeration ValuesHandling Iterations – Automation for the “Planned For” AttributeUsing Work Item Queries for AutomationUsing Expressions for AutomationUsing an Expression to Synchronize Attributes for Work Items of a Specific TypeManipulating Work Item Workflow StatesResolve Parent If All Children Are Resolved ParticipantDo Not Modify The Triggering Element In An OperationAdvisor

Analyzing a Project Areas Members and Roles Using The Plain Java Client Libraries


I was interested in how to read the data from a Project and Team Area. This post shows my progress so far.

The purpose of the endeavor really is to be able to create a new project area from a certain template project area, copying over all information. I am not even nearly there yet. However Jian’s question on Jazz.net indicates that what I found so far might be interesting anyhow. If you are just starting with extending Rational Team Concert, start reading this and the linked posts to get some guidance on how to set up your environment.

License and how to get started with the RTC API’S

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. Enjoy!

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 and the Plain Java Client Library Snippets. The example in this blog shows RTC Client API.

Download

You can download the code from here.

* Update *

The code to launch the methods is included in the download. It can also be found in several of the older posts, for example in Uploading Attachments to Work Items in the main() and the run() methods.

As a general statement, you should use the Java API to access the process information and not try to read the XML of the process directly. Please be aware that only the API documented in the Plain Java Client Library JavaDoc documentation is public API. You might end up using internal API and that is subject to change. Even for the public API there is no guarantee that it won’t change as far as I know.

You can access similar information by using the Generate Runtime Report on the project area’s context menu as shown below.
Generate Runtime Report*Update*

This code also works for

Finding a Project Area

The code provided either iterates over all project areas of a repository, or it finds the project area with a given name. Once it has located the project area, it calls the method analyzeProjectArea(teamRepository, projectArea) that takes care for the details.

The code needs some data. You can call it by providing the RepositoryURI, the user and password and optional a project area name like in the examples below.

Here the interesting code in the main method.

if (projectAreaName == null) {
	// For all project areas
	IProcessItemService itemService = (IProcessItemService) teamRepository
		.getClientLibrary(IProcessItemService.class);
	List pAreas = itemService.findAllProjectAreas(null, null);
	for (Iterator iterator = pAreas.iterator(); iterator.hasNext();) {
		IProjectArea projectArea = (IProjectArea) iterator.next();
		analyzeProjectArea(teamRepository, projectArea);
	}
} else {
	// For a specific project area
	IProcessClientService processClient = (IProcessClientService) teamRepository
		.getClientLibrary(IProcessClientService.class);

	URI uri = URI.create(projectAreaName.replaceAll(" ", "%20"));
	IProjectArea projectArea = (IProjectArea) processClient.findProcessArea(uri, null, null);
	if (projectArea == null) {
		System.out.println("Project area not found.");
		return false;
	}
	analyzeProjectArea(teamRepository, projectArea);
}

Analyzing The Project Area

The code below is called and prints the process area description and the contributors for the project area and then iterates the team area hierarchy doing the same.

/**
 * Analyze a project area
 *
 * @param teamRepository
 * @param projectArea
 * @throws TeamRepositoryException
 */
public static void analyzeProjectArea(ITeamRepository teamRepository,
		IProjectArea projectArea) throws TeamRepositoryException {
	printProcessAreaDescription(teamRepository, projectArea);
	dumpContributors(teamRepository, projectArea);
	List teamAreas = projectArea.getTeamAreas();
	for (Iterator iterator = teamAreas.iterator(); iterator.hasNext();) {
		ITeamAreaHandle handle = (ITeamAreaHandle) iterator.next();
		ITeamArea teamArea = (ITeamArea) teamRepository.itemManager()
			.fetchCompleteItem(handle, IItemManager.DEFAULT, null);

		printProcessAreaDescription(teamRepository, teamArea);
		dumpContributors(teamRepository, teamArea);
	}
}

Printing the Process Area Description

This method prints the process are description that is not accessible as a string but as content attached to the process area.

/**
 * Print the description of the process area
 *
 * @param teamRepository
 * @param pa
 * @throws TeamRepositoryException
 */
public static void printProcessAreaDescription(ITeamRepository teamRepository, IProcessArea pa)
		throws TeamRepositoryException {
	IDescription desc = pa.getDescription();
	IContent content = desc.getDetails();
	String description = "";
	if (content != null) {
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		teamRepository.contentManager().retrieveContent(content, stream, null);
		try {
			description = stream.toString(content.getCharacterEncoding());
		} catch (UnsupportedEncodingException exception) {
			description = stream.toString();
		}
	}
	String summary = desc.getSummary();
	System.out.println(summary + "\n\nDescription:\n" + description);
}

Printing the Administrators and Team Members of a Process Area

This operation prints the contributors associated to the process area. It separates the administrators and the team members.

/**
 * Iterate over the contributors of the process area and print them sorted
 * as admins and as team members
 *
 * @param teamRepository
 * @param processArea
 * @throws TeamRepositoryException
 */
private static void dumpContributors(ITeamRepository teamRepository,
		IProcessArea processArea) throws TeamRepositoryException {
	System.out.println("Process Area: " + processArea.getName());
	System.out.println("Administrators");
	dumpContributors(teamRepository, processArea, processArea.getAdministrators());
	System.out.println("Team Members");
	dumpContributors(teamRepository, processArea, processArea.getMembers());
}

It uses the following code to iterate a list of contributors and print the information.

/**
 * @param teamRepository
 * @param processArea
 * @param contributors
 * @throws TeamRepositoryException
 */
private static void dumpContributors(ITeamRepository teamRepository,
		IProcessArea processArea, IContributorHandle[] contributors)
		throws TeamRepositoryException {

	for (int i = 0; i < contributors.length; i++) {
		IContributorHandle handle = (IContributorHandle) contributors[i];
		dumpContributor(teamRepository, processArea, handle);
	}
}

This code finally does the printing of the details of the contributor.

/**
* Dump the details of the contributors
*
* @param teamRepository
* @param processArea
* @param handle
* @throws TeamRepositoryException
*/
private static void dumpContributor(ITeamRepository teamRepository,
		IProcessArea processArea, IContributorHandle handle)
		throws TeamRepositoryException {
	IContributor contributor = (IContributor) teamRepository.itemManager()
		.fetchCompleteItem(handle, IItemManager.DEFAULT, null);
	System.out.print(": " + contributor.getUserId() + "\t"
		+ contributor.getName() + "\t" + contributor.getEmailAddress()
		+ "\t");
	IProcessItemService processService = (IProcessItemService) teamRepository
		.getClientLibrary(IProcessItemService.class);
	IClientProcess process = processService.getClientProcess(processArea, null);
	IRole[] contributorRoles = process.getContributorRoles(contributor, processArea, null);
	for (int j = 0; j < contributorRoles.length; j++) {
		IRole role = (IRole) contributorRoles[j];
		System.out.print(role.getId() + " ");
	}
	System.out.println();
}

* Update *

I just realized that using IRole there is no way to access the role name. Sometimes along the way the interface probably needed to be made richer and to not break the older version, an additional interface IRole2 was introduced. This code provides with the additional information.

	for (int j = 0; j < contributorRoles.length; j++) {
		IRole role = (IRole) contributorRoles[j];
		IRole2 role2 = (IRole2) role;	
		System.out.print(role.getId() + "[" + role2.getRoleName() +" " + role2.getRoleLabel() + "] ");
	}

Get The Latest Data

The API caches Data. If you want to make sure that the data is refreshed, use IItemManager.REFRESH instead of IItemManager.DEFAULT in the code above.

Only Analyze the Process Areas of One User

This code, result of this question on jazz.net, shows how to get only the process areas a certain user is member of in a repository:

	IContributor user = teamRepository.loggedInContributor();
	// If having an ID for the user as string
	//IContributor user = teamRepository.contributorManager().fetchContributorByUserId(user)
	IProcessItemService processItemService = (IProcessItemService) teamRepository
			.getClientLibrary(IProcessItemService.class);

	List processAreasOfUser= processItemService.findProcessAreas(user, null, IProcessClientService.ALL_PROPERTIES , monitor);

Summary

Using the API as described above allows you to run automation against project areas. although this describes client API, the server API is similar enough and allows you to do similar things for example to extend this example to iterate the team hierarchy.

I hope that these code examples will help users out there that have a need for more automation.