RTC Process Customization – What you can and cannot do

Rational Team Concert Process Customization – What you can and cannot do, that is the title of the webinar I presented two days ago.

If you are interested in my view on this, you can find the replay of the webinar here in the Rational Team Concert Enlightenment Series.

The slides are shared here.

Also see What API’s are Available for RTC and What Can You Extend? for more links.

Attribute Customization – Java Based Value Providers, Conditions and Validators

I had to look into how RTC Attribute Customization can be done using Java based Eclipse Extensions. This post shows how this works and provides with examples if you need to get started on the topic.

Update: Also look at A Custom Condition to Make Attributes Required or Read-Only by Role to see more on this kind of extensions.

The Lab 5 talks about using JavaScript for implementing custom providers. It also talks about limitations of the JavaScript API. The current JavaScript API limits you to accessing attributes of the current work items and is also very limited when working with more complex attributes. The JavaScript API in RTC 3.x and 4.x is so limited as it

  • Does not provide means to access extended user information such as team membership or roles
  • Does not allow to access links to Items such as other work items and other work items attributes
  • Does not allow to access information such as subscriptions or attachments

In addition there are few built in providers (see Lab 4 in the workshop) that can be used with list type attributes.

Recently I had to work for a customer request where the built in functionality and the available JavaScript API were clearly not sufficient to match the requirements. This gave me the opportunity to look into extending Attribute Customization using Java based extensions. I thought this would be a nice experience to share.

License and Download

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.

The code presented in this post can be downloaded from here. The code is also available in the RTC Extensions Stream (RTC AttributeCustomization component) in the Jazz In Flight project @ JazzHub. The code shows some simple providers as an entry point and some more complex ones to show the potential.

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 code attached to this post in the development environment you set up in the Rational Team Concert Extensions Workshop and get your own extensions working there as well.

In this context, please also consider to at least read through the Process Enactment Workshop for the Rational solution for Collaborative Lifecycle Management lab 4 and lab 5 to understand how attribute customization works.

I looked into Attribute Customization for the RTC Process Enactment Workshop. You should look into Lab 4 if you don’t know how value providers work and are configured in RTC.

See JavaScript Attribute Customization – Where is the log file? for the log file location for debugging.

The code in this post is common code running in the Eclipse client and the RTC Server.

Finding Information in the SDK

Because I never did this kind of extension before, I started with reviewing the Wiki Article about Attribute Customization on Jazz.net. It is also a good idea to review existing java code to get some ideas.

This section describes how to find example code in the SDK, which is very important to be able to do this kind of work.

The following steps require the development environment to be set up like described in this post. This allows basically to search for references to the extension point and find the code that is involved.

First create a plugin project based on the suggestions in the Wiki Article. Then search for references to the Extension point.

Search for References to an extension point
Search for References to an extension point

The search will find some plugins extending this extension point.

Available extensions
Available extensions

Just double click the plugin com.ibm.workitem.common to open the plugin XML and review the extensions. You can switch to the Extensions tab in the plugin editor and get a better overview. You can navigate to the extending classes by simply looking their name up in the Extension Element Details section, copying the class name and running a Java Search for the declaration for the type. In the search result, find the result related to the SDK and open it in the package explorer.

Type in the search result - Show in Package Explorer
Type in the search result – Show in Package Explorer

Browse the package explorer and explore the SDK source of all the interesting classes. Open classes to look into the code and find out how they work.

Explorer the SDK code
Explore the SDK code

There are some very interesting examples that are worth looking into. Especially the RoleBasedUserProvider and the SecurityContextProvider.

Code to explore
Code to explore

I find the SecurityContextProvider interesting, because I am interested in automation to control the visibility of work items in the context of working with customers. I have a todo created to try to find time to create my own implementation.

Example Implementations

As usual I started with some very simple examples.

  • A default value provider for string type attributes that returns some text
  • A calculated value provider for string and string list type attributes that returns some text
  • A validator for string type attributes that looks if the string contains some sub string and is configurable in the process configuration

These are available out of the box, but as examples easy enough to do without having to fight with more API and I learned a lot from them for reasons I did not expect.

Example Implementations: attributeType

The first decision necessary when following the suggestions in the Wiki Article and creating the extension, is to decide which type the provider should work for. The Article provides a list of examples for the type ID’s, but this is by no means complete. I am not sure yet, where to find the complete list. This section of a Wiki article could be a good starting point. You can look up examples in the extensions mentioned above. Another Theme seems to be to add List in case you want a list type attribute. For example if the return type is string and you want to support a list attribute, the attribute type is stringList.

Also be aware you can and should add several attribute type definitions if you want the extension to support more than one type. String provider are typical in this case. If you want to be able to select them for all string type attributes, define small, medium and large string like below.

