Build On State Change Work Item Save Participant

In the unlikely event you have missed it or just to complete the hit list if you search for examples on this blog, the Build On State Change Work Item Save participant/follow up action is a complete example as part of the Rational Team Concert Extensions Workshop.

The Build On State Change Work Item Save participant monitors work item state changes of configured work item types and state changes. If a qualifying change happens, it issues a build request for a configured build definition. The example comes with a complete package including the configuration UI.

Just Starting with API work?

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.

Setting Access Control Permissions for Work Items

This is the second post in the series around very fine grained access control permissions for work items and RTC SCM versionables. It explains how to set the access control permissions for work items.

See the problem description in the first post of the series

Related posts

The posts in this series are:

  1. Manage Access Control Permissions for Work Items and Versionables
  2. Setting Access Control Permissions for Work Items – this post
  3. Setting Access Control Permissions for SCM Versionables
  4. Setting Attributes for SCM Versionables

License

The post contains published code, so 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. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, 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.

Compatibility

This code has been used with RTC 5.0.2 and is prepared to be used with RTC 6.0.x with no changes and it is pretty safe to assume, that the code will work with newer versions of RTC.

The code in this post uses common libraries/services that are available in the Plain Java Client, Eclipse client and Jazz Eclipse server API. If client or server API is used, this is stated.

Work Item Access Control Code

Now that we have all the rules and pieces together as explained in this post, lets have a look at the work item access control. Again, note, that work items can only have access groups and project areas set for access control in manual mode as shown in the image below.

WorkItemAccessContextSelection

To set the access context for work items, the interface IWorkItem provides the method

IWorkItem.setContextId(UUID contextId);

It should also be possible to set the internal attribute with the ID “contextId” also available as constant

IWorkItem.CONTEXT_ID_PROPERTY

in the IWorkItem interface.

Working With the UUID Representing the Access Context

How to find the objects we are interested in, is explained in this post.

To be able to set the access control context for work items, it is necessary to understand how to access the value that represents the access context of a process area (project or team area) or access group.

The common API provides this method to access the access context of an item:

com.ibm.team.repository.common.IAuditableHandle.getItemId()

This interface implements (through a number of intermediate interfaces) the interface IItemHandle, which defines the method getItemId() for all items. So strictly speaking the method is available as

com.ibm.team.repository.common.IItemHandle.getItemId()

This means, the access context can be accessed already from the item handle e.g. in case only a handle such as IProcessAreaHandle, IProjectAreaHandle, ITeamAreaHandle, IAccessGroupHandle or IContributorHanlde is available.

It is also available at all resolved IItems such as IProcessArea, IProjectArea, ITeamArea, IAccessGroup or IContributor as all these interfaces implement IItemHandle. In fact it is available on all IItems. Example code:

IProcessAreaHandle paHandle = ..... get the handle
IProcessArea pa = ..... resolve the handle

// These values are the same
UUID paHandleContext1 = paHandle.getItemID(); 
UUID paHandleContext2 = pa.getItemID();

Note, that all items in RTC have an itemID like the one above and can be uniquely be identified by it. Other itemID’s are just not used as access context.

The return value is of the type

com.ibm.team.repository.common.UUID

The UUID is an object that uniquely identifies an item in the repository and can be used to recreate the item as well. The string representation shows up in several places, for example in OSLC links. To get the string representation use

com.ibm.team.repository.common.UUID.getUuidValue()

If it is necessary to recreate an UUID from the string representation as an example named contextIDString, use the method

UUID.valueOf(contextIDString)

This recreates the UUID of the object if the string is a valid string representation of a UUID.

This way, you have the choice to use the UUID or its string representation to set the access context of RTC work items.

Setting the WorkItem Access Context

Given the UUID provided by a project area or access group, it is possible to set the access context of a work item.

Although all the code to retrieve the access context is common code, the code to set the work items context differs on client and server.

In the Client API, use a WorkItemOperation like the code below.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2015. 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.access.control.client;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.common.UUID;
import com.ibm.team.workitem.client.WorkItemOperation;
import com.ibm.team.workitem.client.WorkItemWorkingCopy;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.ItemProfile;

/**
 * WorkItem Operation to set the access context for the work item. This is
 * client code only.
 * 
 */
public class SetAccessControlOperation extends WorkItemOperation {

	private UUID fAccessContext;

	public SetAccessControlOperation(UUID accessContext,
			ItemProfile loadProfile) {
		super("Set Access Context", loadProfile);
		fAccessContext = accessContext;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		workingCopy.getWorkItem().setContextId(fAccessContext);
	}
}

The operation can be called like this:

	SetAccessControlOperation operation = new SetAccessControlOperation(
		UUID.valueOf(context), IWorkItem.FULL_PROFILE);
	operation.run(workItem, monitor);

Because the WorkItemOperation is not available in the server API, a save has to be performed manually like below.

	// Set the work item visibility
	// Get the full state of the parent work item so we can edit it
	// and avoid stale data exceptions
	// Get the required service interfaces
	IWorkItemServer wiServer = getService(IWorkItemServer.class);
	IWorkItem fullItem = (IWorkItem) wiServer
			.getAuditableCommon()
			.resolveAuditable(workItem, IWorkItem.FULL_PROFILE, monitor)
			.getWorkingCopy();

	// Set the context of the work item
	fullItem.setContextId(UUID.valueOf(context));

	// Save the work item
	HashSet additionalParams = new HashSet();
	additionalParams.add("WORK_ITEM_CONTEXT_CHANGE");

	IStatus status = wiServer.saveWorkItem3(fullItem, null, null,
			additionalParams);
	if (!status.isOK()) {
		String parameter[] = new String[1];
			parameter[0] = Integer.toString(fullItem.getId());

		String description = NLS.bind(
				"Failed to save work item ''{0}''.", parameter);
		IReportInfo info = collector.createInfo(
				"WORK_ITEM_CONTEXT_CHANGE"
				+ ": Error running the participant.",
				description);
		info.setSeverity(IProcessReport.ERROR);
		collector.addInfo(info);
		return;
	}

Please note, if this is performed in a participant/follow up action, it is important to send an additional save parameter like “WORK_ITEM_CONTEXT_CHANGE”. This protects the participants from causing a recursion ultimately crashing the server. The participant can protect itself to run again for the save it just performed by testing for the additional save parameter. If the parameter is detected, the participant knows it caused the save and should exit to prevent the recursion.

Please also note, that in some cases you might have a WorkItemWorkingCopy, instead of an IWorkItem. In these cases use a cast or the method getWorkItem() on the working copy to get the IWorkItem item to be able to set the context.

