RTC 6.0 – Does the API Change and Break my Code?


I was curious if there will be a lot of rework needed to bring extensions and automation over to RTC 6.0. Here is what I have seen so far.

I found the time to setup my RTC 6.0 environment for Extensions development. This went very smoothly and did not differ to what I describe in Running the RTC Extensions Workshop With RTC 6.0. I then brought all the extensions, API and automation examples from my RTC API Development environment over into the new workspace in the RTC 6.0 environment.

I did not see any errors except in the code related to Contributing Attribute Presentations and the error can likely be fixed by using a different method. So although my API code touches a broad range of the API in RTC, there are only minor changes. The majority of the API seems to be very stable, since this was also the case when I brought over my code into RTC 4.0 some years ago.

There are likely additions to the API, but the API that is there seems to be very stable otherwise. This includes even internal API that I have used on some occasions. Of course, you still want to test that everything work after you brought it over.

I have to confess that I am so far doing the majority of the work in an environment based on RTC 4.0.1. The reasons are basically that RTC 5.x had an annoying issue as explained in Running The RTC 4.x Extensions Workshop With RTC 5.0.x. Since I did not want to put up with it all the time I kept working with 4.0.1 and only tested the result in different versions.

Summary

The RTC client and server API used in extensions and for automation seems to be very stable and there should be not a lot of effort required to bring over your own automation and extensions over to RTC 6.0

As always I hope this saves users out there some time and worries.

Advertisements

Manipulating Work Item Workflow States


I was interested in modifying the workflow state of a work item using the Java API. This is interesting for various types of automation on the server as well as on the client. If you ever wondered how to drive a work item through its workflow, you will find this post of interest. 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.

Updates

*Update* The post A RTC WorkItem Command Line Version 2 contains downloadable code that performs most of the activities required for reading and modifying work items, their attributes, and all kinds of links. This includes reading and writing work item attribute of all kinds, including state changes. The interesting code can be found in the com.ibm.js.team.workitem.commandline.helper package in the class WorkItemHelper. All techniques described below are used there. You can familiarize yourself with the concepts in this post and then look into that project for how it is used.

*Update* I published the server API code in the post Resolve Parent If All Children Are Resolved Participant
*Update* changed the title to reflect we are talking about the workflow state. A work item has a history. Each historical version is also called state and is accessible through the API for example using ItemManager.fetchAllStateHandles(). This blog does talk about the work item state and not historical states of a work item.

Modifying the workflow state of a work item is interesting in various scenarios such as

  • Data migration tooling
  • Copying or moving work item data between project areas or repositories
  • Modifying the workflow state of a work item based on the state of related work items for example in Participants

Work Item Workflow

Initially I looked at the server API and wanted to figure out how to close a parent work item when all children were closed. I found some code in the SDK that actually does this and immediately recognized that there is a general challenge when trying to code this kind of automation. The challenge is that the server API does not allow to set the workflow state of a work item directly. It requires to provide a workflow action that changes the workflow state to the desired target state. If the work item is in a specific workflow state and there is no valid workflow action from that state into the desired target state, it would be necessary to find a path of several workflow actions from the current state to the target state.

I also looked at the Client Libraries and the client work item API. IWorkItem exposes a method setState2(Identifier value) which could be used to directly set the workflow state of a work item. However, this method is deprecated. The reason to deprecate it is most likely related to the fact that directly setting the workflow state of a work item does not respect the workflow. Essentially it would be possible to set a workflow state that is not even reachable from the current workflow state. Instead the method WorkItemWorkingCopy.setWorkflowAction(String action) is provided to allow to apply a workflow action to the work item.

This leaves you with the same challenge: find a path of workflow actions from the current workflow state of a work item to the desired state. If you ever lost in a computer game because your NPC followers staggered around an impossible trail and got stuck, you realize this is not a trivial challenge.

Resolving a Work Item

