EWM/RTC Extensions Workshop works with EWM 7.0.x

As published in Issues with the EWM/RTC Extensions workshop in 7.0.x versions, there was an issue that caused the RTC Extensions Workshop to fail starting a Eclipse debug client in Lab 1.9. The problem and how it was caused has been found. I have just tested the RTC Extensions Workshop Lab 1 with the new EWM Client SDK (7.0.1 and 7.0.2) and it works now as it should. I will update my previous post.

Update for the Extensions Workshop now available

All the issues have been addressed in an update to the RTC Extensions Workshop for the 7.0.x and later versions. This should address all the issues.

While running the test, I found that I had to increase the maximum heap size for the launch [RTCExt] Jetty RTC Server, because the JUnit Test that is run to create the test database, ran out of heap memory. I doubled the value from -Xmx256M to -Xmx512M and the error was gone.

Increase the available heap

The fix is only in the EWM/RTC Client SDK, so it is only necessary to download that again.

Please note that due to the name changes and newer versions of Eclipse, there might be some small differences between the screen shots in the RTC Extensions Workshop and the newer versions. However, they are so minor, it should not be an issue. I am currently looking into updating the current Extensions Workshop material with small changes to help guide the user with these changes.

The issue with server debugging explained in Issues with the EWM/RTC Extensions workshop in 7.0.x versions, is still apparent in 7.0.2. The suggestion to change the JRE of the server in that blog post holds.

As always, I hope that my posts here help the Jazz user community and save them some time.

Advertisement

Searching and Exploring the RTC SDK

I have answered many customer questions about the RTC Java APIs in the past. Many of the posts in this blog are the result of such questions. I have no privileged access to the RTC source code. As far as I can tell, there is no secret library of RTC API documentation that the IBM development team is hiding from everybody else. Even if there was such documentation, I do not have access to it. So, how do I come up with those answers?

In short, by using the available capabilities to search for answers.

Prerequisites

The content below requires to know Learning To Fly: Getting Started with the RTC Java API’s and the Extensions Workshop.

Know where and how to search

The whole answer is, that it is not necessary to know everything. It is just necessary to know how and where to search for the answers. These days, there are basically two answers to where and how to search.

  1. The Internet using search engines
  2. The RTC SDK using the Eclipse IDE’s capabilities.

I have already explained how and where to search the internet in the post Learning To Fly: Getting Started with the RTC Java API’s in the section Where can I find Examples and Example Code?.

Search the internet

Search the internet and limit the search to sites that are likely to have relevant information. Examples are Jazz.net and stackoverflow.com. Jazz.net has the Forum and the development Wiki. Stackoverflow has a lot of questions and answers from many contributions.

I did contribute at stackoverflow as well, but gave up when they would not accept a link to external content as answer. The purpose of this blog is to be able to provide answers and share examples. For that very reason rsjazz.wordpress.com is also a good place to search. The search capability in the top right of this blog is also a valid place to search. The page Interesting Links is a collection of links to other sources I have come across over the years.

Very recently the Jazz community and Github have become a source as well.

Many questions in the context of RTC Extensions might be just related to Eclipse Plugin development. https://www.eclipse.org/ has a lot of examples and documentation around such questions.

Search the RTC SDK

If there are no ready examples and answers in the internet, there is a host of example code available in the RTC SDK. The RTC SDK contains at least the following:

  1. Unit test code for RTC the Java API. These contain especially examples for the administration API, but also for other parts of the client API provided by the Plain Java Client Libraries.
  2. The Java Code for the client API provided by the Plan Java Client Libraries, including the documentation that is used to create the JavaDoc for the Plain Java Client Libraries.
  3. RTC Rich Client Platform code used in the RTC Eclipse Client, including comments. This code uses the public client API but also uses internal API.
  4. RTC Client plug in code, including Client operation behavior such as advisors (pre-conditions), participants (follow up actions), aspect editors providing the Eclipse Admin UI and related information.
  5. RTC Server API and RTC Server code, including documentation for interfaces and methods.
  6. RTC Server plug in code, including server operation behavior such as advisors (pre-conditions), participants (follow up actions), aspect editors providing the Eclipse Admin UI and related information.
  7. Client and server extension points and related code.
  8. Code for asynchronous tasks.

All the code mentioned above is included in the RTC Server SDK that is shipped with RTC in the all download section. The RTC Client SDK that is shipped in addition since 6.0.3 only contains the client code which is kept compatible to earlier versions of the Eclipse client.

You can search the RTC SDK by using the capabilities provided by Eclipse. Especially the Eclipse Plugin Development (PDE) and Rich Client Platform (RCP) development tools provide a comprehensive set of features to search the RTC SDK. There are several approaches that can be used to search in Eclipse and the Eclipse PDE:

  1. Java Search for types, interfaces, methods and other Java Related properties. This includes the capability to use search pattern e.g. use an asterisk. It is possible to specify the relationships to search for e.g. search for classes that implement an interface.
  2. Search for Plugins, Extension points, references to extension points. This includes the capability to see the plugin.xml for other extensions, revealing all the implementation classes and relevant information that can be used again in 1 to narrow down the search to the relevant code.
  3. Eclipse RCP/SWT Inspectors such as Yari or tools like the built in Plugin Spy allow to use the declarative and reflective capabilities of Eclipse and the RCP to analyze the UI and other information provided in the plugins and features. This allows to find which classes are called in menus and to look at which data is used by views and how it is used.
  4. Other search capabilities such as File search and Text search can help in certain conditions.

These capabilities allow to pretty much find anything that is available in the RTC SDK. This does not make it trivial to understand what is found, especially the RCP UI code of RTC is sometimes very hard to understand, but it provides at least entry points, where to start.

Once something is found, there are various means available to

  1. Open or navigate to the element that is found
  2. Open the package containing the element, if applicable
  3. Open other items that are related to the found element

This is really all one needs to know. The rest is just using these capabilities to your advantage. Some examples how to use this are given below.

Finding Built-in Extensions in the SDK

The SDK contains all the client and server extensions and their source code. When developing a custom advisor or follow up action e.g. following the Extensions Workshop it is a good idea to get inspiration from the existing code. How does that code work? How does it use the API?

Any such scenario starts with finding examples that use one of the relevant extension points. The extension points are shown in the Extensions Workshop and other examples. Most likely there is an example using an extension point in the plugin.xml already, or a new plugin.xml has been just created.

In the open open editor of the plugin.xml on the Extensions tab, right click the extension point that is of interest. To understand where this is used select Find References and click to start the search.

Search for references to an existing extension point.
Search for references to an existing extension point.

The search window will open below and show the references found after a while. This can take some time, dependent on the performance of the client computer. The search result will show multiple hits. The image below shows a part of the hits in the current workspace. It especially shows the references from RTC SDK code to the extension point used by server operation advisors. All the references shown here have the same namespace prefix com.ibm.team. This hints that the origin of the code is in the RTC SDK.

Operation Advisors shipped with RTC.
Operation Advisors shipped with RTC.

