Friday, 22 June 2012

How to add a jira service plugin in Jira platform

This post is about how to write a service in JIRA platform, which is responsible for executing a periodically task. One example could be pushing meta information of projects from jira platform to a stand-alone database for analysis or any other purpose.

I am assuming that, reader is familiar with development of type-2 plug-ins in jira platform.

Problem statement: Push meta information of projects in Jira platform to a stand-alone database.

1. SAL scheduler component needs to be imported into customized jira plugin by including following xml code snippet into atlassian-plugin.xml file.


<component-import key="pluginScheduler">
        <description>Atlassian SAL Scheduler</description>
        <interface>com.atlassian.sal.api.scheduling.PluginScheduler</interface>
</component-import> 



2. Create an abstract service class "DatabaseSyncService.java"


package com.company.application.schedule;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.sal.api.scheduling.PluginScheduler;
import com.
company.application.ao.service.EventLogService;

/**
 *
 *
 * @author sateesh.b
 *
 */
public abstract class DatabaseSyncService {

    private final PluginScheduler pluginScheduler;  // provided by SAL
    private ActiveObjects activeObject;
    private EventLogService eventLogService;
   
    /**
     * Constructor
     */
    public DatabaseSyncService(PluginScheduler pluginScheduler, ActiveObjects activeObject,
            EventLogService eventLogService) {
        this.pluginScheduler = pluginScheduler;
        this.activeObject = activeObject;
        this.eventLogService = eventLogService;
    }
   
    public ActiveObjects getActiveObject() {
        return this.activeObject;
    }
   
    public EventLogService getEventLogService() {
        return this.eventLogService;
    }
   
    public PluginScheduler getPluginScheduler() {
        return this.pluginScheduler;
    }
}
 


3. Sync Service interface

package com.company.application.schedule;

/**
 * This interface is used to schedule the services added in the application.
 *
 * @author sateesh.b
 *
 */
public interface SyncService {
   
    public void reschedule(long interval);
   
}


4. Create class which would register the service with the Jira Plugin Scheduler, so that it would run as a periodical service. This class should implement interface LifecycleAware, so that it would get called when this plugin is deployed in application server.


package com.company.application.schedule;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.sal.api.lifecycle.LifecycleAware;
import com.atlassian.sal.api.scheduling.PluginScheduler;
import com.
company.application.ao.service.EventLogService;
import com.
company.application.core.util.PropertyHelper;

/**
 * This is the component class, which adds <b>"Projects Meta Database Sync"</b> service to the
 * <code>JiraPluginScheduler</code>
 * 
 * This class is executed at the time of server startup
 * @author sateesh.b
 *
 */
public class
ProjectsMetaDatabaseSyncServiceImpl extends DatabaseSyncService implements SyncService, LifecycleAware {

    static final String KEY =
ProjectsMetaDatabaseSyncServiceImpl.class.getName() + ":instance";
    private static final String JOB_NAME = "
Projects Meta Info Database Sync";
   
    /**
     * constructor
     */
    public
ProjectsMetaDatabaseSyncServiceImpl (PluginScheduler pluginScheduler,
            ActiveObjects activeObject, EventLogService eventLogService) {
        super(pluginScheduler, activeObject, eventLogService);
    }
   
    /** 
     * Registering the "Projects Meta Info Database Sync" service in the application.
     * This service will run periodically with an interval of 1 day
     *
     */
    @Override
    public void reschedule(long interval) {
        getPluginScheduler().scheduleJob(JOB_NAME, ProjectsMetaInfoDatabaseSyncTask.class, new HashMap<String,Object>() {{
            put(KEY,
ProjectsMetaDatabaseSyncServiceImpl .this);}}, getServiceStartupDate(), interval);       
    }
   
   
    /** 
     * this method will be called at the start of the instance
     */
    @Override
    public void onStart() {
        //final String interval = PropertyHelper.getInstance().getPropertyValue("database.sync.interval");
        // interval information is fetched from property file
        final String interval = "60";        reschedule(Long.valueOf(interval));
    }   
   
    private Date getServiceStartupDate() {
        Calendar cal = Calendar.getInstance();
        GregorianCalendar gCal = new GregorianCalendar(cal.get(cal.YEAR), cal.get(cal.MONTH),
                cal.get(cal.DAY_OF_MONTH)+1, 0, 5);
        /*GregorianCalendar gCal = new GregorianCalendar(cal.get(cal.YEAR), cal.get(cal.MONTH),
                cal.get(cal.DAY_OF_MONTH), 15, 20);*/
        return gCal.getTime();
    }
}




5. Create a class which will handle the business logic of executing the repeatable task. This class needs to implement PluginJob.java interface as mentioned below. The below class will be invoked by the Jira Plugin Scheduler and "execute" behavior will be called with an interval mentioned in the class "ProjectsMetaDatabaseSyncServiceImpl.java"


/**
 *
 */
package com.company.application.schedule;

import java.sql.SQLException;
import java.util.Map;

import org.apache.log4j.Logger;

import com.atlassian.sal.api.scheduling.PluginJob;

/**
 * This <code>
ProjectsMetaInfoDatabaseSyncTask</code> class would connect to jira database to get the
 * jira project configurations and will be sync with stand-alone database
 *
 * This class is invoked by the <code>ServiceManager</code> of Jira framework.
 *
 * @author sateesh.b
 *
 */
public class
ProjectsMetaInfoDatabaseSyncTask implements PluginJob {
   
    private static Logger logger = Logger.getLogger(
ProjectsMetaInfoDatabaseSyncTask.class);
    private static boolean isRunning = false;
   
    /**
     * this method is invoked by the Jira Service manager
     */
    public void execute(Map<String, Object> jobDataMap) {
        if (!isRunning) {
            isRunning = true;
            try {
               
ProjectsMetaDatabaseSyncServiceImpl monitor = (ProjectsMetaDatabaseSyncServiceImpl) jobDataMap.get(ProjectsMetaDatabaseSyncServiceImpl.KEY);
                assert monitor != null;
            
            // now we can access the monitor object (ProjectsMetaDatabaseSyncServiceImpl) and write the
            // needful logic to get the project info and push them to stand-alone database.
            // errors can be logged into jira database 
                              
            } catch (ClassNotFoundException e) {
                logger.error("Unable to run database sync due to driver exception", e);
            } catch (SQLException e) {
                logger.error("Unable to run database sync due to sql exception", e);
            } finally {
                isRunning = false;
            }
        }
    }
}
 


6. Finally, we need to add the component/service plugin information into atlassian-plugin.xml file
Don't forget to mention this component as public (by mentioning public="true"), then only this component will be available with jira plugin service component.


   <component key="database-sync" class="com.company.application.schedule.ProjectsMetaDatabaseSyncServiceImpl"
             system="true" public="true">
        <description>The plugin component that schedules the projects meta info sync process.</description>
        <interface>com.atlassian.sal.api.lifecycle.LifecycleAware</interface>
        <interface>com.company.application.schedule.SyncService</interface>
    </component>


No comments:

Post a Comment