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.
Pingback: Resolve Parent If All Children Are Resolved Participant | rsjazz
Updated link creation on the server with a new approach that allows to trigger operational behavior.
Hi Ralph,
I might have found a issue in the code in private linkWorkItemsCLM().
IWorkItemReferences triggerItemReferences = fWorkItemServer
.resolveWorkItemReferences(resolvedTriggerItem, monitor);
triggerItemReferences isn’t used anywhere. And later in the code, targetReferences is used without initialization. So is triggerItemReferences actually the targetReferences?
Thanks.
Clement, the code was grabbed from an example follow up action that did more.
I modified some of the pieces and might have failed to get it all. I believe you are right.
May I have a copy of the example follow up action sourcMay I have a copy of the example follow up action source code? I’m writing myself one but having problem creating a reference link. Thanks!e code? I’m writing myself one but having problem creating a reference link. Thanks!
Sorry, my reply above is kind of messy. I was just trying to see if I can have a copy of the example follow up action source code. Or have you posted it here or Jazz.net? Thank you.
Clement, there were problems with creating the back links, if the work items where newly created, when I developed that. Therefore I never finished it really. I will also dig into the tons of code I have, if I can find it again and potentially get it to work.
I can try to find some time – no promises – if you manage to get me your e-mail address.
You can provide me the e-mail in a reply here.
The comments and replies are managed – I have to approve them to get spam at bay. I can trash the reply, so that your mail won’t be seen by anyone else.
Thank you.
I was trying the exact same thing. In my Operation Participant, I was trying to create a new work item and then link it back to current work item. I was able to create the new work item but when I used your code in this post to create the link, I got Null Pointer error in IWorkItemServer.saveWorkItem3().
We’re doing a PoC with IBM and we use RTC, UrbanCode Release and Deploy to streamline our DevOps process. RTC plays as a work item hub and manages the release change approval. Creating the reference link is the last step before I finish the RTC customization.
Appreciate your help!
Clement, can you look at https://jazz.net/jazz/web/projects/Rational%20Team%20Concert#action=com.ibm.team.workitem.viewWorkItem&id=288105&tab=com.ibm.team.workitem.tab.links.enhancement ?
There is an example advisor creating a CLM link with backlink.
It basically boils down to creating a link without back link to the newly created item and then creating a reverse link from the newly created item back to the original work item.
I updated https://rsjazz.wordpress.com/2013/11/06/creating-clm-links-with-back-link/ to make this more clear.
Hi,
Can you tell me about the possibility of creating link between a workitem and a test plan by plain API?
Regards ..
If such a link can be created, is dependent on who (RTC or RQM) owns the link. If you can create this link in the RTC UI, it can be created in the RTC API. The key is to be able to get the correct URL to the element in RQM or another tool. You can look into the links t osee what format is requested. You will have to look at the API of the other tool to find ou how to get the element and how to create the link URI.
After writing the following code,
IEndPointDescriptor tracksEndpoint = ILinkTypeRegistry.INSTANCE
.getLinkType(WorkItemLinkTypes.PARENT_WORK_ITEM)
.getTargetEndPointDescriptor();
IReference targetEndpoint = IReferenceFactory.INSTANCE
.createReferenceToItem(targetWorkItem);
You mentioned a note : Note: Don’t use this method, as it avoids triggering operational behavior on save.
Then what are we supposed to use if we want to create parent-child references in the same repository without using URI reference method.
How about the code below that block?
Sorry, it is too long ago and I don’t know the details any more. Try to use the other code here, or check the code in the work item command line. As far as I know that works.