RTC Extensions Workshop – How to fix SSL protocol errors preventing connection to Jetty Debug server


Since some time now I started to run into a blocking issue with the Extensions workshop. I was not able to find a solution so far. Today a colleague saw the same and asked for help. Here what I found.

Problem: the browser prevents connecting to the Jetty server

When launching the Jetty Debug server, everything seems to be fine. You can even connect with an Eclipse client to the server. However, if you try to follow the description in the Extensions Workshop and you try to connect with a browser to https://localhost:7443/jazz/admin you see a screen like the one below.

SSL Error_1

In Chrome the error claims

This site can’t provide a secure connection localhost sent an invalid response.
Try running Windows Network Diagnostics.

ERR_SSL_PROTOCOL_ERROR

Firefox is even more alarming and says

Secure Connection Failed

An error occurred during a connection to localhost:7443. Peer reports it experienced an internal error. Error code: SSL_ERROR_INTERNAL_ERROR_ALERT

The page you are trying to view cannot be shown because the authenticity of the received data could not be verified.
Please contact the website owners to inform them of this problem.

Learn more…

Report errors like this to help Mozilla identify and block malicious sites

Edge complains

Can’t connect securely to this page
This might be because the site uses outdated or unsafe TLS security settings. If this keeps happening, try contacting the website’s owner.
Try this:
Go back to the last page

It looks like the usual SSL issue with the standard certificate, but it actually is not. It does not allow you to proceed and add an exception to connect to the web site.

You are basically stuck.

I had the same issue yesterday with 6.0.5 and 6.0.6 and I knew it was working very recently – when I moved to my new laptop. I searched the internet and was not really sure about a solution still. The answers pointed at version issues and certification mismatches. So I decided to switch the Eclipse environment to a current JDK and that solved the problem. I have tried it with RTC 6.0.5 and 6.0.6 but I am pretty sure it would work with other versions as well.

Please note that the Extensions workshop suggests to use the same JDK the Server uses. I think since Browsers have recently increased their security measures, this is no longer true.

Solution

Here how to fix the problem.

  • Download an install a recent Java JDK/JRE that is compatible with the version of the RTC SDK you are working with.
    • 6.0.6 is compatible with Java 1.8
    • I use a JDK because there are other development tools that require a JDK
  • Open the Eclipse server development workspace
  • Open Window>preferences
  • Type JRE and navigate to Java>Installed JREs
    • Add the new JDK/JRE
    • Select the JDK/JRE as active click Apply and Close
      NewJDK
    • Navigate to Java>Installed JREs>Execution Environments
    • Select the Execution Environment with the matching version
    • Select the new JDK/JRE as active click Apply and CloseExecEnvironment
  • Launch the Jetty debug server
    Launch

Now try to login to https://localhost:7443/jazz/admin again. You still get an error, however the browser provides the Advanced link to continue connection to the site.

SSL Error_2

The different browsers display it different, but the concept is the same for all of them.

Summary

Recent increases of borwser security checks and invalidation of certificates requires to update to recent versions of the Java JDK/JRE to be used with the Extensions workshop. As always I hope that this helps people out there.

 

 

Advertisements

Adding a check-in participant to Rational Team Concert source control


Rational Team Concert 6.0.4 tries to make the check-in operation available to advisors and follow up actions.

Michael Valenta recently published an article on Adding a check-in participant to Rational Team Concert source control. on Jazz.net which provides details about this.

If you are interested in advisors and follow up actions, especially for SCM, this is an important article. Have a look and give him a thumbs up.

Please make sure to check with the links in the next section if you are just starting with the RTC Java API.

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.

 

Jazz Community Contributions


The Jazz Community starts sharing their tools here: http://jazz-community.org/. The code for their tools can be found here.

There is a very active Jazz user community of members of several companies in Europe that are heavily using the Jazz products such as Rational Team Concert, Rational Quality Manager and Doors Next Generation.

The community members try to meet to share their experience with using, administrating and running the Jazz tools in their environments. It became clear that the different companies and community members face similar challenges and that it would be beneficial if they could share tools they created to make running such an environment easier.

