EWM Work Item OSLC CM API

After looking into EWM Discovery, how can one create or update a work item using the techniques explained so far? This post will look into the steps that are required to create and update a work item. The techniques explained in the previous posts in this series are important. If necessary, go back to the previous posts to understand the details.

This blog was originally separated into two sections, creating a work item and then updating the work item. The first part mentioned programming and Python, the second used RESTClient to demonstrate the concepts. This was just due to the code I have written so far and due to complexity of the operations. The blog post was recently updated with Python code snippets for work item update and additional content.

Update: The blog post has also been rearranged and updated to separate getting a work item and updating a work item.

Update: The blog post has been rearranged and updated to show the usage of the oslc.properties query parameter for partial get and update.

Update: The blog post has been rearranged and updated to show how to resolve a work item and setting the resolution.

Update: The blog post has been updated with how to get the complete workflow information needed to navigate the workflow states and actions.

Context of the blog post is the series

This is the series of planned posts I intent to publish over time. Most of the examples will be EWM based, but quite a lot of the content applies to more ELM applications. The examples where performed with versions 6.0.6.1 and 7.0.x.

External Links

I used at least the following links for exploring this mechanism.

Discover the Creation Factory

To create a work item using the OSLC API requires some information that is provided by the OSLC API and can be discovered. To create a work item first requires a URI/URL of a creation factory providing the capability to create the work item.

EWM supports customizable work item types with customizable attributes of customizable attribute types. Each work item type has its own creation factory URL that needs to be looked up to create a work item of that type.

Work item types have potentially different sets of attributes configured. Each attribute has a type from a number of available attribute types. EWM allows to create custom attribute types. Some of the attribute types are primitive and are easy to handle. As an example the summary of a work item is just a string and it is easy to provide data. Other attribute types are far more complex. Many attributes are enumeration types. Enumerations are a number of available literals with an ID and a display name for each literal. A work item usually has an owner or subscribers. These represent users that are available in the system referenced by a unique URI. Work items can be assigned to a project or team area, which are other attribute types available in a project areas process. Which process area a work item is assigned to is controlled by a Category that maps a user readable string to the process area represented by its URI.

Note, some data that shows up in the work item is not attribute based. As example the collection of reviews and approvals, the attachments and the comments are not attribute type based. Some attributes that show up are only presentations or are calculated and can not be changed. Some of the not attribute based data is provided as pseudo attribute e.g. to be able to export the data.

To create a work item it is necessary to find out which attributes of which types a work item can have and which values the attribute of such a type can have. Attribute types can be single value such as enumerations or strings or they can have multiple values such as enumeration lists or string lists. All this information is required when creating a work item.

The information which work item attributes with what types a work item type can have, can be discovered in OSLC APIs as part of discovering the creation factory. The information is accessible via a resource shape. How the discovery process works in general is described in EWM Discovery.

Reminder, for all requests use the mandatory OSLC header:

OSLC-Core-Version 2.0

To receive RDF encoded data use the Accept header and for sending RDF use the content Type header as shown below:

Accept application/rdf+xml; charset=utf-8
Content Type application/rdf+xml; charset=utf-8

The image below shows the RDF form of a part of the service provider catalog for a project area. Using the namespace

xmlns:oslc="http://open-services.net/ns/core#"

the oslc:CreationFactory entry contains the resourceType(s) and the resourceShape. The first resourceType http://open-services.net/ns/cm#ChangeRequest is used for OSLC integrations. The second resourceType provides the link to the work item type definition. The entry oslc:creation contains the creation factory URI.

RDF form of the creation factory for defect and task

To receive JSON encoded data use the Accept header and for sending JSON use the content Type header as shown below.


Accept application/json; charset=utf-8
Content Type application/json; charset=utf-8

The image below shows the JSON format of the creation factory entry for the work item type defect. The data is provided analogue to the RDF format and can also be used to access the data for the creation factories.

JSON form of the creation factory for defect

Discover Resource Type and Shape

Following the resourceType URI provides the details of the work item type. The image below shows the details for the defect. Getting the resourceType

GET https://elm.example.com:9443/ccm/oslc/types/_8e5qfFpmEeukW7cqqDjAuA/defect

with the headers introduced above provides more details. The display name is Defect and the work item type ID is defect. The data also contains the URI for the EWM project area that defines the work item type. The image below shows the result of such a GET request.

Type definition for defect

The Python code block below processes the RDF data and extracts the important data. The important take away are the predicates to select the information to be iterated.

Extract the creation factory information

The code operates on some simplifications. The code gets the work item type ID by filtering out the entry that starts with the public URI root. The code assumes that the end segment is equal to the work item type ID. This just saves the call to the resourceType URI which would contain the information. The code gets the creation factory URI and the resource shape URI.

To be able to create a work item, GET the resource shape using its URI and the headers described above. As an example:

GET https://elm.example.com:9443/ccm/oslc/context/_8e5qfFpmEeukW7cqqDjAuA/shapes/workitems/defect

The resource shape contains a number of attribute type definitions for the attributes (and link types) the work item type can have. The information contains the name (id) of the type, the title (display name) of the attribute type, the allowed values, the default value, if the attribute is read only etc.
The image below shows one example for the attribute Priority with ID priority, the default attribute literal and the link to the allowed values. The attribute is defined for values in an enumeration. The allowed values URI describes the available values defined in the enumeration.

Allowed Values

To get the allowed values for the attribute Priority perform a GET using the shape URI. As always provide the OSLC headers.

GET https://elm.example.com:9443/ccm/oslc/context/_8e5qfFpmEeukW7cqqDjAuA/shapes/workitems/defect/property/internalPriority/allowedValues

The result of the request is shown below.

Allowed values for the work item attribute Priority with ID internalPriority

Note that the allowed values, the priority enumeration literals, do not provide any information about the display name or any other details. To get the display name it is necessary to GET (OSLC headers!) each of the priority literal resource URIs and analyze the results.

To create a work item, the next steps would be to get allowed values for all required attributes and to create a request body containing this information.

Required Attributes

At the time of writing, OSLC does not provide a mechanism to get the list of attributes that are required to create a work item due to restrictions in the process. If the target system requires special attributes to be set e.g. in certain states, or expects special values, the work item creation or save will fail. To successfully create or update work items, it is necessary to know such limitations ore provide a configuration capability to adjust for such information.

Request Body

For the JKE Banking Example that is shipped with EWM, the process requires the following information to create a defect:

  • Summary
  • Category

To make the example a bit nicer we also want to also set the description of the work item. The summary and the description attributes are easy. This is just a string value. The category is the mapping to a process area. This is not a trivial attribute and it is necessary to get an allowed value. A valid approach is to get all allowed values into an array and to pick one value. If it is desired to find the correct allowed value based on a display value or some other characteristics, iterate all allowed value URIs and get the details.

The code below is the code that creates the payload for the request body needed to create the work item. It looks more complicated than it is, probably because of the RDF library. The code creates an empty graph, a node for the work item and then adds the predicate object configuration that represents the desired information e.g. the text for the summary and the description.

Create the payload and the work item

The payloadString serialized in createRDFWorkItem from the graph simply looks like the RDF XML below. It only contains the information about the summary, description and the category

Creating the Work Item

To create the work item, POST to the work item types creation factory URI like shown below:

POST https://elm.example.com:9443/ccm/oslc/contexts/_8e5qfFpmEeukW7cqqDjAuA/workitems/defect

Provide the required headers to declare OSLC, and the headers to control the content encoding that were already explained. The Content-Type defines the format of the request body. The request body is the payloadString created above.

OSLC-Core-Version 2.0
Accept application/rdf+xml; charset=utf-8
Content-type application/rdf+xml; charset=utf-8

The response status should be a 201. The response headers should contain the location header with the URI of the work item. The URI of the work item is the unique URI of the work item and can be used in subsequent operations to identify the work item. E.g. in GET, or PUT operations to retrieve or modify the work item. The URI is also used in the process of linking work items. The URI can be used in a redirect to open the work item editor in a web UI.

The response headers contain a new important header called ETag. The ETag value describes the state of the work item. Every time the work item is changed it gets a new ETag. When trying to update a work item it is necessary to send the ETag value from the previous GET in a special If-Match header. The server can use this value to evaluate the state of the work item the update is supposed to be applied to based on the current ETag. If the ETag in the request is not the current ETag of the work item state, the request is based on stale data. New changes where done to the work item. The server detects the mismatch and alerts the client that the change can not be executed. The client needs to refresh the work item information, GET the latest state, modify it accordingly and retry to update the work item based on this information.

The response body sent back by the server, contains the data for the new work item. The information is usually much more than the information that went into the creation of the work item. The server creates several special attributes such as the creation date, the creator and similar information. In addition attributes with default values are automatically set and will be returned.

Response body for the POST to the creation factory.

All subsequent operations on the work item should be based on this state, or a never version state that was retrieved by a GET on the work item URI.

The code below shows the creation of the work item and gets the work item URI from the results location header and the etag from the results ETag header.

The work item creation and the response analysis for RDF.

The same process can be used with the JSON format. The image below shows an example for a work item creation using the JSON encoding with the same attributes used with RDF above.

JSON payload to create a work item

JSON requires to use the headers shown below instead. The header Content-Type only needs to be set when a request body is sent.

OSLC-Core-Version 2.0
Accept application/json; charset=utf-8
Content-type application/json; charset=utf-8

Get a Work Item

The following sequence is done with the RESTClient extension installed in Firefox. RESTClient is a Firefox extension that runs in the browser. This allows you to log in to the server with the web UI in Firefox and then use the RESTClient without worrying about the authentication because RESTClient reuses the authentication done in the UI. Another Option would be Postman, but that requires to log into the CCM server as explained here. Install RESTClient and Firefox to do these steps yourself.

To get a work item we need the public URI of the work item. The public URI was returned in the Location header when creating the work item. It can also be located e.g. using an OSLC Query. The public URI has this form:

https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/242

where the number at the end is the work item unique ID for the repository. Note that the URI of the work item is not the URL for the work item editor you get when you copy the address URL from the browser. That URL contains information like the project area name and is not stable.

Open the Web UI of EWM. Open a work item e.g. the one created before. Get the work item URI by copying it out of the Web UI like shown below. You can also use the work item URI from the automation to create the work item.

Use copy link location on the icon to get the work item URI

To get the current state of the work item it is necessary to perform a HTTP GET operation on the public URI. Open a RESTClient and prepare to get a work item. As an example select GET as method and paste the work item URI into the URL address.

GET https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/242

Use the OSLC, Accept and Content Type headers as explained above.

Prepare the GET request

Push the Send button to execute the request.

The request should succeed with a status 200. Here the Request and the response headers in RESTClient in Firefox.

The successful GET request to read the work item 242

Browse the response Headers tab first. Note the ETag header. This is important for a later update.

Property Selection

Update: It is possible to use the oslc.properties query parameter to select which properties (attributes) are of interest in this OSLC request. Note that the oslc.properties require URL encoding to allow to send them in the request URL. The code shown below shows which part of the URI needs to be URL encoded. It is the the comma separated section of property definitions behind the

oslc.propertis=

The oslc.properties can be used in a GET request. In this case it can be used to specify which properties the requestor is interested in. The server does not have to collect and transmit all properties, but only the ones of interest. This saves computing resources and bandwidth.

The image below shows the result of a GET request on a work item URI, providing a oslc.properties selection. Note the response body only returns a limited amount of properties compared to the full GET above.

OSLC properties selection in a GET request.

The image below shows how the requested properties are selected using Python code.

Selection of the properties the request is interested in.

Update a Work Item

To update a work item it is necessary to get the current state of the work item. For this it is necessary to perform a HTTP GET operation on the public URI as shown above. Go back to the result of the previous section or open a RESTClient and prepare to get a work item. For the latter make sure to select GET as method have the work item URI into the URL address.

Make sure to use the OSLC, Accept and Content Type headers as explained above.

Perform the GET request

Push the Send button to execute the request.

The request should succeed with a status 200.

The ETAG in the successful GET request to read the work item 242

Browse the response Headers tab first to prepare for updating the work item. Copy the ETag header into a text editor, we need the value of the header later.

Switch over to the Response tab. Search the content for :title to locate the XML containing the work item summary attribute. Mark the response body and copy its content.

The work item summary attribute

Open a new RESTClient window. Paste the response body from the previous GET request into the request body of the new request. Copy the work item URI into the address URL. Change the method to PUT.

Search for the :title in the new request body. Modify the text enclosed in the tag e.g. to An updated summary like below.

<dcterms:title rdf:parseType="Literal">An updated summary</dcterms:title>

Add the OSLC and content headers:

OSLC-Core-Version 2.0
Accept application/json; charset=utf-8
Content-type application/json; charset=utf-8

Now add another header If-Match with the ETag value from the GET method.

If-Match "c6eebbc9-257c-335a-9726-287f39732012"

See the image below how the request looks like. Perform the send.

The update should succeed with a 200 status and contain a new ETag.

Successful update with a new ETAG.

Open the work item in a work item editor. You can do this by pasting the work item URI into the browser address field. If the work item editor is already open the editor should show the work item was changed. Refresh the editor if this is the case. The summary attribute was successfully updated:

The updated work item summary in the Web UI

Performing a subsequent GET in the initial RESTClient window provides the same information and the new ETag. The ETag will change if the work item is changed again.

Update: change a work item in Python code

The code below retrieves the work item based on its URI.

Getting a work item and the ETag for updating it.

After getting the work item and the etag, the response body is parsed into a graph that has the required namespaces bound to it. The response has the work item URI as subject in the response, this is retrieved and validated. The work item data is analyzed and some data is pulled out by querying the graph for objects with a specific predicate (e.g. DCTERMS.identifier) and the data is printed.

The code below shows the modification of the summary of the work item.

Updating the work item with If-Match header

The new summary is computed based on a timestamp. Then the value of the subject node is set using the code below.

# Set a RDF entry for a node, predicate and object.
    def setRDFAttribute(self, graph, node, rdfPredicate, rdfObject):
        graph.set((node, rdfPredicate, rdfObject))

The update header is created, including the If-Match header with the work items ETag from the get operation. The RDF graph is serialized to be sent. Then the PUT operation is performed. The change will have a new ETag, so that is received.

The response body is parsed into an RDF graph again and the information for some of the attributes is again analyzed and printed. The image below shows the console output that is created.

Successful update of a work item with the original and new ETag

Property Selection

Update: It is possible to use the oslc.properties query parameter to select which properties (attributes) are of interest in this OSLC request.

The oslc.properties can also be used in create and update requests. This can be used for partial updates. Please carefully read this section about updating work items and also check the examples that are provided. Especially for creating and removing links and custom attributes.

For using the oslc.properties query parameter, to limit the amount of properties to a subset, please check the sections oslc.select and oslc.properties in the post EWM OSLC Query API. Syntax and encoding for these query parameters are the same. You have to provide the oslc.properties as a query parameter URL encoded like shown below.

PUT https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/242?oslc.properties=rdf%3Atype%2Cdcterms%3Aidentifier%2Cdcterms%3Atitle%2Cdcterms%3Adescription%2Cdcterms%3Amodified%2Crtc_cm%3AmodifiedBy%2Cdcterms%3Acreated%2Cdcterms%3Acreator

As a summary, the usage of the oslc.properties query parameter allows to

  • Limit the properties returned in a GET request to the selected properties. Instead of sending all properties, the server can just respond with the sub set of properties.
  • Limit the properties that are considered to be received to the selected properties in PUT and POST requests. This can be used to just have to send a limited amount of attributes, and not all, to achieve a partial update. This can also used to remove properties by including a property in the oslc.properties but not sending data for it.

The link above mentions to use the PATCH method for partial updates a couple of times. I briefly tried to use PATCH to partially update a work item, but I got an “Error 501: Not Implemented”, so I assume PATCH is not available.

Work Item Workflow State change

One important work item attribute is the workflow state also abbreviated with state of the work item (not to be confused with the ETag). When checking the Resource Oriented Work Item API V2 it mentions that the state is read only. It mentions the capability to use an action to perform a state change, but there is no documentation provided that shows how that works. Digging deeper into the Jazz.net forum there are answers that hint on how it could work, but every one of the answers I found was lacking some detail. Here what I was able to come up with to make it work for me.

The work item workflow is defining the states of the workflow and actions that are valid to get from one state to another. States and actions have unique ID’s.

Analyzing the resource shape of the work item type, the state attribute with ID internalState can be located. The attribute has allowed values that can be discovered. The allowed values are the states of the workflow. As an example for one state there is a URI like below:

https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/states/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.state.s4

We are not interested in the state, we need the ID of the action to perform the workflow action. I have not found any simple way to discover the URI to get the workflow actions. There is however a pattern how to access the required information for workflows and actions and other information required to manipulate the workflow state of a work item.

Looking at the path of the URI for the workflow state reveals that the path section after the section workflows is the project area ID. The next path section is states and the section after that describes the workflow ID. The last section is the workflow state ID.

If we remove the workflow state ID section, and perform an GET on the resulting URI e.g.

GET https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/states/com.ibm.team.workitem.defectWorkflow

the resulting response is a list of the workflow states available for the workflow.