The examples above uses the string version of the UUID, however, it would have been possible to get the UUID directly from the IItem.

Getting the WorkItem Access Context

It is possible to get the access context of a work item, if the work item is accessible for read, by using IWorkItem.getContextId(). The resulting UUID can be used to get the related item.

Summary

This post explains how to set read access control for work items. The next post will explain the details around setting access control for SCM versionables.

Manage Access Control Permissions for Work Items and Versionables

A customer requires very fine grained access control to work items and versionable objects such as files in the SCM system. In addition the customer had the requirement to be able to set attributes on elements in the SCM system.

I knew that it can be set for work items and kind of how to because I had briefly looked at it in the A RTC WorkItem Command Line Version 3.0. But I was not completely sure about the rules and how to access the SCM part and the attributes in SCM.

Since the customer needed this urgently I looked at what the rules are and what can be done.

The content is way too big for just one post, so this is going to be a series. The first post explains the rules around access control permissions and some basic API’s around finding the objects that can be used to provide the access control context.

Related posts

The posts in this series are:

  1. Manage Access Control Permissions for Work Items and Versionables – this post
  2. Setting Access Control Permissions for Work Items
  3. Setting Access Control Permissions for SCM Versionables
  4. Setting Attributes for SCM Versionables

Also see

Controlling access to source control artifacts in Rational Team Concert

License and Download

The post contains published code, so 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. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, 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.

Compatibility

This code has been used with RTC 5.0.2 and is prepared to be used with RTC 6.0.x with no changes and it is pretty safe to assume, that the code will work with newer versions of RTC.

The code in this post uses common libraries/services that are available in the Plain Java Client, Eclipse client and Jazz Eclipse server API. If client or server API is used, this is stated.

Problem Description

The first question was, if it was possible to set the permission to access

  1. Work Items
  2. Items in the SCM system such as versionables

The second question was, if it was possible to set the attributes introduced in RTC 5.0 that can be specified and set using the API. The documentation only mentioned the SCM command line.

The third question was, how team areas and access groups can be created automatically.

The forth question was, what exactly the rules are:

  1. Can this be done in the server?
  2. Can this be done in the client?
  3. Can this be done in a command line tool or from another application?
  4. What permissions are required and what are the rules in the different contexts?

Solution Approach

The approach that we chose was to try to implement a minimal demonstrator to demonstrate the capabilities and prove them as a proof of concept. this demonstrator was also intended to serve as a platform to find the rules.

Learning

Here is what we learned creating the demonstrator.

The Rules – Work Items

Work Items have an attribute called “Restricted Access” that can be used to control access to them. There are basically the following modes available for work items that can be used.

  1. Access control using access control of the project area
  2. Restricted access by category
  3. Restricted access by setting the restricted access attribute

Option 1: Only the users that have access to the context set for the project area can access these items. This can be set to everyone basically exposing the work items to everyone. Other options a re project area membership and access control list etc. The rules here are very clear.

Option 2: It is possible to set RTC in a project area to automatically determine access permissions based on the category (filed against) of the work item. This sets the restricted access to the project area or team area associated to the category. Only members of the team area (and members of sub team areas of the team area associated to the category) have access to the work items which are restricted by category. It is worth noting, that a user being associated to the project area can not look into the work items filed against teams, if these limit access, it is quite the opposite.

Option 3: Use a special editor presentation to set the restricted access attribute manually to a project area or an access group. this could potentially be supported by automation.

The most important learning for option 3 is, that it is only possible to set the restricted access attribute to a project area or an access group this way. It is not possible to set the access context to a team area. If the context is set to a team area, it automatically picks up the containing project area. This also holds for automation.

See this help topic on how to set up the work items to allow setting the restricted access.

The Rules – Versionables

Prior to RTC 6.0.1, which has just been released, it is only possible to set read access control to the default (which is controlled by the component) project areas, team areas (called process areas if the distinction is unimportant) and a single user.

If access control is set to a team area, any member of that team area and its sub team areas have access. Note that this is different from option 2 for work items. If access control is set to a project area, any member who has access to the project area has access to the item.

RTC 6.0.1 introduces the capability to set access control to an access group. Only members of this access group have access to the item. This seems to be the best option by far, especially since access groups can contain project areas and team areas which adds their members to the access group.

Please note, that access control applies to the whole item and its history.

JazzAdmin Repository Role

Users with the JazzAdmin repository role have access to any work item or SCM controlled item, regardless of the access control setting. Users with this role (and sufficient licenses) can access all data.

Project Area Administrators

Administrators of a team or project area and don’t have the JazzAdmin repository role can not access items that have access control set to a context they don’t have access to.

General Rules

Users can only set the access context of work items or items in the SCM system to an access context that preserves their access permission. It is not possible to find or select an access group, process area the user has no access to/is member of or a different user and set the access context to it. This would remove the read permissions and is prevented.

Users with the JazzAdmin role and sufficient licenses however can do this, because they don’t loose read access.

General Rules For Automation

All automation can usually only perform the operations that are permitted to the user that is used to run the automation. This especially means that automation running in the context of a user with JazzAdmin repository role and the required licenses can perform all operations. The user context available in an automation depends on the automation.

  1. Follow up actions (participants) run in the context of the user that is performing the operation on the client or the server. They are especially not elevated run in a JazzAdmin repository role, if the user that performs the operation does not have this role. It is also not possible to use a different users or services to elevate the permissions. **Update, it is possible to elevate the access to an administrator using a special com.ibm.team.repository.service.internal.TeamServiceContext.runAsAdmin(ITransactionRunnable<TeamContent>) runnable.**
  2. Preconditions (advisors) must not change the triggering elements. Otherwise the same rules apply as described in follow up actions. **Update, it is possible to elevate the access to an administrator using a special com.ibm.team.repository.service.internal.TeamServiceContext.runAsAdmin(ITransactionRunnable<TeamContent>) runnable.**
  3. Asynchronous tasks or services in RTC are run with JazzAdmin permissions and can use the IImpersonationService to change the operation to a different user context. Note, that changes the user ID and name to the impersonated user and adds their permissions, it does not remove the administrator permissions.
  4. Plain Java Client Library or other client based automation run in the context of the user that was used to log into the system.

Finding ProcessAreas, Access Groups and Contributors

How to find the access context information needed using the Java API?

Finding ProcessAreas

There are many ways to find ProcessAreas (project areas and team areas) using the API, the simplest one assumes an java.net.URI constructed from a string based name path of the process area separated by ‘/’.

Lets assume a project area named TestProject1 and a team area within the project are named TestTeam1 and a sub team area underneath TestTeam1 named TestSubTeam1.

