Creating CLM Links With Back Link Between Work Items


CLM Links, especially the back links can be tricky. I ran into some issues with CLM links when working with a partner recently. I wanted to share this learning and protect others from bad surprises.

*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.

I thought I had CLM links under control as posted in Following CALM Links using the Java Client or Server API and related posts. I had to realize that this was not entirely true and I am still baffled, why I did not realize this during testing.

The issue is, that back links are not automatically created when linking a work item to another work item with a CALM link. I have not looked into back links when linking to other CLM objects. This post will describe how CLM links with related back link between work items can be created. There are some limitations for versions prior to 4.0.6 that restrict when you can actually do this as described below.

License

As always, 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. Remember that this code comes with the usual lack of promise or guarantee. Enjoy!

Just Starting With Extending RTC?

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

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

To keep it simple this example is, as many others in this blog, based on the Jazz Team Wiki entry on Programmatic Work Item Creation and the Plain Java Client Library Snippets. The example in this blog shows RTC Client API.

Creating Back Links from a Newly Created Item – Version 4.0.6 and later

* Update * See example participant attached to Enhancement Request 288105 for how creating a link with back link to a newly created item works in the server API.

Creating Back Links – Limitations for Versions Prior to 4.0.6

There are currently limitations for versions prior to 4.0.6  on using the back link creation. The back link creation is done behind the scenes. It requires the work items to already exist. Trying to create the link with back links when creating a work item can lead into a deadlock situation, where the created work item is not yet saved and locked, so that the other operation will time out on the lock. This is, for example, the case in a work item save operation participant.

Enhancement Request 288105 was created to provide this capability and has been solved for 4.0.6.

I have tried to get around the deadlock in previous versions, by changing when what gets saved and when the link is created in which direction, however, to no avail.

Creating Back Links on Saving a Work Item

The RTC API provides additional parameters, that can be used to communicate information across save operations. This can be used in several ways. One example is to communicate between participants and other server extensions. This can, for example be used to detect that a save was initiated by a participant and to avoid to run the same participant being triggered by the subsequent save, to avoid recursion as described in RTC Update Parent Duration Estimation and Effort Participant.

This mechanism is also used to communicate the need to create a back link for CLM links. The API provides the constant IAdditionalSaveParameters.UPDATE_BACKLINKS in the interface com.ibm.team.workitem.common.internal.IAdditionalSaveParameters. Please be aware that this is an internal API with all that entails.

If you provide the additional parameter during a work item save operation, the back links are automatically created too.

You can add the additional parameter in several ways as the code below will show.

Creating Back Links – Client API Example

The example below shows client API code that can be used in Eclipse client extensions and in automation provided with the Plain Java Client Libraries.

The code below is based on the WorkItemOperation class that I usually use in client extensions to avoid having to deal with all the exception handling. The WorkItemOperation is a client API class, that can be extended with your own code and allows to create, modify, and save a work item. The code below adds a CLM (for examples Tracks) link between two existing work items.

The constructor passes the load profile to load the work item that gets modified by adding the outgoing link. It gets the endpoint for the link that is supposed to be created and the other work item passed and stores them in fields.

In the overwritten execute() method, the magic happens. First a URI for the work item is created. In this case I use an URI that is based on the ID of the work item. Another way to create URI’s is based on the work item UUID. I chose this version, because that is the version created when linking work items manually. The URI is used to create a location and to create a target endpoint from the passed endpoint descriptor and the Location’s Absolute URI.

The important step is to add the additional save parameter in the line

workingCopy.getAdditionalSaveParameters().add(IAdditionalSaveParameters.UPDATE_BACKLINKS);

This is only one valid way to do it. As you can see in the server code, you can also add it to the save statement if you are using other ways of saving. This should also work for the WokringCopyManager in the client API.