Use the up and down arrows to browse the referencing plugin XML’s, or click one of the rows in the search that is of interest. Make yourself familiar with the name spaces that are used. This makes it easier to focus on the important examples you are looking for.

The namespaces refer to the following components of RTC

  • com.ibm.team.apt – Agile Planning
  • com.ibm.team.build – Build
  • com.ibm.team.filesystem – Source Control Filesystem
  • com.ibm.team.scm – Source Control
  • com.ibm.team.workitem – Work Items

When looking for an example for SCM related operations such as deliver, look at the examples in the namespaces filesystem and scm. When looking at work item related extensions look into the examples with the workitem namespace.

Click the search result to open the related plugin.xml. As an example the search result
com.ibm.team.workitem.service. Eclipse allows to find strings in open files, so it is possible to search for strings using CTRL+F. E.g. search for Required to find because you are interested in advisors for required attributes.

Required Attributes by Tape and State Advisor
Required Attributes by Tape and State Advisor

In the plugin.XML you can see the implementing class. You can use the full qualified class name e.g. com.ibm.team.workitem.common.internal.RequiredAttributesByTypeAndStateAdvisor to open the class using the Navigate>Open Type menu of Eclipse.

Open the implementation class of the advisor
Open the implementation class of the advisor

This repository workspace has the SDK set up as well as the RTC Plain Java Client Libraries. Select the class in the SDK. Browse the classes code and documentation. Open the Class in the package explorer.

Explore the package containing the class
Explore the package containing the class

In the package explorer view in Eclipse browse the package e.g. C:\RTC605Dev\installs\rtc-server-sdk\plugins\com.ibm.team.workitem.common_3.2.900.v20170928_1641.jar.

Scroll down to the lower area. There are some packages that do not contain Java classes. For example there is a package or rather folder, that contain files such as the schemas used by the aspect editors in the admin UI. You can open the files to look at the details.

Package explorer shows related content.
Package explorer shows related content.

Please note: the one I was really looking for is a bit elusive, and I have not been able to locate it until now. I will update the post as soon as I have a solution.

This is only one way of doing it. Dependent on what is available and what one knows there are many other ways to look into the source code. For example search for all extension points with a package name of com.ibm.team* to find all the extension points. Search for specific Plugins, in case the name is available.

Search for References to an extension point.

Try to explore different angles to find what you are looking for.

Search the Java Code

As already mentioned, it is possible to search for Java classes. This can also use search patter using an askerisk. Search for different aspects such as methods and interfaces and limit the search result set.

Once a class is open, it is easy to navigate within the class hierarchy, open the jar file in the package explorer etc..

Explore the UI to add Menus

Here a key point is knowing and using Eclipse RCP/SWT Inspectors such as Yari. How it is done is This has been explained in Adding Context Menus for Jazz Objects to the RTC Eclipse Client and Hiding UI Contributions in the RTC Eclipse Client.

Summary

As seen above, there are many ways to find stuff in the RTC SDK that can be used. As always I hope this helps users out there with their endeavors. I will try to add more examples.

A component naming convention advisor

Organizations sometimes would like to implement naming conventions for components based on the architecture for example. This post shows a simple example advisor that checks for a naming convention.

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 6.0 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. It should run with any version that provides the operation ID.

The code in this post uses common and server libraries/services that are available in the RTC Server SDK.

Download

The code is included in the download in the post DIY stream naming convention advisors.

Solution

In the last few versions of RTC several operations have been made available for operational behavior. At least since RTC 5.0.2 the operation to modify a component is available with the operation ID com.ibm.team.scm.server.component. This allows to create advisors/preconditions as well as participants/follow up actions that operate on such events. An example shipped with the product is implemented in the class com.ibm.team.scm.service.UniqueComponentNameAdvisor.

There are examples shipped with the product in the SDK that you can look at for more sample code. For example: com.ibm.team.scm.service.internal.process.advisors.UniqueComponentNameAdvisor

The code below shows a very basic example how to test the component name for some simple naming schema.

The important information to take away is that the information about the save operation is provided in a special interface IComponentModificationData which allows access to the type of the operation, to old and new properties and to the component directly.

componentmodification

So it is possible to find out what operation is done on the component and based on that look at the properties that the component has.

The code below does exactly that. It checks what operation is going on and then takes the new name of the component and checks it.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2017. 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.scm.naming.advisor.service;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IAdvisorInfo;
import com.ibm.team.process.common.advice.IAdvisorInfoCollector;
import com.ibm.team.process.common.advice.runtime.IOperationAdvisor;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.scm.service.internal.AbstractScmService;
import com.ibm.team.scm.service.internal.process.IComponentModificationData;
import com.ibm.team.scm.service.internal.process.IComponentModificationData.OpType;

/**
 * An example advisor that checks the name of a component for some naming convention
 *
 * Also @see com.ibm.team.scm.service.UniqueComponentNameAdvisor for product example code
 */
@SuppressWarnings("restriction")
public class ComponentNamingAdvisor extends AbstractScmService implements
		IOperationAdvisor {
	public static final String REQUIRED_PREFIX = "TEST_";
	public static final String COMPONENT_NAMING_ADVISOR = "com.ibm.js.scm.naming.advisor.service.componentNaming";

	@Override
	public void run(AdvisableOperation operation,
			IProcessConfigurationElement advisorConfiguration,
			IAdvisorInfoCollector collector, IProgressMonitor monitor)
			throws TeamRepositoryException {

		IComponentModificationData data = (IComponentModificationData) operation
				.getOperationData();
		if (data == null) {
			throw new TeamRepositoryException("Missing component data"); //$NON-NLS-1$
		}

		String compName;

		if (data.getOpType() == OpType.CREATE) {
			compName = data.getNewName();
		} else if (data.getOpType() == OpType.RENAME) {
			compName = data.getNewName();
		} else {
			return;
		}
		if (validateName(compName)) {
			// Nothing to do
			return;
		}
		String description = "The component name violates the naming conventions component name must have prefix '"
				+ REQUIRED_PREFIX + "'!";
		String summary = description;
		IAdvisorInfo info = collector.createProblemInfo(summary, description,
				COMPONENT_NAMING_ADVISOR);//$NON-NLS-1$
		collector.addInfo(info);
	}

	/**
	 * Validate the name against the convention
	 * 
	 * @param compName
	 * @return
	 */
	private boolean validateName(String compName) {
		// Implement your own naming convention here
		if (compName.startsWith(REQUIRED_PREFIX)) {
			return true;
		}
		return false;
	}
}

Summary

This is as usual a very basic example with no or very limited testing and error handling. See the links in the getting started section for more examples. As always I hope this helps someone out there with running RTC.

The RTC Work Item Command Line on Bluemix

I was talking to a customer recently. They are using the WorkItem Command Line for some automation purposes. Since this can trigger e-mail notifications to a huge amount of users they wanted to use the new Skip Mail save WorkItem Parameter introduced in RTC 6.0 iFix3.

I had the time and went ahead implementing it. The resulting source code is available on IBM Bluemix DevOps Services in the project Jazz In Flight

ibm-bluemix-devops-services-2016-10-24_17-55-35

Access the Source Code

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!