Lets look at a method that is based on code I discovered when looking into the SDK that tries to resolve a work item. That code shows all the issues described above.
It gets the IWorkflowInfo for the work item. It then checks if the work item is in the group of closed states.

If this is not the case, it tries to get the resolves workflow action defined in the workflow. If there is one defined it tries to find this action in the list of workflow actions that are defined for the current workflow state.

If there is no valid action the code does nothing. An alternative would be to stop the operation. If there is a valid action available the code saves the work item providing the workflow action to close the work item.

/**
 * Resolve using the resolve transition
 * 
 * @param workItem
 * @param monitor
 * @throws TeamRepositoryException
 */
public void resolve(IWorkItem workItem, IProgressMonitor monitor)
		throws TeamRepositoryException {
	wiServer = getService(IWorkItemServer.class);
	IWorkItem wi = (IWorkItem) wiServer.getAuditableCommon()
		.resolveAuditable(workItem, IWorkItem.FULL_PROFILE, monitor)
		.getWorkingCopy();

	IWorkflowInfo workflowInfo = wiServer.findWorkflowInfo(wi, monitor);
	String actionId = null;
	if (workflowInfo != null) {
		// check if already closed
		if (workflowInfo.getStateGroup(wi.getState2()) == IWorkflowInfo.CLOSED_STATES) {
			// nothing to do.
			return;
		}
		if (workflowInfo.getStateGroup(wi.getState2()) != IWorkflowInfo.CLOSED_STATES) {
		// Get the resolve action
			Identifier resolveActionId = workflowInfo.getResolveActionId();
			// If there is a resolve action 
			if (resolveActionId != null) {
				Identifier availableActions[] = workflowInfo.getActionIds(wi.getState2());
				// Find the resolve action in the actions available for this workflow state
				for (int i = 0; i < availableActions.length; i++) {
					if (resolveActionId == availableActions[i]) {
						actionId = resolveActionId.getStringIdentifier();
						break;
					}
				}
			}
		}
		if (actionId == null) {
			// can't use an action from the current state
			// Do nothing or throw some error
			return;
		}
		// This can be used to filter it out for preconditions and follow up
		// Actions
		Set additionalParams = new HashSet();
		additionalParams.add(IExtensionsDefinitions.UPDATE_PARENT_STATE_EXTENSION_ID);
		// We found a valid action
		IStatus status = wiServer.saveWorkItem3(wi, null, actionId, additionalParams);
		if (!status.isOK()) {
			// TODO: Throw an exception or do nothing
		}
	}
}

The solution above would obviously only work for workflows where there is a resolving workflow action for each given workflow state. This is not always the case, not even in the out of the box workflows.

Workflow Considerations

Lets look at the example workflow below.

This workflow is set up in a way that the Done state can only be reached by entering the In Progress state first. That means an algorithm as described above won’t work for example in the New state.

To be able to get to the Done state an algorithm would have to find a series of actions from the given state to the Done state.

Another issue with the workflow above is the Dead State Verified. There is no way out of this workflow state and once it is set, it is impossible to get anywhere else.

I would consider to avoid dead states when designing workflows. One example of dead states in the out of the box RTC workflows is the Invalid state in the Scrum Process Templates User Story workflow. There are reasons for the chosen design, for example documentation of a decision.

However, dead states cause issues and I would consider to always have some reopen or similar workflow action to get out of a dead state.

Workflow Path Finder

I finally decided to create some code to solve the dilemma of finding paths for general workflows. The Code can be downloaded from DropBox.

The code provides the following classes:

The class PathElement stores a state transition described by a state and an action. It can be uses to store a source state and an outgoing action as well as to store an incoming action and the target state.

The class Path is used to create and store paths using an ArrayList of PathElements.

The class VisitTracker uses a HashMap to store objects that have already been visited while searching the path. The class is necessary to avoid loops like the Still working action in the Example workflow above. It prevents from going to the same workflow state or using the same action to a target state more than once.

