RTC Custom Scheduled Async Task example explained

Back from Innovate, it has been a great time meeting lots of Jazz practitioners and buddies; most of them it was the very first time I was meeting in person, after long time working together. After the usual time for catching up with tasks that were waiting for me, I am back to my blog. I have been recently working in an engagement helping building a custom asynchronous scheduled task, so I wanted to write about it.

I have had some difficult time trying to find an example of such a task that was both simple enough and yet close to a real case; so finally instead of complicating myself, I decided to use as basis the example from the Query Dev Guide wiki page with some little modifications, but with focus on the details of building such an async task.

In this post I am going to publish 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. Please also remember, as stated in the disclaimer, that this code comes with lack of promise or guarantee. In addition, let me point out that the main example code makes use of some calls to internal API code; the general usage of such internal code is discouraged, and any code based on it may stop working in a future release without further notice.

Quick review of the task sample logic implementation

As previously stated, the task logic itself is derived from the example in the Query Dev Guide wiki page. You can check that page to understand some of the quey logic elements used, as it is not the main focus of this post (it may serve as basis for other future posts heh); but it’s worth introducing a bit the logic of the example.

At regular basis, the task will check for work items which due date has expired more than a week ago and they are not in a closed state. For each project area we query the work items that match the criteria; logic implemented in the method:

private List<IWorkItem> findWorkItemsCriteria(IProjectAreaHandle projectArea, IProgressMonitor monitor) throws TeamRepositoryException

In this case, I hardcoded the “week from due date” criteria in this query expression:

IQueryableAttribute dueDateAttribute = findAttribute(projectArea, IWorkItem.DUE_DATE_PROPERTY, monitor);

if (dueDateAttribute == null)
 return Collections.emptyList();

Duration duration;

try {
       duration = fDurationFormat.parse("1 week");

     } catch (ParseException e) {
            throw new TeamRepositoryException("Illegal duration value"); //$NON-NLS-1$
     }

Timestamp value= new Timestamp(System.currentTimeMillis() - duration.longValue());

AttributeExpression dueExpression= new AttributeExpression(dueDateAttribute, AttributeOperation.BEFORE, value);

As the task runs in the context of the repository, not a specific project area, we have to specify the project area to look for work items. The method

private List<IProjectAreaHandle> findProjectAreas(IProgressMonitor monitor) throws TeamRepositoryException

will return us all the project areas in the repository. For each project area, and each set of matching work items, the task will send an email to the work item owner. The mail itself is very dummy, but allows to test it easily in my dev environment:

private void sendRememberingMail(IWorkItem workItem, String mail) throws TeamRepositoryException 
{

   String subject = NLS.bind("Due Date Reminder Notification - {0}", workItem.getId());
   String message = NLS.bind("Work item with ID {0} has a delay of more than a week according to its due date", workItem.getId());
   MailSender sender = getMailService().getDefaultSender();

   try {
        getMailService().sendMail(sender, mail, subject, message, null);
   } catch (MessagingException e) {
        String warningMessage = NLS.bind("Failed to send DueDate Notification message of work item {0} to {1}", workItem.getHTMLSummary().toString(), mail);
        getLog().warn(warningMessage, e);
    }

}

The example in the wiki which I used as basis generates change events. In this case I modified it with the dummy mail to bother the owner 🙂

mail

Note the simplicity of the code that assumes (doesn’t check), for different conditions. But let’s keep it simple as it is, and let’s get back to the task implementation details …

Building the Async Scheduled Task

I will review the basic building blocks of an asynchronous scheduled task, using the Asynchronous Tasks wiki topic as reference:

  • The task implementation needs to use the com.ibm.team.repository.service.asynchronousTask extension point.
  • Tasks are extension services, so we have to reference the component owner of this extentionService in our declaration. Moreover, the task will have to have a task ID unique within the component. In this case, the ID I will use is: WIDueDateNotifierTask
  • We can declare our task to be “owned” by an existing component (for example, com.ibm.team.repository); but in this sample case I will also declare my own component: com.ibm.js.widuedatenotifier.
    This way you see that you can make your tasks be part of a broather customization and follow the componentization and naming conventions you use.
  • You can specify configuration properties for the task, that can be then managed in the application Administration UI or “teamserver.properties” file. I will show more of this later in this post, but for the moment important to know that the task declaration must define at least one configuration property. This configuration property has to be called “<TaskID>.fixedDelay”, and will specify the time in seconds between task executions. If this property is not specified, the task will fail to register in the server and will never be executed.
  • The declaration of this sample task implementation elements look this way:

asyncTaskComp2

  • The class implementing the task must extend “AbstractAutoScheduledTask”, implementing the abstract methods of: “getTaskId” which returns a String with the ID; and “runTask” which is the actual method being called for every task execution.

skeleton

The following is the “plugin.xml” declaration of these skeleton elements, defining the task to be executed every 86400 seconds (a day delay):

 <extension
         point="com.ibm.team.repository.common.components">
      <component
            id="com.ibm.js.widuedatenotifier"
            name="Work Item Due Date Nofitier">
      </component>
   </extension>
   <extension
         point="com.ibm.team.repository.service.asynchronousTask">
      <asynchronousTask
            taskId="WIDueDateNotifierTask">
         <extensionService
               componentId="com.ibm.js.widuedatenotifier"
               implementationClass="com.ibm.js.repository.widuedatenotifier.task.WIDueDateNotifierTask">
            <prerequisites>
               <configurationProperties>
                  <configurationProperty
                        accessPolicy="OPEN"
                        default="86400"
                        description="Time delay between the Work Item Due Date Notifier executions"
                        displayableName="WI DueDate Notifier Delay"
                        name="WIDueDateNotifierTask.fixedDelay"
                        required="true"
                        type="LONG"
                        updatePolicy="FRAMEWORK_RESTART_REQUIRED">
                  </configurationProperty>
               </configurationProperties></prerequisites>
         </extensionService>
      </asynchronousTask>
   </extension>

The runTask method, which leverages the discussed logic, looks like the following:

@Override
protected void runTask() throws TeamRepositoryException 
{

   IProgressMonitor monitor = new NullProgressMonitor();

   List<IProjectAreaHandle> projectAreas= findProjectAreas(monitor);

   for (IProjectAreaHandle projectArea : projectAreas) 
   {

      List<IWorkItem> workItems= findWorkItemsCriteria(projectArea, monitor);

      if (!workItems.isEmpty()) 
      {
        for (IWorkItem workItem : workItems) 
        {
          IContributor contributor = (IContributor) getRepositoryService().fetchItem(workItem.getOwner(), IRepositoryItemService.COMPLETE);
          sendRememberingMail(workItem, contributor.getEmailAddress());
        }
       }

    }
}

With these simple pieces, we have built a task that will be called every “.fixedDelay” seconds, executing its “runTask” method.

Understanding and using Task properties

We have seen that a task declaration must at least define the “.fixedDelay” configuration property, which specifies the execution interval. You can also specify additional properties that are needed in the task logic. All the properties have to be contained within the “<prerequisites>” section of the “plugin.xml”, along with any services dependencies needed for your extension.
For example, the following would declare an additional property “custProperty”:

<configurationProperties>
    <configurationProperty
        accessPolicy="OPEN"
        default="30"
        description="Time delay between the Work Item Due Date Notifier executions"
        displayableName="WI DueDate Notifier Delay"
        name="WIDueDateNotifierTask.fixedDelay"
        required="true"
        type="LONG"
        updatePolicy="FRAMEWORK_RESTART_REQUIRED">
    </configurationProperty>
    <configurationProperty
        accessPolicy="OPEN"
        default="30"
        description="Sample custom property"
        displayableName="Sample property"
        name="WIDueDateNotifierTask.custProperty"
        required="true"
        type="STRING"
        updatePolicy="FRAMEWORK_RESTART_REQUIRED">
    </configurationProperty>
</configurationProperties>

The configuration properties have some attributes that allow you to specify, among other things, the name and data type; but also how their values can be seen and modified:

  • The “accessPolicy” attribute: determines the visibility of the the property in the Admin UI. It can be visible to all users (OPEN), just to Admins (RESTRICTED), or nobody (SENSITIVE). Note that it defines just Admin UI visibility behavior, not modification permissions. For example if I specify the example properties as “OPEN”, they will show up in the Advanced Properties section of the Admin UI with the assigned values (click to enlarge):

    adminPropertiesUI

    While defining them as “SENSITIVE” will make the values to be hidden, in a password-like way:

    adminPropertiesUI2

  • “required”: this attribute specifies whether the property value can be null
  • “updatePolicy”: determines if a restart is required to make an attribute value change available to the task logic.

You can see in the previous screenshots that the task properties are held in a section of the Admin UI with the title “Work Item Due Date Nofitier”, which is the name of the custom component I declared: the task is managed in the context of the component it “belongs” to.

The component against which the task is declared is also important if you want to specify the value of a task property in “teamserver.properties” file, as you will have to specify it following the convention of <component>.<taskID>.<property>=<value>; like the following:

com.ibm.js.widuedatenotifier.WIDueDateNotifierTask.fixedDelay=172800

A new value specified in “teamserver.properties” will be picked up in next restart, regardless of the update policy.