The community has now started sharing their tools in this community project and in this code repository.

JazzCommunity2017-05-12_11-34-52

Some of the tools have already been shared on other sites. I have linked the ones I am aware of to the Interesting links page in the ‘Extensions Provided by the Community’ section. These are the ones I am aware of (and the code for some of them is already available in the community repository):

I am looking forward to see more community created tools soon. Visit the community to find out what they have to offer. The code for their tools can be found here.

Last but not least, a special thanks to Dani for getting this awesome user groups started and for the members of said community for their spirit, engagement and willingness to contribute and help each other. You know whom I address here!

Watch Bartosz Chrabski on how to create Rational Team Concert – Advisor & Participant Extensions


If you are new to extending RTC, you might want to look at Bartosz Chrabskis post How to create Rational Team Concert – Advisor & Participant Extension (step by step) and the related video for creating a participant as well as the video for creating an advisor. Don’t forget to like the post and give the videos a thumbs up.

More information about the process can be found in Learning To Fly: Getting Started with the RTC Java API’s.
 

Learning To Fly: Getting Started with the RTC Java API’s


I intend this to become the beginners guide for the RTC java API’s. The reason is, I spend far too much time on the forums trying to answer beginner questions on how to get started over and over again. I intend this post to be my link to answer these questions without having to repeat everything all over.

Where to Start?

You first have to understand what API’s are available and what you can extend. If you don’t understand these basics, you can’t decide on how to proceed. It is also a good idea to understand RTC Process Customization and what you can and cannot do.

Setting up Your Development Environment

If you determined you are interested in one of the Java API’s, you need to set up your environment for Java API development. It is extremely important to perform the steps in that post to have an environment, that has the RTC SDK installed as well as the Plain Java Client Libraries. Do the whole workshop even if you just want to use the Plain Java Client Libraries to code up some automation. This familiarizes you with the concepts of plug-ins and give you some first glimpse on the API.

Even if you intent to develop only Plain Java Client Libraries, you should use a plug-in project to develop your code, because that allows to trick the Eclipse Plugin Development Environment (PDE) into showing you the whole source code of the RTC SDK. It requires to set up your environment as explained in Setting up Rational Team Concert for API Development.

This allows you to

  • See the comments on interfaces, classes and on methods
  • Search interfaces, classes and methods even with using an asterisk if you are not sure about the names and packages
  • Search for references to interfaces, classes, methods, extension points to go looking for inspiration and example code

Search The APIGetting started with the RTC Plain Java Client Libraries

To understand how that works read the post Understanding and Using the RTC Java Client API. Especially read the sections

  • Getting Started With Developing for the Plain Java Client Libraries
  • Debugging Client Code and Finding Information about the API
  • Prepare Your Project to Allow Debugging
  • Finding Information About the API

You should carefully read the whole post Understanding and Using the RTC Java Client API, as it explains how the basic mechanisms in the API work. This is also interesting if you intent to write server extensions.

Getting started with Eclipse Client and Server Extensions

If you are not (yet) interested in this section skip to the next sections and go to “Where can I find Examples and Example Code?“.

The best place to start is following the Rational Team Concert Extensions Workshop. This provides you with a fully set up environment for debugging and guides you through all the important steps that are necessary to develop client and server extensions. You might also want to look at Bartosz Chrabskis post How to create Rational Team Concert – Advisor & Participant Extension (step by step) and the related video for creating a participant and the video for creating an advisor.

RTC provides a lot of ways to extend the Eclipse based clients and the server. The most important in general are

  • Pre-conditions which are also referred to as Advisors, because they drive the process advisor behavior of operations
  • Follow-up action which are also referred to as participants, because they participate in the process behavior of operations
  • Attribute Customization which are also referred to as providers, because most of them provide values for work item attributes; please  find examples here
  • Components which are used to group extensions

The wiki topic Team Process Developer Guide provides a great overview about what you can do and what the rules are.

On the RTC Server side it is also possible to create asynchronous tasks, that can perform operations such as event generation and mail notification. An example with code and explanation can be found in the post Due Date Notifier – an Asynchronous Task Example.

