Creating Plans With the Plain Java Client API


Recently a customer approached me with the question how to automate creating plans. The customer had tried to get this done and got stuck. I was pretty convinced I had never tried this. However, I looked into the automation examples I had created over the years and was quite surprised to find an example I created some years back.

As always I thought it might be a good idea to share the code with the community.

Why is this interesting?

Wouldn’t it be nice if you could automate the process of managing timelines as much as possible?

I imagine having some code that:

  • Adds releases and iterations to timelines
    • Automatically setting start and end dates
    • Create ID’s, iteration types and names as expected in a naming convention
  • Creates all the standard plans you need to a new iteration for
    • The project area
    • Each team area
    • Name the plans as expected in a naming convention
  • Extends iterations (if you have to) and aligns the start and end dates of the iterations that come after

I think that would very beneficial and save a lot of time. Ultimately I’d like to have an example. Even bits and pieces could be interesting e.g. creating plans that are missing automatically.

I recently had to manually do this for 12 plans and it was no fun.

*Update*

Please be aware that the code below uses classes that are not shipped in the plain java client libraries. They are part of the client SDK.

The dependencies

import com.ibm.team.apt.common.IIterationPlanRecord;
import com.ibm.team.apt.internal.common.rcp.IIterationPlanService;
import com.ibm.team.apt.internal.common.rcp.dto.DTO_IterationPlanSaveResult;
import com.ibm.team.apt.internal.common.wiki.IWikiPage;

require the library com.ibm.team.apt.common  from the SDK. You can either copy it from the SDK and add it to the classpath or you add the SDK to your classpath.

Please also note that beginning with RTC 6.x the Eclipse client does no longer support browsing plans. So it is unclear if some of the SDK classes mentioned above would be removed from the RTC Client SDK over time.

Please note that the classes in com.ibm.js.team.api.workitem.common.utils are not shown in the code below, but they are part of the download.

Warning License, Further Reading

The code in this post is client API.

This blog post uses internal API which can be changed at any time.

Warning, some of the code uses internal API that might change in the future. If the Internal API changes, the code published here will no longer work.

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!

As always, please note, If you just get started with extending Rational Team Concert, or create API based automation, start reading this and the linked posts to get some guidance on how to set up your environment. Then I would suggest to read the article Extending Rational Team Concert 3.x and follow the Rational Team Concert 4.0 Extensions Workshop at least through the setup and Lab 1. This provides you with a development environment and with a lot of example code and information that is essential to get started. You should be able to use the following code in this environment and get your own extension working.

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 or automation working there as well.

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.

How The Code Works

As always lets look at how the code works a bit to understand it and be able to reuse it.

The code is, as most of my code examples based on the code that comes with the snippets in the Plain Java Libraries and the code from examples on Jazz.net.

The code comes in two Eclipse projects. Both are Plug In Development projects, because that allows me to use the Eclipse PDE to see, search and debug with the existing RTC SDK as described in Setting up Rational Team Concert for API Development and Understanding and Using the RTC Java Client API. It can later be run like any other java class.

It is two projects, because the are several helper classes, that I reuse across other examples and I get tired copying the classes around. You can move them into your project if you like. I will take them under SCM and re-use them across solutions in the future, enhancing them over time as well.

The main class is the usual structure that connects to the TeamPlatform and passes the parameters to the method run().

CreatePlan requires the following parameters:

CreatePlan [repositoryURI] [userId] [password] [planName] [planTypeID] [ownigProcessAreaName] [planIterationName]

It needs a repository URI, a user ID and password to log in and do anything. The user must have the required permissions to perform the operation.

The minimal information needed to configure a plan is:

PlanConfiguration

  • A name for the plan.
  • The Plan Type ID
  • The project or team area that is going to be the owner of the plan
  • The iteration that is set for the plan.

An Example:

CreatePlan https://clm.example.com:9443/ccm/ ralph ralph "Ralph's Plan" "com.ibm.team.apt.plantype.crossProject" "JKE Banking (Change Management)/Energy Efficiency Matters" "Main Development/Release 1.0/Sprint 3"

The plan type ID can be found in the process configuration:

