Get an Item From its URL


This came up on the form recently. It is interesting, because in several areas, including the web UI the URL is what is exposed and not an item ID.

I basically browsed the RTC SDK (using my standard setup described here) and searched for java.net.URI as parameter.

I finally found com.ibm.team.repository.common.Location being used for this purpose. You can basically create a location from an URI and get to the object. There are several methods to get at the item handle. For example Location.itemOidUriToHandle(). See Arne’s answer to his own question for details how he does the trick in an example. To be able to find it in searches, here the code:

URI snapUri= URI.create(snapshotIdentifier.replaceAll(" ", "%20"));
           
Location snapLoc = Location.location(snapUri);
IBaselineSet snapshotItem = (IBaselineSet)
         teamRepository.itemManager().fetchCompleteItem(snapLoc,IItemManager.DEFAULT, monitor); 

I used Location also in this post to create links with back links between items.

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.

Advertisements

Using Reference Presentations to Create Links and Show Linked Work Items in RTC


I recently complained to our development that creating and displaying links between work items is too complex. I wanted a special presentation for that purpose. To my excitement I was instructed, that we have such a feature. Joy! This post explains how you use it.

I have done so much customization in RTC, that I was convinced I knew all of it already. Hubris! My bad. So there are things I haven’t done in RTC. I assume others also have not spotted this and I think it is worth blogging about.

Add a Presentation of Type References

To be able to show specific references (aka links) from the current work item to other items you basically have to add a new presentation to your editor presentation. On the Editor presentation editor select the section where you want to show the presentation and click Add Presentation….

The Presentation you want is a Non-Attribute-based Presentation. Choose this option and select References from the available set of choices.

In the Link Type selection drop down, select the reference (aka link type) you want to see and maintain here.

The image below shows the steps.

Add Reference Presentation

Use a Presentation of Type References

Once you have done that, you have a new presentation that will show only the references/links that qualify. It will also allow you to create new references/links from that dialog. The image below shows what you can do:

Use Reference PresentationSummary

This kind of presentation allows you to view and modify relationships in a more organized way on the work item editor and can help you understanding the relationships better, especially if the relationship represents important information in your object model.

As always I hope this information is useful to someone out there and helps using RTC more effective.

Resolve Parent If All Children Are Resolved Participant


This post is about a Participant that resolves the parent work item, if all children are resolved. It is the example I used to understand the server API for manipulating work item states. I would probably not use it on a production system, because I strongly believe that a human being should do a conscious decision in cases like that, but it is a nice example about the RTC Work Item Server API.

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.

The example in this blog shows RTC Server and Common API.

Download

You can download the code here.

The download contains the (slightly enhanced) UpdateParentDuration Participant described in this post, the UpdateParentStateParticipant and the WorkFlowPathfinder from this post as Eclipse projects. In Eclipse use File>Import and select Existing Projects into Workspace. Select the downloaded archive file and do the import. This version of the WorkFlowPathFinder has the Plain Java Client Libraries dependency removed and is treated as real plug in, to provide its services to the other plug ins.

Please note, If you just get started with extending RTC, I would suggest to start reading this and the linked posts to get some guidance on how to set up your environment. Then read the article Extending Rational Team Concert 3.x and follow the Rational Team Concert 4.0 Extensions Workshop at least through the setup and Lab 1. This provides you with a development environment and with a lot of example code and information that is essential to get started. You should be able to use the following code in this environment and get your own extension working.

UpdateParentSateParticipant

The project com.ibm.js.team.workitem.extension.updateparent.participant contains the class UpdateParentState. This implements the Participant that

  1. Checks if there was a state change
  2. Checks if the new state is a closed state
  3. Checks if there is a parent
  4. If there is a parent, checks if all children are closed
  5. Resolves the parent If all children are closed

The UpdateParentStateParticipant can use different strategies to do the state change. The code below shows some of the available options.

