Using an Expression to Synchronize Attributes for Work Items of a Specific Type

When I created my first custom attribute in RTC long ago, I quickly realized that work items that I created before the attribute was defined did not have the attribute. This is one of the favorite questions on Jazz.net forums and I believe every project administrator ran into it sooner or later. The RTC Eclipse client allows to Synchronize the attributes, which basically creates the new and missing attributes at all work items that need it. This is described in one of my favorite article series Cool ‘Hidden’ Features in Rational Team Concert in Part 2 in the section ‘Applying Work Item Attribute Changes to Existing Work Items’. If you did not come across the series, Part 3 contains the links to the other parts.

This seems to solve the problem, so why blog about it? The reason is, that there is a result set size limit on queries as described in the post Using Work Item Queries for Automation. This makes it problematic to use the UI if the number of work items grows. This post describes how to create a small standalone tool that uses the Plain Java Client Libraries to query the work items and a WorkItemOperation to do the synchronization.

License and how to get started with the RTC API’S

As always, our lawyers reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license, which basically means you can use it for internal usage, but not sell. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee. Enjoy!

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

You should be able to use the following code in this environment and get your own automation or extension working.

To keep it simple this example is, as many others in this blog, based on the Jazz Team Wiki entry on Programmatic Work Item Creation and the Plain Java Client Library Snippets. The example in this blog shows RTC Client API.

Download

The code can be downloaded from here. You can import it as an eclipse project from the archive. Please be aware that the project is set up as a plug-in project that requires the RTC SDK (setup described in this article) as well as refers to a user library for the Plain Java Client Libraries. This setup helps with spying the API. If you don’t have the RTC SDK set up, delete the plug in development related artifacts.

*  Update * there seems to be an issue with the cache refresh in 6.0.x. See https://jazz.net/forum/questions/231559/after-work-item-type-updateattribute-sync-the-new-attributes-are-not-visible-on-work-item-object for a work around.

* Update * found a last minute change caused issues. Need to use the FULL_PROFILE as load profile. Did realize that I loaded the work item twice and fixed that.

* Update * See this forum answer with specific information about how the API works with respect to attributes for type changes.

This post only describes the core part of the code. Essentially the code

  1. Gets the required information from the arguments passed. Required arguments are the repositoryURI a user Id, the password for this user, the projectArea and a workitem Type ID.  An example input would look like https://clm.example.com:9443/ccm” “ralph” “ralph” “JKE Banking (Change Management)” “com.ibm.team.apt.workItemType.story”
  2. Constructs an Expression to get all work items of the type as described in Using Expressions for Automation
  3. Retrieves the unresolved result set for the Expression
  4. Overrides the result set limit as described in Using Work Item Queries for Automation
  5. Creates a WorkItemOperation to synchronize the attributes
  6. Iterates all results and runs the WorkItemOperation passed

The core of the algorithm looks like this code:

IQueryResult expression_results = resultsUnresolvedByExpression(
	teamRepository, projectArea, typeinProjectArea);
expression_results.setLimit(Integer.MAX_VALUE);
SynchronizeWorkItemOperation synchronize = new SynchronizeWorkItemOperation(workItemClient);
processUnresolvedResults(projectArea, expression_results, synchronize, monitor);

The SynchronizeWorkItemOperation operation is an inner class that looks like the code below.

private static class SynchronizeWorkItemOperation extends WorkItemOperation {

	private IWorkItemClient fWorkItemClient = null;

	public SynchronizeWorkItemOperation(IWorkItemClient workItemClient) {
		super("Synchronize Work Items", IWorkItem.FULL_PROFILE);
		fWorkItemClient = workItemClient;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy, IProgressMonitor monitor) throws TeamRepositoryException {
		IWorkItem workItem = workingCopy.getWorkItem();

		IWorkItemType type = fWorkItemClient.findWorkItemType(
		workItem.getProjectArea(), workItem.getWorkItemType(), monitor);
		if (type != null) {
			fWorkItemClient.updateWorkItemType(workItem, type, type, monitor);
		}
		System.out.println("Synchronized: " + workItem.getId());
	}
}

The operation does not assume a specific work item type and, therefore, would work for any work item query. You could further optimize the performance by passing the work item type to the constructor and find it there once, assuming the work items are all of this type.

The other interesting method is the one that uses the unresolved result set, iterates it and runs the operation on it. this method looks like below.

public static void processUnresolvedResults(IProjectArea projectArea,
		IQueryResult results, 
		WorkItemOperation operation, IProgressMonitor monitor)
		throws TeamRepositoryException {

	long processed = 0;
	while (results.hasNext(monitor)) {
		IResult result = (IResult) results.next(monitor);
		operation.run((IWorkItemHandle) result.getItem(), monitor);
		processed++;
	}
	System.out.println("Processed results: " + processed);
}

The code has very little exception handling and you might want to enhance that.

With this small tool you can synchronize all workitems of a specific type. I hope the code helps some RTC Administrator out there to save some time.

Using Expressions for Automation

If there is no query available to drive automation, it is possible to create your own expressions and use them to automate tasks. The expressions can be used similar to queries as described in Using Work Item Queries for Automation. As far as I can tell expressions are actually the fundamental mechanism that is used to create queries.

This post shows basic mechanisms to create expressions and to run them to get the result set back. If you are just starting with extending Rational Team Concert, start reading this and the linked posts to get some guidance on how to set up your environment.

License and how to get started with the RTC API’S

As always, our lawyers reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license, which basically means you can use it for internal usage, but not sell. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee. Enjoy!

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

You should be able to use the following code in this environment and get your own automation or extension working.

To keep it simple this example is, as many others in this blog, based on the Jazz Team Wiki entry on Programmatic Work Item Creation and the Plain Java Client Library Snippets. The example in this blog shows RTC Client and Common API.

