Only Owner Can Close WorkItem Advisor


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

License

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

You can download the final code here.

Just Starting With Extending RTC?

If you just get started with extending Rational Team Concert, or create API based automation, start with the post Learning To Fly: Getting Started with the RTC Java API’s and follow the linked resources.

You should be able to use the following code in this environment and get your own automation or extension working.

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

Creating the Plug-in(s)

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

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

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

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

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

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

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

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

Plug-in Overview

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

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

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

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

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

The domains are

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

amongst others.

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

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

Search and add dependencies

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

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

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

This is a typical first iteration of dependencies:

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

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

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

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

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

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

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

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

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

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

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

public class RestrictClosingToOwner extends AbstractService implements IOperationAdvisor {

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

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

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

Specify Extension Service

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

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

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

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

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

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

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

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

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

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

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

Implement the Extension

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

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

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

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

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

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

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

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

Here is the code:

/*******************************************************************************
 * Licensed Materials - Property of IBM (c) Copyright IBM Corporation 2005-20014.
 * All Rights Reserved.
 * 
 * Note to U.S. Government Users Restricted Rights: Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 ******************************************************************************/
package com.ibm.js.team.workitem.extension.advisor.statechange.service;

import org.eclipse.core.runtime.IProgressMonitor;

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

public class RestrictClosingToOwner extends AbstractService implements IOperationAdvisor {

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

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

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

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

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

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

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

Download

You can download the final code here.

Summary

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

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

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

34 thoughts on “Only Owner Can Close WorkItem Advisor

  1. Hello Ralph,
    thanks for your blog.
    Unfortunately I was not able to create such an extension even thought it should have been only copy paste.

    But I am always wondering why:
    – all extension HOW-TOs are always out of date. I find help for RTC 3.x (sometimes 4.x) but never 5.x. Why can’t there be an up-to-date documentation by IBM? JAZZ is no open-source-tool.

    – such functionalities that you described here are not build-in. They are absolutely basic stuff and IBM’s competitors have them build-in, so that no one is forced to create some own extensions which will be out-of-date with future upgrades and which might have some influence on other functionalities..

    Christian

    • Christian,

      1. From my perspective, the API has not changed significantly since 3.x. All the extensions I have written are working for 4.x as well as 5.x. There might be changes to internal API, that should not affect you, as you should not use it.
      2. It is very hard to generalize these kinds of requirements. It is practically impossible to put all of them into the tool. This is proven also by the history of massive customization of our other tools. I have seen a lot of requests that have some kind of common behavior, but if one looks into the details, there is no common core. Also other customers demand new functionality and it comes down to resources at some point in time. Open source lives by all the people that provide their work for free.

      This is the reason why we provide examples on Jazz.net and blogs like this.

      Even for copy paste, you can do something wrong. Especially for extensions a typo in the plugin.XML can prevent your code from being run. It is crucial to follow the descriptions closely and set up the environment as described in the blog. My experience is, that users try to skip that step (to save an hour) and deploy without knowing if the code even works in a debug environment. Then we start discussing the issue on Jazz.net where it costs me 3 hours.

      My best advise is to follow my blog and do the extensions workshop, before trying to do your own examples. This is usually the most successful way. Saving a day by skipping this will cost a week in the long run.

      I will try to upload the code for this example. Stay tuned.

      • Hello Ralph,
        thanks for your kind reply to my frustrated comment.
        I will try to start at the beginning…
        Christian

      • Christian, I have attached the code at the end. In the section before that is also a hint to make deployment a little easier. However, you should do the workshop.

        Don’t forget this is a bit complex and hard to do if you have never done it before.

        I hope you have success the next time.

    • Just one more thing. Unfortunately you humans [ 8) lol ] use only one character set it is sometimes really hard to show code in the blogs. Especially Java Generics is a problem, as they look like a XML or HTML tag and sometimes don’t show. If they do I had to add quoted characters.

  2. Hi Ralph
    I’m stuck in “Click on the operationAdvisor node and add the extension service. You can also add a description.”
    After I add the extension service you point out that I should create a different plugin?
    “Create the Extension providing a jazz component in a different plug-in.”
    So I should create 2 plugins right?

    I have download the code and saw the example and they are 2 plugins.

    At last you point out that in next entrys of your blog you will be teaching how to deploy. Can you point out where?

    I have done the workshop tutorial until point 1.6 for setting up my environment is this enought?

      • Hi Ralph, I added the feature (code attached on the article) to my Jetty server like any other plugin (as a bundle) but it does not show anything on the web client. Do I need to do anything else? Like activate it somewhere ?