The Class WorkflowPathFinder finally implements the search strategies to find a path.

In general the class provides two strategies.

Depth First Recursive Descent

Depth first recursive descent basically picks one action and tries to find a path to the target using this action. It recursively calls itself as long as there are available options depth first. If it can’t find a path it returns and the next upper recursion that has option will try the alternatives. It tracks actions and target states to avoid crashing in the recursion. It only takes one action to a specific state once.

  • public Path findPathToState_DF(Identifier currentState, String targetStateID) tries to find a path from a current state to the target state.
  • public Path findPathToAction_DF(Identifier currentState, Identifier targetActionId) tries to find a path from a current state to the target action.

The depth first algorithms are very easy to implement and reliable, the only issue is that, dependent on the order of the actions, a recursive descent approach does not necessarily find the shortest path.

Breadth First Recursive Descent

Breadth first recursive descent basically tries to find paths to the target using any available action and can be set to return the shortest path found. This algorithm has to find more paths and is therefore slower than the depth first search, but it would likely not return a path that runs through unnecessary transitions.

  • public Path findPathToState_BF(Identifier currentState, String targetStateID, boolean shortestDistance) tries to find a path from a current state to the target state.
  • public Path findPathToAction_BF(Identifier currentState, Identifier targetActionId, boolean shortestDistance) tries to find a path from a current state to the target action.

All algorithms have the issue, that they don’t understand the context and don’t necessarily return a path that a human would have used. By providing the desired target action instead of the target state you can restrict the path likely to be found a bit.

Usage Examples

The classes can be used with the client API as well as the Server API.

This example code shows how to run it on the client:

IWorkItemClient workItemClient = (IWorkItemClient) teamRepository.getClientLibrary(IWorkItemClient.class);
IWorkflowInfo workflowInfo = workItemClient.findWorkflowInfo(workItem,	monitor);
if (workflowInfo != null) {
	// check if already closed
	if (workflowInfo.getStateGroup(workItem.getState2()) != IWorkflowInfo.CLOSED_STATES) {
		Identifier resolveActionId = workflowInfo.getResolveActionId();
		if (resolveActionId != null) {
			WorkflowPathFinder tManager = new WorkflowPathFinder(
				workflowInfo, monitor);
			Path path = tManager.findPathToAction_BF(workItem.getState2(), resolveActionId, true);
			WorkItemSetWorkflowActionModification workflowOperation = new WorkItemSetWorkflowActionModification(null);
			ArrayList transitions = path.getElements();
			for (Iterator iterator = transitions.iterator(); iterator.hasNext();) {
				PathElement pathElement = (PathElement) iterator.next();
				workflowOperation.setfWorkFlowAtion(pathElement.getActionID());
				workflowOperation.run(workItem, monitor);
				System.out.println("Workflow Action for work item " + workItem.getId() + " : "+pathElement.getActionID());
			}
		}
	}
}

The WorkItemOperation used in the example looks as follows:

private static class WorkItemSetWorkflowActionModification extends WorkItemOperation {

	private String fWorkFlowAtion;

	public WorkItemSetWorkflowActionModification(String workFlowAtion) {
		super("Modifying Work Item State", IWorkItem.FULL_PROFILE);
		fWorkFlowAtion = workFlowAtion;
	}

	@Override
	protected void execute(WorkItemWorkingCopy workingCopy,
			IProgressMonitor monitor) throws TeamRepositoryException {
		workingCopy.setWorkflowAction(fWorkFlowAtion);
	}

	public void setfWorkFlowAtion(String fWorkFlowAtion) {
		this.fWorkFlowAtion = fWorkFlowAtion;
	}
}

You can download the Plain Java Client Library based tool here.

This example is based on the wiki entry on Programmatic Work Item Creation. The API used in the following example is client API.

On the Server the difference is to get wiServer = getService(IWorkItemServer.class); instead of IWorkItemClient. The following code does the save operation.