The URI for the project area could be constructed from the string “TestProject1”. The URI for TestTeam1 could be constructed from the string “TestProject1/TestTeam1”. Similar the URI for TestSubTeam1 could be constructed from the string “TestProject1/TestTeam1/TestSubTeam1”.

Process area names could contain spaces. Spaces are not allowed in URI’s, therefore they need to be replaced by the encoding character string “%20”.

To construct an java.net.URI from a string value use the following code.

String namePath = "TestProject1/TestTeam1/TestSubTeam1";
URI anURI = URI.create(URI.create(namePath.replaceAll(" ", "%20")))

The service to find the process area by its URI is provided by the interface IAuditableCommon. This interface class, as its name implies, is a common interface that is available in the RTC Plain Java API, as well as the RTC Eclipse client and Eclipse server SDK. This allows to use this API call in any possible context, either by

IAuditableCommon auditableCommon = (IAuditableCommon) teamRepository
	.getClientLibrary(IAuditableCommon.class);

in Plain Java and client SDK based code, or using

IAuditableCommon auditableCommon = getService(IAuditableCommon.class);

in the server extensions that all extend AbstractService (or in case of SCM server extensions AbstractScmService, which extends AbstractService).

For code that already retrieved the common Service IWorkItemCommon, for example to find work items, IAuditableCommon is available using the call

IAuditableCommon auditableCommon = workItemCommon.getAuditableCommon();

So the following code would retrieve the access context for the team area “/TestTeam1/TestSubTeam1” nested in the team area “TestTeam1” in the project area “TestProject1”

String namePath = "TestProject1/TestTeam1/TestSubTeam1";
IProcessArea area = auditableCommon.findProcessAreaByURI(URI.create(namePath.replaceAll(" ", "%20")), null, monitor);
UUID context = area.getItemId();

Note, for work items only a project area is a valid context. Setting the context above will result in the project area containing the process area to be set. Alternatively use

UUID context = area.getProjectArea().getItemId();

for work items and document the fact to save the time to figure it out the hard way during testing (like I did).

Finding the Public Access Context

Work items can also be set to public read access. The constant com.ibm.team.repository.common.IContext.PUBLIC is available for that.

UUID publicContext = IContext.PUBLIC.getUuidValue();

provides with the public access context.

Finding Access Groups

Access Groups are managed in the administration pages of the RTC application. It is possible to create, modify and delete access groups. The image below shows this section.

Access Group ManagementAccess Groups are also accessible using the IAuditableCommon common interface that was already used in the section above.

To get all access groups use this code:

IAccessGroup[] groups;
groups = auditableCommon.getAccessGroups(null, Integer.MAX_VALUE,
	monitor);

This returns all access groups up to a maximal number that is passed to the method.

It is possible to pass a filter to select only access groups with specific name pattern like below.

IAccessGroup[] groups;
groups = auditableCommon.getAccessGroups("My*", Integer.MAX_VALUE,
	monitor);

The access groups can be iterated like below to compare the name or do something similar.

for (IAccessGroup group : groups) {
// Compare the value to the access group name.
	if (group.getName().equalsIgnoreCase(value)) {
		return group;
	}
}

Similar to the process areas above, there is also a public access group which can be obtained like this:

return auditableCommon.getAccessGroupForGroupContextId(
	IContext.PUBLIC, monitor);

Finding Contributors

Finding contributors is different in the client and the server. There is no common API available.

In the client API the IContributorManager can be used for example using the following methods.

IContributorManager contributorManager= teamRepository.contributorManager();
// Find a user by the users ID returns a handle
IContributorHandle contributorHandle = contributorManager
	.fetchContributorByUserId(attributeValue, monitor);

// Get all users, returns a list of full IContributor items
List contributors = contManager.fetchAllContributors(monitor);

In the server API the IContributorService is available and provides the following method.

IContributorService contributorService = getService(IContributorService.class);
contributorService.fetchContributorByUserId(userId);

I was not able to locate a way to search for all users like in the client API. The server API usually gets a user passed and there is no need to find all users.

Summary

This post explains the rules around read access control for work items and RTC SCM versionables and some basic API’s around finding the objects that can be used to provide the access control context. The next post will explain the details around setting access control for work items.

Running The RTC 4.x Extensions Workshop With RTC 5.0.x

Since the new 5.0 version of Rational Team Concert and the CLM tools is out now, would the Rational Team Concert 4.x Extensions Workshop still work with this version?

The short answer is: Yes!

* Update * The error described below can be fixed. See the update in the description.

I just quickly tested if the Rational Team Concert 4.x Extensions Workshop works with the newest release 5.0 and I was able to successfully smoke test it. The same applies to newer versions of RTC.

To test if it still works, I performed Lab 1 completely. The new setup tool introduced recently ran like a charm and this was successful.

I then ran Lab 2 and finally Lab 5 with the complete code accepted. All labs worked as advertised. I would not expect any surprises in Lab 6 (except the normal issues when trying to deploy).

Observations

As there are some things that I notice that are different from the 4.x versions. I will summarize them here.

After setting up the SDK an error shows up in some of the project explorers. The error looks like below in the project explorer:

ExternalPluginError_1It seems to be an issue with the build path if one looks at the details:

ExternalPluginError_2I can’t remember having seen this in the past. Initially it only seems to come into the way when launching and does not seem to have any ill effects. Unfortunately it prevents refactoring operations.

To get around it, when launching, use this dialog to skip this issue:

ExternalPluginError_SkipIf you check the ‘Always launch without asking’ option, be a ware that this could be problematic if your own code has errors as well.

* Update *

To get rid of this error you can delete the folder resources in the SDK folder installs/rtc-sdk/plugins/com.ibm.team.log4j.ui_1.2.0.v20140307_1622 after extracting the SDK.

In the other labs, the only thing that seemed to be different is that the Eclipse password secure storage is getting more persistent. You should probably provide a password, to avoid having to deal with it every time.

Summary

So you can run the Rational Team Concert 4.x Extensions Workshop with the current Rational Team concert version 5.0 and it is likely it will run also with later versions.

As always, I hope the code above helps someone out there with extending RTC.

Finding A Process Area Using its Name

There is a question on Jazz.net right now about how to fins a RTC team area by its name. I happened to have the code on my disk, so I think its worth showing it here.

License

The post contains published code, so 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. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, 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.

How the Code Works

The code in this post is client API.