private static class LinkTrackingWorkItemOperation extends
		WorkItemOperation {

	private IWorkItem fOpposite; // The other work item o link to
	private IEndPointDescriptor fIEndPointDescriptor; // The endpoint to use

	public LinkTrackingWorkItemOperation(IEndPointDescriptor iEndPointDescriptor, IWorkItem opposite) {
		super("Linking Tracking Work Item", IWorkItem.FULL_PROFILE);
		fOpposite = opposite;
		fIEndPointDescriptor=iEndPointDescriptor;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		IWorkItemClient workItemClient = (IWorkItemClient) ((ITeamRepository)fOpposite.getOrigin())
				.getClientLibrary(IWorkItemClient.class);

		URI myURI = ItemURI.createWorkItemURI(workItemClient.getAuditableCommon(), fOpposite.getId());
		Location location = Location.location(myURI);
		IReference targetEndpoint = IReferenceFactory.INSTANCE.createReferenceFromURI(location.toAbsoluteUri());
		workingCopy.getAdditionalSaveParameters().add(IAdditionalSaveParameters.UPDATE_BACKLINKS);
		workingCopy.getReferences().add(fIEndPointDescriptor,
				targetEndpoint);
	}
}

The operation is called using the code below, assuming the work items have been found already.

LinkTrackingWorkItemOperation operation = new LinkTrackingWorkItemOperation(
		ILinkTypeRegistry.INSTANCE.getLinkType(
				WorkItemLinkTypes.TRACKS_WORK_ITEM)
				.getTargetEndPointDescriptor(), opposite);
operation.run(linkdest, monitor);

Creating Back Links – Server API Example

The server API looks very similar, except it is not wrapped into an operation as shown above. Instead of adding the save Parameter to the workingCopy, it is added to the work item save.

	IWorkItemReferences targetReferences = fWorkItemServer
		.resolveWorkItemReferences(currentItem, monitor);

	IEndPointDescriptor tracksEndpoint = ILinkTypeRegistry.INSTANCE
						.getLinkType(WorkItemLinkTypes.TRACKS_WORK_ITEM).getTargetEndPointDescriptor();
	URI myURI = ItemURI.createWorkItemURI(fWorkItemCommon.getAuditableCommon(), currentItem.getId());
	Location location = Location.location(myURI);
	IReference target = IReferenceFactory.INSTANCE.createReferenceFromURI(location.toAbsoluteUri());

	targetReferences.add(tracksEndpoint, target);
	Set additionalParams = new HashSet();
	additionalParams.add(IAdditionalSaveParameters.UPDATE_BACKLINKS);
	// Save the trigger work item with the links we created
	IStatus saveStatus = fWorkItemServer.saveWorkItem3(currentItem,
				currentReferences, null, additionalParams);

Please note, this only works if both work items already exist when the link is created.

Creating Link and Back Link to a New Item  –  Server API Example

See example participant attached to Enhancement Request 288105 for how back link creation works in the server API in case the back link is from a newly created work item.

Summary

This post explains how to create CLM links with back link. The code above is experimental. I have tested the client code. The server code is untested and copied together from different methods. There might be typos and other issues due to copying the code together from different places. I couldn’t test due to the deadlock mentioned above. But that the deadlock occurs hints the code would work.

As always, I hope the code is an inspiration and helps someone out there to save some time.