RTC SCM Access

In the project you can access the source code of several extensions and automation I have created over the years. If you click Edit Code and you are not yet member of the project, you have to request access which I will allow.

The project contains a Stream called RTC Extensions with several components. One of the components is Work Item Command Line.

configure-eclipse-request-access-2016-10-24_18-13-14

To configure your RTC Eclipse client follow the instructions in the Configure eclipse client link. You can then create yourself a repository workspace and download the code. Please use the tracking and planning section (work items) if you want to do any changes to coordinate with me.

Changes

The current version uploaded there contains the capabilities described in A RTC WorkItem Command Line Version 3.0 plus a variety of bug fixes and a new switch /skipEmailNotification to disable work item update notification for the commands that modify work items such as

  • update
  • importworkitems
  • migrateattribute

The feature to suppress work item update notification is implemented in RTC 6.0 iFix3 where a new Skip Mail save WorkItem Parameter was introduced in RTC. When this additional save parameter is provided, the work item change does not trigger a work item change notification mail.The adoption in the WorkItem Command Line is done in a way that the implementation does not break the older API.  It introduces the additional save parameter value into the work item command line source code as new String constant instead of referencing the constant in the API. This way the WCL can be compiled with RTC Plain Java Client Library versions of RTC prior to 6.0 iFix3. If the WCL is run with versions earlier than 6.0 iFix3, e-mail notification is not suppressed. The behavior does not change in such versions of RTC and the additional save parameter is simply ignored.

Additional Download

You can also download the latest version 3.4 here:

Please note, there might be restrictions to access Dropbox and therefore the code in your company or download location.

Usage and install

Please see the posts A RTC WorkItem Command Line Version 3.0.

For the general setup follow the description in A RTC WorkItem Command Line Version 2.

For usage follow the description in A RTC WorkItem Command Line Version 2 and in A RTC WorkItem Command Line Version 2.1. Check the README.txt which is included in the downloads.

Summary

The work item command line is now available on IBM Bluemix Dev Ops Services and can be accessed and worked on there.

Watch Bartosz Chrabski on how to create Rational Team Concert – Advisor & Participant Extensions

If you are new to extending RTC, you might want to look at Bartosz Chrabskis post How to create Rational Team Concert – Advisor & Participant Extension (step by step) and the related video for creating a participant as well as the video for creating an advisor. Don’t forget to like the post and give the videos a thumbs up.

More information about the process can be found in Learning To Fly: Getting Started with the RTC Java API’s.

 

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.

Getting the RTC User Work Environment Information

Recently I was asked how to access the users work environment information. This is basically the information displayed in the Work Environment Tab of the user editor.

User Work EnvironmentI was able to get at the Information. The code below shows how that works and what the information means.

Related code on this blog can be found in

Manage Scheduled Absences Using The PlainJava Client Libraries.

Please note, I looked briefly into ways to update that information, but I was not able to find any public interface to do so. I found internal methods that apparently do it, but not in a way that has an easy API provided to the user.

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!

You can download the latest version here: as Eclipse project.

Please note, there might be restrictions to access Dropbox and therefore the code in your company or download location.

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 code linked from this post contains Client API.

Solution

The few methods below show how to access the data.

As usual, I use a very simple base class that connects to the repository and only performs the operations needed to get at the data. Please look at the downloadable code, if you are not sure how all that works together.

The specific API for the data in question is in the packages.

 com.ibm.team.apt.common,
 com.ibm.team.apt.client

The run() method below is called with all the parameters needed and basically connects to the team repository. It then finds the user it should look at based on the ID provided.

Next it gets the IResourcePlanningClient client library, and from that the ResourcePlanningManager which provides access to the data we are interested in.

The data we want is stored in the IContributorInfo that is returned by the call to the method getContributorInfo() providing the contributor object for the user we are interested in.

The data of interest is in the IWorkLocationDefinition available from the IContributorInfo. The code gets the information and then evaluates the details to print them.

private static boolean run(String[] args) throws TeamRepositoryException {

	if (args.length != 4) {
		System.out
				.println("Usage: GetWorkLocationAndWorkDayDetail    ");
		return false;
	}

	String repositoryURI = args[0];
	final String userId = args[1];
	final String password = args[2];
	String userToLookup = args[3];
	IProgressMonitor monitor = new NullProgressMonitor();

	ITeamRepository teamRepository = TeamPlatform
			.getTeamRepositoryService().getTeamRepository(repositoryURI);
	teamRepository
			.registerLoginHandler(new LoginHandler(userId, password));
	teamRepository.login(monitor);
		
	// Get the user to look at
	System.out.println("Data for user: " + userToLookup + ".");
	IContributor inspectUser = teamRepository.contributorManager()
			.fetchContributorByUserId(userToLookup, monitor);
		
	// Get the resource planning client 
	final IResourcePlanningClient resourcePlanning = (IResourcePlanningClient) teamRepository
			.getClientLibrary(IResourcePlanningClient.class);

	// Get the contributor related planning information
	IContributorInfo info = resourcePlanning.getResourcePlanningManager()
			.getContributorInfo(inspectUser, true, monitor);
		
	// Get the worklocation information that contains the working days
	IWorkLocationDefinition location = info.getWorkLocation(inspectUser);
	printWorkLocation(location);
	printWorkDays(location);

	teamRepository.logout();

	return true;
}

First it prints the work location information, basically the user environment information with the timezone and the regional settings.

There is some data “Variant” I am not sure it is used.

/**
 * Print the work location data
 * This includes the available location with timezone
 * 
 * @param location
 */
private static void printWorkLocation(IWorkLocationDefinition location) {
	System.out.println("Location");
	System.out.println("Language: " + location.getLanguage());
	System.out.println("Timezone: " + location.getTimeZone());
	System.out.println("ZoneOffset: " + location.getZoneOffset());
	System.out.println("Country: " + location.getCountry() + " not used");

	// Not used
	System.out.println("Variant: " + location.getVariant() + " not used");
}

Then the code prints the work day information.

/**
 * Print all work days for a users location
 * 
 * @param location
 */
private static void printWorkDays(IWorkLocationDefinition location) {
	System.out.println("\nWorkdays");
	Collection workDays = location.getWorkDays();
	for (IWorkDayDefinition workDay : workDays) {
		printWorkDay(workDay);
	}
}

private static int HOUR_IN_MILLISECONDS=60*60*1000;

/**
 * Print the work day data
 * 
 * @param workDay
 */
private static void printWorkDay(IWorkDayDefinition workDay) {
	int hour = HOUR_IN_MILLISECONDS;
	System.out.println("WorkDay: " + workDay.getDay().getName()
			+ " Literal " + workDay.getDay() + ".");
	System.out
			.println("\tWorking time: " + workDay.getWorkingTime()
					+ " milliseconds equals to " + workDay.getWorkingTime() / hour
					+ " hours");
	System.out.println("\tEndTime " + workDay.getEndTime()
			+ " milliseconds equals to " + workDay.getEndTime() / hour + " hour.");
}

