Delivering Change Sets and Baselines to a Stream Using the Plain Java Client Libraries


How does delivering change sets from a repository workspace work and how can I create baselines and deliver them?

This post shows some of the Client API used to do this kind of operations.

As described in the articles

  1. Deploying Templates and Creating Projects using the Plain Java Clients Library
  2. Managing Workspaces, Streams and Components using the Plain Java Client Libraries
  3. This post
  4. Extracting an Archive Into Jazz SCM Using the Plain Java Client Libraries

I want to automatically set up a project, manage the streams and components and seed the SCM system with SCM data.

The last posts show how to create and manage the project area and how to manage the components, streams and repository workspaces. What is left is to extract some kind of archive file and share the files with a component in a repository workspace using Jazz SCM. Then it is necessary to do some SCM operations on the repository workspace.

This post explains how the SCM operations on the repository workspace and stream work. Specifically, how to

  1. Deliver outgoing change sets from a repository workspace to a stream
  2. Baseline the state of a component, in the repository workspace
  3. Deliver the baseline to the stream
  4. Set a component of the repository workspace to a specific baseline

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.

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.

Deliver Change Sets and Baselines

Continuing in the run method of Deploying Templates and Creating Projects using the Plain Java Clients Library we now want to extract some data from an archive file, add the code Jazz SCM and manage the changes and baselines.

This happens in the code below. The code only shows changes to one component. The same mechanism can be used for other components however and we grab the component we want from the map components we created in the last post.

As you can see the code below basically delegates the work to another method exctractToComponentBaseline(). For the first step it remembers the baseline that was created. It uses this baseline at the end, to set the current baseline for the component in the repository workspace to the first baseline created. This allows the user to load the repository workspace and begin with the first version of the projects. Later the user can accept incoming baselines, that are already delivered to the stream, to discover the code changes made for the next labs.

public static void main(String[] args) throws Exception {
.
.
.
.

	IBaselineConnection lab2Baseline = exctractToComponentBaseline(
			teamRepository, workspace, extensionStream,
			components.get("Component 2"),
			"./LabCode/Lab2Code.zip", "Share Projects - Lab 2 Code",
			"Lab 2 Code", "Lab 2 Code", monitor);

	exctractToComponentBaseline(teamRepository, workspace, extensionStream,
			components.get("Component 2"),
			"./LabCode/Lab3Code.zip", "Share Projects - Lab 3 Code",
			"Lab 3 Code", "Lab 3 Code", monitor);

	exctractToComponentBaseline(teamRepository, workspace, extensionStream,
			components.get("Component 2"), 
			"./LabCode/Lab4Code.zip", "Share Projects - Lab 4 Code",
			"Lab 4 Code", "Lab 4 Code", monitor);

	exctractToComponentBaseline(teamRepository, workspace, extensionStream,
			components.get("Component 2"),
			"./LabCode/Lab5Code.zip", "Share Projects - Lab 5 Code",
			"Lab 5 Code", "Lab 5 Code", monitor);

	setActiveBaseline(workspace,
			components.get("Component 2"),
			lab2Baseline, monitor);
	return result;
}

The interesting code is used in exctractToComponentBaseline() below. The first step is to extract an archive file into a component in the repository workspace. The extraction process creates a change set with all the folder and file creations and changes. How that works is going to be discussed in the final post of this series to keep the tension up, and because it is quite a lot of code.

The next lines of code look quite innocent, but I had a lot of trouble to get to the point that it was working for me.

In the first step after extracting the archive, the code uses the IWorkspaceConnection.compareTo() API to compare the repository workspace to the target stream. It uses the flag WorkspaceComparisonFlags.CHANGE_SET_COMPARISON_ONLY to only compare the change stets. The 3rd parameter could be used to exclude components on the workspace from the compare operation. This is not used in this example and an Collections.EMPTY_LIST is passed instead. The effort to create the exclusion list would increase the code complexity but does not really save that much time in this example. If this is used with a lot of components with a lot of files, it would be worth spending the effort.

The result of the compare operation is a IChangeHistorySyncReport which is used in the next step to deliver the change sets.

To deliver the changes the code uses the IWorkspaceConnection.deliver() API of the repository workspace. The call provides the deliver target stream, the IChangeHistorySyncReport from the compare, an empty list of baselines and the outgoing change sets from the IChangeHistorySyncReport.

This operation also adds the target stream (workspace connection) as a flow target. You can access the flow targets using IWorkspaceConnection.getFlowTable() and use this information to get a target in other scenarios.

The next step is to create a new baseline on the repository workspace. This is done using the IWorkspaceConnection.createBaseline() API call. This call can be used with exactly on component, which is what we want in this example. The name of the baseline and a comment for the baseline are also provided in the call.

If we wanted to create a snapshot IWorkspaceConnection.createBaselineSet() would have been the AI of our choice. This creates a baseline set across several components (and can exclude components).

The next steps are similar to the delivery of the change sets. First, do a compare() of the repository connection and the target, then do a deliver() with the change set sync report and the outgoing baselines and change sets from the sync report.