Playing with the scheduled delay

We have seen that the “<task>.fixedDelay” property specifies the number of seconds between task executions, value of type long. If you specify a non-positive value, the task will be called to be executed continuously.

This property can be modified during runtime if you configure it as “updatePolicy = NO_RESTART_REQUIRED”. The new delay will take place after the next task execution; the new value doesn’t make already planned execution scheduled delay to be modified, but the scheduler asks for it after task run completion.

You can also override in your task the “public long getFixedDelay()” method. This is the method called by the scheduler to get the configured delay, which originally will return the property value, but you could return a delay based on some computation:

@Override
public long getFixedDelay() 
{
   if (!isDataPrepared())
   {
     return 600;
   }
     return 300;
 }

I have seen that this method is not just called after every time the task is executed; it is also called several times at server start time, while the scheduler is registering the tasks (maybe some other times as well, that I haven’t found out yet), so generally speaking embedding self-incrementing logic in the method might result in some unexpected scheduled delay.

Some additional operations with a task

So far we have reviewed how to build a custom task and the operations with properties and the schedule. However, I wanted to also discuss some additional operations I have reviewed while “playing” with tasks implementation:

Cancelling a task: you can mark your task to be cancelled calling the “setCancelled” method:

setCancelled(true);

The task will be removed from the scheduler and will not be called again.

Making the task interruptable: your task can also implement the “IInterruptibleAsynchronousTask” interface. This interface defines the following interesting mehod to implement:

/**
     * This method will be called to signal to the task that it should stop
     * executing as soon as possible.  This method would be called in situations
     * like server shutdown when the user wants the server to stop executing soon.
     */
    public void stopExecuting();

Providing your custom “stopExecuting()” method implementation, allows you to define a set of operations that you may want to perform if the task is called to be stopped while executing.

Calling a service with the logic implemented: in this post, the task logic is contained within the task itself. However, the logic can be part of a service, which in turn may serve this and other more general operations. In that case, you want your task to be merely a time-interval based way of calling the service operation. A skeleton of such an implementation could be as follows:

protected void runTask() throws TeamRepositoryException {
  getService(<serviceClass>).<operation>();
}

(you can see a real implementation example of calling a service from a task in the SDK in the “com.ibm.team.build.internal.service.delete.BuildResultPrunerTask” class):

Conclussion

In this post, I tried to review the details of how to build a custom Async Scheduled Task, providing some hints on the configuration options that I hope will speed up your work if you have to face coding one.

The sample code I have used in this post to illustrate the explanation can be downloaded from here. I have also uploaded a very simple task that just writes the system time and date to system output, you can download it from here. The latter is meant to provide you with a working skeleton to easy try and understand some of the explained details.

Advertisements

10 thoughts on “RTC Custom Scheduled Async Task example explained

  1. Reblogged this on rsjazz and commented:
    I always wanted to do an example like that, but Jorge managed to beat me on this one. Look at this post if you are interested in creating an asynchronous task in RTC.

  2. Pingback: RTC Custom Scheduled Async Task example explained | rsjazz

    • I believe I have installed this correctly.
      CRJAZ0300I Installing the feature “com.ibm.js.repository.wiupdatenotifier.feature_1.0.0.201309260907”.
      CRJAZ0299I Installing bundle from the URL “file:/c:/IBM/JazzTeamServer/server/conf/ccm/sites/com.ibm.js.repository.wiupdatenotifier.updatesite/plugins/com.ibm.js.widuedatenotifier_1.0.0.201309260907.jar”.
      CRJAZ0307I Starting the bundle “com.ibm.js.widuedatenotifier 1.0.0.201309260907”.

      However, when I look at the Component Status page for the CCM Application, I see this:
      com.ibm.js.widuedatenotifier
      No Services

      I have also set the execution time to be every five minutes so I can test.

      Is there a way to have the service log to the tomcat log file when it runs?

    • Great to see it’s working!
      Regarding your question of having the subscribers. The code will be basically the same, but you will have to get the subscribers from each work item with a call similar to:

      IContributorHandle[] contribsHandle = workItem.getSubscriptions().getContents();

      Then you will have to iterate and get the actual contributor from the handle in the same way it’s working now:

      IContributor contributor = (IContributor) getRepositoryService().fetchItem(, IRepositoryItemService.COMPLETE);

      Regarding your implementation, take into account that implementing something like that would potentially send emails to lots of people that may not be directly involved in the resolution of the work item.

  3. Pingback: Custom Services Configuration Properties – naming collision issues | Jorge Díaz's Blog

  4. Pingback: Due Date Notifier – an Asynchronous Task Example | rsjazz

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s