Rational Team Concert plain Java API’s
There is a set of Java API’s that is available as part of Rational Team Concert. With these API’s you can write programs that can interact with your server in areas such as source control, build and work items. It is a useful mechanism to perform certain custom operations or to integrate with another product.
Note: If you want to use rich source control capabilities (e.g. load files into a sandbox, resolve conflicts, annotate files) then please try the source control command-line interface (CLI) instead.
Setting Up
The Rational Team Concert (RTC) plain Java API’s are in the form of a collection of JAR’s that is included in your classpath to compile and later run your program. Any Integrated Development Environment (IDE) that supports Java can be used or even just a text editor and the command-prompt. It is assumed that you will use the Java development environment that is the most familiar to you.
The plain Java API zips are downloaded from jazz.net. Go to the All Downloads section of the Rational Team Concert download page. Choose the version that matches the version of your RTC server to avoid any version mismatch problems. The Plain Java Client Libraries contains the JAR’s and the Plain Java Client Libraries API documentation contains the javadoc HTML documentation for the available API’s.
Only the classes present in the HTML documentation are considered part of the public API. All other classes can change in ways that will break your code between releases. It is recommended that you use only the classes from the stable API.
Once you have downloaded unzipped the client JAR’s you can include them into the classpath of your IDE. If you are manually compiling and running your client from the command-line then you can use the following commands:
Compile on Unix/Windows: $ javac *.java -extdirs <path_to_jars> Run on Unix: $ java -Djava.ext.dirs=<location_of_your_VMs_ext_dir>:<path_to_pjc_jars> MyMainClass Run on Windows: $ java -Djava.ext.dirs=<location_of_your_VMs_ext_dir>;<path_to_pjc_jars> MyMainClass
Platform Startup and Shutdown
Before beginning to use any of the plain Java API’s there is an explicit startup call that must be made in order for the client to initialize itself. Similarly, when the API’s are no longer needed you must tell the platform to shut itself down so that it can clean up.
public static class MyMainClass { public static void main(String[] args) { TeamPlatform.startup(); try { /* Do all of my work with RTC servers here. */ } finally { TeamPlatform.shutdown(); } } }
Figure 1: Starting up and shutting down the team platform
In Figure 1 the team platform is started up and all of the other work goes into a try block. The shutdown is in the finally block to make sure that even when exceptions are thrown the shutdown routine is invoked. Failing to shut down the platform can potentially hang the Java process even after your program has finished.
Logging Into the Server
Most of the useful work is done once you have logged into a RTC server. In order to log in you will need a valid set of credentials (user name and password) for that server. Those credentials will need to have permission to access everything that you need in order for your program to work such as project areas, team areas, source control streams and components.
public static void main(String[] args) { IProgressMonitor myProgressMonitor = ...; // Create a progress monitor String userId = ...; // Retrieve the userId in a secure way String password = ...; // Retrieve the password in a secure way String repoUri = "https://myserver:9443/ccm"; TeamPlatform.startup() try { // Login to the repository using the provided credentials ITeamRepository repo = TeamPlatform.getTeamRepositoryService().getTeamRepository(repoUri); repo.registerLoginHandler(new ILoginHandler2() { @Override public ILoginInfo2 challenge(ITeamRepository repo) { return new UsernameAndPasswordLoginInfo(userId, password); } }); repo.login(myProgressMonitor); /* Do all of my work with myserver here. */ repo.logout(); } catch (TeamRepositoryException e) { /* Handle repository exceptions such as login problems here. */ } finally { TeamPlatform.shutdown(); } }License terms that govern use of this code can be found here.
Figure 2: Logging into a server
It is important to take appropriate precautions to protect the credentials because they can be used by anyone to gain access to your server. The method you use to protect them is beyond the scope of this article but should be given careful consideration.
In order to log into a server you need the repository URI. It is usually the same URI that you use to log into the server using the eclipse client, CLI or web UI and usually has the form "https://<server_host_name>:<port_number>/<jazz_or_ccm>." If the login is unsuccessful then an exception is thrown otherwise you can begin to access the server.
In any client and server interaction the client is susceptible to network failures and lag. The server can be susceptible to high load at certain times. The code in Figure 2 catches a "TeamRepositoryException" and must handle the variety of possible problems that can occur. For instance, it could simply add the exception to a log file and return from the main method.
There are times when the problem is that the network is lagging or the server has a large load on it. The effect on the client is that when it tries to contact the server there is a delay before it gets a response. This is why many of the API’s accept an IProgressMonitor object. In most cases you can just pass in null but then your program is unable to provide progress information to the end user. The solution is to provide your own implementation of IProgressMonitor that relays information (e.g. "Connecting…" or "Delivering…") to the user. Your program can relay its own progress as well using the same monitor.
Working With Items
Whenever you are accessing build results, work items or project areas they are all items on the server. An item has a unique identifier to distinguish it from any other items. As well, an item has a type (e.g. build result or work item). Each type of item has its own unique properties and there is usually a special client class that can be used to work with those items.
There are different clients for different areas within RTC:
- ITeamBuildClient – Builds
- IWorkspaceManager – Source Control
- IWorkItemClient – Work Items
- IProcessClientService – Project Areas, Timelines, Process
- ILinkManager – Links between different types of items
If you need a particular client in your program you access them using the ITeamRepository that you used to log into the server. Once you have a client you can begin accessing items on the server.
/* Use the repo to get the build client library */ ITeamBuildClient buildClient = (ITeamBuildClient) repo.getClientLibrary(ITeamBuildClient.class); /* Get the build definition for "my.build.id" */ IBuildDefinition definition = buildClient.getBuildDefinition("my.build.id", myProgressMonitor);
Figure 3: Using the build client to get a build definition
When an item is retrieved from the server it sometimes has references to other items. In order to avoid getting too much information from the server at one time the item will provide item handles with the unique ID of the other item. If a method on an item returns an IChangeSetHandle or an IBuildResultHandle it means that you must fetch the rest of the information about that item from the server before you can get more information about it such as the label or the comment. Any method that returns an object with a type that ends in "Handle" returns a handle and not the complete item.
IItemManager itemManager = repo.itemManager() IBuildAverageDataHandle buildAverageData = definition.getBuildAverageData(); IBuildAverageData data = (IBuildAverageData)itemManager.fetchCompleteItem(buildAverageData, IItemManager.DEFAULT, myProgressMonitor); /* The average build time is not available from the handle. It has to be fetched from the server. */ long averageBuildTime = data.getAverageBuildTimeTaken();
Figure 4: Fetching a complete item using the item manager
There are costs associated with fetching items from the server so it is better to try to fetch more than one item at a time. The server processes all of the requests and sends them back in one batch saving the cost of initiating multiple connections and processing multiple response streams.
/* Find all of contributor handles for people who resolved the defects in a certain range. There could be alot of duplicates. Handles do not override equals() or hashCode() so a HashMap with the UUID as a key helps to eliminates duplicate contributor handles. */ Map<UUID, IContributorHandle> resolverHandles = new HashMap<UUID, IContributorHandle>(); for (int i=1000; i<1234; i++) { IWorkItem workItem = workItemClient.findWorkItemById(String.valueOf(i),IWorkItem.FULL_PROFILE, myProgressMonitor); IContributorHandle resolver = workItem.getResolver(); resolverHandler.put(resolver.getItemId(), resolver); } /* Fetch all of the unique resolvers from the server in one request. */ @SuppressWarnings("unchecked") List<IContributor> resolvers = itemManager.fetchCompleteItems( new ArrayList<IContributorHandle>(resolverHandles.values()), IItemManager.DEFAULT, myProgressMonitor);License terms that govern use of this code can be found here.
Figure 5: Fetching multiple items at once
Special Case: Versionables
Some source control items cannot be fetched using the item manager. These special types of items are called versionables and implement the IVersionableHandle or IVersionable interfaces. Files, folders and symbolic links are examples of versionables. If you have an IVersionableHandle you can fetch the IVersionable using the versionable manager from the source control workspace manager client.
IWorkspaceManager workspaceManager = (IWorkspaceManager)repo.getClientLibrary(IWorkspaceManager.class); IVersionableHandle handle = ...; IVersionable versionable = workspaceManager.versionableManager().fetchCompleteState(handle, myProgressMonitor); versionable.getName(); /* Gets the name of the file, folder or symbolic link in this state. */
Figure 6: Fetching versionables using the versionable manager
Querying for Items
Most of the examples up until this point have focused on directly retrieving an exact item based based on a unique ID. Sometimes items must be discovered using queries with more than one result. Fortunately, a number of clients provide a query API to discover their items. For example, you must use a query to discover build results for a build definition. You can order and filter the build results so that you narrow it down to the results in which you are interested.
/* The build definition is easy to get once you know the build ID */ IBuildDefinition definition = buildClient.getBuildDefinition("my.build.id", myProgressMonitor); IBuildResultQueryModel buildResultQueryModel = IBuildResultQueryModel.ROOT; IItemQuery query = IItemQuery.FACTORY.newInstance(buildResultQueryModel); /* Build up a query filter predicate that accepts a build definition as input to the query and checks for any non-personal builds that have a completed state. */ IPredicate predicate = (buildResultQueryModel.buildDefinition()._eq(query.newItemHandleArg()))._and( buildResultQueryModel.personalBuild()._isFalse())._and( buildResultQueryModel.buildState()._eq(BuildState.COMPLETED.name())); query.filter(predicate); /* Order by build start time in descending order */ query.orderByDsc(buildResultQueryModel.buildStartTime()); /* Query for items using the build definition's item ID as the argument. */ /* Use a smaller page size if possible depending on what is queried. */ IItemQueryPage queryPage = buildClient.queryItems(query, new Object[] { definition.getItemId() }, IQueryService.ITEM_QUERY_MAX_PAGE_SIZE, myProgressMonitor); /* Iterate through the results of this page */ while (queryPage.hasNext()) { queryPage = (IItemQueryPage) buildClient.fetchPage(queryPage.getToken(), queryPage.getNextStartPosition(), 1, new SysoutProgressMonitor()); /* Iterate through each subsequent page. Break out of the loop if finished early. */ }License terms that govern use of this code can be found here.
Figure 7: Querying for build result items with multiple pages
The query in Figure 7 will find all of the build results for a build definition. Depending on what is needed from the query the filter can be expanded to produce a larger or smaller set of results. Also, the page size can be limited to the number of build results that are required. Each time a page is fetched it contacts the server to retrieve it based on the query token.
Discovering Links Between Items
RTC has a powerful linking mechanism that allows different types of items to be linked together. For example, a work item can be linked to change sets that implement the enhancement or fix the defect. You can use the ILinkManager client to retrieve the links for a particular item.
ILinkManager linkManager = (ILinkManager) repo.getClientLibrary(ILinkManager.class); IItemReference itemReference = linkManager.referenceFactory().createReferenceToItem(myItem); ILinkCollection linkCollection = linkManager.findLinksByTarget(workItemReference, myProgressMonitor).getAllLinksFromHereOn(); for (ILink link: linkCollection) { /* Links can have types that may help to figure out the meaning of the link. Also, a specific link type can be provided to findLinks() methods on the ILinkManager to filter out all links except the provided type. */ String linkTypeId = link.getLinkTypeId(); IReference sourceRef = link.getSourceRef(); IReference targetRef = link.getTargetRef(); /* If the source reference is an item reference then it can be resolved to an item handle.*/ if (sourceRef.isItemReference()) { Object source = sourceRef.resolve(); /* You can use instanceof to see if it is an instance of a particular item handle type (e.g. IChangeSetHandle). And fetch the complete item in the usual way. */ } else if (sourceRef instanceof IURIReference) { /* IURIReference provides a URI and an option content type. */ } /* The target of the reference can be inspected in the same way as the source. */ }License terms that govern use of this code can be found here.
Figure 7: Using the ILinkManager to find links to an item
Conclusions
Once you have a working development environment with the plain java API’s you can begin automatically logging into your RTC server. Once a login session has been established there are a number of clients available to discover different types of items (e.g. build, work items, source control). Items often contain handles to other items that are fetched using the item manager when needed by your program. Queries are used in cases where items are not directly available using one of the clients such as build result items. The links between items are discovered using the link manager client. With this article you have the information needed to build powerful customizations to RTC and integrations with other products.
More Examples
Sometimes it is helpful to see a number of working examples in order to get a better grasp of how to work with a set of API’s. Three examples are provided below.
Example 1: Check for approvals for delivered changes between two builds
In this example, two builds, either the two latest or two provided as arguments to the program, are examined to discover whether the change sets that are delivered have work items with approvals on them. The work items must have approvals and be in an approved state or a warning is printed to standard out. Download
Example 2: Find the list of changed files and folders on a work item
Sometimes it is helpful to get a list of changed files and folders on a work items without having to manually examine each attached change set. The example program takes a work item ID and prints out this information to standard out. Download
Example 3: Retrieve all of the important dates in an RTC project area
A project area is examined to find all of the development lines and iterations. The user provides the name of the project area and the name of an output file where iCalendar data is generated. This file can be imported into a large variety of programs to show a complete calendar of everything that is happening in your project. An optional parameter narrows it down to only the current iterations to avoid too much calendar clutter. Download
About the author
Chris McGee is a member of the Rational Team Concert source control team.
Copyright © 2013 IBM Corporation