The code below accepts a string as input parameter. It then converts it into an URI that is used to find the element. See the documentation for how the URI path segment should look like:

	/**
	 * Returns the process area for the given URI. URIs have been chosen for
	 * future extensibility. Currently only hierarchical URIs are supported.
	 * Only the path segment of the hierarchical URI is considered. The first
	 * segment should be the name of a project area, the following segments
	 * should be the names of team areas.
	 *

* The returned area is managed by the item manager. * * @param areaURI the URI * @param properties the list of required process area properties; use * ALL_PROPERTIES to retrieve a complete item. * @param monitor a progress monitor or null * @return the process area for the given URI or null * @throws TeamRepositoryException in case this operation fails * @throws org.eclipse.core.runtime.OperationCanceledException in case this * operation has been canceled * @LongOp This is a long operation; it may block indefinitely; must not be * called from a responsive thread. */ IProcessArea findProcessArea(URI areaURI, Collection properties, IProgressMonitor monitor) throws TeamRepositoryException;

The code below works for project areas. However, if you add the segments for the team area hierarchy, it also works for team areas. Here an example of how to create the URI.

       URI newTeamAreaURI = URI.create(projectAreaName + "/" + newTeamAreaName);

The utility code below does this for only the project area, but could be extended to team areas. It would be necessary to know the hierarchy, if searching in a team area hierarchy.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2014. 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.api.tools.process;

import java.net.URI;

import com.ibm.js.team.api.tools.core.URIUtils;
import com.ibm.team.process.client.IProcessClientService;
import com.ibm.team.process.common.IProjectArea;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.common.TeamRepositoryException;

public class ProcessAreaUtil {

	public static IProjectArea findProjectArea(ITeamRepository repository,
			URI targetProjectAreaName) throws TeamRepositoryException {
		IProcessClientService processClient = (IProcessClientService) repository
				.getClientLibrary(IProcessClientService.class);
		IProjectArea projectArea = (IProjectArea) processClient
				.findProcessArea(targetProjectAreaName, null, null);
		return projectArea;
	}

	public static IProjectArea findProjectArea(ITeamRepository repository,
			String targetProjectAreaName) throws TeamRepositoryException {
		return findProjectArea(repository,
				URIUtils.getUriFromString(targetProjectAreaName));
	}
}

The tool URIUtils looks as below:

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2012. 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.api.tools.core;

import java.net.URI;

public class URIUtils {
	public static URI getUriFromString(String name) {
		return URI.create(name.replaceAll(" ", "%20"));
	}

	public static String getStringFromUri(URI uri) {
		return uri.toString().replaceAll("%20", " ");
	}
}

RTC Extension Workshop Overhaul

It took a while, but the Rational Team Concert Extension Workshop overhaul was finally published on Jazz.net.

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.

 What did change?

The new version of the workshop provides

  • Compatibility to newer versions of RTC
  • Compatibility to all RTC and CLM installation types
  • Enhanced, simplified setup
  • Small enhancements of the workshop to address minor changes in the SDK

The Rational Team Concert Extension Workshop is one of the most used workshops in the Jazz.net library. Unfortunately several changes in RTC since its design for RTC 3.0 caused a lot of problems for users. It needed a major change in how the required RTC repository is set up to make it easier to consume.

The main challenge was the set up of the RTC Development repository. The workshop was designed to use TAR database exports created with the repotools to import them and provide the repository as presented below. This repository contains the required development data which is accessible in a repository workspace and a stream with baselines and data.

ExtensionWorkshop Development Repository

This was a lot of manual effort, took time and worse, was not compatible across different versions of RTC. The old files could not be used with newer versions.

As described in this series of posts, the solution was to create the required project, stream and workspace using the Plain Java Client Libraries and upload the required source code to the stream.

The new workshop now uses a small Java Tool WorkshopSetup working with the Plain Java Client Libraries to create the project and to upload the content required to run the workshop. The tool can be downloaded with the Rational Team Concert Extension Workshop.

This allowed to completely remove the steps to import the repository content from the TAR files, which overwrite the repository you set up and rely on a specific setup of the servers done before to avoid conflicts. The removal of the repository exports also reduces the download size considerably.

Since the repository is no longer overwritten, there are no more potential issues with public URI’s and registered applications. It is also no longer necessary to recreate the index files after the import. All these steps where very manual, error prone and time consuming and the workshop is now much more consumable.

The server setup can now use the express setup and is less manual and much faster.

Since the repository is created during the server setup and not overwritten, the evaluation licenses are not expired. There are no license issues anymore. The step to upload the 10Free licenses could be skipped entirely and performed later, once the evaluation licenses expire, if one so desires.

The workshop workbook uses the Web install for RTC as default, but it also describes how to use the Plain ZIP version of RTC. I typically use the latter approach which has some advantages. One of it is, that Installation Manager is not involved and I can safely just delete the files, instead of doing an uninstall. This also works offline or with small bandwidth, if the files are already downloaded.

The WorkshopSetup tool can actually be run against any existing repository and it does not mater which other applications are installed. It should always be able to create the project and upload the content. If you want to start all over with the workshop, it would also be possible to run the WorkshopSetup tool multiple times (e.g. renaming the RTC Extensions Workshop project created for a prior run of the workshop).

The WorkshopSetup tool is started using a batch file or shell script. It will work if you follow the setup instructions. However, you can configure it, if needed. The parameters to configure it are highlighted in the image below.

WorkshopSetupThere are no requirements on the administration user ID anymore, except that it is easier to follow the workshop workbook, if you use the proposed names. If you want to use a different ID, modify the user id and password entries in the workshop setup batch file. All other parameters can also be changed as desired.

Another benefit of the change of the install procedure is that all required data, the source code, licenses, the configuration and debug launch files are now available as exported Eclipse projects. These exports can be used even without having to run the workshop at all. E.g. the launches and configuration files are in the file WorkshopSetup/Data/Configuration/RTCEXTConfiguration.zip and can easily be imported into an Eclipse workspace.

The server launch configurations now contain all the bundles for the licenses that are used in the different ways to package RTC and CLM. Just import the ones from your setup and you are ready to go.

As long as there are no drastic changes to the Plain Java Client Library API the WorkshopSetup tool should work with any version of RTC in the future. It is no longer necessary to start with RTC 4.0 and then to upgrade the repository. I ran the tool with all available RTC 4.x versions up to 4.0.6 and it worked with no problems. The WorkshopSetup tool works also with RTC 3.0.x and it is safe to assume it will also work with upcoming versions of RTC.

* Update * It also works with 5.x.

In addition to these major changes, the launch configurations were tidied up to better map to the newer structures in RTC 4.x and later.

Some feedback from users was also used to make small changes in the labs to make things clearer, easier to follow and address small changes in the API since 4.0 in the text and screenshots.

Starting with Extensions and Automation?