Please note, that I experienced issues with the headers to be used here, dependent on the headers used different information was returned in some cases. I would suggest to test using the OSLC headers. If there is a lack of information try to only use the header Accept application/xml (or application/json). The result could contain more information. Please note that in this case the format is not RDF.

If we replace the section states in the URI by actions we get an URI that allows to GET all the workflow actions.

GET https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/actions/com.ibm.team.workitem.defectWorkflow

Accept application/xml

Please note, to get the complete workflow information, use only the header Accept application/xml (or application/json). Do not use the Accept application/rdf+xml and do not use the OSLC-Core-Version or any other header. Run the call as shown above.

This way you receive the full workflow table. This contains the information of the workflow action including the resulting state. Given this information it is then possible to extract all workflow information necessary to navigate the workflow. I found this hint in this answer.

To get the information in JSON use only the header application/json.

The image below shows how the complete workflow information looks like, if you get it in the correct way.

The complete workflow information, including the result state of an action.

Once a desired workflow action ID is selected, it is possible to perform a workflow action. This can be achieved by adding a query parameter ?_action=<actionID>, to the work item URI, where <actionID> is replaced by the ID found as workflow action. The result is shown in the image below. Please note the familiar OSLC and content headers as well as the ETag for the last state of the work item are needed as well.

Updating the work item performing a workflow action with an empty resource representation.

It is also necessary to send the representation of the work item, otherwise the PUT call will fail. To get an ETag, it is necessary to do a GET, so all the information is already available. By default OSLC expects the complete representation and deletes data that is no longer available in the request.

I have tried to send an “empty” work item representation as hinted in one of the forum questions and the one shown above in the image and below as code worked for me. Note that there is no guarantee that this works with all versions and forever. An attempt to do a partial update using PATCH resulted in an not implemented response. In general see some hints on partial representations in Resource Oriented Work Item API V2.

Important Update about partial update: Please see the Updating Work Items section in the Resource Oriented Work Item API with OSLC_CM 1.0 wiki page and scan through the documentation, especially for partial update. Note the usage of the query parameter oslc_cm.properties. By providing a list of properties that is to be updated it is possible to send only part of the representation containing said properties. The oslc_cm.properties uses the same syntax used by the oslc_cm.select statement. See the post EWM OSLC Query API and check the section about oslc.select for how to create, compose and encode the parameter.

To get the minimal RDF shown here, I cut out all content and stripped it down to the bare minimum. RDF libraries should be able to serialize empty graphs as well and it should be easy enough to find a minimal empty body for JSON.

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> 
</rdf:RDF>

The URL below successfully updated the work item state without the need to pass additional attributes. The minimal request body above was used with the URL below and the work item state update was successfully performed.

PUT https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/241?_action=https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/actions/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.action.startWorking&oslc.properties=

With headers:

OSLC-Core-Version 2.0
Accept Type application/rdf+xml; charset=utf-8
Content Type application/rdf+xml; charset=utf-8
If-Match "fcd3b360-d152-367d-956e-d1f9f428ced4"

Note that the oslc.properties statement did not work with rdf.nil. But it was possible to omit a value as shown above to clarify that no property value would be included. IT would be possible to specify additional values in oslc.properties that would be sent in the request body. One example is the resolution – see below how that looks like.

Please note, that it is necessary to satisfy all required attributes when performing a workflow action. The new state might require additional values that you might have to provide, in which case you want to definitely use the full information from the resource.

Please also note that I am not seeing errors if the workflow action can not be done or does not exist. The response is 200, regardless of this fact.

Resolve Work Item With Resolution

One example for attributes that might become required with respect to a state change is the resolution attribute

rtc_cm:resolution 

that might be required in certain states when resolving/closing the work item. Following the pattern we discovered the resolutions can be found using the workflow URI and replacing the section states by resolutions. The resulting URI provides the available resolutions.

Please note, that I experienced issues with the headers to be used here, dependent on the headers used different information was returned in cases such as the actions above. I would suggest to test using the OSLC headers. If there is a lack of information try to only use the header Accept application/xml (or application/json). The result could contain more information. Please note that in the latter case the format is not RDF.

Since I had built all the logic to get the action information using XML (minidom) and not RDF in python, I used only Accept application/xml as header to get the resolution information and reused the code written to analyze the actions.

GET https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/resolutions/com.ibm.team.workitem.defectWorkflow

Accept application/xml

With the call above it is possible to GET all resolution information like ID’s and names. The image below shows an example. Mind the request header used.

Get the resolution information.

It is then possible to add or set the attribute value like below.

<rtc_cm:resolution rdf:resource="https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/resolutions/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.resolution.r4"/>

See the response from a GET response to an work item URI, containing the value below as the example.

Setting the resolution results in this additional attribute.

The example below shows all that is needed to close the work item and set the resolution updated in the same request. In addition to the workflow action as above use the the oslc.properties and define an additional property, the resolution attribute, to be provided.

PUT https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/241?_action=https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/actions/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.action.resolve&oslc.properties=rtc_cm%3Aresolution

The request URL contains the workflow action and the request body contains the data for the additional attribute to be set as follows:

<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:rtc_cm="http://jazz.net/xmlns/prod/jazz/rtc/cm/1.0/" >
	<rdf:Description rdf:about="https://elm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/241">
	<rtc_cm:resolution rdf:resource="https://elm.example.com:9443/ccm/oslc/workflows/_8e5qfFpmEeukW7cqqDjAuA/resolutions/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.resolution.r3"/>
	</rdf:Description>   
</rdf:RDF>

The request headers, including the If-Match header, have been shown already and show up in the image below which shows all the important data in RESTClient:

Resolving the work item with setting the resolution attribute.

This is a complete example of a state change while changing the resolution attribute in addition. This can be expanded on to set more properties using oslc.properties.

I found numerous questions around this in the Jazz forum, but there was still some considerable amount of work to be done, to get the state change working.

The page Resource Oriented Work Item API V2 also describes other interesting concepts like query mechanisms and representations. Some of them will be presented in subsequent upcoming posts. Something I have not yet explored are the draft work items. Intriguing. Maybe I find time to look at it.

Summary

I am fully aware that I am quite late to the party, but despite trying, I was unable to find examples for the whole process that were consistent and simple enough, covered most of the ground or worked for me. There was quite some research that needed to be done to get all the concepts covered. All the information in the blog series I am currently writing has been collected to help customers as well as IBM internal resources with the ELM APIs.

So, as always I hope that the content of this blog (series) is of interest to users out there and helps to save some time achieving their goals.

A RTC WorkItem Command Line Version 3.0

I was interested in how complex it might be to export work items and import them again. So I looked into this and enhanced the work item command line (WCL) to support this.

I found it quite challenging to develop this. There are a lot of things to consider, so I drove this to a point that was sufficient for my purposes. The tool is used by my team to import work items from a CSV file we receive every now and then.

I implemented another export/import mode with some more capabilities and it works in tests, but is by no means production ready. The amount of necessary tests and test automation to make this reliable, is just overwhelming. So this is not thoroughly tested.

So be careful if using these commands, and do a good amount of testing before actually using this. The problem is, there are so many possible use cases and dependencies that it is very hard to develop this kind of capability and to test it.

One special case is importing/creating links. Some links have constraints i.e. parent and especially child links. A work item can not have multiple parents. So setting child links can cause the save to fail if the new child has already a different parent.

Solution Overview

The work item command line WCL now supports two new commands

  • exportworkitems
  • importworkitems

To export work items to a CSV file and import work items from a CSV file.

I chose to use a CSV file, because RTC itself can export and import that format already. It would be ideal if export and import from XML would be supported as well, but this would require a substantial effort to abstract the export and import operations to be able to use a strategy (or some other useful pattern to support abstraction).

No Support or Maintenance

This is provided as-is with no support or guarantee. This is not a tool that is officially supported by IBM or any other organization.

Please note, that I have very little time to do this and testing is always lacking. So take the code published here with a grain of salt. On the positive side, you have the code, can debug and enhance it.

Compatibility

This code has been used with RTC 4.x and 5.x with no changes and it is pretty safe to assume, that the code will work with newer versions of RTC. The code requires two external libraries that need to be downloaded and installed separately.

License and Download

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!

Update: Added switch to change the export and import formats for dates, see details below. I also added a switch to suppress attribute not found errors and other frequent errors during export.

Update: Fixed duration set problem. Version updated to 3.2

You can download the latest version from this post The RTC Work Item Command Line On Bluemix. The older version 3.0 can be found here:

Please note, there might be restrictions to access Dropbox and therefore the code in your company or download location.

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 following code in this environment and get your own automation or extension working. The code linked from this post contains Client API.

Setup and Usage

For the general setup follow the description in A RTC WorkItem Command Line Version 2 and look at the additional setup steps below.

For usage follow the description in A RTC WorkItem Command Line Version 2 and in A RTC WorkItem Command Line Version 2.1. Check the README.txt which is included in the downloads.

Export Work Items

The export work items command has the syntax

-exportworkitems {Switch} repository=”value” user=”value” password=”value” projectArea=”value” query=”value” exportFile=”value”  [columns=value] [encoding=value] [delimiter=value] [querysource=value]

Required Parameters are

  • repository=”value” – the repository URI, for example repository=”https://clm.example.com:9443/ccm
  • user=”value” – The user ID of the user executing the command, for example user=”ralph”
  • password=”value” – the password of the user, for example password=”password”
  • projectArea=”value” –  The project area to export items from, for example projectArea=”JKE Banking (Change Management)”
  • query=”value” – the name of the query to use, for example query=”All WorkItems”
  • exportFile=”value” – The path of the export file, for example exportFile=”C:\aaTemp\Export\Test.csv”; the folder that contains the export file must exist

Optional Parameters are

  • columns=value – The names or ID’s of the work item attributes to export; example columns=”Type,Id,Planned For,Filed Against,Description,Found In”; To specify the colums it is possible to use the name or the ID of the attribute, the switch headerIDs specifies the output format to use the ID instead of the name in the output; It is possible to use the values from an RTC Eclipse client export
  • encoding=value – The encoding; default encoding=”UTF_16LE”; options see available charset names; if the encoding is chosen different for export and import, the values will not be recognizable
  • delimiter=value – The delimiter to be used between the columns. Default is comma delimiter=”,”
  • querysource=value – If the parameter is omitted the command searches a personal query with the given name; if the value is provided a query shared by the process area is searched, a complete path from the project area to the sharing process area must be provided, for example querysource=”JKE Banking(Change Management),JKE Banking(Change Management)/Business Recovery Matters”
  • timestampFormat=value – To specify the time stamp format to be used; default “MMM d, yyyy hh:mm a”;  see SimpleDateFormat for the supported pattern

Available switches are:

  • /ignoreErrors – Ignore minor errors in mapping and value lookup
  • /asrtceclipse – Export in a format compatible to the RTC CSV export and import; if the switch is not provided, the data is exported in a format that is compatible with the syntax used by the work item command line WCL to identify elements; see A RTC WorkItem Command Line Version 2 for the supported value representations
  • /headerIDs – Export header values as attribute IDs and not as attribute names
  • /suppressAttributeExceptions – Suppresses exceptions thrown for attributes that are not available on the work item type of for attribute types that are not yet implemented

Example

-exportworkitems /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" exportFile="C:\aaTemp\Export\Test.csv" query="All" columns="workItemType,summary,Attachments"

Import Work Items

The import work items command has the syntax

-importworkitems{Switch} repository=”value” user=”value” password=”value” projectArea=”value” query=”value” importFile=”value”  [columns=value] [encoding=value] [delimiter=value] [querysource=value]

Required Parameters are

  • repository=”value” – the repository URI, for example repository=”https://clm.example.com:9443/ccm
  • user=”value” – The user ID of the user executing the command, for example user=”ralph”
  • password=”value” – the password of the user, for example password=”password”
  • projectArea=”value” –  The project area to export items from, for example projectArea=”JKE Banking (Change Management)”
  • importFile=”value” – The path of the import file, for example importFile=”C:\aaTemp\Export\Test.csv”

Optional Parameters are

  • mappingFile=”value” – A RTC work item import mapping file, for example mappingFile=”C:\temp\mapping.xml”; the file must be generated by RTC and customized to match the value mapping
  • encoding=value – The encoding; default encoding=”UTF_16LE”; options see available charset names; if the encoding is chosen different for export and import, the values will not be recognizable
  • delimiter=value – The delimiter to be used between the columns. Default is comma delimiter=”,”
  • timestampFormat=value – To specify the time stamp format to be used; default “MMM d, yyyy hh:mm a”;  see SimpleDateFormat for the supported pattern

Available switches are:

  • /ignoreErrors – Ignore minor errors in mapping and value lookup
  •  /importmultipass – Import the work items from the CSV file in a first iteration and build up a mapping for the ID’s provided in the import file and the actual ID’s created and recreate the work item links between the new work items based on that mapping in a second pass; the old work item ID for a work item has to be provided in a special column with header name com.ibm.js.oldid
  • /forcelinkcreation – if no target work item can be found in the map, use the given ID to create the link
  • /importdebug – Print more information during import attempts to help with finding issues
  • /enforceSizeLimits – Attributes such as description and medium strings have size limits, if this switch is set, the importer tries to clip content to avoid exceptions due to the size limits

Example

-importworkitems  /enforceSizeLimits  /importmultipass  /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="ImportTest1" importFile=""c:\aaTemp\ExportImport\TestExportAll.csv"

RTC Eclipse Compatible Export Mode

In mode asrtceclipse, all data is exported the way RTC would export them in Eclipse. This means that certain information for example links, team areas, iterations, attachments and other data is exported in a way that makes it hard to map to data in the repository.

The import command does its best to map based on names, but for complex hierarchical information such as iterations and team areas, there is currently no search implemented that will find the object successfully. It would be possible to implement such methods, with some effort.

Example: An iteration is part of a timeline. The timeline is needed to find the iteration within. If there is no information about the timeline, it would be required to iterate all timelines with a good chance of mismatch.

The import command will try to find things by name and ID, with the limitations above.

If the work item ID attribute is provided as a column the importer will try to find the work item and update it during the import.

Default Export Mode

In the default export mode, the RTC Work Item Command line export command exports the data in greater detail, which makes it easier for the importer to identify the item.

Attachments

In default mode, the attachment is exported as a file relative to the location of the generated csv file. The attachment is downloaded to a location ./attachments//. So for each exported work item with attachments, a separate folder is created. The attachments are stored in that folder and the export information in the csv file is created compatible to the WCL parameter format to allow later import of the attachments, including upload and applying the additional information.

Other Complex Items and Links

Other complex items such as iterations and team areas are also exported with a lot more details. An iteration is being exported as path, including the timeline and parent iterations. A team area is also exported as path containing the Project are name and parent team area names

Multi-Pass Import

Importing work items and recreating the link relationships between them is problematic, because while importing the work items the link target may not yet exist. To be able to import a set of work items and then recreate the linkage, it is necessary to do the import and then map the ID of the old work item to the ID of the new work item.

When using the RTC CSV importer in the Eclipse client, existing work items are provided with a # in front of the work item ID. To do an import and then recreate the links between the new work items (and not to the old ones in the import), a user would have to run the import without the links, then replace the work item ID’s in the import file by the new work item ID’s and update the work items with a second import. This is very manual and error prone.

The switch importmultipass  enables an import mode, where the WCL tries to create the links between the imported work items, rather to the old ones. It imports the work items in two passes. It creates the work items in the first pass and ignores the link creation. In the second pass it tries to create the links. For links between work items WCL tries to find the work items that were created during the import and tries to match the links to the new work items, where possible.

Note: Only links between work items are handled this way. Links to objects other than work items are recreated using the values provided in the import file.

To be able to do this, the import file has to provide the old work item ID of the work items that are imported. The import requires a special ID for the columns containing the old ID’s. The column header for this column has to be specified with com.ibm.js.oldid.

The import file below has been created using an export that included the ID of the work item in the export. The old column header for example ID of the column has been replaced by com.ibm.js.oldid. The work item links show the ID’s of the linked work items with their old ID’s.

Import Work Items With LinksThe import works as follows.

The WCL runs the first pass and imports the work items. It stores the mapping between the original work item ID from the column com.ibm.js.oldid and the ID of the newly created work item in a map. Links are not created in this pass.

In the second pass WCL reads the import file again and only handles the columns that represent links. It detects if the link target represents a work item. If not, it tries to recreate the link as it is. If the link is a work item link, WCL tries to calculate if a new work item was created for the target using the map. If the work item was imported and a new ID is available, the new work item ID is used to create the link.

If the ID of the link target can not be found in the mapping, WCL can either ignore the link or it can try to create the link to the original work item. WCL supports these two modes. By default, the link is not created. If the switch forcelinkcreation is specified, the original value of the target work item ID is used as target for the link, if no mapping to a newly imported item was found.

Creating links is not trivial. One special case is importing/creating links. Some links have constraints i.e. parent and especially child links. A work item can not have multiple parents. So setting child links can cause the save to fail if the new child has already a different parent. This can create issues in import scenarios, especially if an export from the same repository is imported and the import causes child links to be created that have already another parent. In this case the import will fail with an error.

