How can I access work items associated to a change set in an Advisor or precondition on the server? Here is an Advisor example that prevents delivery of change sets if they are associated to the wrong work item type.
I tried to answer some questions in the forum. They were related to how to get the work item from a change set in an Advisor. I thought it would be simple because I had done a lot with the work item link API on client and server already. This is called hubris, I guess, and I found out the hard way, that the API from the Jazz SCM side is very different from what I was used to so far. Since it was so tough, I thought I should publish what I found, so that others can use it to jump start solving their own requirements.
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
The code for the example can be found here. The code uses the RTC 4.0 SDK and should work with lter versions of the SDK.
Advisors and Participants
I have already blogged about the RTC Update Parent Duration Estimation and Effort Participant. This blog is about an Advisor, so what is the difference?
A Participant is basically operational behavior that happens after a certain operation. It can fail and roll back the save, but does not have to. Participants are also called follow-up actions. An Advisor or precondition would be used If it is necessary to check certain conditions before an operation is performed. A good example for this is the Descriptive Change Sets Advisor that requires a comment or a work item associated to a change set before it can be delivered.
In general Advisors and Participants are very similar. Even most of the required API code can be used in both extension types. However there are some differences.
Participants
- Extend the extension point com.ibm.team.process.service.operationParticipants
- Implement the interface com.ibm.team.process.common.advice.runtime.IOperationParticipant
- Are allowed to modify the changes they are triggered for
Advisors
- Extend the extension point com.ibm.team.process.service.operationAdvisors
- Implement the interface com.ibm.team.process.common.advice.runtime.IOperationAdvisor
- Shall not modify the changes they are triggered for
- Example for an Operation Advisor
They get different information when called but other than that most of the code is similar. It is also important for both implementations to extend com.ibm.team.repository.service.AbstractService in order to be able to get services and other vital information from the API.
Participants as well as Advisors need to provide an operation ID for the operation they want to be triggered and they need to specify a component and required services.
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. The techniques used there have been used to create the Advisor in this post. Please note that all code below is for a Server extension.
How does that API Work?
The implementation started like any other Advisor or Participant. I looked at this question that originally started my effort and tried to figure out how to use the link API.
And I completely failed. I was able to locate similar code in the SDK but I could not figure out how to get the ProviderFactory needed to get the links. Now I did some really serious “search for references” in the SDK. I searched for plugins that extend the same extension point and use the same classes I was using, especially the link API.
As a side note, that is the only technique, besides using a search engine and scanning the SDK documentation for the RTC versions 3.x, 2.x and 1.x in the Jazz Team Wiki looking for examples, that I use to find around in the API.
The Eclipse Plugin Development Environment allows to search for references, declarations of classes, methods and extension points. It allows to use the Java search for classes and methods. It can also restrict to what kind of results one is looking for. This is really powerful and helped solving almost all my questions so far.
Robins blog about using the Plugin Spy to find Client API is also a great resource if looking for API. In most of the cases, the Client PAI works similar in the Server, only the Client Libraries need to be replaced by a Service.
Finally I found two classes that helped me solving the issue. Only, at first, I did not notice it was two classes. The Class com.ibm.team.filesystem.service.internal.ServerRequireWorkItemAdvisor uses the link code. It is the Descriptive Change Sets Advisor that requires a comment or a work item associated to a change set before it can be delivered. However, it gets the required factory in its constructor which does not match the way my implementation gets created by the extension point. I figured after a lot of confusion that this class is actually created by com.ibm.team.filesystem.service.internal.ServerRequireWorkItemAdvisorRunner which is the class that is called in the Advisor implementation. And it is able to get the factory I was looking for.
The Advisor Implementation Explained
So I tried the code and .. it did not compile. Another close look revealed that ServerRequireWorkItemAdvisorRunner extends AbstractScmService and not only AbstractService which allows it to get the data needed to get the factory. This finally led to my code below.
/******************************************************************************* * Licensed Materials - Property of IBM (c) Copyright IBM Corporation 2005-20012. * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ******************************************************************************/ package com.ibm.js.team.scm.extension.restrictdelivery.advisor; import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import com.ibm.team.filesystem.common.workitems.ILinkConstants; import com.ibm.team.links.common.ILink; import com.ibm.team.process.common.IProcessConfigurationElement; import com.ibm.team.process.common.advice.AdvisableOperation; import com.ibm.team.process.common.advice.IAdvisorInfo; import com.ibm.team.process.common.advice.IAdvisorInfoCollector; import com.ibm.team.process.common.advice.runtime.IOperationAdvisor; import com.ibm.team.repository.common.TeamRepositoryException; import com.ibm.team.repository.service.IRepositoryItemService; import com.ibm.team.scm.common.IChangeSetHandle; import com.ibm.team.scm.common.links.ChangeSetLinks; import com.ibm.team.scm.common.process.DeliverOperationData; import com.ibm.team.scm.common.providers.ProviderFactory; import com.ibm.team.scm.service.internal.AbstractScmService; import com.ibm.team.scm.service.internal.utils.ServerProviderFactory; import com.ibm.team.workitem.common.model.IWorkItem; import com.ibm.team.workitem.common.model.IWorkItemHandle; import com.ibm.team.workitem.service.IWorkItemServer; /** * Finds the work items a changeset is related to and checks if they are od a * specific type. Prevents from delivery if the type iswrong. * */ @SuppressWarnings("restriction") public class RestrictDeliveryToWorkitemType extends AbstractScmService implements IOperationAdvisor { public static final String COM_IBM_JS_TEAM_SCM_EXTENSION_RESTRICTDELIVERY_ADVISOR_RESTRICTWORKITEMTYPE = "com.ibm.js.team.scm.extension.restrictdelivery.advisor.restrictworkitemtype"; // Services we need private IWorkItemServer workItemServer; IRepositoryItemService itemService; @Override public void run(AdvisableOperation operation, IProcessConfigurationElement advisorConfiguration, IAdvisorInfoCollector collector, IProgressMonitor monitor) throws TeamRepositoryException { Object operationData = operation.getOperationData(); if (!(operationData instanceof DeliverOperationData)) { return; } DeliverOperationData data = (DeliverOperationData) operationData; List changeSetHandles = data.getChangeSetHandles(); // Get the required service interfaces workItemServer = getService(IWorkItemServer.class); // @see com.ibm.team.filesystem.service.internal.ServerRequireWorkItemAdvisorRunner // @see com.ibm.team.filesystem.service.internal.ServerRequireWorkItemAdvisor itemService = getService(IRepositoryItemService.class); // Get the ProviderFactory to be able to find the links ProviderFactory providerFactory = new ServerProviderFactory(this, itemService); // Check all relationships for (IChangeSetHandle iChangeSetHandle : changeSetHandles) { List links = ChangeSetLinks.findLinks(providerFactory,iChangeSetHandle, new String[] { ILinkConstants.CHANGESET_WORKITEM_LINKTYPE_ID }, monitor); for (ILink link : links) { Object resolved = link.getTargetRef().resolve(); if (resolved instanceof IWorkItemHandle) { checkWorkITemType(resolved, collector, monitor); } } } } }
This part of the code basically gets the data, gets the Services and the ProviderFactory. Then it uses the ProviderFactory and iterates all links to find if it is associated to a work item. If it is it checks for a valid work item type.
To allow better display in this blog, I pulled checking the work item type into a method.
public void checkWorkITemType(Object resolved, IAdvisorInfoCollector collector, IProgressMonitor monitor) throws TeamRepositoryException { // We found a work item. IWorkItemHandle wiHandle = (IWorkItemHandle) resolved; IWorkItem workItem = (IWorkItem) workItemServer .getAuditableCommon().resolveAuditable(wiHandle, IWorkItem.FULL_PROFILE, monitor); String workitemType = workItem.getWorkItemType(); if (!(workitemType.equalsIgnoreCase("defect") || workitemType.equalsIgnoreCase("task"))) { IAdvisorInfo info = collector.createProblemInfo( "Change Set associated to wrong work item type. Delivery denied ", "The change set is assotiated to work item " + workItem.getId() + " of type " + workitemType + ". Changesets are allowed only for work items of type Defect or Task. Please remove this assotiation", COM_IBM_JS_TEAM_SCM_EXTENSION_RESTRICTDELIVERY_ADVISOR_RESTRICTWORKITEMTYPE); collector.addInfo(info); } }
If the code detects a wrong work item type, it creates a problem info and that provides the failure information.
If you are looking for the Client API, here are some code hints in this forum question. More information about the client SCM API can be found in this and the related posts.
As usual the exception handling is very basic and you might want to improve that if using this code. In addition you probably want to add a schema to make the code configurable in the process configuration. However, I hope this is helpful and saves some cycles searching through the RTC SDK.
Hi Ralph
How to access the Workitem and Attributes of a Project Area ?
If I knew I would answer. I have no time to dig into the API at the moment.
Hi Ralph,
I want to complete an active changeset (make it be completed from active) in a server side participant. May you advise which API method will fit this? Many thanks
Kenneth
Client API: IWorkspaceConnection.ensureClosed(changeSets, monitor)
Kenneth found out Server API:
SCMService closeChangesets()
Hi Ralph – Since RTC 4.0, users can also leverage the super powerful and flexible “Require Work Items to Match Query” pre-condition on the deliver operation. As usual with RTC, wonderful things happen when you combine the power of Work Item and SCM – here, the SCM advisor runs against a Work Item query! And since work item queries are so powerful, there are lots of possibilities, including ones similar to your scenario here. https://jazz.net/library/article/1075#recipe_block_enhancements_from_bugfix and
https://jazz.net/library/article/1075#com.ibm.team.scm.server.workItemsMatchQueryDeliverAdvisor contain details. It’s a really interesting advisor worth getting familiar with.
Hi Christophe,
Thank you for the hint. I will make myself familiar with that new precondition. The best extension is the extension you don’t have to develop. The code in the post might still be useful in scenarios that exceed the query capabilities.
Hi Christophe and Ralph,
The extension plug-ins developed by administrators could be more flexible/powerful and easy-using to end users and the hint/error information could be customized and easy to understand by developers/end users; instead, if you depend on the “Required Work Item and Comment” and “Query”, the project area administrator have to customize many queries in each project area, and when the delivery is prevented, the end users/developers will be confused.
The main intention of this blog and the examples is to provide extension and code examples that others can use to develop their own code.
HI Ralph, my name is Kelvin Lui (IBMer) and I am working on the Websphere Application server Tooling for enabling RTC migration. I have deployed your sample code to the Jetty Test Server in debug mode. However the action of delivery changeset (without a Task associated to it) doesn’t invoke the validation check deployed on the server side. I have written another advisor (this one is to detect WorkItem Save action and check for specific keyword in summary) and it’s working fine.
Could you please help to shine some light here?
Thanks
Hi Kevin, to make sure that there actually is a work item use the out of the box providers.
This Advisor only checks the type if there is actually a work item reference. Try to associate a work item and run it then. Place the break point at the very beginning e.g. the line:
Object operationData = operation.getOperationData();
If you have successfully configured it in the process configuration it should trigger. If not, make sure to configure it for everyone role only to get started.
Thanks Ralph. I restarted my eclipse environment this morning and all of the sudden the plugin was loaded successfully in my test environment :).
I am going to try to package the plugin and deploy to the actual Jazz server.
Thanks for your help. By the way, it’s really a great article.
May you also send me your intranet email such that we could SameTime too 🙂
Thanks.
Hi Kelvin, you can find my e-mail address at the end of this article: https://jazz.net/library/article/807 .
I found a small issue in the uploaded code. You want to change the component id in the plugin.xml to
componentId=”com.ibm.js.team.scm.extension.restrictdelivery.common.component”
It does not cause any issues but strictly that was the ID I had defined in .com.ibm.js.team.scm.extension.restrictdelivery.common.IComponentDefinitions.
I uploaded a changed version.
Hi Ralph,
I have encountered some errors and warnings after copy the source code to my RTC 4.0.x extension development environment,
should the code sentence:
List changeSetHandles = data.getChangeSetHandles();
change to:
List changeSetHandles = data.getChangeSetHandles();
?
List changeSetHandles = data.getChangeSetHandles();
It’s very strange, the angle brackets in the source code could not be displayed in this blog.
It is not trivial to display special characters in a blog. It is actually a pain in the neck to make it work.
Not all code might be 100% error free due to that fact and that I have to rework the code sometimes while blogging.
Also, the code can be downloaded, you should download the code rather than cut and paste from the blog. If you cut and paste, you might need to do some rework.
Hi Ralph,
I have try to download the source code directly first, but failed to open the link, the dropbox website is prohibited in China.
Could you help me this question:
https://jazz.net/forum/questions/137132/which-feature-is-corresponding-to-the-plug-in-comibmteamfilesystemcommonworkitems
I have not yet heard that dropbox is also blocked in China. Workdpress used to, but why dropbox?
Which feature is corresponding to this plug-in “com.ibm.team.filesystem.common.workitems”?
This is either com.ibm.team.workitem.server.rtc.feature but most likely com.ibm.team.scm.server.rtc.feature (which is the RTC SCM part.
Ralph, Thank you very much for your examples. I am trying to create a similar advisor to restrict delivery unless the changesets are linked to specific types of work items (including various linked chains of work items). Anyway, I followed your example as a starting point and I have my advisor working when delivering change sets. The problem I have now is that if I have a baseline that includes new change sets, the advisor isn’t seeing the changesets. I see that the OperationData object has a getBaselines method that I can use to get the baselines being delivered. What I haven’t figured out yet is how to find the new change sets in those baselines. The built-in/provided delivery advisors apply to the changesets in the delivered baselines, but I don’t know how to make that happen myself yet. If you can provide a pointer for this, that would be great.
Thanks,
Jamie.
Jamie,
I have not looked a lot deeper into the SCM API. So I don’t have any specific pointers. Sorry.
Solution to Jamies Jazz.net Question: https://jazz.net/forum/questions/166202/how-do-you-obtain-list-of-change-sets-being-delivered-in-a-baseline-in-a-deliver-operation-advisor/166242
This topic is very useful. But, i require to get the Parent workitem of its associated workitem here. Can anyone suggest me how to do?
I already pointed you to https://rsjazz.wordpress.com/2012/07/31/rtc-update-parent-duration-estimation-and-effort-participant/ on Jazz.net, didn’t I?
Please also note, you can use the search field in the top right section to search for keywords such as “Parent”.
Thank you so much Ralph.
Hi Ralph,
Most interesting and most useful. In your code, the allowalbe work item types are hard coded. I need to extend it so the list of allowable work item types can is read from the process config (xml).
The RTC extension workshop contains example for “participants” but not for advisor.
I am hoping in the advisor to read the process config (xml) something along the lines of the following (exact form depends on API). I have not been able to find any examples. Can you point me in the right direction. Many thanks.
Lewis, I think the schema access works identical in preconditions and follow up actions. So far, I think I only spent the effort in follow up actions, but it should be absolutely the same approach.
Another hint:
From the extension point in your plugin open references. Find the references and find the implementation classes (then you have XML as well as usage examples for the Advisors we ship).
On the implementation class of an advisor, open the IOperationAdvisor interface. Right click as find references in workspace. Open implementing classes to see example code.
You can do the same with the extension point.
Example: find com.ibm.team.workitem.common.internal.RequiredApprovalsAdvisor
Look how it works. Look at its schema.
public void run(AdvisableOperation operation, IProcessConfigurationElement advisorConfiguration, IAdvisorInfoCollector collector, IProgressMonitor monitor) throws TeamRepositoryException {
advisorConfiguration is the passed schema data.
Thanks Ralph. It works a charm.
Hi Ralph,
A quick question, I ‘m using IBM RTC 6.0.4 version and I ‘m having a problem checkin and delivering to the main stream. The problem is “you may no update the change set link on the work item because it’s closed” but the work item was re-open and cannot checkin any files. please help.
Eddie,
Obviously the pre condition thinks the work item is still closed.
This is either a question to your process people to figure out why you have this problem (e.g. wrong workflow configuration) or for https://jazz.net/forum or for support. There is too few information in the question to be able to act on it.
It is not a question for this blog, unfortunately.
Ralph,
I’m trying to do a similar thing but with “Change Requests” on another /ccm server (instead of the change set to work item link, using change set to “Change Requests” link to the remote server).
When I inspect the link from the change set in the deliver operation, I can get the URI of the linked change request, but what I can’t figure out is how to use the existing http connection to server A to read the URI on server B (both /ccm servers).
Is there a way to reuse the http client session across servers from within a plugin? And if so, how?
Thanks,
Joe
Joe,
I have not done this. In the client, it is possible to maintain multiple connections and I did that.
I have not looked into the server API.
There are mechanisms like OAuth etc. available that can be used in OSLC/REST calls. what and how you can do using the Java API, I can not tell at the moment.