/**
 * Save a state transition
 * 
 * @param workItem
 * @param pathElement
 * @param workflowInfo
 * @param monitor
 * @throws TeamRepositoryException
 */
public void saveWorkItemStateTransition(IWorkItem workItem,
		PathElement pathElement, IWorkflowInfo workflowInfo,
		IProgressMonitor monitor) throws TeamRepositoryException {
	IWorkItem saveWi = (IWorkItem) wiServer.getAuditableCommon()
		.resolveAuditable(workItem, IWorkItem.FULL_PROFILE, monitor)
		.getWorkingCopy();

	// This can be used to filter it out for preconditions
	// and followup Actions
	Set additionalParams = new HashSet();
	additionalParams.add(IExtensionsDefinitions.UPDATE_PARENT_STATE_EXTENSION_ID);
	IStatus status = wiServer.saveWorkItem3(saveWi, null,
	pathElement.getActionID(), additionalParams);
	if (!status.isOK()) {
		// Do something
	}
}

As always, to keep it simple, there is few error handling. You might want to enhance this for later usage. I hope the code helps someone out there to save some time and get the work done.

Handling Iterations – Automation for the “Planned For” Attribute


One thing that I started thinking about several times during the last few years is how to handle the “Planned For” or other iteration attributes during automation. I was looking into how to copy work items from one repository to another for example. It was not immediately obvious how to work with it and I always had other more pressing demands so that I never finished thinking about it. Some pretty big projects at IBM, with very mature and highly automated processes, that have migrated to RTC. I am helping some of them and the question came up again. This time around, I found the time to really look into it and I can share something. Here goes….

*Update* The post A RTC WorkItem Command Line Version 2 contains downloadable code that performs most of the activities required for reading and modifying work items, their attributes, and all kinds of links. This includes reading and writing work item attribute of all kinds, including the iteration attribute types. The interesting code can be found in the com.ibm.js.team.workitem.commandline.helper package in the class WorkItemHelper. All techniques described below are used there. You can familiarize yourself with the concepts in this post and then look into that project for how it is used.

* Update * I had to do manual changes to the code snippets below. So it might not compile. Use the downloaded code, instead of copying the code examples into your code.

* Update * Pugazhenthi Samidurai also published a neat solution here Jazz.net Forum question.

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.

Download

* Update *: The code can be downloaded from here. The code also has some enhancements over the code shown below, e.g. more options to compare.

About Iterations

If you look at iterations in the API they appear to be an ordinary item at the first glance. Looking deeper, they are a bit special. Like categories, iterations, together with development lines (timelines), compose a tree structure. Unlike categories there is no built in API that allows to find the iteration object based on the path. Categories can be found using code like this.

List path = Arrays.asList(categoryName.split("/"));
ICategoryHandle category = workItemClient.findCategoryByNamePath(projectArea, path, monitor);

There is no such API available for iterations. However, to display an iteration some means to get the path and create a string are required. In case a string, describing the path in the iteration structure, is available some means to find the related iteration is also required. To make it more complex an iteration and a development line have an ID as well as a display name. If someone wants to do a mapping based on some strings it would be nice to be able to map by a path by id as well as by name.

So I ended up developing some helper classes to support me with the effort. One helper class just creates a path from any given iteration up to the development line. The other helper tries to find an iteration based on a given path.

Lets look at the path helper, that creates a paths for a given iteration handle, first. The code I developed can be found below. Some explanations and how to use it follow.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2012.  
 * 
 * PathHelper
 * 
 * Note to U.S. Government Users Restricted Rights:  Use, 
 * duplication or disclosure restricted by GSA ADP Schedule 
 * Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.api.tools.process;

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

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IDevelopmentLine;
import com.ibm.team.process.common.IDevelopmentLineHandle;
import com.ibm.team.process.common.IIteration;
import com.ibm.team.process.common.IIterationHandle;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.common.IFetchResult;
import com.ibm.team.repository.common.TeamRepositoryException;