Limitations

Approvals and comments are imported into one comment. The effort to recreate approvals is just too big and I can’t see the added value.

Special Notes On Setup

For the general setup follow the description in A RTC WorkItem Command Line Version 2 and look at the additional setup steps below.

The export and the import commands of WCL need two libraries that are not shipped the downloads.

If you use the packaged WCL and want to use the export/import capability follow the steps below to add the required libraries to the folder lib in the folder lib in the WCL folder.

If you use the Eclipse project for WCL and want to use the export/import capability follow the steps below to add the required libraries to the folder lib in the Eclipse project com.ibm.js.team.workitem.commandline.

The export and the import commands of WCL use the Open CSV Library. I had issues with the newer versions of Open CSV that I could not resolve, so this code assumes the version 2.3. Download the version 2.3 from here. Uncompress and untar the the file opencsv-2.3-src-with-libs.tar.gz you downloaded. Look for the folder opencsv-2.3\deploy\ copy the JAR file opencsv-2.3.jar and put it into the lib folder of your version of WCL.

The import commands of WCL can only provide the capability to use a mapping file by using a JAR file that only ships with the RTC Eclipse client and the SDK. The classes used for the mapping file capability are located in the library com.ibm.team.workitem.rcp.core.  Open the Install location of the RTC Eclipse client and search for com.ibm.team.workitem.rcp.core*. You should find a file names similar to this one: com.ibm.team.workitem.rcp.core_3.1.900.v20141010_0124.jar. The version numbers at the end could be different. Copy the JAR file into the into the lib folder of your version of WCL.

The packaged version should look like below.

Deployed Packaged WCLIf you have imported the Eclipse project for WCL open Configure Build Path and create a user library named openCSV and add the Open CSV library opencsv-2.3.jar. Create a user library named rtcmapping and add the com.ibm.team.workitem.rcp.core library you just copied to it.

Your Eclipse Project should now look like below.

Eclipse Project and LibrariesCode Changes

During the work on import and export, the code structure was left untouched. Some classes were added to be able to handle the column header and some additional mapping of id’s and names i.e. for link types. In addition some of the code that was piling up in the WorkItemUpdateHelper (formerly known as WorkItemHelper) was moved to utility classes. this makes it also easier to look for useful API in case you are interested in how things work in the RTC API. See the scree shot below.

Code StuctureSummary

This WorkItem Command Line should allow for most of the automation needs when creating work items. In addition it is a nice resource for the RTC work Item API.

As always, I hope the post is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.

A Custom Condition to Make Attributes Required or Read-Only by Role

Recently, a customer wanted to make attributes required and read only in a way the built in operational behavior does not support. So I tried to find out if there would be a way to achieve this by an extension. After struggling for some time, I finally found a way and want to share this with the community.

Update: see A Custom Condition to Make Attributes Required or Read-Only by Role Version 2 for some more tricks and information.

The Requirement

The requirement was,

  1. A user can have multiple roles
  2. Attributes are required or read only for a role in a certain state
  3. For a user all roles shall be evaluated and the attribute be required or read only if any of the roles the user has, specifies so

So the intended behavior was more like permissions work in RTC, accumulate over all roles.

The built in RTC mechanisms for required and read only attributes are based on operational behavior. Built in advisors can be configured to provide the information and behavior.

RTC Operation BehaviorYou can configure operational behavior for each role, in this example it is for the default role Everyone. However, the operational behavior in RTC does only look for the first configured operational behavior for the operation that is configured for a role the user has, as described in Process behavior lookup in Rational Team Concert 2.0. This is which is still valid for later versions of RTC.

A user has one (every user has the Everyone role) or more roles configured if he is member of a project or team area. The roles have an order, from top to bottom. The first configured operational behavior for the first role in that order that is found in the context of an operation will be executed. Only that first found operational behavior will be executed.

The idea behind this concept is, that it is possible to overwrite the operational behavior by having specific roles in a specific order. It is, for example, possible to have configured that users with the role Everyone need to provide several required attributes to save a work item and several attributes can be read only. But the Team Lead may have a lot less required attributes and no read only attributes, because the operational behavior is specified in a different way for the Team Lead role.

This also means, if the check box “Preconditions and follow-up actions are configured for this operation” is checked for a role and no preconditions are configured, RTC will do nothing, if the user has that role, even if a lot of preconditions are configured for the role Everyone. RTC will find the specification for that role and as there is nothing configured assume there is no operation behavior needed for the role.

It also means, if operational behavior is specified for all roles, only the operation behavior of the primary role the user has in the context, is executed.

The customer wanted a behavior that was different. Assume role1 and role2 exist. A work item attribute is specified to be read only for role1 but not read only for role2. The attribute shall be read only for all users that have role2 assigned, regardless of the order of the roles.

This is, again, more like permissions as described in Process permissions lookup in Rational Team Concert 2.0 where if one role a user has, has the permission, the user has this permission.

How Does the RTC Code Work?

I looked at how this is implemented in RTC by looking into the SDK in the hope to find a way to extend it somehow. It is pretty easy to find the code of the advisors/preconditions shipped with RTC.  It took me a while to figure out what is going on in the code.

One thing that had bothered me for a while became understandable in the process: Operation behavior is run after the save button is pressed. How can operational behavior have any impact on the UI before the button is pressed? How can the UI show attributes as read-only or as required before the operation is performed?

Well, it turns out, that the built in advisors have a static part, that reads the configuration. The UI knows the built in advisors and calls this part to get the configuration data in order to require attributes or make them read only. Mystery solved.

This, of course means there is no way to create your own advisor/precondition to extend RTC to work with different rules and the UI showing behaving like with the built in advisors. The UI does not know that it needs to get the custom configuration and that’s it. It is still possible to prevent a save, but the UI won’t provide any information up front.

At this point I was very close to giving up. The issue had passed a lot of smart people’s desks at that time, including mine – 5 times at least. Why should I find a solution if no one else had the slightest idea?

The Solution

Well, when looking at the code, I realized some other built in operation behavior was wired up there as well. Code for the advisors to control required and read only attributes for conditions.

Advisors for ConditionsThis turned out to be the approach that allowed to implement the requirement. Create a custom condition that allows to configure the condition advisors in a way to provide the functionality as requested.

One important issue that became apparent in the process of creating this solution is that conditions are different from all the other attribute customization providers. This almost led to me failing in finding a solution to implement this.

Conditions need to be instantiated like all the other providers but

  1. Condition instances are not configured at the attribute
  2. Conditions are only configured in the aforementioned preconditions
  3. Conditions don’t get the information about the attribute they are configured for

My initial idea was to somehow configure the condition’s additional script parameters in the process XML with information about attribute ID’s, for which roles in which states the condition returns false. This does not work. Instead the condition gets information for which state Id for which work item category, which roles are relevant. This way one condition can handle all cases as we will see in the following sections.

Solution Summary

The condition, lets call it User Role for Type and State Condition is a Java based extension for the Eclipse client and the RTC server. It allows configuring roles mapped to work item type categories and states.
The condition checks if the current work items category and state has roles configured. If roles are found, and the user has any of the roles, the condition returns true, false otherwise.
This condition can be configured and be used to make work item attributes read-only or required for a work item type category in a specific state based on the roles a user has in this context.

License and Download

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!

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.

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.

In this context, please also consider to at least read through the Process Enactment Workshop for the Rational solution for Collaborative Lifecycle Management lab 4 and lab 5 to understand how attribute customization works.

Import The Code

Use the Eclipse importer to import existing projects into the workspace from an archive file.

Import Step 1

Browse for the archive.

Import Step 2

Select all projects and press finish. The code now shws up in your Eclipse workspace.

How The Code Works

The code comes in four projects.

Project Structure

com.ibm.js.team.workitem.attribute.user.role.condition.providers: The main project for the extension. It contains all the code that is needed and defines the plugin.xml.

com.ibm.js.team.workitem.attribute.user.role.condition.providers.feature: The feature project needed to be able to deploy the code.

com.ibm.js.team.workitem.attribute.user.role.condition.providers.updatesite: The update site needed to generate the code for deployment. This project output is also used to deploy the extension in RTC Eclipse clients.

com.ibm.js.team.workitem.attribute.user.role.condition.providers.serverdeploy: A special project to help deploying the code on RTC servers.

The structure of the main project looks like this.

Condition Code StructureThe source code is provided in four classes. The majority of the code is implemented by the class AbstractUserRoleTypeAndStateConditionProvider. It implements all of the behavior needed.

AbstractUserRoleTypeAndStateConditionProviderThe only piece missing in the code is the part that provides the process areas (project and team areas) used to look for the roles the user has. It is possible to use different approaches to configure this in RTC and based on how this is configured in RTC there are different possible approaches you want to use to get that information. So this is left abstract to be implemented in an extending class.

Possible strategies are:

  1. Look for the roles a user has in the team area that owns the work item
  2. Look for the roles a user has in the project area
  3. Look for all the roles a user has across the hierarchy of the area that owns the work item up to the project area

There might be other strategies, dependent on the context this is used in. It is possible to extend the abstract class and to provide the process areas to look into.

The class

  • ProcessAreaHierarchyUserRoleTypeAndStateConditionProvider implements strategy 3
  • ProcessAreaUserRoleTypeAndStateConditionProvider implements strategy 1
  • ProjectAreaUserRoleTypeAndStateConditionProvider implements strategy 2

Lets have a quick look at the code provided in the abstract class.it implements the method matches required by the Interface ICondition that is needed to be implemented for a condition.

/* (non-Javadoc)
 * @see com.ibm.team.workitem.common.internal.attributeValueProviders.ICondition#matches(com.ibm.team.workitem.common.model.IWorkItem, com.ibm.team.workitem.common.IWorkItemCommon, com.ibm.team.workitem.common.internal.attributeValueProviders.IConfiguration, org.eclipse.core.runtime.IProgressMonitor)
 */
@Override
public boolean matches(IWorkItem workItem, IWorkItemCommon workItemCommon,
		IConfiguration configuration, IProgressMonitor monitor)
		throws TeamRepositoryException {

	// Get the work item type category of the work item to determine if this
	// condition is configured for it
	IWorkItemType wiType = workItemCommon.findWorkItemType(
			workItem.getProjectArea(), workItem.getWorkItemType(), monitor);
	String typeCategory = wiType.getCategory();

	// Get the workflow action
	String actionId = configuration.getProviderContext()
			.getWorkflowAction();
	// Get the work item state. 
	// The state could be the current one, or it could be determined 
	// by the workflow action that is currently selected
	String wiState = findTargetStateId(workItem, actionId, workItemCommon,
			monitor);
	// Find the roles that are configured for the work item type (by type
	// category) for the work item state
	Set roles = getRoleConfiguration(typeCategory, wiState,
			configuration);
	if (null == roles) {
		// No Roles found, we can exit.
		return false;
	}

	// Get the roles the contributor has in this context
	Collection contributorRoles = getContributorRoles(workItem,
			workItemCommon, monitor);
	// Return true, if the contributor has any of the configured roles
	return hasMatchingRole(roles, contributorRoles);
}

This method first looks up the work item type and from that the work item type category. We use the type category, because all work item types of the same category have the same attributes and workflow. It would be possible to use the type directly, if needed.

Then it looks up the current workflow action from the provider context. As described in the AttributeCustomization wiki entry, conditions get the currently selected workflow action. The condition needs this to determine if a state change is about to happen and to get the roles configured for that state and not the current one. The state that is relevant for this operation is looked up.

The method then uses the work item type category, the relevant state and the configuration to determine the roles that are configured for this context. If there are no roles valid for this context the condition can end and return false.

If there are roles configured for this situation, the method gets the roles of the user trying to perform the operation.

The final check is, if the current user has any of the roles configured for this context.

Lets look at how finding the state of the work item works in findTargetStateId(). The code can be found in the SDK in the context of the preconditions.

/**
 * Find the target state of the work item for the condition. 
 * The target state is the current state if there is no workflow action selected
 * If there is a workflow action selected, the target state is the state
 * the action results in.
 * 
 * @param workItem
 * @param actionId
 * @param workItemCommon
 * @param monitor
 * @return the state ID (or null if there is no identifiable state) 
 * @throws TeamRepositoryException
 */
private static String findTargetStateId(IWorkItem workItem,
		String actionId, IWorkItemCommon workItemCommon,
		IProgressMonitor monitor) throws TeamRepositoryException {
	Identifier state = workItem.getState2();
	IWorkflowInfo wfInfo = workItemCommon.findWorkflowInfo(workItem,
			monitor);
	if (state == null && actionId == null && wfInfo != null) {
		actionId = wfInfo.getStartActionId() == null ? null : wfInfo
				.getStartActionId().getStringIdentifier();
	}
	if (wfInfo != null && state != null) {
		if (!Arrays.asList(wfInfo.getAllStateIds()).contains(state)) {
			actionId = wfInfo.getStartActionId() == null ? null : wfInfo
					.getStartActionId().getStringIdentifier();
		}
	}
	if (actionId != null && wfInfo != null) {
		state = wfInfo.getActionResultState(Identifier.create(
				IWorkflowAction.class, actionId));
		if (state == null) {
			actionId = wfInfo.getStartActionId() == null ? null : wfInfo
					.getStartActionId().getStringIdentifier();
			if (actionId != null) {
				state = wfInfo.getActionResultState(Identifier.create(
						IWorkflowAction.class, actionId));
			}
		}
	}
	// This is code that addresses a change in the process,
	// where the state of a work item can have only a number
	// Make sure the number is modified to reflect the state ID
	if (state != null) {
		String stateId = state.getStringIdentifier();
		try {
			Integer.parseInt(stateId);
			stateId = "s" + stateId;//$NON-NLS-1$
		} catch (NumberFormatException e) {
		}
		return stateId;
	}
	return null;
}

The code gets the work item state and the workflow information first. If there is no state and no action, then the work item is new and the action is the start action.

Then it looks at the case where there is a state and a work flow action, if it can’t find the current state in the workflow, there was a type change and the action is the start action (or none).

With the action identified, it calculates the target state. If there is none, the current state remains.

Finally there is a handling of the state ID’s. In some cases, for historical reasons, only a number is returned and not a sate id, The last bit of the code makes a proper state ID from the number, if needed.

The correct target state of the work item is returned at the end.

Another interesting part is to get the configuration for the roles from the process configuration done in getRoleConfiguration().

/**
 * Get the roles that are configured for the work item category and current target state
 * 
 * @param typeCategory
 * @param wiState
 * @param configuration
 * @return returns a set of roles that are configured for the 
 * work item category and state, or null, if there is not matching configuration 
 */
private Set getRoleConfiguration(String typeCategory,
		String wiState, IConfiguration configuration) {
	List workflowConfigurations = configuration
			.getChildren(CONFIGURATION_ELEMENT_WORKFLOW_PROPERTIES);
	if (null != workflowConfigurations) { // We got a configuration
		// For all configuration elements 
		for (IConfiguration workflowConfiguration : workflowConfigurations) {
			// Get the workitem state for this configuration element
			String foundStateID = workflowConfiguration
					.getString(CONFIGURATION_WORKFLOW_PROPERTY_ATTRIBUTE_STATE_ID);
			// Get the work item category for this configuration element
			String foundWorkflowCategory = workflowConfiguration
					.getString(CONFIGURATION_WORKFLOW_PROPERTY_ATTRIBUTE_WORK_ITEM_TYPE_CATEGORY);

			// If the configuration element applies to the current work item
			// state and category, get the roles that are configured
			if (foundStateID != null && foundWorkflowCategory != null
					&& foundWorkflowCategory.equals(typeCategory)
					&& foundStateID.equals(wiState)) {
				return getRoles(workflowConfiguration);
			}
		}
	}
	return null;
}

The conditions can be configured in the process configuration source as described here. The code reads the configuration data in the process.xml. It tries to find a configuration for the work item category and the state in the configuration. If one is found, it gets all roles specified and returns them.

The method getRoles() looks as below:

/**
 * Get the roles configured for this configuration element
 * 
 * @param workflowConfiguration
 * @return the roles found
 */
private Set getRoles(IConfiguration workflowConfiguration) {
	Set roles = new HashSet();
	List roleConfigurations = workflowConfiguration
			.getChildren(CONFIGURATION_ELEMENT_ROLE);
	for (IConfiguration roleConfiguration : roleConfigurations) {
		roles.add(roleConfiguration.getString(CONFIGURATION_ATTRIBUTE_ROLE_ID));
	}
	return roles;
}

It basically also reads the next level in the process configuration XML to get the configured role ID’s.

When designing the condition the following structure for the configuration was chosen.

Configuration SyntaxBasically provide the workflowProperties for the workitem type category and the state. Underneath provide the ID’s for the roles the condition should trigger.

As an example of the configuration in the process XML:

ConfigurationThe last step is to get the roles of the user that tries to perform the operation. This is done in getContributorRoles().

/**
 * Get the roles for a contributor
 * 
 * @param processArea
 * @param user
 * @param workItemCommon
 * @param monitor
 * @return
 * @throws TeamRepositoryException
 */