      • It is like any other operational behavior and needs to be configured in the Project areas Process Configuration>Team Configuration>Operation Behavior for a role. I’d like to emphasize that this is exactly what is described in the Extensions Workshop and you should probably take the time to actually perform that.

  3. I did upload the owner only can close wi op. behaivour , and also created a modified version of that.And activate it on the RTC process configuration. But the toolbar option to create defect is also present in the op. behaivour of the process model?

    • Creating a work item does obviously not have anything to do with closing a work item.
      Custom advisors also only run AFTER you press Save and have no influence whatsoever on the UI, before the button is pressed.

    • I must confess, I don’t understand what you are asking. Adding custom behavior does not remove any existing operation behavior. Custom operation behavior does not have any influence on the UI. Even permissions don’t. You could still create a work item initially of a type you can’t create, but you would not be able to save it.

      • Sorry Ralph, I think I did not explain myself clear. I meant that I did upload this example “Only owner can close workitem advisor” on the Jetty and the Tomcat servers successfully.

        On the other hand, I tried to upload the code example from https://jazz.net/library/article/782 . And I did not saw any change on my webui toolbar from the workitem. Has something changed in the API from 3x to 5x that would affect this code?.
        On the Jetty server I added this plugin as a bundle, do I need to add anything else?.

        Again sorry for the misunderstood.

        Thanks

        .

      • I quickly tried the example and it does not seem to show in the UI. I have no time to take a deeper look. So something seems to be missing.

  4. Ralph, Thanks to share this link to me, I read the article and gone through the code also.

    I have some query please clarify me,
    1. How to this “Only Owner Can Close WorkItem Advisor” with other or existing work items?
    2. Where I want to deploy the plugin.
    3. How to integrate or call with other work items?

    Many Thanks.

  5. Hi Ralph,
    I am very new to this server API development in RTC, so please share information for develop and test the API’s.

    “only owner can close workitem advisor” using this source code i created the plugin, Feature Plugin and Update-site Plugin.

    Now I want to know how to use this plugin,
    1. Shall I want to deploy this plugin in server?
    2. How to integrate this server API(deployed plugin in server) with workItems? or shall I need to give this “update-site” plugin path in “Install new software”?
    3. How to test the server API with existing workitems?

    Thanks

    • Like in th other question, follow the section new to extening RTC and do the enablement material suggested there. The Extension Workshop explains how that works. The adviser is server API and needs to be deployed on the server.

  6. Hi, I am working on the server side Plug-In development, I have queries on that, if any body knows please help me to resolve this, I developed the plug-in and deployed the same as in the server and now how to integrate the deployed server side plugin with RTC-client?
    My requirement is same as “only-owner-can-close-workitem”.
    I read the articles but am not clear on that.
    So please help me on this.

  7. Thanks Ralph.
    When I tried to deployed server side custom plugin and to check the Provision Status for the same plugin but following error i am getting
    “CRJAZ0300I This feature is being installed: “com.ibm.js.team.workitem.extension.advisor.statechange.service.feature_1.0.0.201509021506”.
    CRJAZ0299I Installing bundle from the URL “file:////apps/IBM/JazzTeamServer_5.0.2/server/conf/ccm/sites/js_only_owner_closeworkitem/plugins/com.ibm.js.team.workitem.extension.advisor.statechange.service_1.0.0.201509021506.jar”.

    CRJZS0383E The “com.ibm.js.team.workitem.extension.advisor.statechange.service” bundle could not be resolved.”
    I tried to resolve this problem but can’t able to fix.
    Development Environment JDK version is 1.7 32 bit and Target Environment Java version is 1.7 64 bit open JDK.
    Please share any idea to solve this issue.

      • Thanks Ralph, I changed the JRE 1.6 to 1.5 now. Server is maintained in different machine and the installed Java version is 1.7, so If i need to change the java version from 1.7 to 1.5?
        My development environment is 32 bit and server environment is 64 bit, It will create any problem?

      • It does not matter, which JRE is installed on that machine, the JAZZ servers run with the JDK that is bundled with the product.
        The entry in the plugin.xml also only hints the lowest compatible version.

  8. Thanks Ralph.
    With all the suggested changes I have redeployed the plugin. In the Provision Status console I am able to see the plugin was loaded successfully but unfortunately the same plugin is not listed in the component status console.
    Please suggest.

  9. How to restrict the state transition by role in RTC?
    I dont have access in State2. Now am in state2 and I can able to make the state transitions like state2 to state3 or state2 to state1 how to restrict this.

    • You can configure that in the permissions. That should be the preferred way to do this in RTC and it does not require an extension.

      If, by all means, you want to create an advisor, the code to detect a state change is in the Extensions Workshop and you would then have to look at the roles and the configuration.

Leave a comment

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