/**
 * Helps to create a path representation of iteration that build up a logical
 * path down to the root development line. Handles three parallel path
 * representations based on an path created from ID's, from a name and from a
 * label. Allows to output the path using a separator that can be customized. *
 * 
 * @author rschoon
 * 
 */
public class PathHelper {

	List fIdPath = null;
	List fNamePath = null;
	List fLabelPath = null;
	ITeamRepository fTeamRepository = null;
	IProgressMonitor fProgressMonitor = null;

	String fSeperator = "/";

	public PathHelper(ITeamRepository teamRepository,IProgressMonitor progressMonitor) {
		super();
		fTeamRepository = teamRepository;
		fProgressMonitor = progressMonitor;
	}

	private void initialize() {
		fIdPath = new ArrayList();
		fNamePath = new ArrayList();
		fLabelPath = new ArrayList();
	}

	/**
	 * Calculate the path for the iteration. Make paths available for ID's displaynames and labels 
	 * 
	 * @param handle
	 * @throws TeamRepositoryException
	 */
	public void calculateIterationPath(IIterationHandle handle)throws TeamRepositoryException {
		initialize();
		IIteration iteration = resolveIterationHandle(handle);
		getIterationPath(iteration);
	}

	/**
	 * @return the calculated path with names or null if the calculation never ran.
	 */
	public List getNamePath() {
		return fNamePath;
	}

	/**
	 * @return  the calculated path with ID's or null if the calculation never ran.
	 */
	public List getIdPath() {
		return fIdPath;
	}

	/**
	 * @return  the calculated path with labels or null if the calculation never ran.
	 */
	public List getLabelPath() {
		return fLabelPath;
	}

	/**
	 * @return the id path as string or null, if it was never created.
	 */
	public String toIDPathString() {
		return toPathString(fIdPath);
	}

	/**
	 * @return the label path as string or null, if it was never created.
	 */
	public String toLabelPathString() {
		return toPathString(fLabelPath);
	}

	/**
	 * @return the name path as string or null, if it was never created.
	 */
	public String toNamePathString() {
		return toPathString(fNamePath);
	}

	/**
	 * @return the seperator used to build up the string representation
	 */
	public String getSeperator() {
		return fSeperator;
	}

	/**
	 * Set a seperator string used to build up the string representation
	 * 
	 * @param fSeperator
	 */
	public void setSeperator(String seperator) {
		this.fSeperator = fSeperator;
	}

	/**
	 * Find the path to an iteration using the iteration handle
	 * 
	 * @param iteration
	 * @throws TeamRepositoryException
	 */
	private void getIterationPath(IIteration iteration) throws TeamRepositoryException {
		this.add(0, iteration.getId(), iteration.getName(),iteration.getLabel());
		IIterationHandle parentHandle = iteration.getParent();
		if (parentHandle != null) {
			// Recurse into the parent
			IIteration parent = resolveIterationHandle(parentHandle);
			getIterationPath(parent);
		} else {
			IDevelopmentLineHandle devLineHandle = iteration.getDevelopmentLine();
			List handles = new ArrayList();
			handles.add(devLineHandle);
			IFetchResult result = getTeamRepository().itemManager().fetchCompleteItemsPermissionAware(handles,
				IItemManager.REFRESH, getProgressMonitor());
			IDevelopmentLine developmentLine = (IDevelopmentLine) result.getRetrievedItems().get(0);
			add(0, developmentLine.getId(), developmentLine.getName(),developmentLine.getLabel());
		}
	}

	/**
	 * Get the iteration from its handle
	 * 
	 * @param handle
	 * @return
	 * @throws TeamRepositoryException
	 */
	private IIteration resolveIterationHandle(IIterationHandle handle) throws TeamRepositoryException {
		List handles = new ArrayList();
		handles.add(handle);
		IFetchResult result = getTeamRepository().itemManager().fetchCompleteItemsPermissionAware(handles,
			IItemManager.REFRESH, getProgressMonitor());
		return (IIteration) result.getRetrievedItems().get(0);
	}