ExtensionPointDefinitonsYou can leave out the settings for isDefault and requiresConfiguration in theExtension Elements Details.

API Available in Providers

All provider implementations get called with the following data:

  • IAttribute attribute, the attribute the provider is called for (not available for conditions)
  • IWorkItem workItem, the workitem context the provider is called for
  • IWorkItemCommon workItemCommon, as an entry point to get more API services
  • IConfiguration configuration, the configuration in the process XML related to this provider
  • IProgressMonitor monitor, the progress monitor (can be null)

You can use the workItemCommon to get other common services. Since the providers run in the client as well as in the server the API you have access to seems to be the API common to both. I found IAuditableCommon and IAuditableCommonProcess so far.

Default Value Provider

The code for a default value provider must implement the interface com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider. The type is correctly generated if you follow the Wiki Article. The implementation here is very simple and just returns a string.

The code below shows a very simple default value provider.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringDefaultProvider implements IDefaultValueProvider {

	public SampleStringDefaultProvider() {
	}

	@Override
	public String getDefaultValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		return "String Default Value";
	}
}

Calculated Value Provider

The next example is a simple calculated value provider that returns a string containing the time since 1970 in milliseconds. The interface implemented is com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider. Please note, all examples below are typed and specify the return type e.g. .

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.Date;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringCalculatedValueProvider implements IValueProvider {

	public SampleStringCalculatedValueProvider() {
	}

	@Override
	public String getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		Date date = new Date();
		return "String Calculated Value: "+date.getTime();
	}
}

Again, very basic to show and understand the concept.

A second example is showing a calculated value for a stringList attribute type. The difference is that it returns a List that contains several values.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

public class SampleStringListProvider implements IValueProvider {

	public SampleStringListProvider() {
	}

	@Override
	public List getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		List result = new ArrayList();
		result.add("Test 1");
		result.add("Test 2");
		result.add("Test 3");
		return result;
	}
}

Validator

The last simple example is a validator that validates if a text field contains a certain sub string. It implements the interface com.ibm.team.workitem.common.internal.attributeValueProviders.IValidator. The validator, configured for a string attribute, by default searches for the search string “Test” in the attribute value and issues a warning.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;

import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValidator;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Validates if a string is contained in the text.
 * 
 * Configure in Process XML
 * 
 * 
 *
 */
public class SampleStringContainsSubstring implements IValidator {

	private static final String SMAPLE_STRING_CONTAINS_SUBSTRING = "com.ibm.js.team.workitem.attribute.customization.providers.SmapleStringContainsTestValidator";
	private static final String REQUIRED_CONTENT = "requiredContent";
	private static final String WARNING_LEVEL = "warning";
	private static final String SEVERITY = "severity";
	private static final String VALIDATION_OK_STATUS = "ValidationOK";
	private static final String CONFIGURATION = "configuration";

	private String fSearchString;
	private int fValidationSeverity;

	public SampleStringContainsSubstring() {
	}

	@Override
	public IStatus validate(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		getConfiguration(configuration);

		String value = (String) attribute.getValue(workItemCommon.getAuditableCommon(), workItem, monitor);
		if(value.indexOf(fSearchString)>=0){
			return new Status(Status.OK, SMAPLE_STRING_CONTAINS_SUBSTRING,VALIDATION_OK_STATUS);
		}
		return new Status(fValidationSeverity, SMAPLE_STRING_CONTAINS_SUBSTRING, "Validation: String must contain '"+fSearchString+"'");
	}

	private void getConfiguration(IConfiguration configuration) {
		fSearchString = "Test";
		fValidationSeverity=Status.WARNING;
		IConfiguration parameters= configuration.getChild(CONFIGURATION);
		if(null!=parameters){ // We got a configuration
			String customSearchString=parameters.getString(REQUIRED_CONTENT);
			if(null!=customSearchString)
				fSearchString=customSearchString;
			String configuredValidationSeverity=parameters.getString(SEVERITY);
			if(null!=configuredValidationSeverity)
				fValidationSeverity =  configuredValidationSeverity.equalsIgnoreCase(WARNING_LEVEL)?Status.WARNING:Status.ERROR;
		}
	}
}

The code is a bit more complex, because it allows to configure the test string and the severity in the process XML. The configuration and defaults are read in getConfiguration() and stored in fields. Then the attribute value is read and tested if it contains the substring. If not, a message is created that shows in the UI. Based on the configuration the severity is of level error or warning. Error would allow to prevent saving a work item together with the Attribute Validation work item save precondition. The configuration would look like this:

Validator configuration in the process XML
Validator configuration in the process XML

Condition

I could not come up with an easy condition. Instead I looked into more complex things next. The next examples I did, were focused to automate something based on the role of a user.

The following condition provides information, if the user has a set of roles in the process area the workitem is owned by. It can for instance be used to make attributes mandatory or read only based on this information. The condition is fully configurable in the process XML and can manage one or more roles.