private Collection getContributorRoles(IWorkItem workItem,
		IWorkItemCommon workItemCommon, IProgressMonitor monitor)
		throws TeamRepositoryException {
	Collection roles = new HashSet();
	// Get Current User - we will check for the roles this user has
	IContributorHandle user = workItemCommon.getAuditableCommon().getUser();

	// Get the relevant process area(s) to look for the role
	Collection processAreas = getProcessAreas(workItem,
			workItemCommon, monitor);
		
	// Iterate the relevant process areas
	for (IProcessAreaHandle processAreaHandle : processAreas) {
		// Resolve the process area
		IAuditableCommonProcess auditableCommonProcess = workItemCommon
				.getAuditableCommon()
				.getProcess(processAreaHandle, monitor);
		IProcessArea processArea = (IProcessArea) workItemCommon
				.getAuditableCommon().resolveAuditable(processAreaHandle,
						ItemProfile.PROCESS_AREA_DEFAULT, monitor);
		// get the roles and add them to the list of roles the contributor has
		roles.addAll(auditableCommonProcess.getContributorRoles(user,
				processArea, monitor));
	}
	return roles;
}

The method gets the current user. Then it calls the abstract method getProcessAreas() to get the process areas to look for roles. It then iterates the process areas retrieved, gets the process and the roles of the user.

The method hasMatchingRole() just iterates the roles found and returns true, if the user has a role that is configured in the configuration for the given work item category and the state the work item has in this context.

/**
 * Check if the contributor has one of the configured roles.
 * 
 * @param roles
 * @param contributorRoles
 * @return true if the contributor has a role that is found in the configuration
 */
private boolean hasMatchingRole(Set roles,
		Collection contributorRoles) {
	for (IRole aRole : contributorRoles) {
		if (roles.contains(aRole.getId())) {
			// The user has a role that was relevant for this configuration
			return true;
		}
	}
	return false;
}

The classes that implement the AbstractUserRoleTypeAndStateConditionProvider basically have to implement which process areas to look at and to return them in getProcessAreas().

The version below is the most complex one, that iterates the whole hierarchy implemented in the class ProcessAreaHierarchyUserRoleTypeAndStateConditionProvider.

/***
 * 
 * Get the list of process areas to look up the roles for the contributor
 * Start with the process area a work item is filed against
 * and iterate the process area hierarchy up to the project area.
 * 
 * All found process areas are added to the search list.
 * 
 */
/*(non-Javadoc)
 * @see com.ibm.js.team.workitem.attribute.roletypestate.condition.providers.AbstractUserRoleTypeAndStateConditionProvider#getProcessAreas(com.ibm.team.workitem.common.model.IWorkItem, com.ibm.team.workitem.common.IWorkItemCommon, org.eclipse.core.runtime.IProgressMonitor)
 */
@Override
Collection getProcessAreas(IWorkItem workItem,
		IWorkItemCommon workItemCommon, IProgressMonitor monitor)
		throws TeamRepositoryException {
	// Get the project area
        HashSet processAreas = new HashSet();
        // Resolve with full data to get the hierarchy
	IProjectArea projectArea = (IProjectArea) workItemCommon
			.getAuditableCommon().resolveAuditable(workItem.getProjectArea(),
					ItemProfile.PROJECT_AREA_FULL, monitor);
		
	// Get the hierarchy to be able to find the process area parents
	ITeamAreaHierarchy hierarchy = projectArea.getTeamAreaHierarchy();

	// Start with the process area the work item is filed against
	IProcessAreaHandle processAreaHandle = workItemCommon.findProcessArea(workItem, monitor);
	do{
		// If this is a team area, add it and look for the parent area
		if (processAreaHandle instanceof ITeamAreaHandle) {
			processAreas.add(processAreaHandle);
			try {
				// Try to get the parent process area
				processAreaHandle = hierarchy.getParent((ITeamAreaHandle)processAreaHandle);
			} catch (TeamAreaHierarchyException e) {
				// this should not happen, if it does, stop the loop
				return processAreas;
			}
		} else if (processAreaHandle instanceof IProjectAreaHandle) {
			// If the area is the project area, we are done
			processAreas.add(processAreaHandle);
			return processAreas;
		}		
	} while (processAreaHandle!=null);	
	return processAreas;
}

It basically gets the project area of the work item and looks up the ITeamAreaHierarchy for it. Then it gets the process area that owns the work item. If that is a team area and not the project area, it adds the team area to the list and then tries the same with its patent process area. If the process area is a project area, it is added to the list and the method is done.

 The Plugin.XML

The plugin.XML basically defines the value providers that are available as well as the component for them.

Plugin.XMLThe condition providers are configured as shown below.

Condition Provider ConfigurationDeploying the Extension

Before deploying, the code has to be built. This is done in the project com.ibm.js.team.workitem.attribute.user.role.condition.providers.updatesite

Make sure the update site project is empty like below

Empty Update Site ProjectDelete any other files and folders visible besides the .project file and the site.xml, e.g. Jar-files and folders like plugins and features.

Open the site.xml and press Build All in the editor.

Build All Update SiteThe update site project now has new files and folders.

Update Site Ready to Deploy to ClientThese files will be used to deploy the extension to the server and later to deploy the extension on the RTC Eclipse client.

Deploy on the RTC CCM Server

There is a special project that was artificially created to help with deploying on the server. The project com.ibm.js.team.workitem.attribute.user.role.condition.providers.serverdeploy contains a folder structure that resembles the structure in the configuration folder of the server. The folder provision_profiles/ contains the provisioning file js_user_role_condition_provider.ini that contains the information needed to deploy the extension on the server. It also contains the reference to the folder site/js_user_role_condition_provider which is reflected in the project structure as well. This folder needs to contain the built features and plugins. By setting this up this way, it is relatively easy to successfully deploy the extension.

Server Deploy Project

After building, copy the folders features, plugins and the file site.xml from the project com.ibm.js.team.workitem.attribute.user.role.condition.providers.updatesite into the folder sites/js_user_role_condition_provider in the project com.ibm.js.team.workitem.attribute.user.role.condition.providers.serverdeploy as displayed below.

Prepare Server Update Site

Open the conf/ccm folder for your deployed server (Try this on a test server first). Open the folder \JazzTeamServer\server\conf\ccm like shown below.

Server Configuration Folder

In the project com.ibm.js.team.workitem.attribute.user.role.condition.providers.serverdeploy, select the folders provision_profiles and sites. Then select Copy.

Copy Server Extension

In the folder \JazzTeamServer\server\conf\ccm paste the folders and files you just copied. Acknowledge overwriting folders (and files if the extension has been deployed).

Request a server reset and restart the server. See Is The Extension Deployed? How Can I Redeploy? for details.

Check if the server Extension is deployed as described in Is The Extension Deployed? How Can I Redeploy? Search for the component com.ibm.js.team.workitem.attribute.user.role.condition.providers.component.

Chack Component DeployedThe condition provider is now deployed on your CCM server.

Deploy on the RTC Eclipse Client

The condition needs to be installed on an Eclipse client to be configured. It also needs to be installed on all Eclipse Clients that are used by users that have to use this condition.

It is possible to install this in an Eclipse client (installed from a zip file) and ship the Eclipse client with the extension installed by zipping it up again and providing the zip file.

For users that use the Web UI, the Condition works as soon as it is set up in the process.

To install the extension on an Eclipse client start Eclipse and select the menu Help>Install New Software…

Install Client Extension Step 1

In the install wizard select add.

Install Client Extension Step 2Then select Local and browse to the folder with your project com.ibm.js.team.workitem.attribute.user.role.condition.providers.updatesite

Select the User Role Condition Providers Feature, you might have to deselect the check boxes like below to see it.

Install Client Extension Step 3Press next and follow the wizard to install the extension. Restart the client.

Configure the Project

Open an Eclipse client that has the condition installed to configure it. Open the project area to configure its process.

Create Conditions for the Attributes

Open the Process Configuration. Select Process Configuration>Project Configuration>Configuration Data>Work Items>Attribute Customization.
For each attribute that needs to be read only or required, use the Add button to add a new condition.

Create New Conditions

Provide a name for the condition. The best approach is to name the condition in a naming schema that contains the usage of the condition and the attribute name. As an example name it Read_Only_AttributeName or Required_AttributeName. the reason will become apparent later. It basically helps finding it later to configure it in the operational behavior and the process XML.

You should now see the providers that you deployed in addition to Java Script. Select the provider that works best for you.

Select the ProviderPlease note, if you have groups of attributes that behave the same for all role configurations, you can create one condition for this group, instead of creating it for only one attribute.

After you created your configurations the Attribute Customization section should look as below.

Created Conditions

Configure the Operational Behavior

After creating the conditions needed, the next step is to activate the conditions for the attributes in the operational behavior.

Select Process Configuration>Team Configuration>Operation Behavior.

Select the “Everyone” role and add the preconditions Required Attributes For Condition and Read Only Attributes For Condition.

Precondition Configuration

Please note: if you want to configure this for another role, you have to configure the conditions for that role as well. How to do this efficiently is described below.

For each condition you created add a configuration for the related precondition. Select the condition and the attribute that the condition governs.

Configure Attribute In PreconditionIf you have groups of attributes that behave the same across all workflows and roles, you can use one condition for that group and select all affected attributes here.

The image below shows an example configuration.

Configured Example
Configure the Conditions in the Process Configuration Source

The Conditions don’t have any configuration for workflow states, work item categories and related roles yet. This needs to be done last. To configure the conditions it is necessary to add information to the process configuration source.
Locate the conditions in the process configuration source e.g. by searching for the name of a condition. The conditions are all in one block.

Configure Conditions In Process Configuration Source

For each condition, remove the closing /at the end of the condition element and add a new ending tag .

Configure Condition Step 1

The data should now look like the image below and there should be no errors. If there are errors, correct your XML.

Configure Condition Step 2

Now add the type, state and role configuration for each condition.

Configure Condition Step 3Configure Condition Step 4

You can configure for each condition for which work item type category (work item types with the same workflow), for which state, which roles should match the condition.

Save your work!

Test your work

To test, create a work item and move it through the workflow using user ID’s with role configurations that match your expectation. Make sure the user has roles that match the condition configuration.

Test ConditionIn this case the Attribute Filed Against is required due to the roles of the user and the description is read only in the state. Please note, that the work item is not yet initialized, but the target state is new. The section below explains the format and how to retrieve the data.

Retrieving the Configuration Data

To configure the condition, it is necessary to get the data to do the configuration. This section describes how to get the data.
The State ID’s and the Work Item Type categories as well as the Role ID’s can be retrieved from the process template.

The work item type category can be found in the web UI as well as in the Eclipse UI.
Find Work Item CategoryThe work item type category here is: “com.ibm.team.workitem.workItemType“.

To find the workflow state names use the Eclipse client and search the process configuration source  for the workflow name e.g. Defect Workflow in this case. Scroll down to find the state elements.

Find Workflow State NamesLook up the state ID’s for the states and document them. This is a one-time action and only needs to be maintained if you change workflows and add states.

The state New has the identifier “s1”, the state In Progress has id=”S2” etc.

To find the role Identifiers, open the project area or team area and select the roles.

Find Role Identifiers Step 1

The role test1 has the identifier “test1” the role Product Owner has the identifier “Product Owner”.

Find Role Identifiers Step 2The role “Everyone” has the identifier “default”.

The configuration below configures the condition to return true for

  • a work item of this type category (a defect) in the New state “s1” for the role “test1”.
  • a work item of this type category (a defect) in the Resolved state “s3” for the role “test1”.
  • a work item of this type category (a defect) in the In Progress state “s3” for the roles “test1”, “test2” and Everyone.

Configuration ExampleSee another more complex example below.

Configuration Example 2

Configuring the Operational Behavior for Multiple Roles

Operational behavior still works as explained in Process behavior lookup in Rational Team Concert 2.0. If it is necessary to configure the operational behavior for multiple roles but to make sure the Required and Read Only Attributes work the same as in the configuration for the Everyone role, this can be easily achieved.

Configure the other role(s).

Multi Role Behavior 1

When configuring the operational behavior name the precondition with the role included. For example Required Attributes For Condition Everyone.

Configure the operational behavior for the new role including the Role Name but don’t configure anything. For example name it Required Attributes For Condition Role1.

Search for the configuration for the configured role in the process configuration source.

Multi Role Behavior 2Copy the configuration details into a file. The interesting parts are the sections and <readOnlyAttributes….> .

Now search for the configuration for the new role the same way. Copy the XML with the configuration for the Required Attributes into the operational behavior configuration.   Copy the XML with the configuration for the Read-Only Attributes into the operational behavior configuration.

Save your work!

Since the conditions are configured globally you have essentially cloned the configuration for the other role.

Summary

Using a custom condition and the out of the box operational behavior for Required Attributes For Condition and Read-Only Attributes For Condition, allows to achieve the required behavior.

Keep in mind this is by no means production code. You might want to do more testing.

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

A RTC WorkItem Command Line Version 2.2

Creating links is not easy. Many things can go wrong.  Testing by a user showed that there was an issue with links between work items and build results. I found that I got the link direction wrong. I fixed that. Here is the updated source code.

Latest Version

See A RTC WorkItem Command Line Version 3.0 for the latest version.

Related posts

License

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.

On the other hand, you have the code and are able to add your own code to it. It would be nice to know what you did and how, if you do so.

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

You can download the latest version here:

Setup and Usage

Follow the description in A RTC WorkItem Command Line Version 2 and in A RTC WorkItem Command Line Version 2.1. Check the README.txt which is included in the downloads.

The WorkItem Command Line Explained

This post explains how the WorkItem Command Line works. It explains its structure and the main classes. This should allow users to extend the capabilities the code and add new commands or extend the current commands.

Please note, as this is work in progress, things might change slightly in future versions, however the general structure should persist.

Latest Version

See A RTC WorkItem Command Line Version 3.0 for the latest version.

Related posts

License

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.

On the other hand, you have the code and are able to add your own code to it. It would be nice to know what you did and how, if you do so.

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.

Importing The Project

Download the code from the post A RTC WorkItem Command Line Version 2. The file with the source code is named WorkItemCommandLine_Project-Vx-YYYYMMDD.zip. The x represents the version number and is followed by the date it was created. The file is an exported Eclipse project.

The project expects the Eclipse workspace to be set up as described in the posts Setting up Rational Team Concert for API Development. It requires the SDK to be set up as well as the Plain Java Client Libraries. The SDK is needed, because the project is a Plugin Project. this is done to be able to use the Eclipse Plugin Development Environment (PDE) to look at the API source code. The Plain Java Client Libraries are needed to run the code a Java application.

Use the Eclipse import File>Import. In the wizard window select “Existing Projects into Workspace” in the section General. Click Next and chose the option “Select archive file”, browse to the file you downloaded and select it. Make sure you see the project com.ibm.js.team.workitem.commandline selected and press Finish to start the import.

After the import you should see the project in your workspace. You should see no errors in the project. If you see errors, the most likely reasons for that are:

  1. The SDK is not set up correctly and the classes can not be resolved
  2. The SDK version is prior to RTC 4.0.1
  3. The Plain Java Client Libraries are not installed or the User Library has a different name

The first two will show in the plugin.xml and the manifest file. Setup the SDK correctly, or change the minimal versions needed in the dependencies.

The third will show as an error in the the build path. Define a user library named PlainJavaApi as explained in  Setting up Rational Team Concert for API Development or remove the existing user library entry and add you own. Make sure the dependency order of SDK and user library are correct as explained in  Understanding and Using the RTC Java Client API.

In case you have other errors you should search the internet for a solution.

Explore the Project

You can now explore the project. The folder structure is shown below.

WCL Project Overview

There are the following files and folders

  • src – contains the source code files.
  • build – contains a jardesc file to build a jar file for packaging
  • Launches – contains launch files used for testing
  • License – contains the license files
  • scripts – contains the script files used to start WCL, as well as a file with help information
  • the root contains a readme file, explaining how to build a releasable version of WCL, scripts used to start WCL in the development setup and a test file for upload attachment tests.

The Source Files

The image below shows the structure of the source code.

Source StructureThe package com.ibm.js.team.workitem.commandline contains the class WirkitemCommandLine, which has the main method to call WCL. The class OperationResult is used to pass result information. This is necessary, since the code could run in RMI mode and the output needs to be transferred to the RMI client. This class needs to support serializing in order to pass the result back. IWorkItemCommandLineConstants contains various constants used by WCL.

The package com.ibm.js.team.workitem.commandline.commands contains the classes that implement the currently available commands. CreateWorkItemCommand creates a work item of a specific type in a specific project area and sets the attributes as provided. PrintTypeAttributesCommand prints the attributes of a specific work item type in a specific project area. UpdateWorkItemCommand finds a work item and updates its attribute values.

The package com.ibm.js.team.workitem.commandline.framework contains a basic framework that is used by commands that are implemented in WCL. The main class requires the interface IWorkItemCommand to run the command. I ended up using this kind of framework, because all commands required some kind of parameters. The command should be able to define the parameters needed. The commands also require to do error handling. To interact with the RTC repository commands also need to login. The framework handles all the common activities and allows to create new commands without having to redevelop all this.