7 thoughts on “Creating CLM Links With Back Link Between Work Items

  1. Hi Ralph,
    I tired creating CALM link type Tracks as per the methods mentioned in your blog. I also tried the way mentioned in the participant in Enhancement request.
    But I am not getting the proper link at both the ends. In the following code I am creating a link from defect work item to Story work item. (I have also tried creating links without creating back links.)
    IWorkItemReferences references = workItemServer.resolveWorkItemReferences(item, null);
    IEndPointDescriptor defectEndPoint = ILinkTypeRegistry.INSTANCE.getLinkType(WorkItemLinkTypes.CONTRIBUTES_TO_WORK_ITEM).getTargetEndPointDescriptor();
    IReference targetEndpoint = IReferenceFactory.INSTANCE.createReferenceFromURI(Location.namedLocation(storyWI,
    getPublicRepositoryURL()).toAbsoluteUri());
    Set additionalParams2 = new HashSet();
    additionalParams2.add(IAdditionalSaveParameters.UPDATE_BACKLINKS);
    references.add(defectEndPoint,targetEndpoint);
    IStatus status = workItemServer.saveWorkItem3(item, references, null,additionalParams2);
    In above case the Contributes to link which i create manually is not created properly .
    But the back link (Tracks)which is created by RTC is proper.
    I don’t know what I mistake I am doing in manual creation.
    Can you guide me how should i debug this issue? I am not able to figure out what mistake I am doing.
    Thanks & Regards,
    Aastha

    • Hard to tell what the issue is. I always look at the links when I create them manually and then I compare what my automation creates. If they are different, I try to figure out what the difference is and how to create the correct one.

      You should look into the work item commandline, as that has a full implementation that seems to be working according to my tests.

  2. Hi Ralph ,
    Thankyou for your help!!
    Lawrence C.Smith helped me in finding the solution to the problem.
    I was creating the link without any comment. Because of which at the quick information the label was null and hence the link was being displayed as Contributes To(1) instead of Contributes To(1):1
    I replaced the following part of above code
    IReference targetEndpoint = IReferenceFactory.INSTANCE.createReferenceFromURI(Location.namedLocation(storyWI,
    getPublicRepositoryURL()).toAbsoluteUri());

    with

    IReference targetEndpoint = IReferenceFactory.INSTANCE.createReferenceFromURI(Location.namedLocation(storyWI,
    getPublicRepositoryURL()).toAbsoluteUri(),
    “cross -project Contributes TO”);

    ———————————————————————————————————–
    At QuickInformationPart.js the following method uses the comment.
    _addUrlItem: function(linkDTO, listObjectToUpdate, attr, id) {
    if (!linkDTO) return;
    var linkName= linkDTO.comment;
    var newId= “id”;
    linkName= linkName.replace(/:/, newId);
    if (linkDTO.url && linkDTO.url.indexOf(“com.ibm.team.repository.Contributor”) != -1) {
    var shortName= this._getUserAbbrev(linkName);
    } else {
    var shortName= “” + id;
    }
    var bidiTooltipFunc= null;
    var bidiHoverFunc= null;
    if (linkDTO.url.indexOf(“scm.ChangeSet”) > 0) {
    bidiTooltipFunc= this._bidiChangeSetTooltip;
    bidiHoverFunc= this._bidiChangeSet;
    } else if(linkDTO.url.indexOf(“jazz/users”) > 0 || linkDTO.url.indexOf(“jazz/web/projects”) > 0) {
    bidiHoverFunc= this._bidiUserNameWithId;
    bidiTooltipFunc= this._bidiUserNameWithIdTooltip;
    }

    listObjectToUpdate.itemList.push({
    isUrlItem: true,
    shortName: shortName,
    stateGroup: attr.stateGroup,
    summary: linkName,
    resourceLinkUri: linkDTO.url,
    href: linkDTO.url,
    bidiHoverFunc: bidiHoverFunc,
    bidiTooltipFunc: bidiTooltipFunc
    });
    },

    var linkName= linkDTO.comment;

    I was missing setting the comment.
    Thanks & Regards,
    Aastha

  3. Hello Ralph,

    I am using followinmg code to add a parent to a WI. But how can I achieve backward linking using this.

    public void linkParentWorkItem(int parentID, ITeamRepository repo, IWorkItemClient wiClient, IProgressMonitor monitor, IWorkItem task){
    try {
    final ILinkManager linkManager =
    (ILinkManager) repo.getClientLibrary(ILinkManager.class);
    IWorkItem wi=wiClient.findWorkItemById(parentID, IWorkItem.FULL_PROFILE, monitor);
    final IReference workItemRef =
    linkManager.referenceFactory().createReferenceToItem(task);
    URI myURI = ItemURI.createWorkItemURI(wiClient.getAuditableCommon(), parentID);
    IReference targetEndpoint = IReferenceFactory.INSTANCE.createReferenceFromURI(myURI);

    final ILink newLink = linkManager.createLink(
    WorkItemLinkTypes.PARENT_WORK_ITEM, workItemRef,targetEndpoint);
    linkManager.saveLink(newLink, monitor);
    } catch (TeamRepositoryException e) {

    }
    }

    • As far as I am aware backlink creation is only required for CLM links. Parent/Child links and other intra repository work item to work item links do this automatically.

      • Am I missing something in this above code snippet which is required to link parent/child.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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