There are additional extension points available on the client and on the server that can be used. Some examples are

  • Eclipse Client UI Extension points
  • RTC Web UI Extension points

There are still more, that I haven’t even looked at yet.

The most important part is to be able to develop and debug your server extensions using Jetty as explained in the Setting up Rational Team Concert for API Development. If you can’t do that, it is just a waste of time and I would consider not trying it out at all. You also always want to have a dedicated server for extensions development, deployment testing and functional testing.

The code used in advisors and participants is very similar except that the interface used is different and the result that is returned is also different.

Advisors and participants always run in the context of the user that calls the operation. This means they can only access data the user is able to access and perform operations the user is permitted to.

In advisors it is not permitted to modify the element for which the advisor is triggered. In participants this is allowed.

Participants that modify or create elements have to perform an additional save for the objects modified. Please note that this can trigger the same or another participant to be executed. This can result in a server crash, if it causes a recursive descent for a recursion that does not stop. To prevent that, it is possible to pass additional arguments in the save that the sub-sequentially called  participant can use to prevent running into a recursion. See for example this post for how that can be used.

The Extension Points and Operation ID’s to assign advisors and participants to the specific operation to work for can be found here.

In general it is the best approach to extend the RTC Server if at all possible. The reason is that this makes deployment easier. Deployment basically has to only happen on the server and deploying on all the clients can be avoided.

Deploying Extensions

If, and only if, your extension works in the Jetty based development and debug environment, you can deploy it on a real server. If you have not successfully tried your extension on Jetty as explained in the Rational Team Concert Extensions Workshop, it does not make any sense to try deploying it on a test server, let alone on a production server.

The Rational Team Concert Extensions Workshop handles deploying extensions very manual. The post A Custom Condition to Make Attributes Required or Read-Only by Role explains how this can be made more automatic in the section Deploying the Extension.

If the extension works on Jetty, the worst that can go wrong deploying it on a real server is that the dependencies in the extension include libraries that are not available on the server.

Other issues could be missing or not satisfiable dependencies, version issues, missing provision profiles or typos in the profile or corrupt files. This would show in the log file during server start up. Check the RTC Server log (i.e. server/logs/ccm.log).

Please note, a deployment error, a typo in the provision profile or missing or corrupted files in the sites folder can lead to the server crashing / not successfully starting up. The server start up can be interrupted and the server may not be reachable. See the server log for hints what might be the problem.

Please refer to the post Is The Extension Deployed? How Can I Redeploy? for checking if the deployment actually worked and for maintaining and upgrading server extensions.

Client extensions are required if the data that it works with is only available on the client, or if the extension is needed on client and server anyway e.g. for Java based attribute customization provider.

Where can I find Examples and Example Code?

There are countless examples out there, here on Jazz.net, on Stackoverflow and on many other blogs and sites. But apparently this is not enough or too hard to find. So I will try to put guidance on how to get started here.

The SDK

As already explained you can search for code that ships with the RTC SDK. This requires a working development environment as described above and here.

This Blog

You can find examples and example code in this blog. The easiest way to do so is to search the blog. This is really easy. Type the interface, method or any other clue you search for in the search window on the top right section underneath the banner and then use the “Search” button.

Search the BlogThen go through the list of posts that appear.

Most posts have working code attached for download as well.

There is also a page with Interesting Links that I maintain and add stuff that I come across. There are many links to examples for API and other interesting sources.

The Internet

We live in the times of search engines. In the past, like 25 years ago, you had to go to a public library and try to find relevant information or books that most likely were not there. Today you can use your preferred search engine and, with very few effort, find all you can ask for.  It is of course always easier to ask someone else to “Google that for me”, but it is way better to skill up to do it yourself in these Darwin times.

A good approach in this case is to just come up with some keywords, interface or class name and run the search engine on it. In my experience some words what you are looking for and RTC usually is enough. I find the interesting information usually on the first two pages. If not, I try to change and refine my query. Examples for typical search paattern are

  • “create work item java API RTC”
  • “access work item attribute java API RTC”
  • “RTC API IIteration”