It would have been possible to do the baseline creation and the deliver in one step, in our case. I choose to keep it separate, because it shows that you have to provide outgoing change sets as well as outgoing baselines from the sync report to be able to deliver a baseline. I contemplated over many IllegalArgumentExceptions to get to this insight.

private IBaselineConnection exctractToComponentBaseline(
		ITeamRepository teamRepository, 
		IWorkspaceConnection repoWorkspace,
		IWorkspaceConnection targetStream,
		IComponentHandle component, 
		String archiveFileName,
		String changeSetComment, 
		String baselineName,
		String baselineComment, 
		IProgressMonitor monitor) throws Exception, TeamRepositoryException {

	// Extract the archive file to the component in the repository workspace
	System.out.println("Extracting...");
	ArchiveToSCMExtractor extract = new ArchiveToSCMExtractor();
	extract.extractFileToComponent(archiveFileName, teamRepository, 
			repoWorkspace, component, changeSetComment, monitor);

	// Compare the repository workspace with the stream to find the changes
	// Deliver the change sets
	System.out.println("Comparing Change Sets...");
	IChangeHistorySyncReport changeSetSync = repoWorkspace.compareTo(
			targetStream,
			WorkspaceComparisonFlags.CHANGE_SET_COMPARISON_ONLY,
			Collections.EMPTY_LIST, monitor);
	System.out.println("Deliver Change Sets...");
	repoWorkspace.deliver(targetStream, changeSetSync,
				Collections.EMPTY_LIST,
				changeSetSync.outgoingChangeSets(component), monitor);

	// Create a baseline and compare the repository workspace with the
	// stream to find the changes and deliver the baselines
	System.out.println("Create Baseline...");
	IBaselineConnection baseline = repoWorkspace.createBaseline(
			component, baselineName, baselineComment, monitor);
	System.out.println("Comparing Baselines...");
	IChangeHistorySyncReport baselineSync = repoWorkspace.compareTo(
			targetStream,
			WorkspaceComparisonFlags.INCLUDE_BASELINE_INFO,
			Collections.EMPTY_LIST, monitor);

	// Deliver the baselines
	System.out.println("Deliver Baselines...");
	repoWorkspace.deliver(targetStream, baselineSync,
			baselineSync.outgoingBaselines(component),
			baselineSync.outgoingChangeSets(component), monitor);
	System.out.println("Extracting successful...");

	return baseline;
}

After extracting, sharing, creating baselines and delivering to setup the scenario right, the repository workspace needs to be set to a specific older baseline. This is done in setActiveBaseline().

The interesting part here is that it is necessary to provide a list of component operations (implementing IComponentOp). The component operations can be created using the IFlowNodeConnection.componentOpFactory() API which is inherited by IWorkspaceConnection.

The code replaces the baseline by an older one, created earlier in the process. The replaceComponent() operation takes the component, the baseline connection and a parameter that allows to calculate detailed item update reports. We don’t need this, because we don’t want to actually do anything with the baseline in our case.

private void setActiveBaseline(IWorkspaceConnection workspace,
		IComponentHandle component, IBaselineConnection lab2Baseline,
		IProgressMonitor monitor) throws TeamRepositoryException {
	workspace.applyComponentOperations(Collections.singletonList(workspace
			.componentOpFactory().replaceComponent(component, 
					lab2Baseline, false)), monitor);
}

Summary

The code in this post basically shows how to work with change sets and baselines, to manage workspace connections, deliver change sets and baselines and to replace a component with a specific baseline.

The last step needed is to understand how it is possible to extract an archive (or any other data source) into Jazz SCM. This is described in the post Extracting an Archive Into Jazz SCM Using the Plain Java Client Libraries.

As always, I hope that sharing this code helps users out there, with a need to use the API’s to do their work more efficient.

10 thoughts on “Delivering Change Sets and Baselines to a Stream Using the Plain Java Client Libraries

  1. Haha — I love this part “I contemplated over many IllegalArgumentExceptions to get to this insight”…. Very useful, thank you1

    • Thanks Andy! I try to make it fun reading if possible.

      I would also like to make the point that I have no more access to the developers than any customer. I suppose I could ask them, but since they are very busy enhancing the tools based on customer requests, I try to avoid disturbing them and 95% of what is published here is done without bothering them.

  2. Really useful as always!
    I’m just using a similar code and noticing that WorkspaceComparisonFlags.CHANGE_SET_COMPARISON_ONLY gives only the change set not included in eventually outgoing (or incoming…) baselines so strictly speaking you don’t get really ALL the change sets but only those not present in a baseline…

  3. Hi,
    I am getting the following exception when i tried to use code
    IBaselineConnection baseline = repoWorkspace.createBaseline(
    component, baselineName, baselineComment, monitor);

    com.ibm.team.scm.common.ComponentNotInWorkspaceException:Unable to obtain all component
    Is there any solution, thanks

    • sorry zhe exception is com.ibm.team.scm.common.ComponentNotInWorkspaceException:Unable to obtain all component locks

      • I do not know why this error happens. Sorry.
        You can ask on Jazz.net if you use English language.

        My best guess is, that the component you try to baseline is not actually a component in the workspace/stream. Maybe the component is private and you do not have access to it.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.