If you just get started with creating extensions or automation for RTC, start with this post Setting up Rational Team Concert for API Development

Summary

A lot of effort went into making the Rational Team Concert Extension Workshop easier to consume and make it compatible to other RTC versions. I hope the result helps users out there to get started with RTC Extensibility and saves some of the precious time. If you have suggestions or run into problems, please provide feedback and questions on Jazz.net in the Forum.

Hiding UI Contributions in the RTC Eclipse Client

Is it possible to hide UI Elements in the RTC Eclipse client? This is a question I have been asked several times already. I always thought it would only be possible to add new UI elements but not hide them. Javier’s initial question and his example on Jazz.net prove that I was completely wrong. Sorry, but I was simply not aware of this capability.

Why is this interesting?

This is interesting, because if you want to extend the RTC Client UI with context menus, you might want to also be able to remove existing UI elements like context menus.

One use case is described in Javier’s question and example. Another example could be to do some checking before someone can complete a change set in the RTC Eclipse UI.There is no extension point that you can use to prevent completing and once it is completed, you can’t reverse the operation. If you could remove the context menu entry and provide a new one that  does some checking before calling the original action, you could greatly enhance this scenario with very few effort.

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.

The Approach

Javier’s example shows how this can be done based on Activities in Eclipse extending the org.eclipse.ui.activities extensionpoint.

The basic idea here is to define Activities. These Activities can then be bond to

  • Existing Activities – e.g. to make sure the UI defined in an Activity will only be enabled and show if the existing Activity is enabled
  • Categories – to group several activities and enable/disable them together
  • UI Contributions

Now you can enable and disable the UI elements defined by this Activity.

If you don’t enable an activity, the UI element is hidden. Dependent on how the Activity is defined, there are different ways to enable and disable the activity, for example using Categories in the preference, or the Activity API. In addition there is an expression based activation mechanism.

See the Eclipse Documentation for Activities for more information.

Example

Lets stay in the area of the example with completing a change set above. We want to disable the context menu entry for the complete Change set action.

First it is necessary to create the plugin and to create the extension for the Activity. Create the plugin with the name com.ibm.js.team.activity.scm.cscomplete for example. Add an extension to the org.eclipse.ui.activities extension point and use com.ibm.js.team.activity.scm.cscomplete.activity as the id of the Activity.

Now it is possible to add a new activityPatternBinding for the menu item to be hidden and bind it to the the Activity com.ibm.js.team.activity.scm.cscomplete.activity defined before.

The plugin.xml looks like below.

Plug-in XML code

The new activityPatternBinding requires a pattern to map to the UI element. The pattern format is

plug-in-identifier + "/" + local-identifier

This is now the challenge. Where to get the ID’s reqired? As described in the last post, it is possible to use inspection tools such as YARI – Yet Another RCP Inspector, to get this data.

By using the Yari SWT Inspector this information can easily be gathered. Prepare an outgoing change set in the pending changes view. Open the SWT Inspector and set it to mouse down capture mode. Then select the Complete action on the context menu entry. Disable capture mouse down and inspect the data captured.  This screenshot shows the data that was gathered.

YARI SWT Data CaptureThe interesting information is the ID of the action and the ID of the plugin. This data can be copied and pasted over to create the pattern.

The plugin Id is com.ibm.team.filesystem.ide.ui and the Id of the action to hide is com.ibm.team.filesystem.ui.changes.actions.CloseAction.

The pattern follow the rules of java.util.regex. The pattern rues are described here. Please note, that the period is a special character and needs to be escaped. The pattern we are looking for is therefore:

com\.ibm\.team\.filesystem\.ide\.ui/com\.ibm\.team\.filesystem\.ui\.changes\.actions\.CloseAction

The final plugin.xml looks as follows.

Resulting Plugin.xmlIf this plugin is deployed, the complete change set action does not appear any longer in the context menus, as long as the Activity does not get enabled, e.g. using the API.

Does this mechanism also work for the RTC Web UI? I am reasonably sure that this is not the case, unfortunately.

Summary

This allows to further modify the UI of the RTC Eclipse Client (or any other Eclipse based UI’s) and to easily hide UI elements. Since it is also possible to add your own UI elements this allows a fine grained modification especially in the context of extending RTC.

As always, I hope this content helps others out there to get their work done.

Adding Context Menus for Jazz Objects to the RTC Eclipse Client

Is it possible to add customized actions to the context menus in Eclipse? This is an interesting question that recently came up  in the context of my work.

This post shows how this can be done, in case you are asked to do this kind of work. It explains

  • What extensions are possible in general
  • What you need to know to try to create such extensions
  • How to get at some of the data you need
  • A simple example that shows the main steps you need to take and the most interesting code snippets

* Update * If you need to get rid of menu entries, check this post.

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.

The Example

The example adds a context menu action on snapshots. It provides an example for an action directly in the context menu as well as an example for a sub menu that can be used to organize several actions. The outcome looks like below.

Pop up menu action for a Snapshot
Pop up menu action for a Snapshot

If the action is performed, the example will get the object and do something with it.

What Can be Extended?

I knew that this would be possible in general. I have created such context menu extensions for our Eclipse based modelling tools in the past. Eclipse is built to be extended. In general you can create all kinds of tools that extend Eclipse. For example

  • Eclipse Views – Tree, Table, Table Tree Views
  • Eclipse Editors
  • Eclipse Menus – menu bars, custom actions, pop up menus
  • Extend existing Eclipse extensions e.g. provide context menu extensions or add functionality
  • Decorators for tree views

All these capabilities are described in numerous books about extending Eclipse and in even more tutorials available on the web. This article describes how to create menu contributions. I won’t go too deep into what is covered in these information sources. Instead I will try to focus on the interesting topics related to RTC.

What is the Problem?

So, if this is all possible, why blog about it here? The problem is, that all these extensions require information about the objects and editors you want to extend. As an example a pop up menu contribution providing an action to the context menu of a view requires the class of the object that is provided in the tree node to work. Without this information you can’t do anything.

I found it very hard to get the information in the past. You can obviously start with a very general object and try to debug. However this can be very tedious. How can you find this information more efficient?

The answer to that is using tools available to find the information. I will show some of the tools available and how they can be used. Lets get started with creating our example.

Example Project

We want a pop up menu extension that allows to perform some action on a snapshot in some views.

The easiest way to start is to use the Plug-in Project wizard Use File>New>Project and create a Plug-in Project. Pick a good project name. As a best practice I always use a naming pattern that is aligned with the naming patterns used in Jazz, adding a unique infix that will later allow me to search for the code in case it is deployed. As an example I use com.ibm.js.team.filesystem.snapshot.menucontribution. Then use Next to go to the next specification step. As a best practice use the name as ID and specify the other values. The image below shows my choices.