The class AbstractCommand implements the interface IWorkItemCommand and leaves some methods abstract that extending classes need to implement.

The class AbstractTeamRepositoryCommand adds a login to the team repository and the class AbstractWorkItemModificationCommand adds a WorkItemOperation to perform the changes to the work item. WorkItemCommandLineException is the exception class that is used to wrap other exceptions and thrown in case of unrecoverable errors.

The package com.ibm.js.team.workitem.commandline.helper contains helper classes. The class DevelopmentLineHelper is from another blog post Handling Iterations – Automation for the “Planned For” Attribute. It allows to find development lines and iterations on a development line. WorkItemHelper implements modifying work item attribute modification. Most of the RTC API related code is in there. WorkItemTypeHelper helps with printing the attribute information for a work item type.

The package com.ibm.js.team.workitem.commandline.parameter contains classes that implement all the parameter handling needed. The class Parameter is used to describe a parameter, if it is required, if it was already consumed, if it is a switch and the like. ParameterIDMapper defines a list of aliases that can be used instead of an attribute ID. You can add your own aliases that can be used for convenience. ParameterList represents a list of parameters. The class ParameterManager manages a parameter list and provides the central access to the parameters. The class ParameterParser is used to parse the parameters passed from outside and store them in a parameter list.

The package com.ibm.js.team.workitem.commandline.remote contains the remote interface IRemoteWorkItemOperationCall that is used in RMI mode.

The package com.ibm.js.team.workitem.commandline.utils contains some utility classes (providing static methods as interfaces). The class ProcessAreaUtil allows to search process areas. The class SimpleDateFormatUtil helps with conversion of timstamps from and to a string representation.

 How The Code Works

The main method of the WorkitemCommandLine basically instantiates the class and then calls the method run(). We will look at that method later.

/**
 * The main entry point into the work item commandline
 * 
 * @param args
 *            - the arguments to be used by the commandline
 * @throws RemoteException
 */
public static void main(String[] args) {

	OperationResult result = new OperationResult();
	System.out.println("WorkItemCommandLine Version "
			+ IWorkItemCommandLineConstants.VERSIONINFO + "\n");
	WorkitemCommandLine commandline;
	try {
		commandline = new WorkitemCommandLine();
		result = commandline.run(args);
	} catch (RemoteException e) {
		result.appendResultString("RemoteException: " + e.getMessage());
		result.appendResultString(e.getStackTrace().toString());
	}
	System.out.println(result.getResultString());
	if (TeamPlatform.isStarted()) {
		TeamPlatform.shutdown();
	}
	if (!isServer()) {
		// If I am not in server mode, I need to exit and return success or
		// failure
		if (result.isSuccess()) {
			// If the operation was unsuccessful, terminate with an error
			System.exit(0);
		}
		System.exit(1);
	}
}

The operation run() will return a result if it terminates. The information in this result is used to create the exit code to terminate the call.

In case this WCL is started as RMI server, the process can not terminate with System.exit(). It needs to persist registered to the RMI registry. The static method isServer() is used to communicate this information.

The method run() parses the parameters passed. It then checks if it is supposed to run as RMI server or as RMI client. If that is the case it starts the RMI server mode or, uses RMI to call the server as client. If this is a normal run, it calls runCommands() with the parameters that have been parsed.

If started as RMI server, the method startRMIServer() is used to initialize RMI and to register the class to the registry. The method runOperation() is basically the interface that is used to run the command on the server and is called by RMI clients. The method runOperation() parses the parameters and calls runCommands() as well.

The method runCommands() really executes the command requested in the parameters.  The first steps it does is to initialize the data it needs. Then it runs addSupportedCommands() to add the commands that are available.

/**
 * Add the supported commands. If introducing a new command, add it here.
 * 
 * @param parameterManager
 */
private void addSupportedCommands(ParameterManager parameterManager) {
	addSupportedCommand(new PrintTypeAttributesCommand(
			new ParameterManager(parameterManager.getArguments())));
	addSupportedCommand(new CreateWorkItemCommand(new ParameterManager(
			parameterManager.getArguments())));
	addSupportedCommand(new UpdateWorkItemCommand(new ParameterManager(
			parameterManager.getArguments())));
}

Now the method runCommands() gets the command from the Parameter Manager. If there is a command string, it gets the class that implements the command. If there is a command registered for this command string, runCommand() calls the command to validate if the required parameters for it to run are available. If this is the case, runCommand() calls the command and returns the result back.

In all other cases runCommand() prepares a result error and also uses the method helpGeneralUsage() to print a help for the command.

Adding Commands to the WorkItemCommandLine

It is easy to add new commands to the WorkitemCommandLine. You need to implement a new command and add a new entry for it in the method addSupportedCommands().

How Commands Work

Commands have to implement the IWorkItemCommand interface. You should pick one of the abstract classes in the framework and extend them. This makes sure the basic workflow will work. If you command needs to create or modify a work item based on property values, use the class AbstractWorkItemModificationCommand. If you only need to have a repository connection, use the class AbstractTeamRepositoryCommand. In both cases all you need to do really is to override and implement the methods required. There are three things that need to be there.

In the method getCommandName() you need to return the name of the command you implement.

@Override
public String getCommandName() {
	return IWorkItemCommandLineConstants.COMMAND_CREATE;
}

If your command needs additional parameters, override the method setRequiredParameters(). Call the method of the superclass to have it add its required parameters and add your parameters. Here is an example

/*
 * (non-Javadoc)
 * 
 * @see com.ibm.js.team.workitem.commandline.framework.
 * AbstractWorkItemCommandLineCommand#setRequiredParameters()
 */
public void setRequiredParameters() {
	super.setRequiredParameters();
	// Add the parameters required to perform the operation
	// getParameterManager().syntaxCommand()
	getParameterManager()
			.syntaxAddRequiredParameter(
					IWorkItemCommandLineConstants.PARAMETER_PROJECT_AREA_NAME_PROPERTY,
					IWorkItemCommandLineConstants.PARAMETER_PROJECT_AREA_NAME_PROPERTY_EXAMPLE);
	getParameterManager()
			.syntaxAddRequiredParameter(
					IWorkItemCommandLineConstants.PARAMETER_WORKITEM_TYPE_PROPERTY,
					IWorkItemCommandLineConstants.PARAMETER_WORKITEM_TYPE_PROPERTY_EXAMPLE);
	getParameterManager().syntaxAddSwitch(
			IWorkItemCommandLineConstants.SWITCH_IGNOREERRORS);
	getParameterManager().syntaxAddSwitch(
			IWorkItemCommandLineConstants.SWITCH_ENABLE_DELETE_ATTACHMENTS);
	getParameterManager().syntaxAddSwitch(
			IWorkItemCommandLineConstants.SWITCH_ENABLE_DELETE_APPROVALS);
}

Parameters added with syntaxAddRequiredParameter() will be assumed to be required. If they are not available the command line will show an error during the parameter validation. The error message is automatically created from the parameter information provided here.

Finally you have to override and implement the method process() to implement the command.

/*
 * (non-Javadoc)
 * 
 * @see com.ibm.js.team.workitem.commandline.framework.
 * AbstractWorkItemCommandLineCommand#process()
 */
@Override
public OperationResult process() throws TeamRepositoryException {
	// Get the parameters such as project area name and Attribute Type and
	// run the operation
	String projectAreaName = getParameterManager()
			.consumeParameter(
					IWorkItemCommandLineConstants.PARAMETER_PROJECT_AREA_NAME_PROPERTY)
			.trim();
	// Find the project area
	IProjectArea projectArea = ProcessAreaUtil.findProjectArea(
			projectAreaName, getProcessClientService(), getMonitor());
	if (projectArea == null) {
		throw new WorkItemCommandLineException("Project Area not found: "
				+ projectAreaName);
	}

	String workItemTypeID = getParameterManager().consumeParameter(
			IWorkItemCommandLineConstants.PARAMETER_WORKITEM_TYPE_PROPERTY)
			.trim();
	// Find the work item type
	IWorkItemType workItemType = WorkItemHelper.findWorkItemType(
			workItemTypeID, projectArea.getProjectArea(),
			getWorkItemCommon(), getMonitor());
	// Create the work item
	createWorkItem(workItemType);
	return this.getResult();
}

To complete the code of this command, here is the method that creates the work item and uses the parameters to update the attributes.

/**
 * Create the work item and set the required attribute values.
 * 
 * @param workItemType
 * @return
 * @throws TeamRepositoryException
 */
private boolean createWorkItem(IWorkItemType workItemType)
		throws TeamRepositoryException {

	ModifyWorkItem operation = new ModifyWorkItem("Creating Work Item");
	this.setIgnoreErrors(getParameterManager().hasSwitch(
			IWorkItemCommandLineConstants.SWITCH_IGNOREERRORS));
	IWorkItemHandle handle;
	try {
		handle = operation.run(workItemType, getMonitor());
	} catch (TeamOperationCanceledException e) {
		throw new WorkItemCommandLineException("Work item not created. "
				+ e.getMessage(), e);
	}
	if (handle == null) {
		throw new WorkItemCommandLineException(
				"Work item not created, cause unknown.");
	} else {
		IWorkItem workItem = getAuditableCommon().resolveAuditable(handle,
				IWorkItem.SMALL_PROFILE, getMonitor());
		this.appendResultString("Created work item " + workItem.getId()
				+ ".");
		this.setSuccess();
	}
	return true;
}

In case you wonder where the actual work gets done – I wondered looking at it. The line

handle = operation.run(workItemType, getMonitor());

does all the work. By calling it this way, the WorkItemOperation creates the work item. The operation is based upon the code in the class AbstractWorkItemModificationCommand.

In that class, the execute() method is overwritten with this code:

/*
 * This is run by the framework
 * 
 * (non-Javadoc)
 * 
 * @see
 * com.ibm.team.workitem.client.WorkItemOperation#execute(com.ibm.team
 * .workitem.client.WorkItemWorkingCopy,
 * org.eclipse.core.runtime.IProgressMonitor)
 */
@Override
protected void execute(WorkItemWorkingCopy workingCopy,
		IProgressMonitor monitor) throws TeamRepositoryException,
		RuntimeException {
	// run the special method in the execute.
	// This is called by the framework.
	update(workingCopy);
}

The call to the method update() does the real work. It walks through all the unconsumed parameters in the parameter list – which should contain the attributes and values to be set and applies the changes to the work item.

/**
 * This operation does the main task of updating the work item
 * 
 * @param workingCopy
 *            the workingcopy of the workitem to be updated.
 * 
 * @throws RuntimeException
 * @throws TeamRepositoryException
 */
public void update(WorkItemWorkingCopy workingCopy)
		throws RuntimeException, TeamRepositoryException {

	ParameterList arguments = getParameterManager().getArguments();

	// We use a WorkItemHelper to do the real work
	WorkItemHelper workItemHelper = new WorkItemHelper(workingCopy,
			arguments, getMonitor());

	// Run through all properties not yet consumed and try to set the values
	// as provide
	for (Parameter parameter : arguments) {
		if (!(parameter.isConsumed() || parameter.isSwitch() || parameter
				.isCommand())) {
			// Get the property ID
			String propertyName = parameter.getName();
			// Get the property value
			String propertyValue = parameter.getValue();
			try {
				workItemHelper.updateProperty(propertyName, propertyValue);
			} catch (WorkItemCommandLineException e) {
				if (this.isIgnoreErrors()) {
					this.appendResultString("Exception! " + e.getMessage());
					this.appendResultString("Ignored....... ");
				} else {
					throw e;
				}
			} catch (RuntimeException e) {
				this.appendResultString("Runtime Exception: Property "
						+ propertyName + " Value " + propertyValue + " \n"
						+ e.getMessage());
				throw e;
			} catch (IOException e) {
				this.appendResultString("IO Exception: Property "
						+ propertyName + " Value " + propertyValue + " \n"
						+ e.getMessage());
				throw new RuntimeException(e.getMessage(), e);
			}
		}
	}
}

The Class WorkItemHelper

This class is basically doing all the work related to modifying work item data. The helper needs to be instantiated. Then the method updateProperty() can be called.

public void updateProperty(String propertyID, String value)
		throws TeamRepositoryException, WorkItemCommandLineException,
		IOException {
.
.
.
}

The method checks if the attribute is one of the special ones like the type, or complex attributes such as workflow or state changes, approvals or other pseudo attribute ID’s and handles these if detected. Otherwise it calls the method updateGeneralAttribute() to handle the update.

private void updateGeneralAttribute(ParameterValue parameter,
		List exceptions) throws TeamRepositoryException,
		WorkItemCommandLineException {
.
.
.
}

The method updateGeneralAttribute() checks if this attribute is actually available on the work item. If so it calls getRepresentation() to get a value that can be set for the attribute.

private Object getRepresentation(ParameterValue parameter,
		List exceptions) throws TeamRepositoryException,
		WorkItemCommandLineException {
.
.
.
}

The method getRepresentation() basically is a huge list of checks to narrow down what type the attribute to modify is. If the type is narrowed down, it calls a related methods to parse the input data and to create a value for the attribute, that can be returned and set.

Summary

This post explains how the code works and how you can utilize it to implement your own commands. As always, I hope that helps someone out there.

While creating this post, I realized, that I should have named some of the classes differently. This framework is not only good for a work item command line. This code could be used for any command line. Maybe I will adjust this a bit in later versions, should time permit.

A RTC WorkItem Command Line Version 2

After publishing the first version of the RTC WorkItem Command Line  I realized several things that users might want to do that were not supported. So I updated the version to 2.0 and added these capabilities. I also found an issue with setting string lists, which is also now fixed.

This post, like the previous, provides a simple Work Item Command Line Client and explains the usage. It comes with code, so you can also enhance it if you need more features.

Latest Version

See A RTC WorkItem Command Line Version 3.0 for the latest version.

What’s new?

This version of the RTC WorkItem Command Line is complete as far as I am concerned.

  • It still supports work item creation and update.
  • It now supports all attribute types and the link types that I think can be supported. The missing Item types are now supported.
  • It now supports several modes to modify the work item properties such as set the value, add to the available value or remove data.
  • It supports a RMI server mode to enhance performance if multiple calls are needed
  • It supports the RMI client mode to delegate requests to the RMI server portion.

Related posts

WorkItemCommandLine Summary

The WorkItemCommandLine – in short WCL – works on Windows and Unix clients. It requires a JDK and the Plain Java Client Libraries to be installed. Please note, I had issues running the Plain Java Client Libraries with just a JRE. You can try to use a JRE, if you like.

It currently allows to

  • Create work items
  • Update work items
  • Show the attributes ID’s available for a work item type in a project area

The WCL allows to set and update all available attribute types (I am aware of) in RTC 4.x up to 5.0.2.

  • String based attributes
  • Number based attributes
  • Enumeration and Enumeration List based attributes
  • Tag based attributes
  • Typed and untyped Item type attributes
  • Typed and untyped ItemList type attributes
  • ……

This works for built in attributes as well as for custom attributes.

In addition to these attribute types various not attribute based work item values can be modified:

  • Subscription
  • Comments
  • Approvals
  • Links to work items
  • CLM links to work items
  • CLM links to Requirements Management and Test artifacts and to SCM change sets
  • Links from build results
  • Attachments can be uploaded
  • Trigger a workflow action

This should be sufficient for most of the automation needs, especially during builds.

The WCL supports the following operations modes on work item attributes:

  • set – set the value of the attribute, overwriting or deleting existing information
  • add – add values or data to the attribute
  • remove – delete values or data from the attribute
  • default – dependent on the type of the attribute set the value or add values

The WCL supports RMI where the WCL runs as a RMI server and WCL can delegate calls to that server to have them processed. This only requires starting the team platform once and saves several seconds in subsequent calls.

License

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.

On the other hand, you have the code and are able to add your own code to it. It would be nice to know what you did and how, if you do so.

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 following code in this environment and get your own automation or extension working.

Download

You can download the latest version here:

Setup

Download the packaged executable application. The file is compressed and will be named like wcl-Vx-YYYYMMDD.zip. The x represents the version number and is followed by the date it was created.

If you have installed an RTC API development environment following the RTC Extensions workshop and this post, you have all else that is needed and can use  the installs folder of your extension development install, for example C:\RTC401Dev\installs.

Extract the file e.g. using 7Zip to a folder, for example C:\RTCWCL (or C:\RTC401Dev\installs).

The destination folder should now contain a folder wcl.

If you don’t have an extension development environment set up, download and install the Plain Java Client Libraries for your version of RTC. Open the All Downloads tab of the RTC version you are interested in. For example https://jazz.net/downloads/rational-team-concert/releases/4.0.1?p=allDownloads and scroll down to the Plain .zip Files section.

PlainJavaDownloadDownload the Plain Java Client Libraries file.

Use 7Zip and unzip the Plain Java client Libraries download file (for example named RTC-Client-plainJavaLib-4.0.1.zip). Use 7Zips Extract Files command and provide the extraction Path C:\RTCWCL\PlainJavaAPI .