The interesting information here is in the interface IWorkDayDefinition.

  • The day returned by getDay() as IWeekDay which provides the day information using a label and a name; label and name are a string
  • The working time (how many hours a day) for each day using getWorkingTime()
  • The time when the user stops working using getEndTime()

Both time values are provided in milliseconds. The working time is the time in hours set in milliseconds. The end time is the end time in milliseconds from midnight.

Running the code with parameters

"https://clm.example.com:9443/ccm/" "admin" "*****" "curtis"

results in the following output (for the same user shown in the image at the beginning):

Data for user: curtis.
Location
Language: en
Timezone: US/Eastern
ZoneOffset: -18000000
Country: US
Variant: null not used

Workdays
WorkDay: MONDAY Literal MONDAY.
	Working time: 28800000 milliseconds equals to 8 hours
	EndTime 64800000 milliseconds equals to 18 hour.
WorkDay: TUESDAY Literal TUESDAY.
	Working time: 28800000 milliseconds equals to 8 hours
	EndTime 61200000 milliseconds equals to 17 hour.
WorkDay: WEDNESDAY Literal WEDNESDAY.
	Working time: 28800000 milliseconds equals to 8 hours
	EndTime 61200000 milliseconds equals to 17 hour.
WorkDay: THURSDAY Literal THURSDAY.
	Working time: 28800000 milliseconds equals to 8 hours
	EndTime 61200000 milliseconds equals to 17 hour.
WorkDay: FRIDAY Literal FRIDAY.
	Working time: 28800000 milliseconds equals to 8 hours
	EndTime 61200000 milliseconds equals to 17 hour.
WorkDay: SATURDAY Literal SATURDAY.
	Working time: 0 milliseconds equals to 0 hours
	EndTime 0 milliseconds equals to 0 hour.
WorkDay: SUNDAY Literal SUNDAY.
	Working time: 0 milliseconds equals to 0 hours
	EndTime 0 milliseconds equals to 0 hour.

Summary

This post provides you with all the code and information needed to read and interpret the WorkLocation information stored in RTC.

As always, I hope this post saves users out there some time – or is at least fun to read.

Due Date Notifier – an Asynchronous Task Example

I always wanted to look into asynchronous tasks and also in creating events and e-mails on the server. Especially mail notification is a popular request on the forum. So here a working example.

Origin – Other Posts Around This Topic

This is not all my work, it is based on the QueryDev Guide in the the Jazz Developer Wiki and on Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained”. I was looking into these sources, but I found the QueryDev Guide did not ship a complete example – it was the basic classes. Jorge ships the extension, but both examples don’t compile with the RTC 6.x versions of RTC and potentially earlier versions. As mentioned in the QueryDev Guide the feed category used there is no longer available and in both posts one of the API methods used is missing a parameter. This one of the few examples where I have actually seen minor changes in the API so far.

This gave me enough reasons to look into creating a working example again, to refine it and to try to better understand what the extension does and how.

Jorge has an additional post around asynchronous tasks. See Custom Services Configuration Properties – naming collision issues for additional hints.

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!

You can download the project source code from here.

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

Solution Outline

The code implements an asynchronous task that is deployed on the server and uses a query to find work items that are overdue and not closed, in any project area. It creates a notification for these work items. The solution runs in two configurable modes.

  1. Mode “event” is the default. In this mode the asynchronous task creates a Message At Me event in the server feed using the IWorkItemFeedConstants.MESSAGES_AT_ME_FEED_CATEGORY. This event also triggers E-Mail notification for the owner. The Event is only created if there is no related MESSAGES_AT_ME_FEED_CATEGORY event that has been created in a configurable time span. This prevents from flooding the events and the user e-mail from duplicates.
  2. Mode “mail” can be configured. This mode sends a dedicated mail to the owner of the work item. This mode can not detect if it has recently sent a mail to the user. This means the asynchronous task should run with a delay that prevents it from running multiple times a day to avoid spamming the mail folders. It would also be worthwhile to look into a way to schedule the task only at a certain time of the day.

The reason why the second mode was added is two fold.

  1. I wanted to have an example how to send an e-mail with the server API.
  2. The MESSAGES_AT_ME_FEED_CATEGORY creates a mail that has a subject similar to

    “You were mentioned in work item ’52: This is a test work item’ [s]”.

    You can see the example message below. Althought he message body mentions its cause, this message is not really specific in its topic and I rather wanted something more specific.

Message Generated By Event

I tested several different event categories, including a custom one, but the results where even worse. For example, the message body did not even contain the event description “The work item was due 6 weeks 3 days 4 hours ago”.

The custom message sent in the second mode looks like this.

Custom Message

Since this is only an example, you might want to think about how to avoid sending multiple e-mails e.g. somehow storing information when a message was sent to whom and for which work item somewhere.

I tried if I could create an event, that would not trigger an e-mail to store the information, but that did not work for me.

Set-up and Use a Test Mail Server and Client

You probably don’t want to run your tests against the company mail server. To be able to test and debug the extension, it is better to install a test server. The same probably applies to the business mail client, so consider to install a test mail client.

There are various free mail servers and mail clients available out there. I used hMailServer as mail server on Windows for my testing. I used Mozilla Thunderbird as mail client for testing.

Set up the mail server and create user mail accounts to be used for testing. Then make sure to be able to to connect the mail client to the mail server and send mails back and forth. This provides all the information needed to set up RTC for mail notification.

To verify the required settings needed for the RTC/JTS server, enable e-mail notification on the development server (the one that is installed in the folder C:\RTCxxDev\installs\JazzTeamServer). Start the development server, if it is not already running.  Open the JTS setup page https://localhost:9443/jts/setup.  Proceed to the step Configure E-mail Notification. Enter the values needed to configure the development test server to enable mail notification. Test the connection. The page should look similar to the one below with different values. Note that you can use domain names or configure IP aliases in your hosts file to avoid using localhost as the server address.

Mail Notification SetupCreate test users with e-mail accounts matching your test accounts on the test mail server. Send test e-mails e.g. invitations for a user to make sure everything works. Note that there is a delay in the server, you would have to wait a while to receive the e-mail. Check the JTS server log for any errors with respect to mail sending if needed.

If this is working, open the JTS teamserver.properties file in C:\RTCxxDev\installs\JazzTeamServer\server\conf\jts to locate the values that were added. The values corresponding to the setup above are shown below.

E-mail settings in teamserver.properties FileThe entries should all start with the prefix com.ibm.team.repository.notification. The best option to locate the values is to search for that prefix, as the properties are not saved in an alphabetic order and will be spread across the file.

Copy the entries into some text editor, as the are needed to set up and enable e-mail notification in the Jetty server debug launch.

How to Test and Debug an Asynchronous Task

For debugging it is important to enable e-mail notification in the Jetty debug server. So it is necessary to provide the inforation above to enable the notification.

The Jetty launch files in the Rational Team Concert Extensions Workshop have a teamserver.property file located in the project RTC Extension Workshop Configuration in the subfolder jazz.

Jetty Configuration teamserver.properties file It would be possible to use this file to pass the special configuration parameters. But the file would be shared between all the launches that are configured this way.

