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.

Advertisements

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.