PlanIDsThe owning process area name is constructed as path created from the area names beginning with the Project area down to the nested process area we are interested in, separated by ‘/’. If the plan is owned by the project area take only that name. Use ” if the names have spaces.

Example:

“JKE Banking (Change Management)/Energy Efficiency Matters”

From the Team Organization:

PlanConfigurationTeamOrganizationThe iteration path is constructed from the name of the timeline and the parent iterations down to the iteration we are interested in, separated by ‘/’. Use ” if the names have spaces. You can also use the iteration ID’s with the same structure.

For example:

“Main Development/Release 1.0/Sprint 3”

From the Team Artifacts view:

PlanConfigurationIteration

The method run() gets the string representation of the required parameters. It tries to connect to the RTC server. Then it tries to find the owning process area using a small utility. It then tries to find the plan iteration.

If it can find those, it calls a method that performs the plan creation.

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

	if (args.length != 7) {
		System.out
				.println("Usage: CreatePlan [repositoryURI] [userId] [password] [planName] [planTypeID] [ownigProcessAreaName] [planIterationName]");
		return false;
	}

	String repositoryURI = args[0];
	String userId = args[1];
	String password = args[2];
	String planName = args[3];
	String planTypeID = args[4];
	String ownigProcessAreaName = args[5];
	String planIterationName = args[6];
		
		
	IProgressMonitor monitor = new NullProgressMonitor();
	ITeamRepository teamRepository = TeamPlatform
			.getTeamRepositoryService().getTeamRepository(repositoryURI);
	teamRepository.registerLoginHandler(new LoginHandler(userId, password));
	teamRepository.login(monitor);

	System.out.println("Logged in as: "
			+ teamRepository.loggedInContributor().getName());

	IProcessClientService processClient = (IProcessClientService) teamRepository
			.getClientLibrary(IProcessClientService.class);

	// Find the project or team area that is going to be the owner of the plan
	IProcessArea ownigProcessArea = ProcessAreaUtil.findProcessArea(ownigProcessAreaName, processClient, monitor);
	if (ownigProcessArea == null) {
		System.out.println("Process area " + ownigProcessAreaName + " not found.");
		return false;
	}
		
	// Find the plan iteration in the development line 
	List path = Arrays.asList(planIterationName.split("/"));
	DevelopmentLineHelper deveLineHelper= new DevelopmentLineHelper(teamRepository, monitor);
	IIteration planIteration = deveLineHelper.findIteration(ownigProcessArea.getProjectArea(), path, DevelopmentLineHelper.BYLABEL); 
	if (planIteration == null) {
		System.out.println("Iteration " + planIterationName + " not found.");
		return false;
	}
		
	// If we have everything, create the paln
	createPlan(ownigProcessArea, planIteration, planName, planTypeID, teamRepository.loggedInContributor(), monitor);
	teamRepository.logout();

	return true;
}

The plan is created using the method createPlan(). This method creates all the pieces needed for the plan. It uses internal API pretty much everywhere. It has to, the plan API is not supported and not made consumable. You have to be aware that internal API can change without notice and even without documentation how to replace the old approach.

On the other hand, the internal API is also used in various places and it is likely to be needed in the future.

The method gets all the required services. Then it creates the new Plan item and gets the IIterationPlanRecord that represents it.

The next step is to set all the values for the plan configuration, name, owner, iteration and plan type ID.

It is not apparent, but the plan needs also a wiki page to be able to save it. This wiki page is created next.

Finally the plan is saved. If this succeeded, the new plan item interface is returned.

The code looks like below. Please note the internal API.

/**
 * WARNING, this method uses INTERNAL API
 * 
 * Creates a plan based on the process area that the plan is to be set in the 
 * Owner configuration element (can not be null),
 * the iteration that is set as root iteration of the plan configuration element
 * (can not be null), name and plan type Id. The creator user can not be null.
 *   
 * @param planOwner - the process area that owns that plan, can not be null
 * @param planIteration - the root iteration the plan selects, can not be null
 * @param planName - the name the plan will show have
 * @param planTypeID - the ID of the plan type from the plan process administration
 * @param creator - the user that will be set as creator of the plan
 * @param monitor - the progress monitor, can be null
 * @return The IIterationPlanRecord if the plan was created successfull or null
 * 
 * @throws UnsupportedEncodingException
 * @throws TeamRepositoryException
 */