Please be aware that the extension point schema has an issue. If you create the class to handle the extension, you get an error. Create it manually or fix it. The class you create should implement com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition.

Please see the code blow for details.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Condition that checks if the user editing the work item has a specific role
 * 
 * Configure in Process XML
 * 
 * 
 * 
 *
 */
public class SampleCurrentUserHasRoleCondition implements ICondition {

	/* (non-Javadoc)
	 * @see com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition#matches(com.ibm.team.workitem.common.model.IWorkItem, com.ibm.team.workitem.common.IWorkItemCommon, com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration, org.eclipse.core.runtime.IProgressMonitor)
	 * 
	 * Return true if the user has the specified role, else false
	 */
	@Override
	public boolean matches(IWorkItem workItem, IWorkItemCommon workItemCommon,
			IConfiguration configuration, IProgressMonitor monitor)
			throws TeamRepositoryException {
		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		IContributorHandle user= workItemCommon.getAuditableCommon().getUser();
		return ContributorUtil.hasRole(workItem, user, lookupRoles, workItemCommon, monitor);
	}
}

That is short. The reason is, that all the code is in the class ContributorUtil. I was able to reuse that code in several ways and ended up collecting it in the utility class. The method getConfiguration() basically gets the roles to look for from the process configuration.

Then the code gets the IContributorHandle of the current user and the method hasRole check if the user has the role.

The code for the ContributorUtil is below.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IProcessArea;
import com.ibm.team.process.common.IProcessAreaHandle;
import com.ibm.team.process.common.IRole;
import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IAuditableCommonProcess;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.ItemProfile;

/**
 * Utility class to provide contributor related utilities
 *
 */
public class ContributorUtil {

	private static final String DEFAULT_ROLE_TEAM_MEMBER = "ScrumMaster";
	static final String ROLE = "role";
	static final String PROCESSAREA = "processArea";

	/**
	 * Returns a list of members that have a certain role in the process area a work item is filed against
	 * 
	 * @param workItem
	 * @param lookupRoles
	 * @param workItemCommon
	 * @param monitor
	 * @return a list of contributor handles
	 * @throws TeamRepositoryException
	 */
	public static List findProcessAreaMembersByRole(IWorkItem workItem, Collection lookupRoles,
			IWorkItemCommon workItemCommon, IProgressMonitor monitor)
			throws TeamRepositoryException {
		IProcessAreaHandle processAreaHandle = workItemCommon.findProcessArea(workItem, monitor);
		IProcessArea processArea = (IProcessArea) workItemCommon.getAuditableCommon().resolveAuditable(processAreaHandle,
				ItemProfile.PROCESS_AREA_DEFAULT,monitor);
		IAuditableCommonProcess auditableCommonProcess= workItemCommon.getAuditableCommon().getProcess(processArea, monitor);
		IContributorHandle[] userhandles = auditableCommonProcess.getContributorsWithRole(processArea.getMembers(), processArea, lookupRoles.toArray(new String[lookupRoles.size()]), monitor);
		return workItemCommon.getAuditableCommon().resolveAuditables(Arrays.asList(userhandles), ItemProfile.CONTRIBUTOR_DEFAULT, monitor);
	}

	/**
	 * Returns true if the user working on the work item has a specified role in the process area 
	 * the work item is filed against
         *
	 * @param workItem
	 * @param user
	 * @param lookupRoles
	 * @param workItemCommon
	 * @param monitor
	 * @return true if the user has the specified role, else false
	 * @throws TeamRepositoryException
	 */
	public static boolean hasRole(IWorkItem workItem, IContributorHandle user, List lookupRoles,
			IWorkItemCommon workItemCommon, IProgressMonitor monitor)
			throws TeamRepositoryException {
		IProcessAreaHandle processAreaHandle = workItemCommon.findProcessArea(workItem, monitor);
		IProcessArea processArea = (IProcessArea) workItemCommon.getAuditableCommon().resolveAuditable(processAreaHandle,
				ItemProfile.PROCESS_AREA_DEFAULT,
				monitor);
		IAuditableCommonProcess auditableCommonProcess= workItemCommon.getAuditableCommon().getProcess(processArea, monitor);
		Collection roles = auditableCommonProcess.getContributorRoles(user, processArea, monitor);
		for (IRole aRole : roles) {
			if(lookupRoles.contains(aRole.getId())){
				return true;
			}
		}
		return false;
	}