Specify the project properties
Specify the project properties

Use Next to get to the extension templates. Select Plug-in with a Popup menu and click Finish. The wizard does its miracles and you now have a new project.

Inspecting the project reveals a plugin.xml and a new action class have been created. The project is fully functional. It would be possible to start an Eclipse Client Debug run-time and find a new pop up menu if clicking on a file in the Eclipse Navigator, Project Explorer or other views. If you click on a snapshot in the search view, you see – nothing.

However, that is not our target. What makes this work and what needs to be done to make this work for the given example?

For what objects this works is basically hidden in the plugn.xml. It looks as follows:

Generated Plugin XML
Generated Plugin XML

The extension is an object contribution. It works for objects displayed in views that are of the class org.eclipse.core.resources.IFile. The menu defines a sub menu that is inserted before a place called additions and defines a new menu group group1. The Action in this sub menu enables for selections of one element and calls the specified action class.

A snapshot is not of the class org.eclipse.core.resources.IFile. So much is clear, but what class do we need here?

YARI – Yet Another RCP Inspector

One way of finding out this crucial detail, that works for me, is using one of the many Eclipse inspection tools. I found YARI – Yet Another RCP Inspector searching the Web due to a hint and use this tool since then. It needs to be installed into Eclipse as described in the install instructions. Once installed it provides various new views that you can use to inspect Eclipse RCP applications, including Eclipse itself. The data it provides can be overwhelming at times, but is very useful. Use the documentation and FAQ’s provided to understand better what you can find in the data.

To find out what is exposed in the search view for a snapshot, open the view SWT Inspector that YARI provides.

Now search for a snapshot. Select the ‘capture mouse down‘ action in the SWT Inspector and click on the snapshot in the search view. Click at the ‘capture mouse down’ action in the SWT Inspector again, to switch it off. Now you can look at the captured data and use the mouse without capturing again.

The inspected data
The captured data

Look at the captured data. The data node in the TableItem element is what we need in this context. The class that is exposed here is class com.ibm.team.filesystem.ui.wrapper.SnapshotWrapper. This is basically the class that we need in the object contribution.

Plug-in Spy

Another Tool, that you can use to look under the covers of Eclipse extensions, is Plug-in Spy. This tool is now bundled with Eclipse in the Plugin Develupment Environment (PDE). You can invoke Plug-in Spy using SHIFT-ALT-F1 on a window. It tells you information such as which class provides the editor and what plugin contributes it. This is useful e.g. to find out how the Editors work and what API they use. We don’t need this information for the example, but other cases have proven this to be useful.

Plug-in Spy
Plug-in Spy

Plug-in Referencs

You can also, from the extension in a plugin.xml search for Plug-in Referencs. This shows other plug-ins that extend the same extension point. Since RTC does this, you can find and open related plugin.xml files and look for contribution classes, editor id’s and other useful information.

Plug-ins referencing the same extension point
Plug-ins referencing the same extension point

In this case com.ibm.filesystem.ide.ui is the best candidate to look at first. We don’t need the information for this example, but other examples would require to look here.

Modifying the Example

We can now modify the example to provide us with the desired solution. The steps are

  • Renaming the action class
  • Renaming ID’s and display text in the plugin XML
  • Adding a new contribution to the plugin
  • Adding required dependencies to RTC plug-ins
  • Adding code to the action

After renaming the acion class – and making sure that the new class name is actually correctly used in the plugin.xml – modify the plugin.xml to show menu captions as desired and add another contribution that directly contributes to the context menu, without creating a sub menu.

After the modifications the plugin.xml looks as below.

Final plugin.xmlBoth contributions are very similar. The difference is that the first contribution in the XML creates a new sub-meu and maps the action on to the menubarPath for this sub menu. The second contribution maps an action directly into the menubarPathadditions‘.

The order these contributions will show up in the menu is actually the reverse order of their definition in the plugin.xml.

Please Note: Don’t ask me how this works in detail. I just know enough and have example pattern that work for me. If you need more information search the Web for articles or find a good book about it.

What is left is to actually do something in the action.

SnapshotAction

The code below shows the class SnapshotAction that executes if the pop up menu is selected. Most of the code that actually works with the API is left out, because it is not that interesting. The code below can easily be used as a starting point for any menu action for any RTC object.

package com.ibm.js.team.filesystem.snapshot.menucontribution.popup.actions;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

import com.ibm.team.filesystem.ui.wrapper.SnapshotWrapper;
import com.ibm.team.process.common.IProjectArea;
import com.ibm.team.process.common.IProjectAreaHandle;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.common.IAuditableHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.scm.common.IBaselineSet;
import com.ibm.team.scm.common.IWorkspace;
import com.ibm.team.workitem.client.IQueryClient;
import com.ibm.team.workitem.client.IWorkItemClient;
import com.ibm.team.workitem.common.IAuditableCommon;
import com.ibm.team.workitem.common.expression.AttributeExpression;
import com.ibm.team.workitem.common.expression.Expression;
import com.ibm.team.workitem.common.expression.IQueryableAttribute;
import com.ibm.team.workitem.common.expression.QueryableAttributes;
import com.ibm.team.workitem.common.expression.Term;
import com.ibm.team.workitem.common.model.AttributeOperation;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.query.IQueryResult;
import com.ibm.team.workitem.common.query.IResult;
import com.ibm.team.workitem.rcp.ui.WorkItemUI;

public class SnapshotAction implements IObjectActionDelegate {

	private Shell shell;
	private IWorkbenchPart fTargetPart;
	private SnapshotWrapper fSelection;
	private IWorkItemClient fWorkItemClient;
	private IAuditableCommon fAuditableCommon;
	private ITeamRepository fTeamRepository;

	/**
	 * Constructor for Action1.
	 */
	public SnapshotAction() {
		super();
	}

	/**
	 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
	 */
	public void setActivePart(IAction action, IWorkbenchPart targetPart) {
		shell = targetPart.getSite().getShell();
		fTargetPart = targetPart;
	}

