Unable to Connect to the Jetty Server Using Current Browsers Due to SSL Error Extending RTC Versions Lower Than 6.x


Attempting to connect to my Jetty server to debug a new RTC Extension I was working on in RTC 4.0.1, failed with an SSL error with all my current Browsers, Chrome and Firefox without being able to find a workaround in the browsers. How to work around that issue?

This problem only occurs when using the RTC SDK for RTC versions prior to RTC 6.x. It will affect all of these versions. It does not prevents performing the RTC Extensions Workshop since most of the tasks there can be done using the Eclipse UI. However, it impacts developing RTC Server extensions and test them properly, if it is necessary to use the Web UI because it is impossible to go into the web UI to test.

The problem I ran into is basically related to Chrome Does Not Work With RTC Debug Server on Jetty. However, it turns out that the work around described there does no longer work. Neither Tomcat nor Chrome allow to enable SSL V3 anymore showing errors like this in FireFox.

SSL ERROR

The messages contain text like

Unable to Connect Securely

Firefox cannot guarantee the safety of your data on localhost:7443 because it uses SSLv3, a broken security protocol.
Advanced info: ssl_error_unsupported_version

or

This webpage is not available

ERR_SSL_VERSION_OR_CIPHER_MISMATCH
Hide details
A secure connection cannot be established because this site uses an unsupported protocol.

The only ways around I found so far are:

You can download Chromium of various versions here. Version 44.0.2385.0 does still work for me and it does not update automatically.

The development team tracks the issue in Defect 354890. As long as there is no other solution, use the workarounds provided above. As soon as there are new news, I will try to provide the details.

Posted in Jazz, RTC, RTC Extensibility | 2 Comments

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


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

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

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

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

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

Summary

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

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

Posted in Jazz, RTC Automation, RTC Extensibility | Tagged , , , , , , | Leave a comment

Running the RTC Extensions Workshop With RTC 6.0


I ran a test to find out if the Rational Team Concert Extensions Workshop still works with RTC 6.0 using the RC1 build. This is what I found.

Summary

The RTC extensions workshop still runs with RTC 6.0 – at least the RC1 build – but you have to increase the memory available for the JUnit test to setup the development time repository database.

Detailed Findings

The workshop worked well, until in step 1.6 Test the Jetty Based Server Launch you create the development time repository database by running the JUnit test Launch.

The original setting is -Xmx256. With this setting I got a memory error in the JUnit test that creates the development time repository database.

Change the memory setting to -Xmx512 as shown below.

Memory Setting For JUnit launch

The second observation I made after successfully running the JUnit test was when I tried to launch the RTC Eclipse client in 1.7 Test the RTC Eclipse Client Launch. The Launch shows a missing bundle.

Eclipse Client launch Missing Bundle

This missing bundle does however not prevent you from running the launch and as far as I can tell at this point in time, the workshop is still working.

Suggestion

Since the memory footprint seems to be increasing at least for the JUnit test, keep this in mind for the other launches. If you run into memory errors, increase the memory settings for your launches. You can find the parameters to consider in the server.startup.bat file.

Additional Information

A user in the Jazz.net Forum pointed out that there is a service error in the server Status summary. You can see it below.

Service Error Test

This error has always been there, as far as I know. I think this is simply a test – the services seem to be just test services to test the error detection capability.

Finally

As always I hope this information helps users of the community in their work.

Posted in Jazz, RTC, RTC Extensibility | Tagged , , , | 1 Comment

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.

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!

Download the Code

The code can be downloaded from DropBox here. Please note, there might be restrictions to access DropBox and the code in your company or download location.

Just Starting With Extending RTC?

If you just get started with extending Rational Team Concert, or create API based automation, start reading this and the linked posts to get some guidance on how to set up your environment. Then I would suggest to read the article Extending Rational Team Concert 3.x and perform the Rational Team Concert 4.0 Extensions Workshop. This provides you with a development environment and with a lot of example code and information that is essential to get started. If you have already done the Rational Team Concert 4.0 Extensions Workshop you can just perform Lab1 to setup your development environment for debugging.

You should be able to use the code attached to this post in the development environment you set up in the Rational Team Concert 4.0 Extensions Workshop and get your own extensions working there as well.