	/**
	 * Reads the role configuration
	 * 
	 * Configure in Process XML
	 * 
	 * 
	 * 
	 * 
	 * @param configuration
	 * @return
	 */
	public static List getConfiguration(IConfiguration configuration) {
		ArrayListlookupRoles=new ArrayList();

		List processAreaConfigurations= configuration.getChildren(PROCESSAREA);
		if(null!=processAreaConfigurations&&!processAreaConfigurations.isEmpty()){ // We got a configuration
			for (IConfiguration roleConfiguration : processAreaConfigurations) {
				String foundRole=roleConfiguration.getString(ROLE);
				if(foundRole!=null){
					lookupRoles.add(foundRole);
				}
			}
		} else {
			lookupRoles.add(DEFAULT_ROLE_TEAM_MEMBER);
		}
		return lookupRoles;
	}
}

The method ContributorUtil.hasRole() basically gets the process area and then gets the roles of the user in this process area and then checks if one of the roles matches a configured role.

ContributorUtil.getConfiguration() basically reads the process XML configured for the extension and returns a list of roles configured. By default and unconfigured it looks for Team Member. Please be aware it looks for the role ID and not the display name.

Value Set Provider

As an example for a value set provider that implements com.ibm.team.workitem.common.internal.attributeValueProviders.IValueSetProvider see the example that provides a set of users with roles. The code looks as shown below:

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueSetProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * This ValueSet Provider returns a list of users that have the configured role 
 * in the process area the work item is filed against
 * 
 * Configure in Process XML
 * 
 *   
 * 
 *
 */
public class SampleUsersWithRoleValueSetProvider implements IValueSetProvider {

	@Override
	public List<? extends IContributor> getValueSet(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {
		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		return ContributorUtil.findProcessAreaMembersByRole(workItem, lookupRoles,
				workItemCommon, monitor);
	}
}

It uses the same methods provided by the ContributorUtil class used by the other more complex providers and conditions.

It works for contributor as well as contributorList attribute types.

Additional Providers

ContributorUtil.findProcessAreaMembersByRole() basically finds the users that have a role matching the list of roles in the process area and returns the users as list. I used this in several other providers.

  • Sample User With Role In ProcessArea Default provides a default value for one user with matching roles (the first of the returned users) for a user type attributes
  • Sample User With Role In ProcessArea Default provides a default value for all users with matching roles  for a userList type attributes
  • Sample User With Role In ProcessArea Calculated Value returns the first user in the list of found users with one of the roles back as calculated value for user type attributes
  • Sample Users With Role In ProcessArea Calculated Value returns all matching users found for userList attribute types
  • Sample Users With Role In ProcessArea provides users with matching roles

The image below shows the classes that implement the required interfaces.

Additional Providers for users with roles
Additional Providers for users with roles

All the provider classes look very similar. They implement one or more interfaces and use the utility class to get the required data. Here an example:

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2013. 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.workitem.attribute.customization.providers;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.repository.common.IContributor;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IWorkItemCommon;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IDefaultValueProvider;
import com.ibm.team.workitem.common.internal.attributeValueProviders.IValueProvider;
import com.ibm.team.workitem.common.model.IAttribute;
import com.ibm.team.workitem.common.model.IWorkItem;

/**
 * Implements a value provider that returns the users that have a specific role in the process
 * area the work item is filed against.
 * 
 * Configure in Process XML
 * 
 * 
 * 
 * 
 *
 */
public class SampleUserWithRoleValueProvider implements IDefaultValueProvider,IValueProvider {

	public SampleUserWithRoleValueProvider() {
	}

	@Override
	public IContributor getValue(IAttribute attribute, IWorkItem workItem,
			IWorkItemCommon workItemCommon, IConfiguration configuration,
			IProgressMonitor monitor) throws TeamRepositoryException {

		List lookupRoles = ContributorUtil.getConfiguration(configuration);
		List users = ContributorUtil.findProcessAreaMembersByRole(workItem, lookupRoles,
				workItemCommon, monitor);
		if(!users.isEmpty()){
			return users.get(0);
		}
		return null;
	}

	@Override
	public IContributor getDefaultValue(IAttribute attribute,
			IWorkItem workItem, IWorkItemCommon workItemCommon,
			IConfiguration configuration, IProgressMonitor monitor)
			throws TeamRepositoryException {

		return getValue(attribute, workItem, workItemCommon, configuration, monitor);
	}
}

All these providers can be configured for one or more roles in the process XML like shown below. The configuration is within the entry for the provider as described in the Jazz.net Wiki entry about attribute customization.

Provider configured with two roles
Provider configured with two roles

Deploying

I ran this in a test environment set up as described here.

The downloadable code contains a feature and an update site project. Follow the Rational Team Concert Extension Workshop and the instructios in the Wiki Article about Attribute Customization to deploy the code on the client and the server.

Related Posts

Summary

The code above is a good starting point if you want to create your own providers. There are more capabilities that you can use with Java compared to JavaScript. What data really is accessible remains to be seen. I still have to look if it would be possible to for example access SCM components and the like. I hope it is work reading and helps some users out there to save some time to get started with this topic.