	/**
	 * @see IActionDelegate#run(IAction)
	 */
	public void run(IAction action) {
		IProgressMonitor monitor = new NullProgressMonitor();
		IBaselineSet snapshot = fSelection.getSnapshot();
		String snapshotUUID = snapshot.getItemId().getUuidValue();
		try {
			fTeamRepository = (ITeamRepository) snapshot.getOrigin();
			fWorkItemClient = (IWorkItemClient) fTeamRepository
					.getClientLibrary(IWorkItemClient.class);
			fAuditableCommon = (IAuditableCommon) fTeamRepository
					.getClientLibrary(IAuditableCommon.class);

			performAction(snapshotUUID, monitor);
			MessageDialog.openInformation(
					shell,
					"SnapshotMenuContribution",
					"My Action was executed on Snapshot: '"
							+ snapshot.getName() + "' UUID [" + snapshotUUID
							+ "]");

		} catch (Exception e) {
			MessageDialog.openError(shell, "SnapshotMenuContribution",
					e.getMessage() + "\nMy Action was executed on Snapshot: '"
							+ snapshot.getName() + "' UUID [" + snapshotUUID
							+ "]");
		}
	}

	private void performAction(String snapshotUUID, IProgressMonitor monitor)
			throws Exception, TeamRepositoryException {
			// Do something here
	}

	/**
	 * @see IActionDelegate#selectionChanged(IAction, ISelection)
	 */
	public void selectionChanged(IAction action, ISelection selection) {
		fSelection = null;

		if (selection instanceof IStructuredSelection) {
			Object first = ((IStructuredSelection) selection).getFirstElement();
			if (first instanceof SnapshotWrapper) {
				fSelection = (SnapshotWrapper) first;
			}
		}
	}
}

Lets look at the interesting parts first. The class implements IObjectActionDelegate. This is an interface of the Eclipse framework that is always used for this type of actions.

The method selectionChanged() is used to get the selected element(s). It is called when selecting elements for the contribution. The code is fairly typical and can also be refined to get a list of selections.

/**
 * @see IActionDelegate#selectionChanged(IAction, ISelection)
 */
public void selectionChanged(IAction action, ISelection selection) {
	fSelection = null;

	if (selection instanceof IStructuredSelection) {
		Object first = ((IStructuredSelection) selection).getFirstElement();
		if (first instanceof SnapshotWrapper) {
			fSelection = (SnapshotWrapper) first;
		}
	}
}

The code inspects the selection, uses IStructuredSelection to get at the elements, and checks that the correct class is passed, casts the input and stores it in a field.

The method setActivePart() is used to store some information about the UI that can be used later. Some UI elements such as message dialogs need the current shell. Others need the target part. These values are stored in fields.

/**
 * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
 */
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
	shell = targetPart.getSite().getShell();
	fTargetPart = targetPart;
}

The method run() finally is called when clicking the action. The interesting parts of this method are getting the snapshot from the wrapper and then getting the UUID.

Another interesting piece of code is to get the ITeamRepository. The code uses the method getOrigin() available from all RTC data objects and gets the team repository of the selected element. Keep in mind, the client could be connected to several different repositories and we want the one that contains the selected element. Once we have the ITeamRepository, it is possible to get client libraries needed for the areas of the API.

The code would do something to the selected element, or search for related elements and then perform some action with it. This code is left out in the example. The example shows a dialog window coming up on the end of the action or in case an exception occurred.

/**
 * @see IActionDelegate#run(IAction)
 */
public void run(IAction action) {
	IProgressMonitor monitor = new NullProgressMonitor();
	IBaselineSet snapshot = fSelection.getSnapshot();
	String snapshotUUID = snapshot.getItemId().getUuidValue();
	try {
		fTeamRepository = (ITeamRepository) snapshot.getOrigin();
		fWorkItemClient = (IWorkItemClient) fTeamRepository
				.getClientLibrary(IWorkItemClient.class);
		fAuditableCommon = (IAuditableCommon) fTeamRepository
				.getClientLibrary(IAuditableCommon.class);
			performAction(snapshotUUID, monitor);
		MessageDialog.openInformation(
				shell,
				"SnapshotMenuContribution",
				"My Action was executed on Snapshot: '"
						+ snapshot.getName() + "' UUID [" + snapshotUUID
						+ "]");
		} catch (Exception e) {
		MessageDialog.openError(shell, "SnapshotMenuContribution",
				e.getMessage() + "\nMy Action was executed on Snapshot: '"
						+ snapshot.getName() + "' UUID [" + snapshotUUID
						+ "]");
	}
}

Some interesting code we found during our work on Jazz.net is how to open a work item editor for a work item in the UI.

The code looks like this:

while (results.hasNext(null)) {
	IResult result = (IResult) results.next(null);
	IWorkItem workItem = fAuditableCommon.resolveAuditable(
			(IAuditableHandle) result.getItem(),
			IWorkItem.SMALL_PROFILE, monitor);
	workItemsFound += workItem.getId() + " ";
	WorkItemUI.openEditor(fTargetPart.getSite().getPage(), workItem);
}

The code runs on a query result as described in the posts Using Expressions For Automation or Using Work Item Queries For Automation and opens the work items found in the Eclipse UI.

Summary

The example above shows how you can add menu contributions for RTC objects in Eclipse and also, how to get the information needed. Interestingly the menu contribution also works in the Snapshot editor context menu.

Menu addition in the snapshot editor
Menu addition in the snapshot editor

As always, the code is very simple and has very few error handling implemented. You want to improve on that. However, I hope the example is useful for others out there trying to extend RTC.

Do not Modify the Triggering Element in an OperationAdvisor

Don’t modify the element that triggers your OperationAdvisor! The API is not designed to support that. You can do this in a participant.

Get a comprehensive overview of the rules and how this works in the Team Process Developer Guide.

I met with the work item team in Zurich two days before and we had some discussion about automation around work items in several areas. This lead to looking into the API for the extension point com.ibm.team.process.service.operationAdvisors and we found that the class providing this actually states that you should not modify the object that triggers the operation advisor.

The Wiki topic Process Execution of Operations also explains that, and why client side advisors and participants must not modify information in the repository.

See the documentation of com.ibm.team.process.common.advice.runtime.IOperationAdvisor below:

/**

* Operation advisors are run before an operation is invoked to decide whether

* or not the operation should be executed. Advisors check conditions and

* report problems via the collector they are given. If an advisor reports a

* serious problem with an operation, the operation will not be executed (though

* subsequent advisors may still be checked).

*

* Advisors may not make modifications to data which is persisted in the

* repository. Clients wishing to participate in operations by modifying data

* should implement an {@link IOperationParticipant} instead.

*

* Operation advisors are contributed on the client and server via the

* com.ibm.team.process.client.operationAdvisors extension-point and the

* com.ibm.team.process.service.operationAdvisors extension-point

* respectively.

*

Why is that important? For example imagine you have the intention to check for a work item state change, and you want to add approvals for specific state changes. You could use an operation participant for that as described in this post.

You could however also have the idea to do that in an operation adviser. If you do that, it violates the contract of the extension point. The extension point does not check that and it might actually work, but there might be ill effects or, if the extension point starts checking in the future, your extension might stop working.

One example where you might have ill effects is that the manipulation and save of the work item will not cause a second save and thus also bypass any other Advisor you might have in place. If you had an advisor that prevented approvals from being added to work items, it would not detect that change.

In general, if you want to modify data, use an operation participant.

Related Posts

RTC Update Parent Duration Estimation and Effort Participant

Resolve Parent If All Children Are Resolved Participant

A Create Approval Work Item Save Participant

Jazz.net article about creating operation Advisors

A Create Approval Work Item Save Participant

This post is about a Participant that creates an approval when saving a work item. I was interested in posting this, because I was interested on how to get at project member and role information in a server extension. I had already helped a partner with a similar effort in the past, where the approver information was supposed to be read in an external system. Back then I couldn’t find how to access the project area information and to find the roles.

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 post shows RTC Server and Common API.

Download

You can download the code here. The API code in this post is Server and Common API.

Solution Overview

The code provides you with several classes. The interesting one is com.ibm.js.team.workitem.createapproval.participant.CreateApprovalParticipant. This class implements the participant that creates an approval if a workitem changes into the state "com.ibm.team.workitem.common.model.IState:com.ibm.team.apt.story.tested".

In case this state change is detected, the participant runs the following code to get approvers by their role Product Owner.

/**
 * @param workItem
 * @param collector
 * @param role
 * @param monitor
 * @throws TeamRepositoryException
 */
private void createApprovalByRole(IWorkItem workItem,
		IParticipantInfoCollector collector, String role, IProgressMonitor monitor) throws TeamRepositoryException {

	IContributorHandle[] approvers=findApproversByRole(workItem, "Product Owner", monitor);
	createApproval(workItem, collector, approvers, monitor);
}

The method called to find the approvers looks like the following code. The code gets the process area that governs the work item. and tries to get contributors with matching roles.

If there are no contributors that could be found with a matching role, it tries the same with the project area. The contributors are returned to create the approval.

Please note, this strategy could be changed into recursively start at the project area an find the enclosed team area hierarchy and then try all team areas in the hierarchy from the one that owns the work item up to the project area. This is left as a good example for the you to implement.

/**
 * Finds Approvers by role. Looks in the process area that owns the work item first, 
 * then looks at the project area if it was not already looking at it.
 * 
 * @param newState
 * @param roleName
 * @param monitor
 * @return
 * @throws TeamRepositoryException
 */
private IContributorHandle[] findApproversByRole(IWorkItem newState,
		String roleName, IProgressMonitor monitor) throws TeamRepositoryException {
	IProcessAreaHandle processAreaHandle = fWorkItemServer.findProcessArea(
		newState, monitor);
	IProcessArea processArea = (IProcessArea) fAuditableCommon.resolveAuditable(processAreaHandle,
		ItemProfile.createFullProfile(IProcessArea.ITEM_TYPE), monitor);
	IContributorHandle[] contributors = findContributorByRole(processArea, roleName, monitor);

	if(contributors.length==0){
		IProjectAreaHandle projectAreaHandle = processArea.getProjectArea();
		if(!projectAreaHandle.getItemId().equals(processAreaHandle.getItemId())){
			IProcessArea projectArea = (IProcessArea) fAuditableCommon.resolveAuditable(projectAreaHandle,
				ItemProfile.createFullProfile(IProcessArea.ITEM_TYPE), monitor);
			return findContributorByRole(projectArea, roleName, monitor);
		}
	}
	return contributors;
}

The code to find the approvers by role gets the members of the process area, then gets the contributors with the role name provided and returns the result. The code can be seen below.

/**
 * Find contributors by role on a process area.
 * 
 * @param processArea
 * @param roleName
 * @param monitor
 * @return
 * @throws TeamRepositoryException
 */
public IContributorHandle[] findContributorByRole(
		IProcessArea processArea, String roleName,
		IProgressMonitor monitor) throws TeamRepositoryException {
	fProcessServerService = getService(IProcessServerService.class);
	IContributorHandle[] members = processArea.getMembers();
	IContributorHandle[] matchingContributors = fProcessServerService
		.getContributorsWithRole(members, processArea,
		new String[] { roleName });
	return matchingContributors;
}

Finally the code below creates the approval if there are approvers that are passed. It gets the full state of the work item. Then it gets the approvals and creates a new descriptor for the new approval. For each approver it creates an approval with the new descriptor and then adds it to the approvals. Finally it saves the work item.

In case there are no approvers or the save is prevented, an error info is generated.

/**
 * Creates an approval and adds all approvers from an array
 * 
 * @param workItem
 * @param collector
 * @param monitor
 * @throws TeamRepositoryException
 */
private void createApproval(IWorkItem workItem,
		IParticipantInfoCollector collector, IContributorHandle[] approvers, 
		IProgressMonitor monitor) throws TeamRepositoryException {

	if (approvers.length==0) {
		String description = NLS.bind("Unable to create the Approval",
			"Unable to find an approver for the work item ''{0}''.",
			workItem.getItemId());
		IReportInfo info = collector.createInfo(
			"Unable to create approval.", description);
		info.setSeverity(IProcessReport.ERROR);
		collector.addInfo(info);
		return;
	}
	// Get the full state of the parent work item so we can edit it
	IWorkItem workingCopy = (IWorkItem) fWorkItemServer.getAuditableCommon()
		.resolveAuditable(workItem, IWorkItem.FULL_PROFILE, monitor)
		.getWorkingCopy();

	IApprovals approvals = workingCopy.getApprovals();
	IApprovalDescriptor descriptor = approvals.createDescriptor(
		WorkItemApprovals.REVIEW_TYPE.getIdentifier(), APPROVAL_NAME);
	for (IContributorHandle approver : approvers) {
		IApproval approval = approvals.createApproval(descriptor, approver);
		approvals.add(approval);
	}
	IStatus saveStatus = fWorkItemServer.saveWorkItem2(workingCopy, null, null);
	if (!saveStatus.isOK()) {
		String description = NLS.bind("Unable to create the Approval",
			"Unable to save the work item ''{0}''.",
			workItem.getItemId());
		IReportInfo info = collector.createInfo(
			"Unable to create approval.", description);
		info.setSeverity(IProcessReport.ERROR);
		collector.addInfo(info);
	}
}

The code to download contains other examples for how to get approvers.

Summary

The code is experimental. I have tested it in a Jetty based test server using the Scrum template. It is by no means production ready and can be enhanced for various scenarios. However, as always, I hope the code is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.