If you don’t have an extension development environment set up, download and install a Java JDK. If you have the Rational Team Concert client installed a compatible JDK is available in the install location e.g. TeamConcert\jazz\client\eclipse\jdk. The easiest way is to download the zip version of the Rational Team concert Client and extracting it to C:\RTCWCL\TeamConcert.

The folder should now look similar to this image.

Install Folder

Adjusting the Scripts to the Environment

If you downloaded a different JDK or have the RTC Eclipse client installed in a different location, follow the next steps to adjust the WCL to the different paths.

Open the folder created when extracting the WCL for example C:\RTCWCL\wcl. The folder contains the script files

  • wcl.bat – for Windows clients
  • wcl.sh – for client with Unix/*ux operating systems such as Linux
  • rmi_no.policy – a RMI policy file
  • README.txt – the online help printed in a file

Open the files relevant for your operating system. They should look similar to this:

Script

The scripts assume an install structure where the JDK and the Plain Java Client Libraries are installed like in the image before. If your setup uses different paths, adjust then according to your setup. The scripts for usage with RMI only add a statement to the rmi_no.policy file.

On Unix operating systems chmod the shell scripts so that they are executable and the RMI policy is readable.

You should now be able to run the WorkItemCommandLine.

In order to allow RMI to work, WCL requires a policy file. Modify the file rmi_no.policy to your requirements and make sure it is in the same wcl folder on the server and the client. Make sure the policy file it is readable for the user that runs WCL.

Test the Environment

Open a shell or cmd window. change the directory to where you extracted WCL for example C:\RTCWCL\wcl. Type wcl and run the WorkItemCommandLine. The command should be executed and print help content like below.

Start WCLIf this does not happen, make sure the paths are set correctly and the JDK is compatible.

Please note, because the version 2.0 supports so much more, the help is very long and the windows shell can cut it off. To avoid this, you can

  1. Redirect the output into a file for example by typing wcl>wcl_help.txt
  2. Change the console setting to increase the size and buffer using mode con: lines=1100 cols=150

The README.txt is provided as help for convenience.

The Syntax of WCL

WCL uses the following syntax:

wcl – {/} {[:]=}

Where , at this time, can be

  • create
  • update
  • printtypeattributes

The commands have their own requirements for base parameters such as repository URL, users, password and the like.

Switches

WCL provides several switches that influence the behavior. Some switches are command specific, others are general. Available values for are:

  • ignoreErrors allows to successfully perform the create and update command if minor errors happen. Errors covered are for example if an optional attribute or its value was not found. If the flag is provided, WCL will continue to perform the next operations and print the error. If the flag is not provided any error will cause the operation to fail.
  • enableDeleteAttachment enables deletion of attachments using the set or the remove mode.
  • enableDeleteApprovals enables deletion of approvals using the set or the remove mode.
  • rmiServer is used to start the RMI server, see the RMI section below.
  • rmiClient is used to run the Workitem Command Line  against a RMI server instead of processing the command itself. See the RMI section below

Parameters

Values for are usually the ID of a work item attribute. The WCL defines various pseudo attribute names, typically prefixed with an @ e.g. to create links and upload files as attachment. The reason for the prefix is that RTC does not allow to start attribute ID’s with special characters and this makes it impossible to define custom attributes with conflicting names.

The parameter sections = must not have spaces in the or in the or before or after the =. The value of , or the whole term can be enclosed in quotation marks “.

Parameter and value example:

projectArea=”JKE Banking (Change Management)”

Each parameter can only be used once in the command line. In some cases like attachment uploads a special section needs to be added in the parameter to allow for multiple specifications in one call.

Multiple parameter example, ‘_2’ is used to make the second parameter unique:

@attachFile:add="./Test.txt:Some Description:text/plain:UTF-8" @attachFile_2:add="./Test.txt:Some Description:text/plain:UTF-8"

The WCL also has an alias mechanism built in, that allows to map different external names for attributes to the internal representation. Currently the names of the AttributeCustomization described here are built in. This allows, for example, to use FOUND_IN=”Sprint 2 Development” instead of foundIn=”Sprint 2 Development”. You can add your own aliases if needed.

Values

The s are specified by a string. Parameter values are usually the display value of elements (enumerations) or composed of display values of the path to this item (category, iterations, process areas). For example setting an enumeration attribute would use the display name “High”, instead of the literal ID. This makes it easier to use. In some cases e.g. for links, subscriber lists and other user lists, it is necessary to specify the ID of the element instead of the display name.

Value examples

  • For enumeration based attributes use the display value for the enumeration literal:

    internalPriority=High

  • For HTML and string based attributes use a string. HTML types like summary, description, comment and HTML support the syntax below:

    description=”Plain text

    bold text

    italic text

    External RSJazz Link

    User link to @ralph

    Work Item link to Defect 3

  • For Wiki and multi line text attributes use

    or \n for line breaks and check the syntax in the wiki editor. The full description for the Wiki Syntax can be found here. Example for a Wiki entry:

    custom.wiki=”

    =Heading1Plain text\n==Heading 2\n\nNormal Text **bold text**

    **bold text**

    //Italics//”

  • For work item type, owner and some other attributes use the object ID:

    workItemType=task

    owner=tanuj

  • Use the display name for simple attributes or the path composed out of the display names for hierarchical attributes;

    category=JKE/BRN

    foundIn=”Sprint 2 Development”

    target=”Main Development/Release 1.0/Sprint 3″

    custom.process.area=”JKE Banking (Change Management)/Release Engineering”

  • Dates have to be specified in the Java SimpleDateFormat notation:

    dueDate=”2015/02/01 12:30:00 GMT+01:00″

  • Duration values are specified in milliseconds:

    duration=1800000 correctedEstimate=3600000 timeSpent=60000Since version 3.2 the value can also be specified in hours and minutes

    timeSpent=“3 hours 3 minutes” or timeSpent=“3 hours” or timeSpent=“3 minutes”

Lists

Work item attribute values of List with a specified item type such as userList. Use the separator  ‘,‘ like in “value1,value2,…,valueN” to separate values.

Example user list: custom.user.list:add=”deb,al,bob,tanuj”

Work item attributes with an general attribute value type such as Item or itemList require encoding to locate the items. The format is: custom.item.list=value

Where value has the form: {,}

Each having the form : with no spaces allowed in the value list.

Available values for and examples for Item and itemList attribute values:

  • ProjectArea – specified by its name.

    Example: “ProjectArea:JKE Banking (Change Management)”

  • TeamArea – specified by its name path from the project area to the team area.

    Example: “TeamArea:JKE Banking (Change Management)/Release Engineering”

  • ProcessArea – specified by the name path from the project area to the process area.

    Example: “ProcessArea:JKE Banking (Change Management)/Business Recovery Matters”

  • Category – specified by the category path.

    Example: “Category:JKE/BRM”

  • User – specified by the users user id.

    Example: “User:tanuj”

  • Iteration – specified by the iterations name path (including the development line name).

    Example: “Iteration:Main Development/Release 1.0/Sprint 3”

  • WorkItem – specified by the work items id.

    Example: “WorkItem:20”

  • SCMComponent – specified by the Jazz SCM components display name.

    Example: “SCMComponent:Build”

Modes

Modes allow different types of changes to attributes such as add values, append text or remove and set other data. The mode is specified using this syntax:

[:]=

Supported values for are default (no mode specified), add, set and remove.

If no mode is specified, the default mode for the parameter is used.

  • Example for default mode: summary=”This is a summary.”.
  • Example for add mode: summary:add=” Add this to the summary.”.
  • Example for set mode: summary:set=”Overwite the existing summary with this text.”.
  • Example for remove mode: custom.enumeration.list:remove=$,Unassigned.

Which modes are supported and their behavior depends on the attributes type.

  • Single value attributes typically support default and set mode, but not add and remove mode.
    • Default mode for single value attributes is set the value.
  • Multiple value attributes, such as lists and links, typically support default, add, set and remove mode.
    • Default mode for multiple value attributes is add, which adds the value(s).
    • Set mode for multiple value attributes removes the old values and then adds the new value(s).
    • Remove mode for multiple value attributes removes the specified values that can be found.
  • String values such as HTML, Summary, Wiki type attributes support default (same behavior as set mode), set and add mode.

The Print Type Attributes Command to get the Attribute ID’s and Types

To set work item attributes, WCL needs the ID of the attribute. You can look up the ID of an attribute in the process configuration. The command printtypeattributes prints the attribute ID’s for the built-in and for the custom attributes of a work item type in a project area. The command requires, in addition to the repository URL, the user and password, at least the project area and the work item type to look up. Syntax and required parameters:

wcl -printtypeattributes repository=RepositoryURI user=userID password=password  projectArea=ProjectAreaName workItemType=WorkItemTypeID {parameter[:mode]=value}

Example

wcl -printtypeattributes repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" workItemType=task

Please note, for the built in attributes this returns an internal ID that might not show up if you look into the process configuration. You can use the ID’s you find there too, the API should translate them correctly.

The Create Command

The command create can be used to create a new work item and set its attributes.

The command requires, in addition to the repository URL, the user and password, at least the project area and the work item type to create. Please note, if the process specifies additional required attributes, these need to be provided as well, otherwise the creation and save operation will fail. Syntax and required parameters:

wcl -create repository=RepositoryURI user=userID password=password  projectArea=ProjectAreaName workItemType=WorkItemTypeID {parameter[:mode]=value}

Here an example for creating a work item.

wcl -create /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" workItemType=task summary="New Item" category=JKE owner=ralph

The command will report back the ID of the newly created work item if the operation was successful.

The Update Command

The command update can be used to update a work items attributes.

The command requires, in addition to the repository URL, the user and password, at least the ID of the work item to update. Syntax and required parameters:

wcl -update repository=RepositoryURI user=userID password=password  id=workItemID {parameter[:mode]=value}

Please note, if the process specifies additional required attributes, these need to be provided as well, otherwise the save operation will fail. This can be relevant if the state of a work item is changed.

Here is an example where a work item gets heavily updated:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 SUMMARY="New summary" FOUND_IN="Sprint 2 Development" owner=ralph target="Main Development/Release 1.0/Sprint 3" internalSeverity=Major foundIn="Sprint 2 Development" internalPriority=High attachFile="./Test.txt:Some Description:text/plain:UTF-8" internalApprovals="approval:Please Approve:ralph,deb" internalSubscriptions=al,ralph,deb internalState="In Progress" internalTags="test1,test2" custom.duration=1800000 custom.boolean=true custom.contributor=al custom.contributor.list=al,deb,tanuj custom.decimal=1500200 custom.integer=234 custom.long=567 custom.tag=tag1,tag2 custom.timestamp="2014/12/31 12:30:00 GMT+01:00" custom.wiki="My Wiki" custom.projectarea.list="JKE Banking (Change Management)" custom.project.area="JKE Banking (Change Management)" custom.teamarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering" custom.team.area="JKE Banking (Change Management)/Release Engineering" custom.process.area="JKE Banking (Change Management)/Release Engineering" custom.processarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering" custom.workitem=3 custom.workitem.list=9,20,7

The command will report back the ID of the updated work item if the operation was successful.

Special Attributes and not  Attribute Based Work Item Modifications

Some attribute types need special treatment or require more complex values to be specified. Some have other limitations and considerations. These are explained below.

For Item List attributes the items need to be provided as a list of items with the separator “,”. As an example a work item attribute of type TeamAreaList would be set like this:

custom.teamarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering"

Please note, that this implies that the separator ‘,’ can not be part of any of the display names of the elements.

Special Properties Handling

Some special properties are protected from changing.

  • Work Item ID: can not be changed
  • Project Area: parameter “projectArea” can only be specified when creating the work item. It can not be set to a different value later.

There might be other limitations imposed by the process e.g. against changing the creator of a work item.

Comments

The parameter “internalComments” can be used to add a comment. This pseudo attribute only supports the default and add mode. Removing comments is not supported in the WCL. Comments support the HTML syntax mentioned above allowing to create web links, user and work item links. Example:

internalComments=””Plain Text

Bold Text

Italic Text

External RSJazz Link

@ralph

Defect 3

User, User Lists

For attributes that require users or user lists the value of the property needs to specify the user or the list of users with the ID. Examples:

internalSubscriptions=al,ralph,deb custom.contributor.list=al,deb,tanuj

Subscriptions

The parameter internalSubscriptions can be used to subscribe a list of users to a work item using their user ID’s. The syntax is:

internalSubscriptions[:mode]={,}

This attribute supports the modes default (same as) add, set and remove mode.

  • Example set specific users (removing all others):

    internalSubscriptions:set=al,tammy

  • Example add users:

    internalSubscriptions:add=deb,tanuj,bob

  • Example remove users:

    internalSubscriptions:remove=sally,bob

Example:

wcl -update repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalSubscriptions:add=al,ralph,deb

Tags

The parameter internalTags can be used to add a list of tags. This attribute supports the modes default (same as) add, set and remove. The syntax is:

internalTags[:] ={,}

Example to set the tag list of the work item to two tags test1 and test2, removing all other tags:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalTags:set="test1,test2" custom.tags="MyTag"

Approvals

The parameter internalApprovals can be used to add approvals and approvers. Approvals only support the modes default (same as) add, set and remove. Set and remove only affects approvals of the same approval type. For example, mode set for approval type review will only remove existing reviews. The syntax is:

internalApprovals[][:mode]=”:{: {,userIDn}}”

The name of the approval to be created is required. The Approver ID list is optional and it is possible to add one or more approver userID’s. Example without approvers

internalApprovals:add=”verification:Please Verify”

The section can be left out if only one approval is specified. If multiple approvals are specified, it needs to be a unique string. In the example below “_1” and “_2” make the parameters uniquely distinguishable:

internalApprovals_1:add="approval:Please approve:ralph,deb" internalApprovals_2:add="verification:Please verify:tanuj"

Available values for are

  • approval – to create an approval record
  • review – to create a review record
  • verification – to create a verification record

Examples:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="approval:Please approve:ralph,deb"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="review:Please review:deb"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="verification:Please verify:tanuj"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="verification:We need a verification"

The implementation of the modes set and remove is as follows. Removal of approvals by either set or remove mode must be explicitly enabled using the switch enableDeleteApprovals, otherwise the command will fail if one of these modes is used with a internalApproval parameter.

The mode set removes all existing approvals of the specified and then adds a new approval of this type as specified.

The mode remove searches for all approvals of the specified and deletes those found with a matching the Approval Name.

Workflow and State Change

The WCL allows to set the state of the work item in different ways. Please note, the state is reached after the save operation, if it can be set.

A pseudo parameter @workflowAction can be used to set a workflow action to change the work item state when saving. This attribute supports only the modes default and set.

When using this pseudo parameter WCL looks up the current state of the work item, tries to find a workflow action with the given display name. If one exists, it  sets the save operation to trigger this action when the work item gets saved.

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=115 @workflowAction="Reopen"

Another example: @workflowAction=”Stop working” . Please note, the workflow change is governed by the RTC process engine. If RTC prevents the state change, the operation will fail on save. It is impossible to detect this prior to the save.

The parameter internalState representing the attribute to read the state can be used to set the state. The parameter only implements the modes default and set, which act equal. The syntax is:

internalState=[:]StateName

Where is the value forceState.

Without the forceFlag provided WCL acts similar to using the pseudo parameter @workflowAction. It looks up the current state, and checks if any workflow action from the current state exists, that leads to the specified target state. If there is one, it sets the workflow action to be performed during the save operation. If there is no workflow action the state change is not performed.

Example with a target state:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalState="In Progress"

If the flag forceState: is added before the target state, WCL uses a deprecated API to forcefully set the state. Please note, that this does not trigger a workflow action and does also not trigger operational behavior. It should be used with caution. If the target state exists in the workflow of the work item type, the state is set, regardless if it is reachable directly or using multiple workflow actions or even if it is not reachable by the workflow at all.

Example with a target state forcefully set:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalState="forceState:New"

State Resolution

The resolution of a work item can be set using the attribute internalResulution. The parameter only implements the modes default and set, which act equal. The parameter value provided is the display value of the resolution.

Example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalResolution=Invalid

 Attachments

The WCL provides a capability to manipulate work item attachments. The pseudo parameter @attachFile can be used to upload and remove attachments. This attribute supports the modes default (same as) add, set and remove. The syntax format is:

@attachFile[]=”SomeFilePath:Some Description::”

Where:

has one of the following values:

  • text/plain
  • application/unknown
  • application/xml

has one of the following values:

  • UTF-8
  • UTF-16LE
  • UTF-16BE
  • us-ascii.

As above, must be unique for multiple attachments in one command. If only one attachment is uploaded, the can be left empty.

The file must be accessible and in the correct encoding for the operation to perform correctly.

As above for approvals the mode set is implemented to remove all attachments first and then add the new attachment. The mode remove is implemented to search for an attachment with the same file path and description and remove it if it is available.

Some examples:

-update /ignoreErrors  repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=150 @attachFile="./Test.txt:Some Description:text/plain:UTF-8" @attachFile_2:add="./Test.txt:Some Description:text/plain:UTF-8"
-update /ignoreErrors /enableDeleteAttachment repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=150 @attachFile:remove="./Test.txt:Some Description:text/plain:UTF-8"
-update /ignoreErrors /enableDeleteAttachment repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=150 @attachFile:set="./Test.txt:Some Description:text/plain:UTF-8"

Duration Types

In duration types provide the value in milliseconds. For example

custom.duration=1800000

Timestamps

Timestamps need to be provided as string in the SimpleDateFormat using the format pattern “yyyy/MM/dd hh:mm:ss z”. For example:

custom.timestamp="2014/12/31 12:30:00 GMT+01:00"

Links

The pseudo parameter @link_ can be used to link the current work item to other objects. The syntax is

@link_={|}

Where specifies the type of link to be created, for example reportAgainstBuild and the values on the right side specifies one target object or a list of target objects to be linked to the current work item using the link type. The separator used here is the pipe symbol ‘|‘. The reason is, that the links can be URI’s and the naming conventions are problematic. It is hard to find a character that is likely not to appear in that string. Not everyone sticks to the specification and the pipe symbol seemed to be appropriate.

The parameter supports the modes default (same as) add, set and remove.  Similar to other implementations above the mode set removes all links of the specified before creating the new links. The mode remove tries to find an existing link of the with the same target and removes this link, if it exists.

There are different ways the links get created, dependent on what link type and what target elements are specified.

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 @link_parent=1 @link_blocks=2|3 @link_reportAgainstBuild=P20141208-1713|@_IjluoH-oEeSHhcw_WFU6CQ|P20141208-1713
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=150 @link_tracks_workitem="https://clm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/80|4|5" @link_affected_by_defect=123 @link_affects_plan_item=20|30 @link_related_change_management=4|7
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=150 @link_related_artifact="https://rsjazz.wordpress.com/"  @link_affects_requirement="https://clm.example.com:9443/rm/resources/_6c96bedb0e9a490494273eefc6e1f7c5" @link_tested_by_test_case="https://clm.example.com:9443/qm/oslc_qm/contexts/_6u2zcH-nEeSJhuhJc8_drg/resources/com.ibm.rqm.planning.VersionedTestCase/_N6HHYX-oEeSJhuhJc8_drg"

Work Item Links – links between this work item and another work item within the same repository

The following are supported from the current work item to a target work item. These links are local to the repository this work item belongs to. This means the value list is a list of work item numbers separated by pipe ‘|’ symbols.

  • copied
  • copied_from
  • successor
  • blocks
  • resolves
  • mention
  • predecessor
  • parent
  • duplicate_of
  • duplicate
  • related
  • depends_on
  • child
  • resolved_by

Please note, that if you try to create a link that can not be supported on the target end, save errors will show up. As an example if a target is set to be the child of this work item and that work item has already some other work item set as parent, the save will fail.

Format example:

@link_related=123|80

CLM Work Item Links – CLM links between this work item and another work item within the same or across repository boundaries

The following are supported from the current work item to a target work item. These links can be local to the repository this work item belongs to, or to work items in another repository. The parameter value is a list of one or more work items specified by their ID (if they are in the same repository) or by their Item URI separated by pipe ‘|’ symbols. To understand the URI format, look at an existing link in the RTC web UI and inspect the link target. Wrong target formats can lead to corrupt data.

  • affects_plan_item
  • tracks_workitem
  • related_change_management
  • affected_by_defect

Format example:

@link_tracks_workitem="https://clm.example.com:9443/ccm/resource/itemName/com.ibm.team.workitem.WorkItem/80|120|150"

CLM URI Links – CLM links between this work item and another item, described by a valid URI, in a different application or repository.

The following are supported from the current work item to a target item such as a requirement, test case or test result. These links are across repositories and applications. The parameter value is a list of one or more items, that support this link type, specified by their Item URI separated by pipe ‘|’ symbols. To understand the URI format, look at an existing link in the RTC web UI and inspect the link target. Wrong target formats can lead to corrupt data.

  • related_test_plan
  • affects_requirement
  • tested_by_test_case
  • blocks_test_execution
  • implements_requirement
  • affects_execution_result
  • related_artifact
  • related_test_case
  • elaborated_by
  • tracks_requirement
  • scm_tracks_scm_changes
  • related_execution_record

Format example:

@link_affects_requirement=https://clm.example.com:9443/rm/resources/_848a30e315524069854f55e1d35a402d|https://clm.example.com:9443/rm/resources/_6c96bedb0e9a490494273eefc6e1f7c5

Please note that the link “Associate Work Item” between a change set and the work item can only be created by the SCM component. The link created here is the looser CLM link. Create the work item change set link using the SCM command line.

Build result Links – Links from a work item to a build result in the same repository.

The following are supported from the current work item to a build result. These links are within a repository. The parameter value is a list of one or more build results. The Build result can be specified by its Build Result Label or by the Build Result ID separated by pipe ‘|’ symbols. The parameter value is a list of one or more Buildresults specified by their ID or their label. The WCL distinguishes between build result ID and Build Label, add @ as prefix to the Build Result Label.

  • reportAgainstBuild
  • includedInBuild

Please note that the link includedBuild should only be created by the SCM system from the snapshot, it is only available for completeness.

Format example: @link_reportAgainstBuild=@_IjluoH-oEeSHhcw_WFU6CQ|P20141208-1713

RMI Modes

The WCL can be run as a local Java application. This is fine if only one work item needs to be modified. However, since each call requires the RTC API TeamPlatform to be started and the process takes around 6 seconds, this does not scale for a lot of calls, it is possible to run the work item command line in a RMI server mode on the same or a different machine. This server waits for incoming requests and only needs to initialize the API once.

Called from another RMI client process, the server can process requests very fast and return the result. This can be achieved with two switches to set up the server and to delegate the call to the server.

The syntax for the switches is

/[=]

Where is one of the following values.

  • rmiServer
  • rmiClient

By default the RMI Name used to connect to the RMI server is “//localhost/RemoteWorkitemCommandLineServer” using a default port of 1099 for the RMI registry.

It is possible to define a different name and port by providing a value. The example below starts WCL as RMI server with name “//clm.example.com/WorkItemCommandLine” with a RMI registry listening to port 1199:

wcl /rmiServer=//clm.example.com:1199/WorkItemCommandLine

By providing the same naming in the rmiClient switch for the requested command, the connection can be established.

wcl -create /rmiClient=//clm.example.com:1199/WorkItemCommandLine /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" workItemType=task summary="New Item" category=JKE owner=ralph target="Main Development/Release 1.0/Sprint 3" internalSeverity=Major foundIn="Sprint 2 Development" internalPriority=High creator=myadmin

If the WCL is started as RMI server, the process will not terminate, but RMI will listen to requests and delegate them to that process. It is not necessary to provide a command or any other input values, when starting the WCL in RMI server mode as they will be ignored. RMI will make the process available and call it to service commands requested by other client instances that are started with the additional switch /rmiClient added to the command that is supposed to be performed.

Please note, that the server and the client require a policy file for the security manager.  A policy file rmi_no.policy is shipped with the download. The policy file opens up everything.

So please rename and modify the file to your requirements

To enable security Java requires to call the class with the additional vm argument -Djava.security.policy=rmi_no.policy where the policy file name must exist and be readable on the server and on the client side.

Predefined Attribute ID Aliases

Aliases for attribute ID’s have been coded into the application. You can add your own aliases in the mapping table.

Available mappings:

RESOLUTION_DATE: resolutionDate

FOUND_IN: foundIn

PRIORITY: internalPriority

RESOLVER: resolver

SUMMARY: summary

ESTIMATE: duration

MODIFIED: modified

FILED_AGAINST: category

CREATOR: creator

RESOLUTION: internalResolution

MODIFIED_BY: modifiedBy

PLANNED_FOR: target

CREATION_DATE: creationDate

STATE: internalState

PROJECT_AREA: projectArea

OWNER: owner

TAGS: internalTags

DUE_DATE: dueDate

TYPE: workItemType

ID: id

TIME_SPENT: timeSpent

DESCRIPTION: description

SEVERITY: internalSeverity

CORRECTED_ESTIMATE: correctedEstimate

Summary

This WorkItem Command Line should allow for most of the automation needs when creating work items. In addition it is a nice resource for the RTC work Item API.

In later posts I will explain the code for users that are interested in adding their own implementation.

As always, I hope the post is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.

A RTC WorkItem Command Line V1.0 – Deprecated

Please refer to the new Version of the RTC WorkItem Command Line. The code has been enhanced and received a lot of testing and will be the basis for future efforts.

Version 1.0 is Deprecated
Version 1.0 is Deprecated

Please refer to the new Version of the RTC WorkItem Command Line. The code has been enhanced and received a lot of testing and will be the basis for future efforts.

I have seen many requests to be able to create and update work items from a command line in the forum. There are enhancement requests and a story for it in the Rational Team Concert development repository. I had a lot of the required code already available and thought I should provide a solution if possible.

This post provides a simple Work Item Command Line Client and explains the usage. It comes with code, so you can also enhance it if you need more features.

Related posts

  • Installing and using the WorkItemCommandLine – this post

WorkItemCommandLine Summary

The WorkItemCommandLine – in short WCL – works on Windows and Unix clients. It requires a JDK and the Plain Java Client Libraries to be installed.

It currently allows to

  • Create work items
  • Update work items
  • Show the attributes ID’s available for a work item type in a project area

The WCL allows to set and update almost all available attribute types.

  • String based attributes
  • Number based attributes
  • Enumeration and Enumeration List based attributes
  • Tag based attributes
  • ……

This works for built in attributes as well as for custom attributes.

The only attribute types currently not supported are Item and ItemList, where the type of the item is not specified or where the type is an SCM object. This might get implemented in the future. The problem is that the syntax needs to be able to specify what Item Type to look for in order to implement this, and to include the SCM component for searching.

Please note, for List attribute types it is currently only possible to set the list, unless described otherwise in the help. I am looking into a common parameter value encoding that can help more flexibility with being able to add and remove items as well.

In addition to these attribute types various not attribute based work item values can be modified:

  • Subscription
  • Comments
  • Approvals
  • Links to work items
  • Links from build results
  • Attachments can be uploaded
  • Trigger a workflow action

This should be sufficient for most of the automation needs, especially during builds.

License

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.

On the other hand, you have the code and are able to add your own code to it. It would be nice to know what you did and how, if you do so.

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 following code in this environment and get your own automation or extension working.

Download

You can download the tool here:

Setup

Download the packaged executable application. The file is compressed and will be named like wcl-Vx-YYYYMMDD.zip. The x represents the version number and is followed by the date it was created.

If you have installed an RTC API development environment following the RTC Extensions workshop and this post, you have all else that is needed and can use  the installs folder of your extension development install, for example C:\RTC401Dev\installs.

Extract the file e.g. using 7Zip to a folder, for example C:\RTCWCL (or C:\RTC401Dev\installs).

The destination folder should now contain a folder wcl.

If you don’t have an extension development environment set up, download and install the Plain Java Client Libraries for your version of RTC. Open the All Downloads tab of the RTC version you are interested in. For example https://jazz.net/downloads/rational-team-concert/releases/4.0.1?p=allDownloads and scroll down to the Plain .zip Files section.

PlainJavaDownloadDownload the Plain Java Client Libraries file.

Use 7Zip and unzip the Plain Java client Libraries download file (for example named RTC-Client-plainJavaLib-4.0.1.zip). Use 7Zips Extract Files command and provide the extraction Path C:\RTCWCL\PlainJavaAPI .

If you don’t have an extension development environment set up, download and install a Java JDK. If you have the Rational Team Concert client installed a compatible JDK is available in the install location e.g. TeamConcert\jazz\client\eclipse\jdk. The easiest way is to download the zip version of the Rational Team concert Client and extracting it to C:\RTCWCL\TeamConcert.

The folder should now look similar to this image.

WCL Folder Structure

Adjusting the Scripts to the Environment

If you downloaded a different JDK or have the RTC Eclipse client installed in a different location, follow the next steps to adjust the WCL to the different paths.

Open the folder created when extracting the WCL for example C:\RTCWCL\wcl. The folder contains the script files

  • wcl.bat – for Windows clients
  • wcl.sh – for client with Unix/*ux operating systems such as Linux

Open the file relevant for your operating system. It should look similar to this:

Script

The script assumes an install structure where the JDK and the Plain Java Client Libraries are installed like in the image before. If your setup uses different paths, adjust then according to your setup.

On Unix operating systems chmod the shell script so that it is executable.

You should now be able to run the WorkItemCommandLine.

Test the Environment

Open a shell or cmd window. change the directory to where you extracted WCL for example C:\RTCWCL\wcl. Type wcl and run the WorkItemCommandLine. The command should be executed and print help content like below.

Start WCLIf this does not happen, make sure the paths are set correctly and the JDK is compatible.

The Syntax of WCL

WCL uses the following syntax:

wcl -<command> [/<flag>] [<parameter>=<value>]

Where <command> can be

  • create
  • update
  • printtypeattributes

The commands have their own requirements for base parameters such as repository URL, users, password and the like.

The flag

  • ignoreErrors allows to successfully perform the create and update command if minor errors happen. For example if an optional attribute or its value was not found. If the flag is not provided any error will cause the operation to fail.

The parameter sections <parameter>=<value> must not have spaces in the <parameter> or in the <value> or before or after the =. You can enclose the value of <parameter>, <value> or the whole term in quotation marks. For example projectArea=”JKE Banking (Change Management)”.

The WorkItemCommandline also defines various pseudo attribute names e.g. to upload files as attachment.

Each <parameter> can only be used once in the command line. In some cases like attachment uploads a special section needs to be added in the parameter to allow for multiple specifications.

The WorkItemCommandline also has an alias mechanism built in, that allows to map different external names for attributes to the internal representation. Currently the names of the AttributeCustomization described here are built in. This allows, for example, to use FOUND_IN=”Sprint 2 Development” instead of foundIn=”Sprint 2 Development”. You can add your own aliases if needed.

Parameter values <value> are usually the display value of elements. For example setting an enumeration attribute would use “High” instead of the literal ID. This makes it easier to use.

In some cases e.g. for links, subscriber lists and other user lists, it is necessary to specify the ID of the element instead of the display name.

The help given by the tool should be enough to figure out how to use it. However, here some more details and examples.

The Command to get the Attribute ID’s and Types

To set work item attributes, wcl needs the ID of the attribute. You can look up the ID of an attribute in the process configuration. The command printtypeattributes prints the attribute ID’s for the built-in and for the custom attributes of a work item type in a project area.

wcl -printtypeattributes repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" workItemType=task

Please note, for the built in attributes this returns an internal ID that might not show up if you look into the process configuration. You can use the ID’s you find there too, the API should translate them correctly.

The Create Command

The command create can be used to create a new work item and set its attributes.

The command requires, in addition to the repository URL, the user and password, at least the project area and the work item type to create. Please note, if the process specifies additional required attributes, these need to be provided as well, otherwise the creation and save operation will fail.

Here an example for creating a work item.

wcl -create /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph projectArea="JKE Banking (Change Management)" workItemType=task summary="New Item" category=JKE owner=ralph

The Update Command

The command update can be used to update a work items attributes.

The command requires, in addition to the repository URL, the user and password, at least the ID of the work item to update. Please note, if the process specifies additional required attributes, these need to be provided as well, otherwise the save operation will fail. This can be relevant if the state of a work item is changed.

Here is an example where a work item gets heavily updated:

-update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 SUMMARY="New summary" FOUND_IN="Sprint 2 Development" owner=ralph target="Main Development/Release 1.0/Sprint 3" internalSeverity=Major foundIn="Sprint 2 Development" internalPriority=High attachFile="./Test.txt:Some Description:text/plain:UTF-8" internalApprovals="approval:Please Approve:ralph,deb" internalSubscriptions=al,ralph,deb internalState="In Progress" internalTags="test1,test2" custom.duration=1800000 custom.boolean=true custom.contributor=al custom.contributor.list=al,deb,tanuj custom.decimal=1500200 custom.integer=234 custom.long=567 custom.tag=tag1,tag2 custom.timestamp="2014/12/31 12:30:00 GMT+01:00" custom.wiki="My Wiki" custom.projectarea.list="JKE Banking (Change Management)" custom.project.area="JKE Banking (Change Management)" custom.teamarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering" custom.team.area="JKE Banking (Change Management)/Release Engineering" custom.process.area="JKE Banking (Change Management)/Release Engineering" custom.processarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering" custom.workitem=3 custom.workitem.list=9,20,7

Special Attributes and not  Attribute Based Workitem Modifications

Some attribute types need special treatment or require more complex values to be specified. Some have other limitations and considerations. These are explained below.

For Item List attributes the items need to be provided as a list of items with the separator “,”. As an example a work item attribute of type TeamAreaList would be set like this:

custom.teamarea.list="JKE Banking (Change Management)/Business Recovery Matters,JKE Banking (Change Management)/Release Engineering"

Please note, that this implies that the separator can not be part of any of the display names of the elements.

Special Properties Handling

Some special properties are protected from changing.

  • Work Item ID: can not be changed
  • Project Area: can not be changed – use the category

There might be other limitations imposed by the process e.g. against changing the creator of a work item.

User, User Lists, Subscribers

For attributes that require users or user lists the value of the property needs to specify the user or the list of users with the ID. Examples:

internalSubscriptions=al,ralph,deb custom.contributor.list=al,deb,tanuj

Links

To create links to other elements a special pseudo attribute/parameter link is used. The syntax is:

link_<linktype>=<value>

The supported <linktype> values are printed in the help. The value is a list of item ID’s to link to. The list of one or more elements can either be a list of one or more work item ID’s or the label or ID, in case of a build result. Examples:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 link_parent=1 link_blocks=2,3 link_reportAgainstBuild=P20141208-1713

 Workflow, States and Resulutions

The WorkItemCommandline allows to set the state of the work item in different ways. Please note, the state is reached after the save operation, if it can be set.

You can specify a workflow action by using the pseudo parameter workflowAction. In this case wcl looks up the current state of the work item, tries to find a workflow action with the given name and sets the save operation to trigger this action when the work item gets saved. An example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=115 workflowAction="Reopen"

In addition the attribute internalState can be used to set the state.

Syntax is:

internalState=[<forceFlag>:]StateName

In this case wcl looks up the target state and tries to reach the state, using any available direct workflow action. If there is no workflow action the state change is not performed. If the flag forceState: is added before the target state, wcl uses a dprecated API to forcefully set the state. Please note, that this does not trigger a workflow action and does also not trigger operational behavior. It should be used with caution.

Example with a target state:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalState="In Progress"

Example with a target state forcefully set:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalState="forceState:New"

The resolution can be set using the attribute internalResulution. Example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 workItemType=defect internalResolution=Invalid

Approvals

The WorkItemCommandline allows to create approvals, reviews and verifications. The syntax is

internalApprovals=”<approval_type>:<Approval Name String>:{<userID1>{,<userID>}}

Where <approval_type> is one of

  • approval
  • review
  • verification

The name of the approval <Approval Name String> is required and it is possible to add one or more approver userID’s

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="approval:Please approve:ralph,deb"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="review:Please review:deb"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="verification:Please verify:tanuj"
wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111  internalApprovals="verification:We need a verification"

Attachments

The WorkItemCommandline allows to upload attachments to work items. The syntax is

attachFileIDString{<uniqueID>}=”<pathToFile>:<description>:<contentTypeID>:<encodingID>”

Where the <uniqueID> is optional, but must be unique if multiple attachment uploads are specified, the <pathToFile> needs to exist  and be accessible, the description is some string.

The values for <contentTypeID> and <encodingID> need to match the file.

Supported values for <contentTypeID> are:

  • text/plain
  • application/unknown
  • application/xml

Supported values for <encodingID> are:

  • UTF-8
  • UTF-16LE
  • UTF-16BE
  • us-ascii

Example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 attachFile="./Test.txt:Some Description:text/plain:UTF-8" attachFile_1="./LintResult.txt:Lint result:text/plain:UTF-8"

Subscriptions

WCL allows to add subscribers to work items. The syntax is:

internalSubscriptions=<userID1>{,<userID>}

To remove subscribers use the pseudo attribute unsubscribe. The syntax is the same as when subscribing:

unsubscribe=<userID1>{,<userID>}

Example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph internalSubscriptions=al,ralph,deb unsubscribe=myadmin

Tags

WCL allows to set tags in the built in tag attribute as well as for custom attributes of the type tag. The syntax is:

<parameter>=<Tag>{,<Tag>}

Example:

wcl -update /ignoreErrors repository="https://clm.example.com:9443/ccm" user=ralph password=ralph id=111 internalTags="test1,test2" custom.tags="MyTag"

Duration Types

In duration types provide the value in milliseconds. For example

custom.duration=1800000

Timestamps

Timestamps need to be provided as string in the SimpleDateFormat using the format pattern “yyyy/MM/dd hh:mm:ss z”. For example:

custom.timestamp="2014/12/31 12:30:00 GMT+01:00"

Summary

This WorkItemCommandLine should allow for most of the automation needs when creating work items. In addition it is a nice resource for the RTC work Item API. If my schedule allows, I will enhance it to support the missing types and potentially some export and import options.

In later posts I will explain the code for users that are interested in adding their own implementation.

As always, I hope the post is an inspiration and helps someone out there to save some time. If you are just starting to explore extending RTC, please have a look at the hints in the other posts in this blog on how to get started.

Only Owner Can Close WorkItem Advisor

I always wanted to do a Server Work Item Save advisor, so here is a simple example. This advisor will prevent closing work items for any user that is not the owner of the work item. Since the code turned out to be very simple, I will try to emphasize some other useful details about creating it. Please note, that this code can be easily changed to do more complex things e.g. only work for certain work item types or workflows, look at roles of the user that does the save to act on that and much more. There are various code examples in this blog that could be used to extend the code below to achieve those goals. The API code in this post is Server and Common API.

License

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!

You can download the final code here.

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 following code in this environment and get your own automation or extension working.

The example in this blog post shows RTC Server and Common API.

Creating the Plug-in(s)

The first step is, as usual, to create a plug-in. This is easily enough using the New Project wizard and choosing the Plug-in Project. All you need to provide is a name for the project and some settings.

Using a name like “OnlyOwnerCanSaveAdvisor” looks great and a lot of people do this. Its a trap!

When choosing the name of the project I have learned over the years, it is a good idea to have a certain naming convention. The most important point here is to be able to easily find your plug-ins and features again, once they are deployed. If you deploy into the Eclipse Client, your extension might end up between 1500 other extensions. If you don’t know an easy way to locate your code, you have a problem. It can take a lot of time to find it. If you don’t know where stuff is going to end on disk and if something is deployed or not, being able to search for a file name helps a lot.

Tip: Name your extensions in a way that allows you to easily search and identify them. Create a namespace pattern to support this.

I use a Java namespace structure to name my projects. I always use com.ibm.js as prefix in the name. So I can easily search for files named com.ibm.js. The rest of the name usually has something to do with the purpose. In this case I chose com.ibm.js.team.workitem.extension.advisor.statechange.service as name for the plug-in project. I chose service, because this plug-in runs on the server.

If I have to develop more complex extensions, I often end up having several plug-in projects that belong together. To be able to easily locate them in my workspace I ended up to have a common name part and a special suffix for each of the projects. The common name here would be com.ibm.js.team.workitem.extension.advisor.statechange and suffixes would be service, component, feature, updatesite and potentially others. This makes it so much easier to find around in the Eclipse UI.

The other choices here are trivial. We don’t need an activator class for this. Keep the other defaults. There is no template to choose from, so finish the wizard.

Once the plug-in project is created, give it a useful name and leave the other information as it is. Especially leave the .qualifier suffix in the version. This allows Eclipse to create a unique version extension. Your plug-in overview tab should look like below.

Plug-in Overview

Tip: Keep the Version number structure with the .qualifier suffix as provided by the default. This allows Eclipse to create a unique version extension.

The next step is typically adding the dependencies. I usually start with some I have from other extensions. org.eclipse.core.runtime is almost always needed.

Tip: Start with dependencies used in other extensions. It is easy to remove dependencies later.

You can add dependencies easily by following the namespace pattern used in RTC. The pattern starts with com.ibm.team then there is a domain such as workitem and the suffix is typically service, common, client where

  • service is API that is only available on the server
  • common is API that is available to the server and (Java/Eclipse) clients
  • client is API only available to (Java/Eclipse) clients

The domains are

  • process for API related to the process specification for process areas (project areas/team areas)
  • repository for API related to accessing data in the repository
  • workitem for work item related API
  • filesystem for the SCM API
  • interop for API to develop work item synchronizers

amongst others.

Tip:Use the namespace pattern provided by the RTC SDK to find the API plug-ins you have dependencies to.

To add dependencies, use the add button and the namespace pattern to find interesting plug-ins.

Search and add dependencies

Please be aware that there is a domain reports that continues with the usual pattern, including the domain names in the suffix as subdomains. Avoid to accidentally pick one of those if you don’t work in the reports API. If you want to use API and the classes can not be found, although the dependency was meant to be added, check if you accidentally picked the reports domain and fix the dependency. This happened to me many, many times.

Tip: The reporting API can sneak in because its namespace space includes the other domains as subdomain. Make sure to pick the right plug-ins.

It is a good idea to start with adding the usual suspects as dependencies. If you need additional API later, you can always add it on the fly and save the plugin.xml to be able to access the API.

This is a typical first iteration of dependencies:

Typical dependencies When adding the dependency, there is compatibility information added. This information would require at least a certain version of the dependency to be available. It is possible to remove this information to make the extension more compatible e.g. to earlier versions of RTC. In the dependencies above, I removed the minimal version. Since this extension was developed with RTC 4.0.5, but would work with other, earlier versions, as well this would now be deployable e.g. in RTC 4.0 or even 3.0.

Tip: Manage the required versions in the dependencies, if you want to be able to deploy in RTC versions with lower version numbers than the version you use to develop your plug-in.

The next step is to add the extension you want to hook up to. In our case we want an operationAdvisor. It can be found the same way we found the dependencies. If you can’t find your extension point, please uncheck the option Show only extension points from the required plug-ins, to make sure you can see all the extension points, even if you have not yet added the required dependency.

The full ID of the extension point is com.ibm.team.process.service.operationAdvisors  from the list of Extension Points and Operation ID’s.

Tip: Make sure to pick all extension points and use the namespace patterns already described to find extension points.

Tip: Look in the list of Extension Points and Operation ID’s to learn more about what is available.

Find and add extension pointsSelect the extension point and add it.

Once the extension point is added, provide the required information. You need to provide data for the operation advisor.

Specify basic informationThere are several mandatory fields here. I stick to my namespace pattern and provide the following information:

  • id – com.ibm.js.team.workitem.extension.advisor.statechange.RestrictClosingToOwner
  • class – com.ibm.js.team.workitem.extension.advisor.statechange.service.RestrictClosingToOwner
  • name – Restrict Closing Work Item to Owner
  • operationId – we want to react on work item save so choose com.ibm.team.workitem.operation.workItemSave from the list of available Extension Points and Operation ID’s.

Clicking in the field name class* in front of the class definition allows to create a class. It also shows you what the class is required to provide to be able to conform to the specification of the extension point. When you create the class make sure to use com.ibm.team.repository.service.AbstractService as superclass and choose the com.ibm.team.process.common.advice.runtime.IOperationAdvisor interface as implemented. The class definition should look like:

public class RestrictClosingToOwner extends AbstractService implements IOperationAdvisor {

The code of the class is going to be presented later below. For now, just quickfix and let it add the methods to implement. In order for the code to run later, you need to specify an extension service to provide the component ID this extension belongs to and specify the implementation class for this service. The interface is required in the operationAdvisor specification. The AbstractService comes in in the next step.

Tip: Make sure to create the extending class with the right interface by looking at the editor.

Click on the operationAdvisor node and add the extension service. You can also add a description.

Specify Extension Service

The extension service needs to be specified.  Stay with the namespace pattern and provide a component ID. As implementationClass, select the one that was just created.

  • componentId – com.ibm.js.team.workitem.extension.advisor.statechange.common.component
  • implementationClass – com.ibm.js.team.workitem.extension.advisor.statechange.service.RestrictClosingToOwner

Tip: Server extensions usually extend AbstractService which provides capabilities needed later e.g. to get services.

This class needs to be based on AbstractService, which was already dealt with.

The Jazz compnent extension with the ID that was just chosen still needs to be defined. There is a special extension point for this. The extension point to specify the Jazz component is com.ibm.team.repository.common.components.

Tip: Create the Extension providing a jazz component in a different plug-in. This allows to use the component later if server as well as client extensions are needed, e.g. to provide an aspect editor to configure the extension in the UI.

It would be possible to define the component in the current plug-in. However, if the component is needed in server as well as client extensions, it is necessary to bundle it with both. In this case it is better to create a special plug-in for the component. Provide the same ID that was used in the definition of the extension service and provide a name for the component.

Specify a component extensionTip: To get more information about the extension point look at the description, the schema, declaration and references. You can find all kinds of information easily, including plug-ins that extend this point and the classes that implement the extension.

There are several means that allow you to find out more about the extension point and implementations. Just be curious and look at it. The image beow shows where to access this information.

Extension Point DetailsAnother place is where you add new extensions to the point.

Add more advisorsIt will be necessary later, to declare the services that are used by the extension. Unfortunately the schema does not contain the node to do so. This is a manual that needs to be done in the plugin.xml.

Implement the Extension

The class that is to be called by the extension point has already been created. However, the implementation is still missing and needs to be provided. It is easy to open the class from the plug-in editor.

The entry point into the class called by the extension point is the run() method. The implementation code is provided below. As all advisors (preconditions)  and participants (follow-up actions) the run() method gets the information about the work item and other information about the context it is running.

This information is checked first and the work item is extracted. The advisor code contains a section that is commented out right now.This code could later be used to limit the restriction only to specific work item types.

The advisor shall not limit saving the work item in closed stats, it should only preventing to change the state to a state in the closed group. So the next check is looking at the workflow action to determine if there is a state change. If not, the advisor does not prevent the operation.  So a user could still update attributes, while it is closed,however, only the owner can change states to states in the closed group.

The last section checks if the new state of the work item is in the closed group. If not, nothing needs to be done.

If there is a workflow action, the next step is to check the state the work item will enter. If the new state is not in the closed group, nothing needs to be done.

If the new state is in the close groups, the final check is comparing the owner and the current user. If the ID’s match, nothing needs to be done. Note, this is also the case if the work item is already closed and someone wants to move it to another closed state. Only the owner will be able to do this, provided the advisor is configured for all roles.

Finally, this is a state change into a closed state and the current user is not the owner. The advisor provides a problem info and returns it. This will block the save. Please note, this is also true if the owner is unassigned.

Here is the code:

/*******************************************************************************
 * Licensed Materials - Property of IBM (c) Copyright IBM Corporation 2005-20014.
 * All Rights Reserved.
 * 
 * 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.workitem.extension.advisor.statechange.service;

import org.eclipse.core.runtime.IProgressMonitor;

import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IAdvisorInfo;
import com.ibm.team.process.common.advice.IAdvisorInfoCollector;
import com.ibm.team.process.common.advice.runtime.IOperationAdvisor;
import com.ibm.team.repository.common.IAuditable;
import com.ibm.team.repository.common.IContributorHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.repository.service.AbstractService;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.workflow.IWorkflowInfo;
import com.ibm.team.workitem.service.IWorkItemServer;

public class RestrictClosingToOwner extends AbstractService implements IOperationAdvisor {

	@Override
	public void run(AdvisableOperation operation,
			IProcessConfigurationElement advisorConfiguration,
			IAdvisorInfoCollector collector, IProgressMonitor monitor)
			throws TeamRepositoryException  {
		Object data = operation.getOperationData();
		if (data instanceof ISaveParameter) {
			IAuditable auditable = ((ISaveParameter) data).getNewState();
			if (auditable instanceof IWorkItem) {
				IWorkItem workItem = (IWorkItem) auditable;

//				// If this needs to be limited to a special type
//				if (workItem.getWorkItemType() != "Enter Type ID Here")
//					return;
				
				// We want to allow saving the work item, if there is no state change happening.				
				String action = ((ISaveParameter) data).getWorkflowAction();
				if(action==null)
					return;
				
				// Get the workflow info and check if the new state is in the closed group.
				IWorkItemServer iWorkItemServer = getService(IWorkItemServer.class);
				IWorkflowInfo workflowInfo = iWorkItemServer.findWorkflowInfo(workItem,
						monitor);
				if (!(workflowInfo.getStateGroup(workItem.getState2()) == IWorkflowInfo.CLOSED_STATES)) {
					return; // nothing to check if the new state is not closed.
				}

				// work item is going to a state in the closed group.
				// Check if the current user is owner of the work item.
				IContributorHandle loggedIn = this
						.getAuthenticatedContributor();
				IContributorHandle owner = workItem.getOwner();
				if ((owner != null && owner.getItemId().equals(
						loggedIn.getItemId())))
					return;
				
				IAdvisorInfo info = collector.createProblemInfo(
						"The work item can only closed by its owner!",
						"The work item can only closed by its owner! If the owner is unassigned and it can also not be closed.",
						"error");
				collector.addInfo(info);
			}
		}
	}
}

Before we can do the debugging the last thing we need to do is to require the service IWorkItemServer we use to be available to the server in the plugin.xml. The plugin.xml needs to be changed to reflect that.

Add the prerequisite section with the service(s) required as presented below.

Prerequisite for the required serviceTip: Since the extension point schema does not have the prerequisite added, this is something you simply have to know how to do it.

Please also see Creating Custom Link Types for Rational Team Concert, especially the sections Prepare to Deploy and the following 2 sections describing the additional project to make deployment a little easier.

Download

You can download the final code here.

Summary

The advisor presented above will prevent anyone, except the owner, to use any action that changes the state of a work item to a closed state. Users, other than the owner can still update work item attributes, but if it is configured for a user, state changes to a closed state are only possible for the owner of a work item.

As always, I hope the code above helps someone out there with extending RTC.

I will blog about the next steps, like debugging and deploying the advisor in one of the next posts.