public static IIterationPlanRecord createPlan(IProcessArea planOwner, IIteration planIteration,
		String planName, String planTypeID, IContributor creator, IProgressMonitor monitor)
		throws UnsupportedEncodingException, TeamRepositoryException {

	// Get the Team Repository
	ITeamRepository teamRepository = (ITeamRepository) planOwner.getOrigin();
		
	// get classes for plan creation
	IAuditableCommon auditableCommon = (IAuditableCommon) teamRepository
			.getClientLibrary(IAuditableCommon.class);
	// The IIterationPlanService - this is an internal API class
	IIterationPlanService planService = (IIterationPlanService) ((IClientLibraryContext) teamRepository).getServiceInterface(IIterationPlanService.class);

	// create necessary plan items
	// The IIterationPlanRecord 
	IIterationPlanRecord plan = (IIterationPlanRecord) IIterationPlanRecord.ITEM_TYPE
			.createItem();

	// setup plan values
	plan.setName(planName);
	plan.setIteration(planIteration);
	plan.setPlanType(planTypeID);
	plan.setOwner(planOwner);

	// The IWikiPage - this is an internal API class
	IWikiPage wiki = (IWikiPage) IWikiPage.ITEM_TYPE.createItem();

	// setup wiki page
	String encoding = "UTF8";
	String xmlText = "";
	byte[] bytes = xmlText.getBytes(encoding);
	InputStream inputStream = new ByteArrayInputStream(bytes);

	// The IWikiPage methods - these methods are all internal API
	wiki.setName("");
	wiki.setWikiID(IIterationPlanRecord.OVERVIEW_PAGE_ID);
	wiki.setCreator(creator);
	wiki.setOwner(plan);
	wiki.setContent(auditableCommon.storeContent(IContent.CONTENT_TYPE_TEXT,
			encoding, inputStream, bytes.length, monitor));

	// save plan
	// The these classes and methods are all internal API
	DTO_IterationPlanSaveResult saveResult = planService.save(planOwner,
			plan, wiki);
	// check if the save was successful
	if (saveResult.isSetIterationPlanRecord() == false) {
		System.out.println("Saving failed!");
		return null;
	}
	return plan;
}

Summary

The example above shows how you can automate creation of plans. With some more code to create and maintain timelines, this can make RTC project administration a lot easier and remove repetitive tasks from your plate.

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

Advertisements

About rsjazz

Hi, my name is Ralph. I work for IBM and help colleagues and customers with adopting the Jazz technologies.
This entry was posted in Jazz, RTC Automation and tagged , , , . Bookmark the permalink.

3 Responses to Creating Plans With the Plain Java Client API

  1. Miguel Tomico says:

    Useful article, Ralph. Thank you.
    If creating 12 plans manually was not fun, imagine 500.
    The source code also works as a server side plugin with minimum changes:
    – Retrieving the IterationPlanService as follows:
    IIterationPlanService planService = getService(IIterationPlanService.class);
    – Adding com.ibm.team.apt.service to the plugin dependencies
    – Adding the requiredService interface “com.ibm.team.apt.internal.common.rcp.IIterationPlanService” to the plugin dependencies.

    • rsjazz says:

      Hi Miguel,

      I am fully aware of the needs for automation that bigger projects have – this is why I try to do and publish as many examples as possible. I am pretty sure there is a lot out there. Unfortunately very few gets shared back to the community.

      Thanks for the server side. Where did you hook it up as server extension?

      • Miguel Tomico says:

        Yeah, there is indeed room for improvement in automation, but RTC is already being surprisingly successful in complex Systems Engineering projects, thanks to its robustness, its open API and committed supporters like you.

        The server extension in which we are using above code is a Save Work Item Follow-up action.
        We know it’s not a common use case at all, but we have evaluated alternatives and this seems the most appropriate for our purpose. We’ll just have to test it thoroughly before going live.

        Thanks again for your help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s