if (!resolveBF(parentHandle, monitor)) {
// if (!resolveDF(parentHandle, monitor)) {
// if (!resolveWithResolveActionID(parentHandle, monitor)) {
// if (!gotoStateBF(parentHandle,"4",true, monitor)) {

The methods resolveBF() and resolveDF() use the Breadth First and the Depth First strategy against the resolve action. The method gotoStateBF() uses the Breadth First strategy but uses a target state instead. The method resolveWithResolveActionID() only tries to apply the action ID. You can play around with the different strategies and pick the one you like best.

The resolve Action needs to be defined in the RTC work Item workflow as shown below, otherwise the state change can’t be done, because no path can be found.

Define teh Resolve Action The WorkflowPathFinder class in the download provides two other ways to find a target state, instead of a target action. The methods are called findPathToState_DF() and findPathToState_BF().

UpdateParentDurationParticipant

The UpdateParentDurationParticipant is slightly enhanced compared to the state in RTC Update Parent Duration Estimation and Effort Participant post. It checks if there are changes to the duration and estimate values before doing anything else.

The code also contains several methods to analyze the work item links on the server as described in The RTC WorkItem Server Link API post.

Summary
The code is experimental. I have tested it in a Jetty based test server using the Scrum template. It is by no means production ready and can be enhanced for various scenarios. However, as always, I hope the code is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.

Following CALM Links using the Java Client or Server API


Going from just Rational Team Concert to a full Collaborative Application Lifecycle Management Solution introduces URI based link types. This post answers this forum question about how to follow URI based references to work items.

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.

*Update* 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, including the creation of all kinds of links. 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. With respect to links, the code shows which kinds of different location types are used.

I updated the post The RTC WorkItem Client Link API – Linking to Work Items and Other Elements and added client API code to analyze the references for a work item, including URI based links.

The code can be reused in server extensions as well, except for the method analyzeReferenceTarget(). The post The RTC WorkItem Server Link API – Linking to Work Items and Other Elements needed to be updated as well, with server specific code for analyzeReferenceTarget(). If you work on a server extension, get the client code from The RTC WorkItem Client Link API – Linking to Work Items and Other Elements and replace analyzeReferenceTarget() with the code for the server from The RTC WorkItem Server Link API – Linking to Work Items and Other Elements.

As always, license statements in the posts apply to the code and remember that this code comes with the usual lack of promise or guarantee. Enjoy!

The RTC WorkItem Server Link API – Linking to Work Items and Other Elements


Yesterday, I blogged about the RTC Client Link API. I excluded the server API, mainly because I realized I was not able to access the references from a WorkItemWorkingCopy on the server. The server API is simply different. I missed looking into an example that I had created some years ago, where I had already solved the issue. So here is the “missing link”.

*Update* See the post Creating CLM Links With Back Link Between Work Items for some new information on CLM links between work items.
*Update* Creating the Links using the ILinkServiceLibrary does not trigger operational behavior.
*Update* I took a deeper look at what to do with URI references on the server.
*Update* Use the new method instead of ILinkServiceLibrary to create links that can be governed by operational behavior.
*Update* Please find some of the code in the download at the post Resolve Parent If All Children Are Resolved Participant.

  • Update * This forum post shows the code that links a work item with a RTC SCM change set.

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 and Common API.

Accessing References on the Server

I found two ways to access references for work items on the server. The post RTC Update Parent Duration Estimation and Effort Participant provides an example that shows how to use the information about existing and new references provided by the ISaveParameter operation. For example using this code:

IWorkItemReferences newRreferences = saveParameter.getNewReferences();

The code provides you with the references for the state of the work item. If you want to access the references for the old work item, or if you just have a work item (for instance a parent) and want the references for that, you have to take a different approach as presented below.

IWorkItemReferences oldReferences=null;
IAuditable oldState = saveParameter.getOldState();
if(oldState!=null){
	IAuditableCommon common = (IAuditableCommon)getService(IAuditableCommon.class);
	IWorkItem oldItem = (IWorkItem)common.resolveAuditable(oldState, IWorkItem.FULL_PROFILE, monitor);
	IWorkItemServer workItemServer = getService(IWorkItemServer.class);
	oldReferences = workItemServer.resolveWorkItemReferences(oldItem, monitor);
}

The code gets the IAuditable from the old state. If the old state is not null (there was a state), the code resolves the IAauditable to the IWorkItem. then it uses the IWorkItemServer service to get the references. Working with the references is identical to the client, except Creating a reference.

Handling CALM or URI References on the server

*Update* I took a deeper look at what to do with URI references.

The code below is the server API replacement for analyzeReferenceTarget() in The RTC WorkItem Client Link API – Linking to Work Items and Other Elements. All other methods to analyze the references found in the post can be used on the server too. The difference on the server is that the client library used in analyzeReferenceTarget() in the post The RTC WorkItem Client Link API – Linking to Work Items and Other Elements needs to be replaced by a service.

The code below tries to find the other end of an URI reference. Those references are used for CALM links such as “Tracks”. In case the other end is a work item it uses the location information to get the work item.

/**
* Further analyze an item referenced by an URI
* @param iReference
*/
public void analyzeReferenceTarget(IReference iReference) {
	URI uri = iReference.createURI();
	try {
		System.out.println(" Resolving URI: " + uri.toString());
		// get the location from the URI
		Location location = Location.location(uri);
		// resolve the item by location
		IAuditableCommon common = getService(IAuditableCommon.class);
		IAuditable referenced = common.resolveAuditableByLocation(
			location, ItemProfile.createFullProfile(
			location.getItemType()), null);
		// look for a referenced work item
		if (referenced instanceof IWorkItem) {
			IWorkItem referencedWI = (IWorkItem) referenced;
			System.out.println(" Resolved URI (resolve): "
				+ uri.toString() + " to: " + referencedWI.getId()
				+ " " + referencedWI.getState2().toString());
		}
	} catch (TeamRepositoryException e) {
	e.printStackTrace();
}
System.out.println(" Resolved URI: " + uri.toString());
}

Creating References on the Server

*Update* Creating the Links using the ILinkManager does not trigger operational behavior.Use creating IReferences instead and save them with IWorkItemServer.saveWorkItem3(). I also ran into a problem with CLM links such as Tracking work item links. I will post a solution as soon as I have one.

This was the hardest part really.

*UPDATE*

I had to write some code for a customer and I needed to create CLM links like Tracks. I used the code I had – the one published in the second next block. It did not work as expected. The issue was, the links where created, but I could not delete them anymore. I had to investigate, talked to one of our developers and we finally came up with the new code below.

/**
 * Link the trigger and the target work item
 * @param sourceWorkItem     the work item to link to
 * @param targetWorkItem     the work item to link to
 * @param monitor
 * @return
 */
private linkWorkItemsCLM(IWorkItem sourceWorkItem, IWorkItem targetWorkItem, IProgressMonitor monitor) {
	fWorkItemServer = getService(IWorkItemServer.class);
	// Get the references for the source work item, we need it to add new links
	IWorkItemReferences targetReferences = fWorkItemServer
				.resolveWorkItemReferences(resolvedTriggerItem, monitor);
	IEndPointDescriptor tracksEndpoint = ILinkTypeRegistry.INSTANCE
			.getLinkType(WorkItemLinkTypes.TRACKS_WORK_ITEM)
			.getTargetEndPointDescriptor();
	Location location = Location.itemLocation(targetWorkItem,
			this.getPublicRepositoryURL());
	IReference targetEndpoint = IReferenceFactory.INSTANCE
			.createReferenceFromURI(location.toAbsoluteUri());
	// The target work item is new, no need to check for duplicates
	targetReferences.add(tracksEndpoint, targetEndpoint);

	// Save the sourceWorkItemwork item with the links we created
	IStatus saveStatus = fWorkItemServer.saveWorkItem3(sourceWorkItem,
			targetReferences, null, null);
}

The code above is able to successfully create CLM and other links from work items to other items, including work items. The code above is different from the code below I came up with first.
It uses the work item server to save the work item, instead of using the ILinkService to create and save the link. That way, operational behavior is called for the save operation and operational behavior can govern the creation of these links. The code below shows how to call the code.

// Load the new trigger work item so that we can modify it and don't get
// stale data
fWorkItemServer = getService(IWorkItemServer.class);
ItemProfile loadProfile = IWorkItem.SMALL_PROFILE
	.createExtension(Arrays.asList(new String[] { IWorkItem.TARGET_PROPERTY }));
IWorkItem sourceWorkItem= (IWorkItem) fWorkItemServer
	.getAuditableCommon()
	.resolveAuditable(workItem, loadProfile, monitor)
	.getWorkingCopy();
linkWorkItemsCLM(IWorkItem sourceWorkItem, IWorkItem targetWorkItem, IProgressMonitor monitor)

In the above code a Location was used to create the target endpoint. This location needs the public repository URI, otherwise the work item won’t be linked properly. The code above works across repositories, if you provide the right public repository URI for the target work item. In the code above the assumption is that both work items are in the same repository.

If you want to link work items with work item references like parent-child references in the same repository, you can use the code below. It avoids having to create the reference from an URI.

IEndPointDescriptor tracksEndpoint = ILinkTypeRegistry.INSTANCE
	.getLinkType(WorkItemLinkTypes.PARENT_WORK_ITEM)
	.getTargetEndPointDescriptor();
IReference targetEndpoint = IReferenceFactory.INSTANCE
	.createReferenceToItem(targetWorkItem);

Note: Don’t use this method, as it avoids triggering operational behavior on save.

In the past, when I dug into the SDK and found bits and pieces. I dug around on Jazz.net and finally found this answer on Jazz.net which I shamelessly used to build my example code. However that was not enough and I had to refine it to get it working properly.

// Get the reference factory to create references
IReferenceFactory refFactory = IReferenceFactory.INSTANCE;
IReference source = refFactory.createReferenceToItem(parent);
IReference target = refFactory.createReferenceToItem(blocked);
// get the link service to have access to the ILinkServiceLibrary
ILinkService linkService = getService(ILinkService.class);
// get the ILinkServiceLibary
ILinkServiceLibrary linkServiceLibrary = (ILinkServiceLibrary) linkService
	.getServiceLibrary(ILinkServiceLibrary.class);
// Create the link
ILink link = linkServiceLibrary.createLink(
WorkItemLinkTypes.BLOCKS_WORK_ITEM, source, target);
// Save the link
linkServiceLibrary.saveLink(link);

Note, please don’t forget to declare the services you use in server extensions as requiredService in the plugin XML as described here.

The RTC WorkItem Client Link API – Linking to Work Items and Other Elements


It is sometimes interesting to follow links in work items. I have seen and answered several related questions at the jazz.net Forum. This post is supposed to summarize what I have found about linking work items using the Plain Java Client Libraries so far. I will focus on the Client Library in this post because I realized that there are some important differences between the client and the server API with respect to accessing the work item’s references. I will try to address the differences on the server side in a later post. For now RTC Update Parent Duration Estimation and Effort Participant provides an example that shows how to follow work item to work item parent-child links on the server in an Advisor or in a Participant. In this case the information about existing and new references can be retrieved using the ISaveParameter.

*Update* 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, including the creation of all kinds of links. 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.

*Update* See the post Creating CLM Links With Back Link Between Work Items for some new information on CLM links betwen work items.

*Update* I figured the server side API and you can find the information in this post.

*Update* I took a deeper look at what to do with URI references.

*Update* See this link for code to get a work item handle from an URI

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 and Common API.

Creating References Using WorkItemEndPoints

The following code shows the code used to create a reference to another work item. It is based on the WorkitemOperation code used in Uploading Attachments to Work Items. It can however be used also with any kind of WorkItemWorkingCopy saved with the WorkingCopyManager too.

The code shows the most basic way to create a reference in RTC using the client API.

/**
 * Inner class to do the modification
 *
 */
private static class WorkItemReferencesModification extends WorkItemOperation {
	private IWorkItemHandle fOpposite;

	public WorkItemReferencesModification(IWorkItemHandle opposite) {
		super("Modifying Work Item References",IWorkItem.FULL_PROFILE);
		fOpposite = opposite;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy, IProgressMonitor monitor) throws TeamRepositoryException {
		// Create a new reference to the opposite item
		IItemReference reference = IReferenceFactory.INSTANCE.createReferenceToItem(fOpposite);
		// Add the new reference using a specific work item end point
		workingCopy.getReferences().add(WorkItemEndPoints.BLOCKS_WORK_ITEM, reference);
	}
}

This is the easiest way to create a reference from a work item to another work item. The code creates a new reference to a work item handle. In the second step it uses endpoints predefined in com.ibm.team.workitem.common.model.WorkItemEndPoints to create the reference. The endpoints of a reference, sort of, define the type of the reference. A reference is some kind of link between two objects. From the perspective of the objects the relationship might be different. For example a work item could have a parent from its perspective. The parent from its perspective would have a child. The relationship between two work items is defined using the endpoints from com.ibm.team.workitem.common.model.WorkItemEndPoints. Both ends of the link have their own endpoint. By defining the opposite endpoint, the other endpoint is also determined.

When looking at the endpoints defined in WorkItemEndPoints it appears that the available endpoints are only for work item to work item links. Taking a closer look would reveal that even not all CLM work item to work item link types are available. In general there is no endpoint for any URL based relationship. You can only use this code if your link is based on these available WorkItemEndPoints.

Creating References Using WorkItemLinkTypes

If it is necessary to create other types of links for example to some URL this mechanism does not work. The code below shows an alternative approach that creates the endpoint using a more fundamental mechanism provided by using the com.ibm.team.links.common.registry.ILinkTypeRegistry.

/**
 * Inner class to do the modification
 *
 */
private static class WorkItemReferencesModification extends WorkItemOperation {

	private IWorkItemHandle fOpposite;

	public WorkItemReferencesModification(IWorkItemHandle opposite) {
		super("Modifying Work Item References",IWorkItem.FULL_PROFILE);
		fOpposite = opposite;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy, IProgressMonitor monitor) throws TeamRepositoryException {
		// Create a new reference to the opposite item
		IItemReference reference = IReferenceFactory.INSTANCE.createReferenceToItem(fOpposite);
		// Create a new end point
		IEndPointDescriptor endpoint = ILinkTypeRegistry.INSTANCE.getLinkType(WorkItemLinkTypes.BLOCKS_WORK_ITEM).getTargetEndPointDescriptor();
		// Add the new reference using a specific work item end point
		workingCopy.getReferences().add(endpoint, reference);
	}
}

The code uses the ILinkTypeRegistry and the WorkItemLinkTypes to create an endpoint and then creates the reference. This code is more flexible and allows more link types to be created. This includes links to elements using a URL such as OSLC links to elements in other OSLC providers. The code below would create a tested by test case link for a work item.

reference = IReferenceFactory.INSTANCE.createReferenceFromURI(new URI("https://clm.example.com:9447/qm/oslc_qm/contexts/_Lm2UIACBEeGZqMjM3RLKTw/resources/com.ibm.rqm.planning.VersionedTestCase/_dJzNgQCBEeGZqMjM3RLKTw"));
workingCopy.getReferences().add(ILinkTypeRegistry.INSTANCE
	.getLinkType(WorkItemLinkTypes.TESTED_BY_TEST_CASE).getTargetEndPointDescriptor(),reference);

Accessing References

I am aware of two ways to access the references of a work item using the Client Libraries. If you have a WorkItemWorkingCopy you can access the references using this code:

// get all references from the work item workingcopy
IWorkItemReferences references = workingCopy.getReferences();

It is easy to get a working copy from a work item that already is derived from a working copy using

WorkItemWorkingCopy workingCopy = (WorkItemWorkingCopy) workItem.getWorkingCopy()

Another way to access the references, if you have a plain work item, is using the IWorkItemCommon client library. Note, in this case the ITeamRepository is required which can be retrieved using the method .getOrigin(). The IWorkItemClient client library provides this method as well, in case it is already available.

IWorkItemCommon common= (IWorkItemCommon) ((ITeamRepository)workItem.getOrigin()).getClientLibrary(IWorkItemCommon.class);
IWorkItemReferences references = common.resolveWorkItemReferences(workItem, null);

Once we have an IWorkItemReferences object we can analyze the references.

/**
 * Analyze the references of a workitem
 */
private void analyzeReferences(IWorkItemReferences references) {
	List endpoints = references.getTypes();
	for (IEndPointDescriptor iEndPointDescriptor : endpoints) {
		System.out.println("Endpoint: "
			+ iEndPointDescriptor.getDisplayName() + " ID: "
			+ iEndPointDescriptor.getLinkType().getLinkTypeId());
		List typedReferences = references.getReferences(iEndPointDescriptor);
		for (IReference iReference : typedReferences) {
			analyzeReference(iReference);
		}
	}
}

The IWorkItemReferences provides all available link types for the contained references. The code above gets the list and iterates it to look at the references for each type. The code prints some information. The next step is to get all the references for a given endpoint and analyze each reference.

/**
 * Analyze a reference
 */
public void analyzeReference(IReference iReference) {
	if (iReference.isItemReference()) {
		Object resolvedRef = iReference.resolve();
		analyzeItem(resolvedRef);
	}
	if (iReference.isURIReference()){
		analyzeReferenceTarget(iReference);
	}
}

The code above checks each reference if it is a reference to an IItem for example an IWorkItem or an URI reference. For the item reference there is more to analyze so the code resolves it to get the object an passes it on. For an URI reference the code gets the URI and prints it.

The resolved object can now be analyzed in the code below. A cast is used to get to the contained element such as an IWorkItemHandle or a BuildResultHandle. Once the handle is available it is possible to use the ITeamreposiory.itemManager() to get the item from the handle and manipulate it.

/**
 * Analyze an Item
 */
private void analyzeItem(Object resolvedRef) {
	System.out.println(" Resolved item: "
		+ resolvedRef.toString());
	if(resolvedRef instanceof IWorkItemHandle){
		IWorkItemHandle handle = (IWorkItemHandle)resolvedRef;
	}
	if(resolvedRef instanceof BuildResultHandle){
		BuildResultHandle handle = (BuildResultHandle)resolvedRef;
	}
}

*Update* I looked deeper in what the client API provides in terms of accessing the elements referenced by the URI. Here is what I came up with. Please be aware that I am unsure if this works for all cases. It seems to be possible to resolve the element for example if it is in the same repository. If it is a work item you can then use the typical interfaces to access its data.

/**
 * Further analyze an item referenced by an URI
 * @param iReference
 */
public void analyzeReferenceTarget(IReference iReference) {
	URI uri = iReference.createURI();
	try {
		System.out.println("   Resolving URI: " + uri.toString());
		ITeamRepository teamRepo = (ITeamRepository) iReference.getLink().getOrigin();
		IAuditableClient auditableClient = (IAuditableClient) teamRepo.getClientLibrary(IAuditableClient.class);

		// get the location from the URI
		Location location = Location.location(uri);
		// resolve the item by location
		IAuditable referenced = auditableClient.resolveAuditableByLocation(location,
				ItemProfile.createFullProfile(location.getItemType()), null);
		// look for a referenced work item
		if (referenced instanceof IWorkItem) {
			IWorkItem referencedWI = (IWorkItem) referenced;
			System.out.println("   Resolved URI (resolve): "
				+ uri.toString() + " to: " + referencedWI.getId()  
				+ " " + referencedWI.getState2().toString());
		}
	} catch (TeamRepositoryException e) {
		e.printStackTrace();
	}
	System.out.println("   Resolved URI: " + uri.toString());
}

This is pretty much the summary of how links work on the client. I hope the code is useful, and it is easy enough to enhance the code for other purposes. Please remember that there is few error handling at this point. You might want to enhance this.

Common API to create Link URI’s

If you want to provide a URI for elements such as work items, you can use the Location class to do so.

The following code creates two different URI’s for a work item that are used in different types of links.

IWorkItemCommon common = (IWorkItemCommon) teamRepository.getClientLibrary(IWorkItemCommon.class);

int id = new Integer(idString).intValue();
IWorkItem workItem = common.findWorkItemById(id,IWorkItem.SMALL_PROFILE, monitor);
if (workItem == null) {
	System.out.println("Work item: " + idString + " not found.");
	return false;
}

System.out.println("Work item: " + workItem.getId() + ".");
Location location = Location.namedLocation(workItem,((ITeamRepository) workItem.getOrigin()).publicUriRoot());
System.out.println("Named Location URI: " + location.toAbsoluteUri());
		
location = Location.itemLocation(workItem,((ITeamRepository) workItem.getOrigin()).publicUriRoot());
System.out.println("Item Location URI: " + location.toAbsoluteUri());

The named location looks something like

https://server:port/rtc/resource/itemName/com.ibm.team.workitem.WorkItem/45943

The item location looks something like

https://server:port/rtc/resource/itemOid/com.ibm.team.workitem.WorkItem/_cdH5kJ0REean7cO1UYIcNw

Different link types use the different locations for their endpoints. The API is common api and available in the client as well as the server.

RTC Update Parent Duration Estimation and Effort Participant


I am helping users with questions in the forum  for quite some time now. One area where a lot of questions come up is around the API and how to extend Rational Team Concert. One very popular question, and really asked a lot recently, is how to update a parent or child work item when saving a work item. Since this comes up so often and I can’t find the example I believe I once found on the Jazz Wiki anymore, I wrote my own code and I intent to show it in this post.

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.

The example in this blog shows RTC Server and Common API.

Download

*Update* I published a slightly enhanced version of the code presented below in the post Resolve Parent If All Children Are Resolved Participant. You can download the code here and it contains this example as well.

Solution Overview

The task is simple: when a work item gets saved, we want to update the estimates, correction and time spent on the parent work item based on the accumulated data of all its children.

Rational Team Concert supports this by creating a so called Participant. The Participant is one or more Eclipse plug-ins that are extending the extension point com.ibm.team.process.service.operationParticipants for, in this case, the operation ID com.ibm.team.workitem.operation.workItemSave. You can find a list of extension points and operation ID’s here in the Jazz Wiki. The Rational Team Concert 4.0 Extensions Workshop shows all the steps required to create a complete participant also sometimes called a follow up action. Please note that all code below is for a Server extension. Client extensions would use client libraries that have similar names.

The following picture shows the data on the work item that we are interested in.

Estimation and effort tracking data of a work item

Estimation and effort tracking data of a work item

*Update* A participant or follow up action works after the fact of saving. RTC also supports preconditions or Advisors. Start here if you are looking into doing something like this.

Lets look at the initial code. The explanation follows.

package com.ibm.js.team.workitem.extension.updateparent.participant;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.links.common.IItemReference;
import com.ibm.team.links.common.ILink;
import com.ibm.team.links.common.IReference;
import com.ibm.team.links.common.factory.IReferenceFactory;
import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.IProjectAreaHandle;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.runtime.IOperationParticipant;
import com.ibm.team.process.common.advice.runtime.IParticipantInfoCollector;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.IWorkItemHandle;
import com.ibm.team.workitem.common.model.IWorkItemReferences;
import com.ibm.team.workitem.common.model.WorkItemEndPoints;
import com.ibm.team.workitem.service.IWorkItemServer;

public class UpdateParentDuration extends AbstractService implements
IOperationParticipant {

	// The attribute ID's hard coded. TODO: make this configurable
	private static final String WORKITEM_ATTRIBUTE_CORRECTEDESTIMATE = "correctedEstimate";
	private static final String WORKITEM_ATTRIBUTE_TIMESPENT = "timeSpent";

	// Services we need
	private IWorkItemServer workItemServer;
	private IWorkItemCommon wiCommon;

	@Override
	public void run(AdvisableOperation operation,
		IProcessConfigurationElement participantConfig,
		IParticipantInfoCollector collector, IProgressMonitor monitor)  throws TeamRepositoryException {

		// First check that the operation was a 'save' and get he operation data.
		Object data= operation.getOperationData();
		if (!(data instanceof ISaveParameter))
			return;

		// Check that this was a save operation on a work item
		ISaveParameter saveParameter= (ISaveParameter) data;
		if (!(saveParameter.getNewState() instanceof IWorkItem))
			return;

		/**
		 *  remove comment from the code below to prevent the code from recursive updates
		 */
		// if (saveParameter.getAdditionalSaveParameters().contains(IExtensionsDefinitions.UPDATE_PARENT_DURATION_EXTENSION_ID)) { return; }

		// Check to see if the work item has a 'Parent'
		IWorkItemHandle parentHandle = findParentHandle(saveParameter, monitor);
		if (parentHandle == null)
			return;

		// Get the required service interfaces
		workItemServer = getService(IWorkItemServer.class);
		wiCommon = getService(IWorkItemCommon.class);
		// Roll the child estimates up into the parent estimate
		updateParent(parentHandle, monitor);
	}

What the code does is essentially, get the parameters and check if it is responsible for this operation. If this is the case it checks if a parent exists, retrieves it, and then tries to update the parent from its children. It tries to decide as fast as possible if it has to run. The reason is that it would block a user interface operation longer than necessary if it does too much work. It would be possible to add additional checks. For example it would make sense if the save has changes to the attributes we are interested in.

Avoid Recursions

The checks contain code that is commented out to be able to avoid recursive calls of the participant. The details are described in the section Save The Parent.

What is missing now is the code to find the parent. This is done in the following operation:

/**
 * Find the parent of this work item
 * @param saveParameter
 * @param monitor
 * @return a work item handle of the parent or null if a parent does not exist.
 */
private IWorkItemHandle findParentHandle(ISaveParameter saveParameter, IProgressMonitor monitor) {

	// Check to see if the references contain a 'Parent' link
	List references = saveParameter.getNewReferences().getReferences(WorkItemEndPoints.PARENT_WORK_ITEM);
	if (references.isEmpty())
		return null;

	// Traverse the list of references (there should only be 1 parent) and
	// ensure the reference is to a work item then return a handle to that work item
	for (IReference reference: references)
	if (reference.isItemReference() && ((IItemReference) reference).getReferencedItem() instanceof IWorkItemHandle)
		return (IWorkItemHandle)((IItemReference) reference).getReferencedItem();
	return null;
}

The  code basically gets the parent references of the new state of the work item that is being saved and returns it if one exists.

The last part we are missing is the most complex one. We want to read the children of the parent we found and update the parent with the accumulated value of the estimation and effort data. This is done with the code below:

/**
 * Update the parent from the estimation data of its children.
 *
 * @param parentHandle
 * @param monitor
 * @throws TeamRepositoryException
 */
private void updateParent(IWorkItemHandle parentHandle, IProgressMonitor monitor) throws TeamRepositoryException
{
	// Get the full state of the parent work item so we can edit it
	IWorkItem parent = (IWorkItem)workItemServer.getAuditableCommon().resolveAuditable(parentHandle,IWorkItem.FULL_PROFILE,monitor).getWorkingCopy();
	IAttribute timeSpentAttribute = wiCommon.findAttribute(parent.getProjectArea(), WORKITEM_ATTRIBUTE_TIMESPENT, monitor);
	IAttribute correctedEstimateAttribute = wiCommon.findAttribute(parent.getProjectArea(), WORKITEM_ATTRIBUTE_CORRECTEDESTIMATE, monitor);

	long duration = 0; // Estimate
	long correctedEstimate = 0; // Corrected estimate
	long timeSpent = 0; // TimeSpent

	// get all the references
	IWorkItemReferences references = workItemServer.resolveWorkItemReferences(parentHandle, monitor);
	// narrow down to the children
	List listChildReferences = references.getReferences(WorkItemEndPoints.CHILD_WORK_ITEMS);

	IReference parentEndpoint = IReferenceFactory.INSTANCE.createReferenceToItem(parentHandle);
	for (Iterator iterator = listChildReferences.iterator(); iterator.hasNext();) {
		IReference iReference = (IReference) iterator.next();
		ILink link = iReference.getLink();
		if (link.getOtherEndpointDescriptor(parentEndpoint) == WorkItemEndPoints.CHILD_WORK_ITEMS) {
			IWorkItem child = (IWorkItem) workItemServer.getAuditableCommon().resolveAuditable( 
				(IWorkItemHandle)link.getOtherRef(parentEndpoint).resolve(), WorkItem.FULL_PROFILE, monitor);
			long childDuration = child.getDuration();
			timeSpent+=getDuration(child,timeSpentAttribute,monitor);
			correctedEstimate+=getDuration(child,correctedEstimateAttribute,monitor);
			if(childDuration>0)
				duration += childDuration;
		}
	}

	// We want to modify the parent, so get a working copy.
	parent = (IWorkItem)parent.getWorkingCopy();
	// Set the duration on the parent to be the total of child durations
	parent.setDuration(duration);
	// Set the corrected estimation
	parent.setValue(correctedEstimateAttribute, correctedEstimate);
	// Set the time spent/remaining
	parent.setValue(timeSpentAttribute, timeSpent);

	// Save the work item with an information that could be used to prevent recursive ascent.
	Set additionalParams = new HashSet();
	additionalParams.add(IExtensionsDefinitions.UPDATE_PARENT_DURATION_EXTENSION_ID);
	workItemServer.saveWorkItem3(parent, null, null,additionalParams);
	return;
}

This code does a lot. First it gets the full state of the parent. We need the parent as a work item to be able to get the attributes we are interested in and we need the latest state so that the work item can be edited at all.

Then the code looks up the attributes for Time Spent/Time Remaining and the Corrected Estimate, using the attribute ID’s.

The code then iterates the references of the parent to find the child work items. For each child it looks up the values of the attributes we are interested in and adds the data up, if there is a value. The value -1 indicates the attribute is uninitialized.

The last steps are to get a working copy of the parent work item so that it can be modified. Then the calculated values are set.

Save The Parent

Finally the work item is saved.

The saveWorkItem3() operation takes an additional parameter, a set of strings. This can be used to detect that a subsequent trigger of the participant was caused by this save operation. The following code inserted into the run() operation would allow to prevent this from happening, e.g. to prevent that the parent’s save causes another roll up.

Communication Between Operations to Avoid Recursions

The code updates the parent work item. This will cause a workitem save operation and also trigger the associated advisors and follow up actions including this one. The saving of the parent will cause this participant to run and update its parent and so forth.

There are cases, where this is OK, like in this case. But there are other cases where this can cause issues like loops and the like. Loops or endless recursions can cause  your server to crash, so you need to prevent this from happening.

This is what the code below can be used for. This code looks at additional parameters – basically strings. If some expected string is present the operation finishes.  The additional parameter is provided when saving the work item already in the code above.

/**
 *  remove comment from the code below to prevent the code from recursive updates
 */
if (saveParameter.getAdditionalSaveParameters().contains(IExtensionsDefinitions.UPDATE_PARENT_DURATION_EXTENSION_ID)) {
	return; 
}

There is still some code missing, that gets the value of the attributes for Time Spent and the Corrected Estimate. If there is no data we return 0 so that we don’t break anything.

private long getDuration(IWorkItem child, IAttribute attribute, IProgressMonitor monitor) throws TeamRepositoryException {
	long duration = 0;
	if(attribute!=null && child.hasAttribute(attribute)){
		Long tempDuration = (Long)child.getValue(attribute);
		if(tempDuration!=null && tempDuration.longValue()>0)
			return tempDuration.longValue();
	}
	return duration;
}

Now the participant’s code is finished. You would have to create the plugin and a component as described in the Rational Team Concert 4.0 Extensions Workshop to deploy it. The plugin.xml would look similar to the code below, please note the prerequisites that you have to enter manually. All services you want to use need to be listed here. There is also a reference to a component com.ibm.js.team.workitem.extension.component that is defined in its own plug-in.

Extension plugin.xml

Extension plugin.xml

* Update* I was leaving out the IExtensionsDefinitions. It just defines the ID of the extension

package com.ibm.js.team.workitem.extension.updateparent.participant;

public class IExtensionsDefinitions {
	/**
	 * The extension id is used to identify the operation participant to Jazz.
	 * It is also included in instantiations of the participant in process
	 * definitions.
	 */

	public static final String UPDATE_PARENT_DURATION_EXTENSION_ID = "com.ibm.js.team.workitem.extension.updateparentduration";
}

Now we have the most important code for the plugin. You should be able to get it working. Please remember that there is few error handling at this point. You might want to enhance this.