If the results on page one are just too many and not promising it is possible – for Google at least – to limit the scope of the search to specific sites.

Using Search EnginesAn example for this pattern would be

  • “create work item java API RTC site:jazz.net”

This is most useful if you know that there has to be something there or recall something was there and you can’t remember where. I find this even more effective than using the search engine available on Jazz.net, because it usually shows the information I am looking for higher in the ranking.

Good sites are

Summary

If you want to get started with the RTC Java API’s, work through this blog post and the resources linked to it. I will try to extend this post with more detailed links for specific topics in the future.

As always I hope that helps users out there.

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.

Update:

For RTC 6.0.1 see this post.

Summary

The RTC extensions workshop still runs with RTC 6.0 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.

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.

Creating Custom Link Types for Rational Team Concert


It can be useful to be able to create custom link types for RTC. This is Interesting if a special business logic/behavior needs to be implemented and the available link types don’t fit. How can this be done?

It is surprisingly easy to do as Eduardo describes in Creating a New Link Type in his blog Extending Rational Team Concert – RTC Extending. I had to do it recently and thought it would be useful to describe the experience, adding a bit more detail to the content of the blog above.

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!

You can download the code for this extension 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.

Important Update

Please note that it is at least up to RTC 6.0.2 not possible to create custom link types that connect items in different repositories. The built in link types for example Tracks/Contributes to and Related Change request are available, but it is not possible to create custom links that behave this way.

Please note that custom link types at least up to RTC 6.0.2 are ignored by the data collection component and can not be used for reporting purposes.

Creating a Custom Link Type

All that needs to be done to create a custom link type is to create a plug-in.

Oh, no! Another Extension! Can’t I just define a new link type in the process configuration? I can literally hear it 8). Unfortunately that is not supported in RTC today. Vote for this work item if you want this kind of capability.

On the other hand, if there is really a need for business logic and operational behavior for the link type, an extension would be needed and it would not matter.

To make the new link type available in the Eclipse UI as well as in the Web UI, the plug-in needs to be deployed in the client as well as in the server. The RTC SDK calls this common API and it makes sense to follow this example.

As explained in other posts already it is crucial to come up with a good naming schema for the various projects and ID’s needed. Have a unique part in it (in my case js as infix) to be able to find it on disk and in the UI.

When creating the link type an ID for the link is needed as well as ID’s for the endpoints of the link. It is crucial to keep these values once they are chosen. If you change them while developing, you would otherwise introduce dangling references into your test model. I managed to damage my test database and got unreliable results when doing this. If this happens, it is possible to delete the folder server that sits in the same folder as the workspace and create a new test repository running the [RTCExt] Create RTC Test Database JUnit test as described in the Extensions Workshop.

This is the extensions editor for the plug-in I created:

Link Type Common Plugin

Link Type Common Plugin

Different to the aforementioned blog post, the plugin defines a component as well as the link type. The reason is that the component allows to see if the plugin was successfully deployed in the server. At some time in the future a component might become required as well. So I always define a component for my extensions.

You can use the context menu of the editor to add the the extension elements for the source, target, endpoints and the itemReferenceTypes.

The endpoints allow to specify the multiplicity. This has actually an impact on how the links behave. If an endpoint specifies 0..1 as multiplicity, only one item can be referenced with the endpoint. If an item is selected already and another item is chosen in the add link dialog, the old item is replaced by the new one.

The plugin.xml looks as follows

Final plugin.xml

Final plugin.xml

The implication of the values in the plugin.xml can be found in the extension point description that can be opened from the extension point itself. Review it to understand the options. From that description:

  • id – String id for the link type.
  • editors – Semicolon separated string of ids of those components permitted to create and delete links of this type. For example, the string “com.ibm.team.repository;com.ibm.team.scm” specifies the two components “com.ibm.team.repository” and “com.ibm.team.scm”. If the attribute is not present, this indicates that there is no restriction regarding which components (or client) is permitted to create and delete links of this type.
  • constrained – If true, the defining component requests that Links of this link type not be created by other components (without permission, or without going through an API provided by the defining component).
  • internal – If true, this link type is an internal detail of the implementation of the defining component, and is not intended as a generic link type which users can freely create, delete and view.
  • componentId – The id of the component defining this link type. Component ids are declared using the com.ibm.team.repository.common.components extension point. If set and if constrained=true, then only services that are part of that component may save and delete links of this type.