As an alternative, it is possible to specify properties in the launch itself. This makes it possible to have various different launches that manage the different setups. Since a launch file can be stored in a project related to the extension itself, this is a way better option and also allows to share the launch in SCM and keep the RTC Extension Workshop Configuration on default values. This is the approach used here.

To configure e-mail notification and other properties, create your own Jetty launch, by copying and giving it a reasonable name. Then add the properties needed to the System Properties of the Jetty Launch.  The next image shows such settings. Use the common tab of the launch to change the save location and save it in one of your extension projects.

Mail Notification Setup in Jetty LaunchTo be able to test and debug an asynchronous task, you want to be able to run the task more frequently as in a typical deployment. To avoid having an unrealistic default setting for the frequency of the task in the production environment, it must be avoided to set the default to a high frequency. This can be solved by configuring the task frequency by adding a configuration property in the same place that was used above.

The special property to change the delay between running the asynchronous task is basically the property that the server would save in the teamserver.properties if the task default would be overwritten. The ID of the property depends on the ID of the Jazz component defined in the task and on the name of the property.

Asynchronous Task Configuration Override PropertyIn the case of this example the component ID is com.ibm.js.team.workitem.extension.duedatenotifier.component

The name of the property is task.remind.fixedDelay.

So the ID of the property is com.ibm.js.team.workitem.extension.duedatenotifier.component.task.remind.fixedDelay

In the image below, the value is set to 10 so that the tasks is run every 10 seconds.

Configuration Parameters in the Server Launch

As shown in the image above there are other parameters for the asynchronous task that can be overwritten for testing in a similar way.

The mail task also does not run very often and for debugging it would be convenient if it runs more frequently. This can be fixed similar to above. The parameter com.ibm.team.workitem.notification.mail.fixedDelay=15 at the end of the  list in the above image is used to set the mailer mail sending frequency to 15 seconds, to avoid to have to wait 10 minutes or whatever the default is until the messages are actually sent.

A launch with these settings is shipped as part of the code. You have to change the e-mail settings for your setup to make it work though.

Launch Included in ProjectChanges in the Code

The documentation in the QueryDev Guide in the the Jazz Developer Wiki is still valid for the code that can be downloaded from this post. There are some few changes that were done.

A new configuration property with name task.remind.notification.type was introduced as shown below.

Additional Configuration ParameterIt can have the following values

  • event (default) – Create notification events, user will get e-mail if configured
  • mail – Create a special e-mail and always send it to the user; run the task only once  day in this mode

The new capability to send a special e-mail was introduced based on Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained”. To support sending e-mails additional prerequisites where added to the Extension as well.

There is some more documentation added in the code.

In the method runTask() a decision is taken to create events or to send a special e-mail, based on the configuration.

/*
 * (non-Javadoc)
 * 
 * @see
 * com.ibm.team.repository.service.async.AbstractAutoScheduledTask#runTask()
 */
@Override
protected void runTask() throws TeamRepositoryException {
	IProgressMonitor monitor = new NullProgressMonitor();

	// get the configuration for the notification
	getEventConfiguration();
	// Get the attributes and durations for the reminders
	List reminders = getReminders();
	// Get the project areas to be able to query the work items
	List<IProjectAreaHandle> projectAreas = findProjectAreas(monitor);
	// Get the recent notifications to be able to suppress additional
	// reminders
	Set<UUID> recentNotifications = findRecentNotifications(monitor);

	// iterate all the project areas
	for (IProjectAreaHandle projectArea : projectAreas) {
		// For each configured reminder
		for (Reminder reminder : reminders) {
			// get the work items that qualify
			List<IWorkItem> workItems = findWorkItems(projectArea,
					reminder, monitor);
			if (!workItems.isEmpty()) {
				// If there are work items, get the attribute for the
				// reminder
				IAttribute attribute = getWorkItemCommon().findAttribute(
						projectArea, reminder.attributeId, monitor);
				// For each work item found
				for (IWorkItem workItem : workItems) {
					if (enableMail) {
						createNotificationMail(workItem, attribute);
					} else {
						// If there is no recent notification
						if (!recentNotifications.contains(workItem
								.getItemId()))
							// Create one as a feed
							createNotificationEvent(workItem, attribute);
					}
				}
			}
		}
	}
}

The code that implements sending the e-mail in Jorge Diaz’ blog post “RTC Custom Scheduled Async Task example explained” is shown below. Jorge’s code was changed to be able to show the URL of the work item in the e-mail. It also does not send mail to the owner if there is no owner (the owner has the ID unassigned).

This might be language dependent. I did not find a great way to get the unassigned user. Please leave better ways to do this in a comment, if you come across one.

It would be possible to send the e-mail to some other account in case it has no owner. It would be possible to search for a user with a certain role such as ‘Project Manager’ and make sure that the task is not forgotten.

/**
 * Creates a custom notification mail to the owner.
 * 
 * @param workItem
 * @param attribute
 * @throws TeamRepositoryException
 */
private void createNotificationMail(IWorkItem workItem, IAttribute attribute)
		throws TeamRepositoryException {
	IContributorHandle ownerHandle = workItem.getOwner();
	if (ownerHandle != null) { // We need to have an owner
		IContributor owner = (IContributor) getRepositoryService().fetchItem(
				ownerHandle, IRepositoryItemService.COMPLETE);
		if(owner.getUserId().equalsIgnoreCase(UNASSIGNED_USER_ID)){
			// You might want to do something, e.g. annoy someone else.
			return;
		}
		String eMail = owner.getEmailAddress();
		sendRememberingMail(workItem, attribute, eMail);
	}
}

The below code creates the message.

/**
 * Create an e-mail and send it around.
 * 
 * @param workItem
 * @param attribute
 * @param mail
 * @throws TeamRepositoryException
 */
private void sendRememberingMail(IWorkItem workItem, IAttribute attribute,
		String mail) throws TeamRepositoryException {
	Location location = Location.namedLocation(workItem,
			this.getRequestRepositoryURL());
	String itemLocation = location.toString();
	long diff = getDueDifference(workItem, attribute);
	Duration duration = new Duration(Math.abs(diff) / HOUR * HOUR);

	String attributeSuffix = "";
	if (!attribute.getIdentifier().equals(IWorkItem.DUE_DATE_PROPERTY)) {
		attributeSuffix = " for work item attribute: "
				+ attribute.getDisplayName();
	}
	String mailSummary = "'" + workItem.getId() + ": "
			+ workItem.getHTMLSummary().getPlainText() + "'"
			+ attributeSuffix;

	String subject = NLS.bind("Due Date Reminder Notification - {0}",
			mailSummary);
	String message = NLS.bind("Work item {0}  was due {1} ago",
			itemLocation, fDurationFormat.format(duration));
	MailSender sender = getMailService().getDefaultSender();

	try {
		getMailService().sendMail(sender, mail, subject, message, null);
	} catch (MessagingException e) {
		String warningMessage = NLS
				.bind("Failed to send DueDate Notification message of work item {0} to {1}",
						workItem.getHTMLSummary().toString(), mail);
		getLog().warn(warningMessage, e);
	}

}