Creating an Expression

An Expression can be composed of multiple Expressions in Terms. A Term represents a Boolean operation across multiple expressions. Because I haven’t used expressions all that much I can’t describe all possible ways to use them, however, I would assume that it would be possible to create own logic for Expressions. The example below is an expression that looks for work items that are owned by a specific project area and have a specific Type (that matches an ID provided as string).

IAuditableCommon auditableCommon = (IAuditableCommon) teamRepository.getClientLibrary(IAuditableCommon.class);
IQueryableAttribute attribute = QueryableAttributes.getFactory(
	IWorkItem.ITEM_TYPE).findAttribute(projectArea,
	IWorkItem.PROJECT_AREA_PROPERTY, auditableCommon, monitor);
IQueryableAttribute type = QueryableAttributes.getFactory(
	IWorkItem.ITEM_TYPE).findAttribute(projectArea,
	IWorkItem.TYPE_PROPERTY, auditableCommon, monitor);

Expression inProjectArea = new AttributeExpression(attribute,
	AttributeOperation.EQUALS, projectArea);
Expression isType = new AttributeExpression(type,
	AttributeOperation.EQUALS, workitemTypeID);
Term typeinProjectArea= new Term(Term.Operator.AND);
typeinProjectArea.add(inProjectArea);
typeinProjectArea.add(isType);

Create a Query from an Expression

It is possible to create a query from an expression and use this as described in the post Using Work Item Queries for Automation. I have not yet found the code to save the query so that it can be used later, but this is really also not the aim of this post.

IQueryCommon queryCommon = (IQueryCommon) teamRepository.getClientLibrary(IQueryCommon.class);
IQueryDescriptor descriptor = queryCommon.createQuery(projectArea, "MyQuery", "MyQuery", typeinProjectArea);

Getting the Result Set of an Expression

It is possible to get at the result set of an expression and process it. The code below shows how. All statements about query result set size limits and running resolved and unresolved from Using Work Item Queries for Automation apply here as well.

The first method gets the result set resolved to the item profile provided.

public static IQueryResult resultsResolvedByExpression(
		ITeamRepository teamRepository, IProjectArea projectArea,
		Expression expression, ItemProfile profile)
		throws TeamRepositoryException {
	IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
			.getClientLibrary(IWorkItemClient.class);
	IQueryClient queryClient = workItemClient.getQueryClient();
	IQueryResult results = queryClient
			.getResolvedExpressionResults(projectArea, expression, profile);
	return results;
}

The second method gets the unresolved result set.

public static IQueryResult resultsUnresolvedByExpression(
		ITeamRepository teamRepository, IProjectArea projectArea,
		Expression expression) throws TeamRepositoryException {
	IWorkItemClient workItemClient = (IWorkItemClient) teamRepository
			.getClientLibrary(IWorkItemClient.class);
	IQueryClient queryClient = workItemClient.getQueryClient();
	IQueryResult results = queryClient.getExpressionResults(
			projectArea, expression);
	return results;
}

Both methods are very similar to the code in Using Work Item Queries for Automation and the result sets can essentially be used the same way.

Handling Work Item States

You can use states and state groups in expressions. I found all these examples on the forum. The expression below shows the open states.

	IQueryableAttribute stateAttribute = QueryableAttributes.getFactory(
			IWorkItem.ITEM_TYPE).findAttribute(projectArea,
			IWorkItem.STATE_PROPERTY, auditableCommon, monitor);

	Expression openStates = new VariableAttributeExpression(stateAttribute,
			AttributeOperation.EQUALS, new StatusVariable(
					IWorkflowInfo.OPEN_STATES));

Sorting Results

You can use sort criteria to create a statement that has an expression as described above and add sort criteria. The criteria below sorts by work Item ID ascending.

	SortCriteria[] sortCriteria = new SortCriteria[] { new SortCriteria(
		IWorkItem.ID_PROPERTY, true) };
	Statement statement = new Statement(new SelectClause(),
		typeinProjectArea, sortCriteria);

The Wiki Page QueryDevGuide shows more example code.

As always I hope the code provided helps some RTC user to solve a problem and save some time.

Synthetic Attributes

There is information on work items that is interesting for querying where there is no real work item attribute available. For some of this information, there are synthetic attributes that you can use. Instead of  using real attributes from com.ibm.team.workitem.common.model.IWorkItem such as com.ibm.team.workitem.common.model.IWorkItem.PROJECT_AREA_PROPERTY it is possible to use synthetic attributes defined in com.ibm.team.workitem.common.query.SyntheticAttributeIdentifiers such as com.ibm.team.workitem.common.query.SyntheticAttributeIdentifiers.TEAM_AREA_PROPERTY for the team area. Code for finding the work items associated to a team area based on the filed against categories can be found in com.ibm.team.reports.workitem.common.QueryUtils.createTeamAreaExpression(IAuditableCommon, IWorkItemCommon, IProjectArea, List, boolean) in the RTC SDK. The code shows:

       List expressions = new ArrayList(
                teamAreaHandles.size());
        IQueryableAttributeFactory factory = QueryableAttributes
                .getFactory(IWorkItem.ITEM_TYPE);
        IQueryableAttribute attribute = factory.findAttribute(projectArea,
                SyntheticAttributeIdentifiers.TEAM_AREA_PROPERTY,
                auditableCommon, new NullProgressMonitor());
        for (ITeamAreaHandle a : teamAreaHandles) {
            expressions.add(new AttributeExpression(attribute,
                    AttributeOperation.TEAM_AREA_EQUALS, a));
        }
        Expression expression = new Term(Term.OR,
                expressions.toArray(new Expression[expressions.size()]));

There is more interesting code in this utility class that you want to have a look at.