If you want to create code with the Plain Java Client Libraries or the RTC Client SDK please also work through Understanding and Using the RTC Java Client API, to understand the basics of the API and how you can use Eclipse to debug the code. Working with Work Item Attributes is also a useful resource to understand how work item attributes work.

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.

Posted in Jazz, RTC, RTC Extensibility | Tagged , , , , , , , , , | 2 Comments

Creating Plans With the Plain Java Client API


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

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

Why is this interesting?

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

I imagine having some code that:

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

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

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

Warning License, Further Reading

The code in this post is client API.

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

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

The post contains published code, so our lawyers reminded me to state that the code in this post is derived from examples from Jazz.net as well as the RTC SDK. The usage of code from that example source code is governed by this license. Therefore this code is governed by this license. I found a section relevant to source code at the and of the license. Please also remember, as stated in the disclaimer, that this code comes with the usual lack of promise or guarantee. Enjoy!

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

Download the Code

The code can be downloaded from DropBox here. Please note, there might be restrictions to access DropBox and the code in your company or download location.

How The Code Works

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

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

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

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

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

CreatePlan requires the following parameters:

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

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

The minimal information needed to configure a plan is:

PlanConfiguration

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

An Example:

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

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

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

Example:

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

From the Team Organization:

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

For example:

“Main Development/Release 1.0/Sprint 3″

From the Team Artifacts view:

PlanConfigurationIteration

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Summary

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

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

Posted in Jazz, RTC Automation | Tagged , , , | 3 Comments

Chrome Does Not Work With RTC Debug Server on Jetty


Recently I tried to use Chrome with a RTC debug Server running on Jetty. I was unable to do so. Why does this happen and is there a work around?

Affected Versions

This problem only occurs when using the RTC SDK for RTC versions prior to RTC 6.x. The RTC 6.0 and later SDK’s are not affected as they enable TLS.

* Update * see Unable to Connect to the Jetty Server Using Current Browsers Due to SSL Error Extending RTC Versions Lower Than 6.x for newest developments and potential workarounds. This work around does no longer work for the newest versions of Chrome.

Symptoms

When I tried to navigate to the public URI https://localhost:7443/jazz/ of my debug server I got a message

This webpage is not available

ERR_SSL_VERSION_OR_CIPHER_MISMATCH

See the screenshot below:

This webpage is not available ERR_SSL_VERSION_OR_CIPHER_MISMATCH

This webpage is not available ERR_SSL_VERSION_OR_CIPHER_MISMATCH

I created Defect 354890 to get it addressed.

Root Cause And Work Around

As described in the defect above, it appears that Jetty uses SSL as a default protocol to talk to the browser. Chrome version 4 has disabled SSL due to defects in the older SSL implementations. This can basically also happen in other browsers soon.

To fix this, Jetty would have to be set to TLS as default. Although this seems to be the documented default for newer versions, the SDK does not seem to have that setting. I searched around and was also not able to find a way to force it.

It would be good if there was a way to set e.g. a System Property in the server launch configuration.

This work around I found on the net worked for me for Chrome v40:

  • Open the URL chrome://flags
  • Look for “Minimum SSL/TLS version supported.
  • Change from Default and choose SSLv3
  • Click on the “Relaunch now” button

Open your public URI https://localhost:7443/jazz/ again. You will be redirected to a page “Your connection is not private“. Click on the “Advanced” link. Finally click on “Proceed to (unsafe)“.

Update

Chromium has the same problem. And the same fix.

Posted in Jazz, RTC, RTC Extensibility | Tagged , , | 2 Comments

Some Community Extensions for RTC


Here are some interesting extensions provided by members of the community that I have seen recently. I thought I should share them and give them a thumbs up as well.

Guido and his colleagues have created these extensions and share them here:

I have seen the Timebox Planning View and it is incredible. I will have a look at JAM as well.

Yasuyuki created this for a customer in Japan and shares it here

  • RTC Work Item Numbering – an extension that allows to create a custom work item number that is consecutive by type

I have added the links to my Interesting Links page as well.

Posted in Jazz, RTC Automation, RTC Extensibility | Tagged , , , | Leave a comment