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