J Tricks - Little JIRA Tricks
 
It is simple. Really! To do things that can give a pretty good mileage during plugin configuration, especially for commercial plugins, is simple? Let us see.

In this short recipe, let us look at creating a custom field automatically during a plugin installation and deleting it while un-installation. Same applied for enabling/disabling of the plugin.

All you need to do is 2 simple steps:
  1. Write an event listener. We will use the atlassian-event library here.
  2. Implement the Spring interfaces InitializingBean and DisposableBean to capture the plugin lifecycle events.

Writing an event listener in JIRA is quite easy. 
  1. Import the EventPublisher instance used to register events. You can do this by adding the following component in atlassian-plugin.xml

    <component-import key="eventPublisher" interface="com.atlassian.event.api.EventPublisher"/>

  2. Now, instantiate our listener class

    <component key="eventListener" >
        <description>A Listener for plugin lifecycle events</description>
    </component>

So, what's the deal with a listener here? Basically, Atlassian plugins are implemented as Spring dynamic modules and the atlassian-plugin.xml is transformed into a Spring XML bean configuration before it is loaded by the JIRA. Since our listener is registered as a <component>, it will become a Sping bean when loaded and hence we can use the Spring interfaces InitializingBean and DisposableBean to capture plugin lifecycle events.

Whenever the component (here PluginListener) is registered, i.e. during the enabling or installation of the plugin, afterPropertiesSet() method from InitializingBean is invoked. Similarly, when the component is un-registered, during plugin disabling or un-installation, destroy() method from DisposableBean is invoked.

Our listener will look like this:

public class PluginListener implements InitializingBean, DisposableBean {

    @Override
    public void destroy() throws Exception {
        //Handle plugin disabling or un-installation here
    
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //Handle plugin enabling or installation here
    }
}


That's it! How does it help during plugin configurations? Read on..

Suppose we have a plugin that needs a Text custom field for its functionality, with a pre-defined name. Users will have to configure this manually when the install a plugin and remove it when they un-install it. This is also prone to manual errors. Why not do this in the plugin itself?

Let us create a Text CF during the plugin enabling/installation and associate it with a screen. We will also remove this when the plugin is disabled or un-installed. The code is an extention to the above and self explanatory. Here it goes:

public class PluginListener implements InitializingBean, DisposableBean {

    private static final String TEST_TEXT_CF = "Test Text CF";
     private final CustomFieldManager customFieldManager;
    private final FieldScreenManager fieldScreenManager;

    public PluginListener(CustomFieldManager customFieldManager, FieldScreenManager fieldScreenManager) {
        this.customFieldManager = customFieldManager;
        this.fieldScreenManager = fieldScreenManager;
    }


    @Override
    public void destroy() throws Exception {
        //Get the already installed custom field by name
        CustomField cField = this.customFieldManager.getCustomFieldObjectByName(TEST_TEXT_CF);
        //Remove if not null
        if (cField != null) {
            this.customFieldManager.removeCustomField(cField);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        //Create a list of issue types for which the custom field needs to be available
        List<GenericValue> issueTypes = new ArrayList<GenericValue>();
        issueTypes.add(null);

        //Create a list of project contexts for which the custom field needs to be available
        List<JiraContextNode> contexts = new ArrayList<JiraContextNode>();
        contexts.add(GlobalIssueContext.getInstance());

        //Add custom field
        CustomField cField = this.customFieldManager.createCustomField(TEST_TEXT_CF, "A Sample Text Field",
            this.customFieldManager.getCustomFieldType("com.atlassian.jira.plugin.system.customfieldtypes:textfield"),
            this.customFieldManager.getCustomFieldSearcher("com.atlassian.jira.plugin.system.customfieldtypes:textsearcher"),
            contexts, issueTypes);

        // Add field to default Screen
        FieldScreen defaultScreen = fieldScreenManager.getFieldScreen(FieldScreen.DEFAULT_SCREEN_ID);
        if (!defaultScreen.containsField(cField.getId())) {
            FieldScreenTab firstTab = defaultScreen.getTab(0);
            firstTab.addFieldScreenLayoutItem(cField.getId());
        }
    }
}


Here is a screenshot on how the field looks like once it is created.
Over to you to explore further! Have a look at the JIRA Development Cookbook if you liked this recipe. You will find more. Also, attached is the full code below.
plugin-listener.zip
File Size: 12 kb
File Type: zip
Download File

 


Comments

03/20/2012 5:34pm

This is neat stuff and a handy reference - thanks.

Minor nits: there is a risk that removing the plugin will delete the custom field and all its data. This may be what is intended but there is also a risk if a user creates their own field named "Test Text CF" and then it is found instead of the intended one!

Reply
J-Tricks
03/20/2012 6:30pm

Totally agree Matt :) I kind of merged 2 recipes into one for the sake of an example and I see where this can be an issue. Thanks for pointing out.

Reply
Ignasi
03/26/2013 5:07am

I review project and seems me that a dependency to springframework is needed because in jira sdk it is not available as a library.

Reply
J-Tricks
03/26/2013 8:41am

Yes, in the later versions, Spring is not a transitive dependency on the jira-api.

For example:

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>2.5.6</version>
<scope>provided</scope>
</dependency>

Reply
sisdog
07/17/2013 10:22pm

I'm very new to JIRA and java (I'm a .net guy trying to customize JIRA). In following this example I'm getting the same dependency issue but I have no idea where this <dependency> tag goes and how, in general, this library is made available to the running JIRA app where my plugin is running. Is it always available and this tag is just a necessity to make it available to my plugin? How do I get the dependency error to go away in eclipse?

thanks.

J-Tricks
07/18/2013 9:02am

It goes in the pom.xml. You will have to rebuild the Eclipse project using the below command and refresh/clean in Eclipse.

mvn eclipse:clean eclipse:eclipse

07/29/2013 6:10am

Is there any problem with the .zip file which you have shared above? because I have tried twice to download the file but it showing 'missing file' error.

Reply
J-Tricks
07/30/2013 7:26pm

Not sure what the issue is but works fine for us. If you are still facing the issue, write to support@j-tricks.com and we will send you the zip file.

Reply

Your comment will be posted after it is approved.


Leave a Reply


J tricks - Little JIRA Tricks