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.

Illegal Argument 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.

34 thoughts on “Understanding and Using the RTC Java Client API

  1. Pingback: Groovin’ with the RTC APIs | perceive:dev

  2. Ralph, brilliant stuff. Especially the “run from commandline” just saved my weekend. I tried to figure this out but when reading your article I finally got the syntax / directories right and got it to run. Life saver!

  3. Hi Ralph,
    Please let me know, how to connect rational team concert from java in eclipse IDE using OSLC.
    I am very new to IBM RTC

    • This blog is – so far – mostly about the SDK and the Java API’s. They use REST/OSLC behind the scenes. If you want to use OSLC, try looking at the OSLC Workshop in Jazz.net. You usually use the apache libraries to send and receive data and you need some XML or other format library to parse the data.

  4. Hi ,
    The article is awesome.
    I am querying the work items and I am able to display the results in the workitems view.
    Now the user will selected any of the work item by double clicking and the corresponding work item will be opened in the editor.
    Can you please tell me how to a get selected work item number from the query editor or from the work items view?

    Regards,
    Raj

      • Hii rsjazz,
        I went through the article. I couldn’t find the solution.
        Actually my problem is I am able to display work items in the work items view by querying.I am using
        QueryUI.showworkItems…(..)
        The result of the above method will be the work items which will be displayed in the work items view.
        Now when the user double clicks on any of the work item editor , that particular work item will be opened so that the user can change.
        I want that particular work item id ?

      • I am not sure what the question is. Do you want the ID or not, or what do you want to do? And where do you want it and what do you want to do with it then?

        If you want to be able to have a selection in an Eclipse view (e.g. one or two work items) and an action (context menu) then you would create and deploy an Eclipse extension that is enabled for that selection and gets started in the context menu. This extension gets called in a workflow, where it gets an IStructuredSelection / ISelection which contains the objects in that selection and then does something with it.

        https://rsjazz.wordpress.com/2014/02/21/adding-context-menus-for-jazz-objects-to-the-rtc-eclipse-client/ explains this for SCM data, however this can be done similarly for other objects and views. The interfaces are the same, the only difference would be how to register the action, how to present it etc.

        If you want a double click action, this is a similar extension and it works almost identically (I think, not sure if or when I have done that).

        This (if my assumption is correct) does not have anything at all to do with RTC API at that level. This is pure Eclipse extension. You can find loads of material on Eclipse.org.

        The RTC API would be involved once you get the selection list and then want to work with it.

  5. Ralph, Good stuff for beginners looking for using Java API for creating RTC work items. I had couple of questions –
    1. How do we create different type of work items say Story/Defect?. I am able to create Task based on the example provided above.
    2. Is there a way to get the hyperlink of the work item created, that can be used in an Email being sent?

    • You need to get the work item type from the ID – it has to be in your example.
      workItemClient.findWorkItemType(projectArea, typeIdentifier, monitor)

      I think there is only the way to create the URI based on the information from the teamrepository. There are two formats. One uses the UUID and one uses the number. It should be easy enough to create that from the data you have just by looking at how the URLS look like in links or in the browser address. You might be able to locate some code in the work item commandline, but I think I haven’t done that conversion yet.

      • I just added some code examples – this post is pretty old and I didn’t publish downloadable code back then.

      • Thanks Ralph for your quick response.
        What should be the ‘typeIdentifier’? I tried passing ‘task’ & ‘Defect’ as ‘typeIdentifier’ and was able to create a Task and Defect work item respectively. but if i pass ‘Story’, it returns me NULL.
        I did use workItemClient.fetchAllWorkTypesNames(projectArea, monitor) and verified that we have ‘Story’ and few other work item types there. May be I am missing something here.

      • The value is the ID that you can find in the process configuration for the work item Types&Attributes in the ID row.

  6. Hello ,

    Article is very useful and good.

    I have query :
    projectArea = (IProjectArea) processClient.findProcessArea(uri, null, null);
    I am getting error Project are not found.
    Value of Project Area Is NULL.
    Please let me know why this error ?

    • I am pretty sure that this is the effect of not finding a project/process area with the given URI. If you get null if you search it is almost always a user error when providing the search information.

    • I think I replied, but can’t find the reply…….. So here again: If this call does not find a process area, the most likely problem is a user input problem and there is no process area for the given URI.

  7. Hello Ralph ,
    Please let me know , is there any way to log RTC Plain JAVA API Internals using log4j.
    Thanks

    • What are RTC Plain JAVA API Internals ? You can use log4j or other loggers for the part of the code you develop. If the API code has no logging implemented you obviously can’t do any logging.

      • Hello Ralph ,

        Thanks for your reply .
        But when we run logger in a debug mode,I get a lot of logs from RTC , this implies that RTC Plain java APIs may do implement logs. I was wondering if I can get this logger names, so that I can filter out the log details which I am not interested.

  8. Hello Ralph,

    I have another query ,we are getting the stale exception .. while we are updating the work item with a particular attribute. I think code is fine.Will you please provide some input on this please.

    Thanks
    Girish

  9. Hi. Thanks for great examples! I was wondering if there is a way to programmatically figure out how many active session a given user has. I am running a testsuite on 5 different servers and it connects to RTC to open a defect from each server. RTC credentials used are same on all servers. Appreciate your help. Thanks!

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.