The itemReferenceType entry is still a bit mysterious. It is easy to review existing examples. On the com.ibm.team.repository.common.linktype extension select “Show References” in the context menu and browse through the examples. Especially the references in com.ibm.team.workitem.common are of interest. It is possible to define different kinds of endpoints, dependent on what items to link.

In this case work items are supposed to be linked to work items. Both ends select this itemReferenceType. Please be aware that this kind of links will only work within one CCM repository. It will not allow to link across repository borders. The are other CLM link types that would allow this to happen.

I added some icons for the link types that show up in the editors. For the final deployment, I made sure that the folders icons and META-INF as well as the plugin.xml file are selected in the binary build.

I tested the new link type in a Jetty based test server OSGI2 launch as well as in an Eclipse2 Application launch.

Prepare to Deploy

As already mentioned the common plugin needs to be deployed in the server as well as in the Eclipse client. Please follow Lab 6 in the Rational Team Concert 4.0 Extensions Workshop if you have never done this and want to understand how deployment works in general. The workshop explains in great detail which files you need to deploy on the server. The procedure below follows the deployment procedure on the server side. Other than in the workshop the client extension gets deployed using an update site.

To make this easier I created a Feature project as well as an Update Site project. To make deploying it in the server easier I also created a normal Eclipse project that contains the deployment folder structure as well as the provision profile INI file. The projects look as follows:

Final Project Structure

Final Project Structure

Once the Update Site project is built, It is easy to copy the site.xml file and the folders  features and plugins into the folder js_custom_linktype in this project.The folders and contained files in this project can then used to easily deploy the extension on the server using copy paste. It is easy to do, reliable and repeatable.

The provision profile js_custom_linktype.ini looks as follows:

url=file:ccm/sites/js_custom_linktype
featureid=com.ibm.team.js.workitem.custom.linktype.feature

It references the sub folder in the site folder that will contain the feature and plugins folders, once it is copied over to the server for deploying.

TIP: Always delete all content of the Update Site project except the site.xml before building the update site again. I have seen cases where subsequent builds did not successfully pick up all changes.

Deploy in the Eclipse client

To deploy in the Eclipse client, use the Help>Install New Software menu, add the Update Site (browsing to the folder that represents the Update Site project) and install the extension. You can package the Update Site or provide it in a web server in a company context as well.

Deploy in the Server

Copy the generated folders and the site.xml file into the folder underneath the sites folder which is referenced by the provision profile js_custom_linktype.ini file in the  serverdeploy project folder. This would be easy to automate with a build script by the way.

Now select the folders provision_profies and site with all the content and copy the into the servers folder /server/conf/ccm. Allow to overwrite the folders and request a server reset. Then restart the server.

Add the New Link Type to the Quick Information Presentation

As Sam suggests in his comment below, you also want to add the new link type so that it shows in the quick Information presentation. This is done in the Process Configuration>Project configuration>Configuration Data>Workitems>Quick Information Presentations. It allows the workitem summary page Quick Information section to show a count of and quick link to the new link in a workitem This would look like below:

Quick Information Presentation

Quick Information Presentation

Admire your work

You can now admire the result of your work in the work item editors. If not, check your deployment setup. As usually the first attempt was, of course, on a test server to not affect the production system until you have perfected your deployment process.

 Too many Link Types! What to do?

If you have created all the new link types your business demanded, users might start to complain that there are too many link types and many are not needed anyways. You knew that would be coming and decided long ago to review the article Customization of work item editor presentation to show or hide link types in Rational Team Concert. to understand how to fix this issue once needed.

Summary
That was easy, wasn’t it? Now you can create behavior based on the link type.

I have tested it against a test server on Tomcat and Derby. There is no real code this time. However, 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.