The interesting part is to create the URL for the work item. It uses the class Location to get the URI (URL) created. It is important to note that the example uses a named location. The related method namedLocation() is used to create the Location. There are different types of locations and the URIs that are created are slightly different dependent of the location type. In some contexts it is important to use the correct location. One example is in creating links between artifacts of different applications such as requirements and test artifacts. Using the wrong location type will cause problems. In this case converting the location to a string creates the link to open the work item we want to be available in the mail.

Location location = Location.namedLocation(workItem,this.getRequestRepositoryURL());
String itemLocation = location.toString();

Packaging

The structure of the projects that are used to ship is similar to all the recent examples posted on this blog.

  • com.ibm.js.team.workitem.extension.duedatenotifier.common: contains the extension for the component
  • com.ibm.js.team.workitem.extension.duedatenotifier.service: contains the extension that implements the service and refers back to the component
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature: defines the feature, combining the component and the service
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature.updateSite: is the update site project to build the extension
  • com.ibm.js.team.workitem.extension.duedatenotifier.server.feature.deploy: is the deploy project that defines the additional files and folders for deployment such as the provision profile and the site folder

Deploying the Extension

To build and deploy the task, open the site.xml in tha update site project and run Build All. Then copy the site.xml file and the folders features and plugins into the folder sites/js_wi_duedate_notifier_task in the deploy project.

Once this is done, it is possible to copy and paste the whole content of the deploy project into the configuration folder server/conf/ccm and accept any overwrites.

The image below shows the first part.

Packaging of ProjectsSee the post Is The Extension Deployed? How Can I Redeploy? to understand how to reset the server to enforce deploying the new extension.

Note, all projects are set up to ignore the files that don’t need to be under version control in RTC.

Configure the Due Date Notifier in Production

The Due Date Notifier Asynchronous Task can be configured in the advanced properties of the CCM server it is deployed.

Configure Advanced Properties for Notifier

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.

A Custom Condition to Make Attributes Required or Read-Only by Role Version 2

A new requirement came up. Prioritize mandatory over read only. Can that be done? It can, lets see how.

As described in A Custom Condition to Make Attributes Required or Read-Only by Role it is possible to create custom behavior for making attributes read-only and required. The solution above has a catch however. If you configure a user with roles that make an attribute mandatory and roles that make the same attribute read-only, the user can not modify the mandatory attribute and can not save the work item. This situation can also arise with the other built in advisors.

So the new requirement was: modify the condition in a way that it checks if an attribute is required for the user and not make it read only in that case, even if the user has roles that specify the attribute read only. This could also be done the other way around, really, or there could be other requirements.

Is this really doable? I had my doubts. It took me a while to find that it can actually be done. It can be done, but there are several tricks that have to be used to do so.

Solution Outline

The solution described here provides a refined version of the condition for role and state provider as outlined below. The code has been extended to be able to:

  1. Detect that a condition is for a read-only attribute
  2. Detect which attribute it is responsible for
  3. Recursively call the operational behavior that determines if an attribute is required and to determine based on the information retrieved if the attribute is required and the condition needs to return false without further checks to prevent to make a required attribute read-only

The solution will be explained in detail below, but first.

A warning that should be carefully considered

The code that comes with this solution is experimental. All content is provided “as is” and without warranties. I am not responsible for any harm or damage caused by this material to you, your computer, your software, or anything else. 8)

The code does not write to the data, so data corruption is not a concern.

However, the code uses a recursive approach to check if an attribute is required to decide if it can be made read only. This can have performance implications (as every extension can). Since the code has to go multiple times through all the information, the user experience could be impacted by slow loading of work item presentations.

The code has to be called every time the work item is

  • opened in an editor
  • saved anywhere

so this runs in the users UI context and could be noticed if its execution time is long. This obvioulsy depends on the number of attributes, work item types and roles. For A attributes and W work item types and R roles the maximum complexity is roughly 3*(A*W*R), for read only attributes because it has to go through two configurations for required attributes preconditions plus one run through the condition itself. So it is o(n) with n=A*W*R for read only attributes. It is A*W*R for required attributes also o(n) with n=A*W*R.

So complexity is linear, however, there is no free lunch and the code has to read potentially a lot of information and perform other operations, so there could be an impact on the user performance, especially if the server is under heavy load.

Since it is recursive, you also have to make sure the code terminates. In this case it is done by getting only the required attributes if the condition is for read only attributes. If you get this wrong in the configuration, the server will probably crash with an OOM error.

I did not see any performance problems when I tested it, but the test was only with a limited set of roles and configuration entries in a single user environment.

To be able to recursively call the operational behavior recursively, the code has to use internal API that can be changed without notice. My experience so far is that the API, also internal API is very stable, but this should be considered anyway.

The code has to make assumptions about the condition names for Read-Only conditions. This naming convention is necessary for the code to get the information it needs to work properly. If the naming convention is not followed carefully the code will not work as expected.

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!

Download the Code

The code can be downloaded from DropBox here. Please note, there might be restrictions to access DropBox and the code in your company or download location.

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.

Solution Constraints

There are several constraints that will impact any possible solution. This is a list of the constraints I have experienced.

User UI Notification

The RTC UI uses a red asterisks to be able to provide the user a visible indication that a work item attribute is required.  The RTC UI also makes read-only attributes not changeable by removing the border or graying out the attribute value selection box. In this state the user can’t accidentally enter data. The image below shows the web UI with required and read only attributes. Severity is read only and can not be changed, a number of other attributes are required and show an asterisk.

UI required and read only attributes

RTC provides out of the box pre-conditions also called advisors for defining work item attributes read-only and required. It is important to know that advisors only run once the save button is pressed.

Running at save and showing information in the UI prior to the save is a contradiction. It should not be possible to show indications in the UI for a work item save advisor.

The way this is implemented in RTC is that the UI knows the built in advisors for Required Attributes for Type and State and for Condition and the Read-only Attributes for Type and State and for Condition. The UI calls these advisors to retrieve the information for the attributes before the advisors will actually run.

Note: This is hard coded into the UI and can not be extended. In fact it is not possible to show these UI indications for a custom work item save advisor.

We will use the same mechanism the UI uses in our solution below.

Conclusion on User UI Notification

It is possible to write a work item save advisor that prevents from saving a work item if attributes are not set or if they are changed, but these advisors would not be able to provide the user with any information in the UI up front. For this reason I believe the only feasible approach to implement behavior for read-only and mandatory attributes is to use custom conditions and using the advisors that make attributes required and read only based on condition values to provide visual feedback in the UI.

Otherwise the RTC developers would have to create an extension point, where custom advisors could provide the information which attributes are read only or required.

Conditions – Limitations

The condition is called with some data that allows it to run. This data contains

  1. The selected work flow action of the work item if available
  2. The label or name the condition has

There is no information available

  1. About the advisor in which context the condition is called: Required Attributes for Condition or Read-Only Requirement for Condition or a custom advisor, so the condition does not know if it is for read only or required.
  2. About the attribute or attributes the condition is responsible for in the context of the advisor