	private IProgressMonitor getProgressMonitor() {
		return fProgressMonitor;
	}

	private ITeamRepository getTeamRepository() {
		return fTeamRepository;
	}

	/**
	 * Add a path segment to the path
	 * 
	 * @param i
	 * @param id
	 * @param name
	 * @param label
	 */
	private void add(int i, String id, String name, String label) {
		fIdPath.add(i, id);
		fNamePath.add(i, name);
		fLabelPath.add(i, label);
	}

	/**
	 * @param list
	 * @return
	 */
	private String toPathString(List list) {
		boolean first = true;
		String outStr = "";
		for (Iterator iterator = list.iterator(); iterator.hasNext();) {
			String name = (String) iterator.next();
			if (first) {
				first = false;
			} else {
				outStr += fSeperator;
			}
			outStr += name;
		}
		return outStr;
	}
}

The code can be called like below.

PathHelper ph = new PathHelper(teamRepository, monitor);
ph.calculateIterationPath(handle);
String paths = "IDPath: [" + ph.toIDPathString() + "] NamePath: (" + ph.toNamePathString() + ")";

Basically you create a new PathHelper and use the new object to call PathHelper.calculateIterationPath(handle) to calculate the paths for ID and name. The path helper stores both paths and makes them accessible using PathHelper.getNamePath() or PathHelper.getIdPath(). You can also get the string representation using PathHelper.toIDPathString() or PathHelper.toNamePathString(). The string is simply a string of the format "development/Release 1.0/Sprint 2". This is a string containing the name or ID of the development line and the iterations separated by a separator string. The separator can be changed if needed.

The PathHelper class basically allows to read and analyze iterations. Now it would be desirable to be able to locate an iteration based on a given path. The code below finds a development line based on the path.

/*******************************************************************************
 * Licensed Materials - Property of IBM
 * (c) Copyright IBM Corporation 2012.  
 * 
 * DevelopmentLineHelper
 * 
 * Note to U.S. Government Users Restricted Rights:  Use, 
 * duplication or disclosure restricted by GSA ADP Schedule 
 * Contract with IBM Corp.
 *******************************************************************************/
package com.ibm.js.team.api.tools.process;

import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IDevelopmentLine;
import com.ibm.team.process.common.IDevelopmentLineHandle;
import com.ibm.team.process.common.IIteration;
import com.ibm.team.process.common.IIterationHandle;
import com.ibm.team.process.common.IProjectArea;
import com.ibm.team.process.common.IProjectAreaHandle;
import com.ibm.team.repository.client.IItemManager;
import com.ibm.team.repository.client.ITeamRepository;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.client.IAuditableClient;
import com.ibm.team.workitem.common.model.ItemProfile;

/**
 * Tries to find a development line and enclosed iteration for a project area. 
 * 
 * @author rschoon
 *
 */
public class DevelopmentLineHelper {

	private ITeamRepository fTeamRepository;
	private IProgressMonitor fMonitor;
	private IAuditableClient fAuditableClient;

	public DevelopmentLineHelper(ITeamRepository teamRepository, IProgressMonitor monitor) {
		fTeamRepository = teamRepository;
		fMonitor = monitor;
	}

