Automated Build Output Management Using the Plain Java Client Libraries
Summary
Rational Team Concert (RTC) and the Jazz Build Engine (JBE) provide continuous integration through build and build result publishing. The build results are part of the Jazz object model and are tightly integrated into the development workflow. The build result views provide all of the essential capabilities to assess published results, including compiler and unit test results.
The article Build Artifacts Publishing Using HTTP Servers in Rational Team Concert demonstrates how to use a HTTP server to publish build output to build results. This approach has many advantages, but lacks the automation for pruning the build data that is no longer needed from the repository.
This article shows how this pruning, and more, can be implemented using the Plain Java Client Libraries (PJCL).
License Statement
This article contains source code. License terms that govern use of this source code can be found here.
Current Situation
At the end of the article Build Artifacts Publishing Using HTTP Servers in Rational Team Concert we have a system of one or more build engines that run on one or more machines and perform builds for a CCM (RTC) application.
Each build creates a unique build output folder that contains all of the build artifacts contributing to the build, and the resulting data. The build output folders are published and accessible via HTTP. The RTC build results created contain links back to this published build output, as shown in the image below.
Each build output folder requires a unique name to allow publishing. Since each build creates its own build output folder, the amount of disk space used grows quite quickly. This can be a potential issue. How do we address this?
RTC has a ‘Pruning Policy’, that can be enabled and configured for each build definition as shown below.
This policy allows for cleaning of the build result list without user intervention. In addition it allows the marking of a build result as a release or deliverable. These build results are marked as “not for deletion”, and they are not pruned from the repository results.
However, this works only for build results in the RTC repository. The published build output folders on disk are not deleted. It would be desirable to have some automated solution that deletes the build output folders that are no longer needed. Ideally this solution shouldn’t cause broken links from still existing build results to deleted build output folders.
Solution Considerations
What would an automated solution for managing the build output folders look like and which general approaches are available?
1. Age Based Deletion
One trivial, but valid, approach for this requirement is “expiration by age”. Look at the age of the folder and delete them after passing a certain threshold. This is easy enough to implement. However it has some drawbacks:
- It does not take any information available in Rational Team Concert into account, especially not data that became available after the build completed. Specifically it does not take into account build results that are marked as a deliverable and should be preserved. It is possible to end up with build results in RTC that are linked to build output that is already deleted.
- The expiration age would be totally unrelated to the build result pruning in RTC
2. Server Extension
Another option would be to somehow extend the RTC server, and act on the deletion of build results. The issues with this approach are:
- The RTC server might neither have access, nor the necessary privileges, to delete files on the build machine.
- It could only delegate deletion to some other tool such as the Jazz Build Engine (JBE). So the JBE would have to be notified, potentially using a build request. That would require a special build definition which makes it hard to maintain and error prone.
- It would be hard to implement a solution, requiring that several build engines be able to work on the same build queue.
- It would probably be quite complex to implement and maintain as a server extension.
3. Plain Java Client Libraries Client
The third option would be to create a tool that uses an API, like the Plain Java Client Libraries (PJCL), to query the RTC server for data. Based on that data it could delete or preserve folders. The advantages of such a solution would be:
- The ability to act on the information for the build results that are still available and delete the build output for the ones no longer available.
- The ability to run on any machine with enough privileges to delete the folders created during builds.
- The ability to deploy the solution on several machines that are used for building and to manage the build output folders.
- Running the solution would only require starting a Java application, which could be easily automated using a scheduler or even the JBE.
- It would be possible to perform additional tasks. One example would be creating a backup for build output that is considered a deliverable.
- Maintenance and enhancing would be simpler, using the documentation provided for the Plain Java Client Libraries.
- The solution is supposed to delete folders that represent build output for build results that have been deleted.
This disadvantage does not appear substantial, but trying to identify build output data that is related to deleted build results, is a real problem. There is no positive query that can be run to get the identifiers of the deleted build results. The only approach is to ask the server if a build result still exists. Assuming a build result has been deleted if it can not be found, could lead to data loss due to an incorrect query, or possibly leave unnecessary data. A more complex server based extension could potentially act on deletion events for build results, but has several other issues which would make using it difficult.
4. Listening to Build Result Deletion Feeds
Listening to a server feed containing build result deletion events would be another good concept. It would avoid a lot of the issues described above, especially the reliable detection of deleted build results. However Defect 193880: Build deletions don’t have events in build feeds currently prevents us from implementing it.
Solution Outline
After a lot of contemplation, I decided to try to start prototyping based on the Plain Java Client Libraries approach. During the prototyping phase, simple solution approaches came up that help mitigating it’s one disadvantage. As soon as the build result deletion feeds become available, a plain Java Client Libraries based solution could potentially be enhanced to remove this disadvantage entirely.
This article focuses on showing implementation hints for a Plain Java Client Libraries based approach, and will discuss how to create a small, stand-alone Java application for automation. The application will perform the following workflow:
- Connect to a RTC server.
- Iterate through a build output root folder containing build output folders. For each qualifying build output folder:
- Identify the build result UUID for the build output and attempt to locate the corresponding RTC build result on the RTC server.
- If the build result is no longer available, delete the build output folder.
- If the build result is still available, open it and examine it for additional information.
- If the build result is marked as a release, publish it to an asset management solution and include it in a backup. After this is completed, attach a link back to the asset to the build result.
- Handle folders that can not be identified as build output folder.
As already mentioned a key concern for such a solution is to be able to detect a relationship between a build output folder and a build result that might be available on a server or already been deleted. Basically it is necessary to:
- Detect if a folder is related to a build output
- Detect if the build result related to the build output has been deleted or is still available
To allow an automation client to perform this check, the build result UUID of the build that was performed in an build output folder is required at a minimum.
This would assume that there is only one repository that contains the build results. In case of running multiple servers that could contain the build result, it would be good to know the URL of the server requesting the build.
For the purpose of this article, the approach used to get the required data to identify the repository and the build result UUID related to a candidate build output folder is to:
- Expect a file named in the candidate folder.
build.properties
- Expect the file to contain the UUID of the build result and the URL of the repository that requested the build.
build.properties
If the
build.properties
The next image shows the file content assumed to be available.
There are other possible approaches to identify the folders and retrieve the data required. For example it would be possible to code the build result UUID and potentially also the repository URL in the name of the build output folder.
Set up the test system
To develop such a solution, a test system to run against is required. We will use the Money That Matters sample that is available in RTC. Setup instructions for a test system can be found in the article Build Artifacts Publishing Using HTTP Servers in Rational Team Concert. In order to download the install files, a valid registration on Jazz.net is required. It is not necessary to implement the Apache™ HTTP Server publishing for this article.
It is also possible to follow this article using other test systems. In this case replace server names, ports and folder names used in the article to match the target system.
Set up for development
To install the required Plain Java Client Libraries, go to the Rational Team Concert 3.0.1.1 “All Downloads” page and scroll down to the Plain Java Client Libraries downloads in the Plain Zips section.
Download the Plain Java Client Libraries and the related API documentation.
Create a root folder to contain the development artifacts. In this article
C:/RTC3011Dev
<RTCDevDir>
Create a folder
<RTCDevDir>/PlainJavaLib
RTC-Client-plainJavaLib-3.0.1.1.zip
RTC-Client-plainJavaLib-API-javadoc-3.0.1.1.zip
The folder should now contain the folder
doc
license
snippets
Please take some time to browse the folders.
- Check the license folder for the license terms and conditions for using the Plain Java Client Libraries.
- In the folder, open the
doc
file to access the Javadoc documentationindex.html
- In the folder, review the snippets provided with the Plain Java Client Libraries.
snippets
Prepare the Build Definition
To provide the build.properties file and the other required data, the build definitions of the sample application need to be enhanced.
Start the RTC Eclipse Client and open a new workspace
<RTCDevDir>/workspace
The Money That Matters sample uses Ant for building. The RTC Ant build configuration editor tab allows creating the build.properties file with the data required for the purpose of this article.
To enable its creation, open the build definition
jke.dev
${outputRootDirectory}${buildLabel}build.properties
Save the build definition.
Locate the build output root folder. In the Money That Matters sample, the build output folders are created into a folder
JKEBuild
<JBE_installDir>
<JBE_installDir>/buildsystem/buildengine/eclipse
Open the build output root folder
<JBE_installDir>/buildsystem/buildengine/eclipse/JKEBuild
jke.dev
buildLabelPrefix
Open the newly created build output folders and check that there is a
build.properties
build.properties
It is also possible to change the ‘Pruning Policy’ on the Overview tab in this step. But this is not required for this article. For testing, it is enough to manually delete some build results.
Setup a Project for Development
After these preparations it is possible to start developing the build output management solution further referred to as
BuildOutputManager
To implement the build output management solution open the Eclipse Java perspective and create a new Java project. Make sure to require at least a Java 1.5 JRE. Provide a meaningful name for the project. Throughout the article
com.ibm.js.team.build.manager
Finish the wizard to create the project.
The project needs to be set up for development using the Plain Java Client Libraries (PJCL). Directions on how to set up a project that uses the PJCL can be found on the Jazz Team Wiki. The detailed description of how to set up projects for development with the Plain Java Client Libraries is very valuable, and describes several additional tricks that can make development and debugging for the PJCL easier. The approach of using a plug-in project, instead of a simple Java project, along with the RTC SDK set up in the Eclipse Plugin Development Environment (PDE) to allow easier debugging is invaluable. The Rational Team Concert 3.0.1 Extensions Workshop has also a very detailed description on how to set up the RTC SDK for development.
To shorten the description for the setup process, this article uses a simpler approach.
Open the context menu of the new Java project and open the Configure Build Path dialog as shown in the image below.
Switch to the Libraries tab and click Add External JARs.
In the selection dialog browse to the location where the Plain Java Client Libraries were unzipped. Select all Jar files and click Open.
The project is now set up for development using the PJCL.
Implement the Java Code
Create a new Java class with a meaningful name in a package. Make sure that you select the check-box to create a main method stub, as shown below. In this article the class is named BuildOutputManager and the package is named com.ibm.js.team.build.manager.
Implement the Main Method
The code that needs to be implemented in the main method is very simple. Please review the
Snippet1.java
BuildOutputManager
public static void main(String[] args) {
TeamPlatform.startup();
try {
run(args);
} catch (TeamRepositoryException e) {
e.printStackTrace();
} finally {
TeamPlatform.shutdown();
}
}
The code starts up the
TeamPlatform
run()
TeamPlatform
TeamRepositoryException
Since the project has been set up for PJCL, and has access to the Jar files, Eclipse should provide help to import the required packages. Using Quick Fix should work, as well as Content Assist. If this is not the case validate the project setup.
Process the Parameters
Now the missing run method needs to be implemented. Be sure to add a
throws
TeamRepositoryException
private static void run(String[] args) throws TeamRepositoryException {
The BuildOutputManager requires the following parameters:
- The URI of the team repository to connect to.
- A user ID of a user with enough privileges to access the
build results. - The password for this user.
- The path to the folder that contains the build output
folders
For simplicity the data is expected in a certain order. More logic for parameter checking could easily be added.
The run method stores the data in more convenient local variables.
String repositoryURI = args[0];
final String userId = args[1];
final String password = args[2];
String buildOutputRootFolder = args[3];
First the code should check if the folder containing the build output folders exists. If not there is nothing to do and the
BuildOutputManager
File buildOutputRoot = new File(buildOutputRootFolder);
if (!buildOutputRoot.exists()) {
return;
}
To use the PJCL to communicate with a RTC server it is necessary to log into the team repository. Again, the code is very similar to the code in
Snippet1.java
TeamRepository
IteamRepository.login()
ITeamRepository teamRepository = TeamPlatform.getTeamRepositoryService().getTeamRepository(repositoryURI);
teamRepository.registerLoginHandler(new ITeamRepository.ILoginHandler(){
public ILoginInfo challenge(ITeamRepository repository) {
return new ILoginInfo() {
public String getUserId() {
return userId;
}
public String getPassword() {
return password;
}
};
}
});
teamRepository.login(null);
There is one noticeable detail that comes up often in PJCL code. Operations that could take some time to complete such as
ITeamRepository.login()
SysoutProgressMonitor.java
At this point the
BuildOutputManager
TeamRepositoryException
Implementing some more graceful handling and user feedback is possible but not really relevant for this article, so the code is left out.
To get out of the static context the
run()
BuildOutputManager
manageFolders()
ITeamRepository.logout()
BuildOutputManager controller = new BuildOutputManager(teamRepository, buildOutputRoot);
controller.manageFolders();
teamRepository.logout();
Manage the Build Output Folders
Now the main control method
manageFolders()
throws TeamRepositoryException
private void manageFolders() throws TeamRepositoryException {
The method
manageFolders()
String[] children = this.aBuildOutputRootFolder.list();
if (children == null)
return;
for (int i = 0; i < children.length; i++) {
File aBuildOutputCandidate = new File(this.aBuildOutputRootFolder,children[i]);
if (!aBuildOutputCandidate.isDirectory()) {
continue;
}
Please note if there are any compiler errors, then you probably need to add a closing bracket for the for loop. Development continues at the current level in the loop..
Check and Load the Build Propertie File
At this point in the loop, the
manageFolders()
manageFolders()
build.properties
java.util.Properties
loadBuildProperties()
The code of the new method can be implemented later, for now create a method stub for
loadBuildProperties()
null
If the build.properties file can not be detected or read, the candidate folder does not qualify according to the rules of the solution. Those folders are passed for processing to a
handleNotQualifyingFolders()
manageFolders()
handleNotQualifyingFolders()
Properties buildProperties = loadBuildProperties(aBuildOutputCandidate);
if (null == buildProperties) {
// no build properties, build may be incomplete
// or not following the format
handleNotQualifyingFolders(aBuildOutputCandidate);
continue;
}
There could be several reasons why the property file is missing. The build definition is not creating it or the build failed before Ant was successfully called. By implementing the method
handleNotQualifyingFolders()
BuildOutputManager
Folders that don’t qualify can be handled according to the local assumptions, they could be deleted based on age or it would be possible to send an e-mail notification to someone responsible.
Get the Build Result ID
If the
build.properties
manageFolders()
buildResultUUID
handleNotQualifyingFolders()
manageFolders()
// Get the build result UUID
String buildResultID = buildProperties.getProperty("buildResultUUID");
if (null == buildResultID )
{
handleNotQualifyingFolders(aBuildOutputCandidate);
continue;
}
Check for a Matching Repository Address
Likewise, the
manageFolders()
repositoryAddress
handleNotQualifyingFolders()
manageFolders()
String buildRepositoryAddress = buildProperties.getProperty("repositoryAddress");
if (null == buildRepositoryAddress) {
handleNotQualifyingFolders(aBuildOutputCandidate);
continue;
}
The
manageFolders()
BuildOutputManager
if (!aTeamRepository.getRepositoryURI().equals(buildRepositoryAddress)) {
continue;
}
Check if the Build Result is Available
The
manageFolders()
manageFolders()
itemManager
IBuildResultHandle resultHandle = (IBuildResultHandle) IBuildResult.ITEM_TYPE.createItemHandle(UUID.valueOf(buildResultID), null);
ArrayList<IBuildResultHandle> handleList = new ArrayList<IBuildResultHandle>();
handleList.add(resultHandle);
IFetchResult fetchResult = aTeamRepository.itemManager().fetchCompleteItemsPermissionAware(handleList, IItemManager.REFRESH, null);
If the fetch result contains permission denied items, the user lacks the permission to open and see the build result. In this case
manageFolder()
if (fetchResult.hasPermissionDeniedItems()) {
continue; // can't access but there is a build result
}
If the fetch result indicates the build result can no longer be found on the server, the build result has been deleted, and the folder can therefore be deleted. The deletion is delegated to the
checkDeleteFolder()
if (fetchResult.hasNotFoundItems()) {
checkDeleteFolder(aBuildOutputCandidate);
continue;
}
At this point the build result is detected to be still in the repository. The candidate folder is still referenced and needs to be preserved. The
BuildOutputManager
processBuildResult()
List retrieved = fetchResult.getRetrievedItems();
for (Iterator iterator = retrieved.iterator(); iterator.hasNext();) {
Object result = (Object) iterator.next();
if (result instanceof IBuildResult) {
IBuildResult buildResult = (IBuildResult) result;
processBuildResult(aBuildOutputCandidate, buildResult);
}
}
This concludes the main control loop performed in the method
manageFolders()
Process the Build Result
If a build result is available for the currently processed build output folder, the folder should not be deleted. Furthermore, it is possible to access the build result data and implement additional automation in the
processBuildResult()
private void processBuildResult(File aBuildOutputCandidate, IBuildResult buildResult) throws TeamRepositoryException {
It is possible to access values in the RTC build result retrieved from the team repository. Example methods that are easy to use are
IBuildResult.isDeleteAllowed()
IBuildResult.isPersonalBuild()
For the purpose of managing build output folders, it is interesting to know if the build is completed. If the build is not completed, there is no need to do anything with the folder. The
IBuildResult.getStatus()
processBuildResult()
System.out.println(aBuildOutputCandidate.getAbsolutePath()+" : Process Build Result!");
if(!buildResult.getState().equals(BuildState.COMPLETED)){
return;
}
If the build is completed, it is interesting to check to see if the build result is associated with a release. The image below shows how this is represented in the build result editor in RTC. If a build result is associated to a release, it is automatically marked as ‘deletion not allowed’. This can be changed again later by checking the deletion allowed check-box.
If the build result is associated to a release, also called a deliverable, the build output folder related to it is important, and special publishing or backup operations can be performed.
The code below shows how to find
IDeliverables
IDeliverables
addToBackup()
After backup or publishing, the folder is marked as being a deliverable by calling the method
markAsDeliverable()
BuildOutputManager
IWorkItemCommon workItemCommon = (IWorkItemCommon) aTeamRepository.getClientLibrary(IWorkItemCommon.class);
List<IDeliverable> deliverables = workItemCommon.findDeliverablesByArtifact(buildResult.getItemHandle(), IDeliverable.FULL_PROFILE,null);
if (null != deliverables && !deliverables.isEmpty()) {
addToBackUp(aBuildOutputCandidate,buildResult);
markAsDeliverable(aBuildOutputCandidate);
}
Handle Backup and Publishing
The
addToBackup()
private void addToBackUp(File aBuildOutputCandidate, IBuildResult buildResult) throws TeamRepositoryException {
Before backup or publishing is done, the code can check if the folder has already been published. In this case, there is nothing to do.
if (isMarkedAsDeliverable(aBuildOutputCandidate)) {
return;
}
The following example code assumes there is some code that publishes to an IBM Rational Asset Manager instance in the enterprise using the available Java API and returns the URL for the published artifact version.
String theURL = publishToIRAM(aBuildOutputCandidate);
The following code attaches the URL of the artefact version, returned by the publishing method to the build result. It creates a new build result contribution with the target URL and adds the contribution to the build result.
IBuildResultContribution link = BuildItemFactory.createBuildResultContribution();
link.setLabel("Download Artifact from Asset Manager");
link.setExtendedContributionTypeId(IBuildResultContribution.LINK_EXTENDED_CONTRIBUTION_ID);
link.setExtendedContributionProperty(IBuildResultContribution.PROPERTY_NAME_URL, theURL); ITeamBuildClient buildClient = (ITeamBuildClient)aTeamRepository.getClientLibrary(ITeamBuildClient.class);
buildClient.addBuildResultContribution((IBuildResultHandle) buildResult.getItemHandle(), link, null);
The last statement throws exceptions that would require handling at some point.
Additional code could copy the build output somewhere, or add it to a backup system.
Handle Unqualified Folders
There can be many reasons why a folder could fail to qualify to be a build output folder. Some examples are
- Build definitions or implementations don’t conform to the rules of a build.properties file.
- Builds fail before the file is written.
- The folder structure is wrong.
There is a delay between the creation of the folder and the creation of the build.properties file. In cases where the
BuildOutputManager
Here are some options for a deeper analysis of the folder and its content to help with a decision:
- Check if the folder contains Jazz SCM metadata, typically stored in a folder named .
.jazz5
- Check the age of the folder.
Since there is no general rule, the code for this method is left blank for the purpose of this article. It would be possible to:
- Delete the folder based on its last modified date. For example, keep those folders only a week.
- Build a list of these folders and notify a person about the folders that don’t conform and need to be checked. A large number of these folders could point to build problems or inconsistencies in the build output structure.
Add some
System.out.println()
handleNotQualifyingFolders()
private void handleNotQualifyingFolders(File aBuildOutputCandidate) {
System.out.println(aBuildOutputCandidate.getAbsolutePath() + " does not qualify as Build Output folder");
}
Load the Build Properties
The method to load the build.properties file from disk still needs to be implemented. The code below shows a possible solution. It just uses the
java.util.Properties
FileInputStream
private Properties loadBuildProperties(File aBuildOutputCandidate) {
try {
File buildOutputPopertyFile = new File(aBuildOutputCandidate,"build.properties");
if (!buildOutputPopertyFile.exists()) {
return null; // there was no property file
}
Properties buildOutputProperties = new Properties();
FileInputStream fis = new FileInputStream(buildOutputProperties.getAbsolutePath());
buildOutputPoperties.load(fis);
fis.close();
return buildOutputProperties;
} catch (Exception e) { // Ignore
} return null;
}
Implement Missing Code
It is not necessary to implement the methods
BuildOutputManager.markAsDeliverable()
BuildOutputManager.isMarkedAsDeliverable()
BuildOutputManager.markAsDeliverable()
private void markAsDeliverable(File aBuildOutputCandidate) {
try {
(new File(aBuildOutputCandidate, "DELIVERABLE")).createNewFile();
} catch (IOException e) {
// do something
}
}
This would result in the following change to the build output folder:
BuildOutputManager.isMarkedAsDeliverable()
private boolean isMarkedAsDeliverable(File aBuildResultCandidate) {
try {
File isMarkedAsDeliverable = new File(aBuildResultCandidate, "DELIVERABLE");
if (isMarkedAsDeliverable.exists()) {
System.out.println(aBuildOutputCandidate.getAbsolutePath()+" : Is a Deliverable!");
return true;
}
} catch (Exception e) {
// do something
}
return false;
}
It is not necessary to implement the
checkDeleteFolder()
private void checkDeleteFolder(File aBuildOutputCandidate) {
if (!isMarkedAsDeliverable(aBuildOutputCandidate)) {
System.out.println(aBuildOutputCandidate.getAbsolutePath()+" : Deleting!");
}
}
The necessary code to
publishToIRAM
private String publishToIRAM(File aBuildOutputCandidate) {
return "http://jazz.net/library";
}
Enhance the example with your own business logic.
Debug the Code
To run or debug the code in Eclipse, create a ‘Debug Configuration’.
In the following dialog, create a new ‘Java Application’ launch configuration. Select the correct project and the
BuidOutputManager
main()
Make sure to switch to the Arguments tab and to enter the Program Arguments like repository URL, user name, password and the build output root folder for example:
"https://clm.example.com:9443/ccm" "build" "build" "C:/RTC3011Dev/jazz/buildsystem/buildengine/eclipse/JKEBuild"
Hit debug to start debugging. It should be possible to follow the code and see the output in the console window.
Create a release for one of the public builds, run the
BuildOutputManager
Deploying the Solution
Since the
BuildOutputManager
- It could be started with any scheduler that is available on the deployed system.
- It could be also be run by the Jazz Build Engine e.g. using the command line. In this case create a build definition for each build engine to schedule build requests running the BuildOutputManager. If that data is provided in a stream including the PJCL libraries, then this would spare the separate install.
To start the tool outside of Eclipse make sure the class is compiled. The following lines in a script should run the tool assuming the eclipse project is in the folder
<rtcdevdir>/workspace
cd C:/RTC3011Dev/workspace/com.ibm.js.team.build.manager
java -cp ./bin;C:/RTC301Dev/installs/PlainJavaAPI/* com.ibm.js.team.build.manager.BuildOutputManager "https://clm.example.com:9443/ccm" "build" "build" "C:/RTC3011Dev/jazz/buildsystem/buildengine/eclipse/JKEBuild"
Since the
BuildOutputManager
addToBackup()
Summary
This article shows how the Plain Java Client Libraries can be used to implement automation for managing build output folders created by Jazz Build Engines for teams using continuous integration. This automation can be used to delete folders no longer needed. It will also publish folders related to deliveries to a backup system, as well as to any other asset management system that provides Java API. The automation provided helps to reduce manual work, as well as reducing required disk space and preserves build output that is needed for audits or delivery creation. Further enhancements to this implementation can enhance the build result with additional modifications and creates the opportunity for even more automation.
For more information
- Getting started with Rational Team Concert: A Deployment Guide
- Tutorial: Building with Jazz Team Build
- Continuous integration using Rational Team Concert
- Continuous integration best practices with Rational Team Concert
- How I learned to stop worrying and love personal builds
- Motivations behind the design of Jazz Team Build
- Using the SCM Command Line Interface in builds
- Packaging and deploying the build output into a rntime environment
- Build Artifacts Publishing Using HTTP Servers in Rational Team Concert
About the author
Ralph Schoon is a member of the Jazz Jumpstart team. The Jazz Jumpstart team is a worldwide group of development specialists who bring new and advanced Jazz-based technologies to customers. Please direct feedback and comments to ralph.schoon@de.ibm.com .
Copyright � 2012 IBM Corporation
Discussion