To summarize this: the condition can not decide with the given data

  1. If it is responsible for making an attribute read-only or mandatory
  2. Which work item attributes the condition acts upon

It is necessary to know in the condition, if it is for making a work item read only and it is necessary to know which attribute this condition is responsible for. This data is needed in the condition to perform the subsequent checks if the attribute is required. This means it is necessary to pass some information to the condition that allows it to get this data. The only possible way I am aware of at this time is the name/label of the condition.

This is the main reason for the design below, where the condition needs to be specified for one attribute only, has to have the mode of working and the attribute ID in the name and has to be configured individually with roles and workflows.

The initial attempt was to have only one condition with a central configuration. This approach was not feasible with the information available in the condition.

Solution Code

To achieve the requested functionality the condition code in the AbstractUserRoleTypeAndStateConditionProvider was enhanced in the following ways.

  1. A new method was added to test if the condition should exit with false based on the attribute being required
  2. The call is wrapped to enable and disable this recursive behavior using the constant ENABLE_RECURSIVE_REQUIRED_ATTRIBUTES . By default this is set to false. In this case the old behavior is maintained. To change this, the value must be set to true and the code must be rebuilt and deployed
  3. Additional supporting methods and data has been added to map attribute ID’s

It would be possible and simple to enhance the code of the condition to get the information if it should do the check from its configuration similar to how it gets the other configuration data.I decided to not spend the time for that. So the line below is responsible and needs to be changed to enable the new capability. Change the value to “true” to enable the recursive feature. Then built and deploy the extension.

Enable check for required

The check if a work item attribute is required and can therefor not be read only is the first check that the new behavior does. the rest of the code is absolutely identical to the old one. Only the methods necessary for this check has been added. The image below shows the code to call the new behavior.

Calling the Check

The new method looks like this:

/**
 * This method tries to extract the attribute name from a naming convention
 * 
 * The name of the condition has to match Readonly_the.id.of.the.attribute
 * 
 * If the naming convention holds, it converts the attribute ID for built-in
 * attributes into an internal form that is used by the UI and tries to
 * retrieve the required attributes for Type and State or by condition
 * 
 * It checks if the attribute it is responsible for is in the list If it is,
 * the attribute is considered to be required.
 * 
 * @param workItem
 * @param configuration
 * @param workItemCommon
 * @param monitor
 * @return returns true if the attribute could be identified as required
 *         false otherwise.
 * 
 * @throws TeamRepositoryException
 */
boolean isRequiredRecursive(IWorkItem workItem,
		IConfiguration configuration, IWorkItemCommon workItemCommon,
		IProgressMonitor monitor) throws TeamRepositoryException {
	/*
	 * Please note, there is no information available about the attribute ID
	 * this condition works for. It is necessary to provide it somehow.
	 * There is also no information available what mode this condition is
	 * used for i.e. is it for required attributes or for read-only
	 * attributes.
	 * 
	 * The code below assumes that the mode and the attribute ID is coded
	 * into the Label of the condition. The assumption is that the name of
	 * the condition follows this pattern:
	 * 
	 * Readonly_the.attribute.id
	 * 
	 * The label is split in the first part which is used to detect the
	 * condition is used for. Basically if the mode is for detecting
	 * read_only or required attributes The second part is used to identify
	 * the attribute ID the condition is used for.
	 * 
	 * If the condition is used to make an attribute read only, it tests if
	 * the attribute is really required due to any other rule.
	 * 
	 * If it is required it can't be set to read only and the code returns
	 * true.
	 */
	String label = configuration.getLabel();
	String[] attrib_Info = label.split("_");
	String operation = null;
	String attributeID = null;
	if (attrib_Info.length > 1) {
		operation = attrib_Info[0].trim().toLowerCase();
		attributeID = attrib_Info[1].trim();
		// get the internal representation of the attribute, if there is one
		attributeID = getInternalID(attributeID);
	}

	// Can we check?
	if (!(operation != null && attributeID != null && operation
			.equals("readonly"))) {
		return false;
	}
	/*
	 * There is enough data to check if there is a required attribute. In
	 * order to do this, call the existing configured operational behaviour.
	 */
	Set requiredAttributes = new HashSet();
	IAuditableCommon auditableCommon = workItemCommon.getAuditableCommon();
	IProcessAreaHandle processArea = workItemCommon.findProcessArea(
			workItem, monitor);
	IAuditableCommonProcess process = auditableCommon.getProcess(
			processArea, monitor);

	// Get the workflow action
	String actionId = configuration.getProviderContext()
			.getWorkflowAction();

	/*
	 * Call the RequiredAttributesByConditionAdvisor advisors recursively to
	 * get the list of attributes that are required for this context by
	 * condition
	 * 
	 * @see com.ibm.team.workitem.common.internal.
	 * RequiredAttributesByTypeAndStateAdvisor
	 * 
	 * Note, there seems to be an issue when running this on the Eclipse
	 * client in debug mode.
	 */
	List advisorDeclarations1 = process
			.findAdvisorDeclarations(processArea,
					WorkItemCommon.WORK_ITEM_SAVE_OPERATION_ID,
					RequiredAttributesByConditionAdvisor.ADVISOR_ID,
					monitor);
	for (IAdvisorDeclaration advisorDeclaration1 : advisorDeclarations1) {
		requiredAttributes.addAll(RequiredAttributesByConditionAdvisor
				.getRequiredAttributes(workItem, workItemCommon, actionId,
						advisorDeclaration1.getConfigurationElement(),
						false, monitor));
	}
	/*
	 * Call the RequiredAttributesByConditionAdvisor advisors recursively to
	 * get the list of attributes that are required for this context by type
	 * and state.
	 * 
	 * @see com.ibm.team.workitem.common.internal.
	 * RequiredAttributesByTypeAndStateAdvisor
	 * 
	 * Note, there seems to be an issue when running this on the Eclipse
	 * client in debug mode.
	 */
	List advisorDeclarations2 = process
			.findAdvisorDeclarations(processArea,
					WorkItemCommon.WORK_ITEM_SAVE_OPERATION_ID,
					RequiredAttributesByTypeAndStateAdvisor.ADVISOR_ID,
					monitor);
	for (IAdvisorDeclaration advisorDeclaration2 : advisorDeclarations2) {
		requiredAttributes.addAll(AbstractAttributesByTypeAndStateAdvisor
				.getConfiguredAttributes(workItemCommon, workItem,
						actionId,
						advisorDeclaration2.getConfigurationElement(),
						monitor));
	}
	// Is the attribute in the list of required attributes?
	if (!requiredAttributes.isEmpty()) {
		if (requiredAttributes.contains(attributeID)) {
			// If the attribute is in the list, don't make it read only.
			return true;
		}
	}
	return false;
}

What it does is essentially as follows.

The first section is used to decode information from the label or name of the condition. The subsequent code needs to know if this condition is to make an attribute read only. If this is the case it needs to know the name of the attribute it is responsible for.