	/**
	 * Find a development line based on the path provided.
	 * 
	 * @param projectArea
	 * @param path
	 * @param byId search by id or name
	 * @return a development line found or null.
	 * @throws TeamRepositoryException
	 */
	public IDevelopmentLine findDevelopmentLine(IProjectArea projectArea,
			List path, boolean byId) throws TeamRepositoryException {
		int level = 0;
		String lookFor = path.get(level);
		IDevelopmentLineHandle[] developmentLineHandles = projectArea
				.getDevelopmentLines();

		for (IDevelopmentLineHandle developmentLineHandle : developmentLineHandles) {
			IDevelopmentLine developmentLine = fAuditableClient
					.resolveAuditable(developmentLineHandle,
							ItemProfile.DEVELOPMENT_LINE_DEFAULT, fMonitor);
			String compare = "";
			if (byId) {
				compare = developmentLine.getId();
			} else {
				compare = developmentLine.getName();
			}
			if (lookFor.equals(compare)) {
				if (path.size() > level + 1) {
					IIteration found = findIteration(iteration.getChildren(),
						path, level + 1, byId);
					if (found != null) {
						return found;
					}
				} else {
					return iteration;
				}
			}
		}
		return null;
	}
}

Now we have the development line, we can find the iteration with the following code:

/**
 * Find an iteration based on the path provided.
 * 
 * @param iProjectAreaHandle
 * @param path
 * @param byId
 * @return an iteration if one can be found or null otherwise
 * 
 * @throws TeamRepositoryException
 */
public IIteration findIteration(IProjectAreaHandle iProjectAreaHandle,
		List path, boolean byId) throws TeamRepositoryException {
	fAuditableClient = (IAuditableClient) fTeamRepository
			.getClientLibrary(IAuditableClient.class);
	IIteration foundIteration = null;
	IProjectArea projectArea = (IProjectArea) fTeamRepository.itemManager()
			.fetchCompleteItem(iProjectAreaHandle, IItemManager.REFRESH,
					fMonitor);
	IDevelopmentLine developmentLine = findDevelopmentLine(projectArea,
			path, byId);
	if (developmentLine != null) {
		foundIteration = findIteration(developmentLine.getIterations(),
				path, 1, byId);
	}
	return foundIteration;
}

The code uses the following method inside, to find the iteration.

/**
 * Find an Iteration
 * 
 * @param iterations
 * @param path
 * @param level
 * @param byId
 * @return
 * @throws TeamRepositoryException
 */
private IIteration findIteration(IIterationHandle[] iterations,
		List path, int level,  boolean byId)
		throws TeamRepositoryException {
	String lookFor = path.get(level);
	for (IIterationHandle iIterationHandle : iterations) {

		IIteration iteration = fAuditableClient.resolveAuditable(
				iIterationHandle, ItemProfile.ITERATION_DEFAULT, fMonitor);
		String compare = "";
		if (byId) {
			compare = iteration.getId();
		} else {
			compare = iteration.getName();
		}
		if (lookFor.equals(compare)) {
			if (path.size() > level + 1) {
				IIteration found = findIteration(iteration.getChildren(),
						path, level + 1, byId);
				if (found != null) {
					return found;
				}
			} else {
				return iteration;
			}
		}
	}
	return null;
}

The DevelopmentLineHelper class can be used like below.

IIteration found = devLineUtil.findIteration(workItem.getProjectArea(), ph.getIdPath(), true);
if (found == null) {
	found = devLineUtil.findIteration(workItem.getProjectArea(), ph.getNamePath(), false);
}
if (found != null) {
	log("Found:" + found.getLabel());
	if(found.hasDeliverable()){
		workItem.setTarget(found);
	}
}

This code uses the DevelopmentLineHelper to try to match the path for the iteration based on ID first. If you are working in the same project area this should work. If not, you might want to check if the path by name matches.

If the iteration can be located, and is marked as a deliverable, you can set it as Planned for the work item.

In case you only have a string representation you can use the following code to use the DevelopmentLineHelper the code can be downloaded from here.

String iterationPath = "DevLine/Release 1/Iteration 1";
List path = Arrays.asList(iterationPath.split("/"));
// map by ID
IIteration found = devLineUtil.findIteration(projectArea, path , true);
if (found == null) {
	IIteration found = devLineUtil.findIteration(projectArea, path , false);
}

As always, to keep it simple, there is few error handling. You might want to enhance this for later usage. I hope the code helps someone out there to save some time and get the work done.