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.

Advertisements

About rsjazz

Hi, my name is Ralph. I work for IBM and help colleagues and customers with adopting the Jazz technologies.
This entry was posted in Jazz, RTC, RTC Extensibility and tagged , , , , , . Bookmark the permalink.

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

  1. Fan Hu says:

    It’s really good stuff! Excellent!
    When I create links based on your code:
    workingCopy.getReferences().add(ILinkTypeRegistry.INSTANCE.getLinkType(WorkItemLinkTypes.RELATED_ARTIFACT).getTargetEndPointDescriptor(),reference);
    I can create a link. But do you know how I can adding an “comment” into it? So the workitem will show the comment instead of the long URL.
    Thanks in advance

    • rsjazz says:

      Hi,

      When you create the reference you can create that with a comment e.g. reference = IReferenceFactory.INSTANCE.createReferenceFromURI(fURI,”Some Comment”);
      Or IReferenceFactory.INSTANCE.createReferenceToItem(item, comment, extraInfo);

      There should be factories to create with comment and extra info for all these calls.

  2. shmulik says:

    I use this to do some grate things.
    when I use
    WorkItemWorkingCopy workingCopy = (WorkItemWorkingCopy) workItem.getWorkingCopy()

    My debuger replay :
    java.lang.ClassCastException: com.ibm.team.workitem.common.internal.model.impl.WorkItemImpl incompatible with com.ibm.team.workitem.client.WorkItemWorkingCopy

    what do I miss?
    Thanks in advance

    • rsjazz says:

      I think I state that it is easy to get the workingcopy from the object if it was already a workingCopy. If you just have a work item, you need to get the working copy first using a workingcopy manager or service.

      This is from snippet5.java in the plain Java Examples:

      IWorkItemClient service = (IWorkItemClient) repo.getClientLibrary(IWorkItemClient.class);
      IWorkItemType workItemType = service.findWorkItemType(projectArea, “defect”, monitor);
      IWorkItemHandle handle = service.getWorkItemWorkingCopyManager().connectNew(workItemType, monitor);
      WorkItemWorkingCopy wc = service.getWorkItemWorkingCopyManager().getWorkingCopy(handle);
      IWorkItem workItem = wc.getWorkItem();
      try {
      List findCategories= service.findCategories(projectArea, ICategory.FULL_PROFILE, monitor);

    • rsjazz says:

      Please note, that in my code the WorkItemOperation creates the workingcopy for me if it is called. So I would suggest to get the workingcopy as described in my answer before, if you don’t have one already.

  3. Jérôme says:

    Hi,

    I’m trying to create a reference to a IBuildResult. I know how to do it manually, and found that the link type id is “com.ibm.team.build.common.link.reportedAgainstBuilds”.
    I can’t find this one in WorkItemLinkTypes ok. But even if I try to create it manually it fails.

    ILinkTypeRegistry.INSTANCE.getLinkType(“com.ibm.team.build.common.link.reportedAgainstBuilds”) returns null and ILinkTypeRegistry.INSTANCE.isRegistered(“com.ibm.team.build.common.link.reportedAgainstBuilds”) false.

    Any idea ?

  4. Moti Werheimer says:

    Hi Ralph,
    I have link of snapshot to workitem using “Related Artifacts”. The link contains the URL of this snapshot.
    When trying to extract it from the link using the code above (auditableClient.resolveAuditableByLocation) I get an exception. Looks like that a “IWorkspaceConnection” is not an auditable.
    Any Idea?

  5. Mac says:

    Hi Ralph,

    I have a changeset with a link to a workitem from a ‘friend’ provider, so the workitem is not found if I try to resolve it, do you know if there’s any server API to retrieve those ‘external’ objects? Just trying to resolve the work item, get some attributes but I get an ItemNotFoundException.

  6. CRoger Layani says:

    Hi Ralph
    I would like using this link to create programmatically a CQ defect and link it to a RTC work Item
    I can create Defect by CQ rest api
    I can create a link to this URL using your example code
    but this link does not have the “hover” property that have a such link created using RTC-CQ bridge interface
    Maybe you have any idea why?

    Thanks
    Roger

    • rsjazz says:

      Hi Roger,

      I have not looked into this in detail. I would suggest to analyze links created with the bridge and try to figure how they have been created.

  7. Andy says:

    Thank you for all the code you give to the community! I have many classes starting with Thank you Ralph and a link here…. 🙂

    Quick question: I am using this almost verbatim to create a simple link between an out of the box defect work item and a custom work item. Odd thing is, it only works if I put workingCopy.save(monitor) in the execute method (rtc 4.0.6). If I don’t do this, then I do not see the monitor output “Saving Work Item” nor does the link appear. I’m fine with that, I’m happy it’s working but was wondering if you might have an idea since I thought WorkItemOperation was supposed to take care of that.

    • rsjazz says:

      That is odd. Here is code I used and I did not do a save:

      private static class LinkBlockingWorkItemOperation extends
      WorkItemOperation {

      private IWorkItemHandle fOpposite;

      public LinkBlockingWorkItemOperation(IWorkItemHandle opposite) {
      super(“Linking Blocking Work Item”, IWorkItem.FULL_PROFILE);
      fOpposite = opposite;
      }

      @Override
      protected void execute(WorkItemWorkingCopy workingCopy,
      IProgressMonitor monitor) throws TeamRepositoryException {
      IItemReference reference = IReferenceFactory.INSTANCE
      .createReferenceToItem(fOpposite);
      workingCopy.getReferences().add(WorkItemEndPoints.BLOCKS_WORK_ITEM,
      reference);
      }
      }

  8. vijaykumar says:

    @rsjazz
    public void analyzeReferenceTarget(IReference iReference)

    if it is not work item and URI is referring for the Requirement or test case how to access the details of those artifacts ??

  9. vijaykumar says:

    @rsjazz I need one clarification
    currently I am using RTC Plain java libraries to fetch stories , Epics linked to task which is in turn linked to change set .So at the level of story i need to get info related to Test case and requirment. So at this level to fetch Test case and Requirement you are telling to use OSLC.

    Since i am new to OSLC programming my basic question is , is it possible to combine both RTC plain java program and OSLC program together(totally it should act as one application) ? .

    if you know any sample code where plain java and OSLC used together please give me link.Thanks in advance.

    • rsjazz says:

      If you look into the RTC Plain Java Client Libraries, you see several Apache libraries. OSLC is HTTP/HTML which basically the RTC inner workings use as well. So yes, you can.

  10. Chandra says:

    @rsjazz
    Usecase is we wanted an OSLC link be created from ALM work item to Clearquest Record.
    By following the above line of code we are able to succeed.
    i need one clarification required,
    How is the Authentication happening to Clearquest side. Because if i manual try the usecase i should authenticate. Why i required this,
    When i try to run the code i get the below error,
    not authorised and not logged in to repository. But when open the same in web and authenticate to Clearquest and run the script ,things started working.
    How is this connected? Could you please answer this?

    • rsjazz says:

      Sorry, I can’t answer this. I use it against one CLM server and in this case apparently you get cross certified. If you run against other servers, I assume you have to use apache to log into these other servers first. I have no example code for that. There might be code in the SDK.

  11. And how to use related work items in query? For example, how to query for all work items where exists link of some type to other work item?

    • rsjazz says:

      I have no example for that and I wasn’t able to find one quickly either. It takes time to find this in the SDK and I basically don’t have that time.

  12. Khuram says:

    This has been really helpful! Do you have an example for removing references from work items too? Thanks.

    • rsjazz says:

      In the workitem commandline. Remove the reference from the collection and save the work item with the new collection, I think. check the workitem commandline.

  13. Girish Chandra P says:

    This blog helped to get the details of the link in a great extent.
    Thanks Ralph.

  14. Alessandro Notte says:

    @rsjazz
    I used the code “WorkItemReferencesModification” to implement “WorkItemLinkTypes.AFFECTED_BY_DEFECT”.
    It seems to work, but when i try to remove the link manually (only to check if everything worked), i had this message:
    java.lang.IllegalArgumentException: Host name may not be null

    Thanks
    Alessandro

    • rsjazz says:

      This is a symptom of one of the following two issues
      1. Wrong direction of the link ends
      2. The Reference was created in the wrong way.

      For 2. there are multiple ways how the URIs are created. Work Item Link types and location or item based link types.

      Location location = Location.location(uri); is for location types. You have to look what types the relationship needs by looking at an example that was created by the tool and then you have to create the link accordingly.

    • Alessandro Notte says:

      First al thanks for the answer, it was very helpfull to understand what it was wrong.
      So with this class:
      private class LinkTrackingWorkItemOperation extends WorkItemOperation {
      private IWorkItem fOpposite;
      private IEndPointDescriptor fIEndPointDescriptor; // The endpoint to use
      public LinkTrackingWorkItemOperation(IEndPointDescriptor iEndPointDescriptor, IWorkItem opposite) {
      super(“Modifying Work Item References”,IWorkItem.FULL_PROFILE);
      fOpposite = opposite;
      fIEndPointDescriptor=iEndPointDescriptor;
      }
      @Override
      protected void execute(WorkItemWorkingCopy workingCopy, IProgressMonitor monitor) throws TeamRepositoryException {
      IWorkItemClient workItemClient = (IWorkItemClient) teamRepository.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);
      }
      }

      I can call with:
      LinkTrackingWorkItemOperation operation = new LinkTrackingWorkItemOperation(
      ILinkTypeRegistry.INSTANCE.getLinkType( WorkItemLinkTypes.AFFECTED_BY_DEFECT).getTargetEndPointDescriptor(), wi);
      …..

      thanks again
      Alex

  15. chakradhar says:

    IWorkItem workItem = workItemClient.findWorkItemById(783, IWorkItem.FULL_PROFILE, null);
    IItemManager itm = teamRepository.itemManager();
    List history = itm.fetchAllStateHandles((IAuditableHandle) workItem.getStateHandle(), null);
    for(int i = history.size() -1; i >= 0; i–){
    IAuditableHandle audit = (IAuditableHandle) history.get(i);
    IWorkItem workItemPrevious = (IWorkItem) teamRepository.itemManager().fetchCompleteState(audit,null);

    now how can i fetch the Change Request type by using workItemPrevious ?

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 )

Google+ photo

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

Connecting to %s