The first block checks if the label of the condition follows the naming conventions. If it does, the label looks like i.e. Readonly_com.ibm.team.workitem.attribute.description. The condition uses this information to determine if the condition is for making an attribute read only and what the id of the attribute is. To do that, it splits the label of the condition using the underscore character ‘_’. If the split has at least two parts, it uses the first part as the indicator for what the condition is used for. The second part is expected to contain the attribute ID for the attribute the condition is responsible for.

The attribute ID, if one was found, is passed through the method getInternalID(). This method converts the ID’s for built in attributes into the internal representation that is available later in the process. If there is no internal ID found for an attribute, the original ID is returned. This mapping is necessary, because many of the built in attributes have an ID that is shown externally in the process configuration editor and an internal id that is used in a lot of the internal processing.

As an example, in the process configuration the attribute Description shows up as com.ibm.team.workitem.attribute.description but the internal representation is description. See the image below.

Internal and external ID for built in attributes

The method and the mapping is explained later in the document.

The next part of the code checks if there is enough data to decide if the current condition is a read-only condition and there is an attribute that can be checked. If the condition label is not matching the conventions there is not enough data available to decide and the method returns false.

In the next steps a HashSet for attribute ID’s is created and the code gets the required API classes that are required for the final steps.

The code then gets the process configurations for the RequiredAttributesByConditionAdvisor advisor and retrieves the list of required attributes from them. This is basically the code that the RTC UI uses itself to determine the read only and the required attributes from the built in advisors.

The code does the same for the RequiredAttributesByTypeAndStateAdvisor configurations.

Once all required attributes are available in the HashMap, the code tests if the attribute that this condition is supposed to check for being read only is in the list. If that is the case the code returns true, false otherwise.

If this method returns false, the normal behavior described in A Custom Condition to Make Attributes Required or Read-Only by Role is executed to determine if the condition should return true (to make the attribute read only or required). If the method returns true, the condition detected it was responsible to make the attribute read-only and the attribute is required and therefore must not be set to read only. In this case the whole condition can return false and does not have to check anything else.

The method getInternalID is very simple and looks like this.

/**
 * Try to get the internal representation for an attribute ID If there is an
 * internal representation, return it, otherwise return the original
 * 
 * @param attributeID
 * @return
 */
private String getInternalID(String attributeID) {
	String newVal = iDMap.get(attributeID);
	if (null != newVal)
		return newVal;
	return attributeID;
}

The method createMap to create the map to look up the internal Id looks like below.

/**
 * Create the built-in attribute mapping of external ID's to the internal
 * representation that can safely be used to compare the required
 * attributes.
 * 
 * @return the map
 */
private static HashMap<String, String> createMap() {
	HashMap<String, String> map = new HashMap<String, String>(40);
	map.put("com.ibm.team.workitem.attribute.severity",
			IWorkItem.SEVERITY_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.priority",
			IWorkItem.PRIORITY_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.version",
			IWorkItem.FOUND_IN_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.id", IWorkItem.ID_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.workitemtype",
			IWorkItem.TYPE_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.projectarea",
			IWorkItem.PROJECT_AREA_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.summary",
			IWorkItem.SUMMARY_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.state",
			IWorkItem.STATE_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.creator",
			IWorkItem.CREATOR_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.owner",
			IWorkItem.OWNER_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.description",
			IWorkItem.DESCRIPTION_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.creationdate",
			IWorkItem.CREATION_DATE_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.resolutiondate",
			IWorkItem.RESOLUTION_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.duedate",
			IWorkItem.DUE_DATE_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.duration",
			IWorkItem.DURATION_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.correctedestimate",
			"correctedEstimate");
	map.put("com.ibm.team.workitem.attribute.timespent", "timeSpent");
	map.put("com.ibm.team.workitem.attribute.category",
			IWorkItem.CATEGORY_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.target",
			IWorkItem.TARGET_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.resolver",
			IWorkItem.RESOLVER_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.resolutiondate",
			IWorkItem.RESOLUTION_DATE_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.tags", IWorkItem.TAGS_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.modified",
			IWorkItem.MODIFIED_PROPERTY);
	map.put("com.ibm.team.workitem.attribute.modifiedby",
			IWorkItem.MODIFIED_BY_PROPERTY);
	return map;
}

It is called when the class is instantiated.

/**
 * A map that is used to translate the external representation of a built-in
 * attribute into the one that is used internally
 */
HashMap<String, String> iDMap = createMap();

It is possible to add custom mappings here.

The following mapping table shows the values of the attribute Name, the internal ID of the attribute and the external ID used in the process configuration.

Attribute ID mapping built in attributes 1

The following attributes are also available but should be considered carefully. Most of these attributes are pseudo attributes like “Subscribed By” or automatically set by the process. Carefully consider if these attributes should be made required or read-only.

Attribute ID mapping built in attributes 2

Do not use this condition to make the Status attribute internalState read only. Only use the built in permissions to define which role can perform which workflow action.

Please note that the Status of the work item is especially critical. The condition evaluates the status of the work item based on the current state and the action selected. If there is an action selected, the state of the work item is calculated as the target state of that action. In case the attribute is now read only and not required, it is basically impossible to save the work item – as designed. However, there is a built in permission for work flow ations per role that should be used.

Configuring the Conditions

The configuration of the conditions works as described in A Custom Condition to Make Attributes Required or Read-Only by Role, except for some details for conditions responsible for read-only attributes.

If you create the condition to make an attribute read-only

  1. The label has to follow the naming condition Readonly_ where is the internal or external attribute ID
  2. The attribute ID has to be correct and is case sensitive; any typo renders the condition not functional
  3. Each condition can only be configured for one attribute and not for a group of attributes

In the example below, the label for required attributes can be chosen arbitrarily I.e. Required_Filed_Against. The Conditions for read only attributes have to follow the naming condition using an external ID i.e.

  1. Readonly_com.ibm.team.workitem.attribute.description
  2. Readonly_com.ibm.team.workitem.attribute.category
  3. Readonly_internalTags.

The 3rd example uses an internal ID instead of the external ID.

Create conditions for attributes

The names of the conditions are reflected in the configuration of the operational behavior.

Configure the Read Only For condition advisor

The result should look like below.

Configure the Read Only For condition advisor - result

The following table shows an example configuration for the specific user roles of a user in the new state for the work item type defect. Several attributes are configured as read-only and required in the available advisors using a condition or using the built in advisors.

Example configuration

The following screen shot shows the resulting display in the web UI if the new condition is used.

Result of the configuration in the UI

Please note that only the severity is read only, because all other attributes that happen to be configured as read only using this condition are changeable, because they are required.

Please note, the built in advisors don’t follow this pattern. If you configure an attribute required and read only for a user, the behavior is like using the condition version with the recursive behavior switched off.

Configuring and Deploying the Condition

Please follow the post A Custom Condition to Make Attributes Required or Read-Only by Role for more details. This version is only a small extension to the original condition and the description there applies to this condition as well.

Summary

Using a custom condition and the out of the box operational behavior for Required Attributes For Condition and Read-Only Attributes For Condition, allows to achieve the required behavior.

Keep in mind this is by no means production code. You might want to do more testing.

As always I hope this helps someone out there to get their job done more efficient.