Why is my EventListener not called?

Matthias Küspert May 1, 2012

Hi,

I'm trying to catch the PluginEnabledEvent in my plugin (JIRA 4.4.4 / Plugin Version 2) and followed the steps in http://www.j-tricks.com/1/post/2012/03/plugin-lifecycle-events.html to do so:

atlassian-plugin.xml excerpt:

<component-import key="eventPublisher" interface="com.atlassian.event.api.EventPublisher"/>
    <component key="eventListener"
               class="com.mycorp.jira.listeners.PluginEnabledListener">
        <description>Class that processes the incoming JIRA events.</description>
    </component>

PluginEnabledListener.java

package com.mycorp.jira.listeners;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.plugin.event.PluginEventListener;
import com.atlassian.plugin.event.events.PluginEnabledEvent;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import java.util.logging.Logger;

public class PluginEnabledListener implements InitializingBean, DisposableBean {

    static final Logger log = Logger.getLogger(PluginEnabledListener.class.getName());

    private EventPublisher eventPublisher = null;

    public PluginEnabledListener(EventPublisher eventPublisher) {
        //
log.warning("---------------- PluginEnabledListener() ----------------");
this.eventPublisher = eventPublisher;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        eventPublisher.register(this);
        log.warning("---------------- afterPropertiesSet ----------------");
    }


    @Override
    public void destroy() throws Exception {
        eventPublisher.unregister(this);
    }

    @PluginEventListener
    public void onPluginEnabled(PluginEnabledEvent event) {
        log.warning("---------------- onPluginEnabled ----------------");
    }
}

The plugin installs and is enabled without errors in the logs. But the log-messages are never printed.

What's wrong with the code?

Thanks in advance,

Matthias

6 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

3 votes
Answer accepted
Joe Clark
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
May 2, 2012

Unfortunately, listening to plugin enable/disable events for your own plugin is a grey area. The lifecycle around when a plugin is transitioning from "enabling -> enabled" and "disabling -> disabled" is not easily determined from outside the plugin system's core. There are cases where you may receive an event, or may not, depending on what JIRA is doing.

This document on JIRA Plugin Lifecycle provides some detailed information about this.

I would suggest:

- Temporarily replace your log statements with System.out.println - this will eliminate the log4j configuration as a source of the problem.

- Try adding this to your atlassian-plugin.xml (important lines bolded):

<atlassian-plugin name="my plugin" key="com.blah.myplugin" plugins-version="2">
<plugin-info>
<version>${project.version}</version>
<bundle-instructions>
<Import-Package>
*
</Import-Package>
<Export-Package>
com.mycorp.jira.listeners.PluginEnabledListener
</Export-Package>
</bundle-instructions>
</plugin-info>

Matthias Küspert May 2, 2012

Joseph,

thanks, it was a logging problem: the System.out.println() messages get printed while the log.warning() are not. But I don't understand it: i.e. the log.warning() calls in other parts of my plugin (i.e. the rest resources) work fine and there are no special filters defined in log4j.properties.

BTW: the <ExportPackage> instruction is not needed.

Joe Clark
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
May 3, 2012

It's possible that different plugin modules in JIRA have different default log settings (REST modules may be more verbose than event modules), but I'm not very experienced with JIRA plugins to know for certain.

I know that in Confluence, you should basically assume that, by default, you will only get plugin log messages at ERROR and above, by default.

Ahmed_A_ February 28, 2016

In JIRA 7.1, I do not see log.warn or System.out.println statements, so I don't think it is working there.

2 votes
Ahmed_A_ March 1, 2016

In JIRA 7.1, I could not make this work except with a combination of things mentioned in the Atlassian documentation, and hints from other answers. This issue is an OLD issue with many people suffering from it. I suspect that the transition to OSGI and converting to Spring during the build process has broken some lifecycle logic somewhere. That's just a suspicion. Another possibility is that I'm just a bad developer.

My solution requires identifying the Listener as a Spring bean explicitly and instantiating it early (before JIRA is fully up). The bean should implement InitializingBean and DisposableBean just to benefit from the callbacks you get early and late with those. More importantly, the bean should implement LifecycleAware to get the real goal: a callback indicating the plugin has been enabled. Finally, the listener must be annotated to expose it as an OSGI service.

First, the POM dependencies I have:

&lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.jira&lt;/groupId&gt;
            &lt;artifactId&gt;jira-api&lt;/artifactId&gt;
            &lt;version&gt;${jira.version}&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. --&gt;
        &lt;!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.jira&lt;/groupId&gt;
            &lt;artifactId&gt;jira-core&lt;/artifactId&gt;
            &lt;version&gt;${jira.version}&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!-- 
        &lt;dependency&gt;
            &lt;groupId&gt;junit&lt;/groupId&gt;
            &lt;artifactId&gt;junit&lt;/artifactId&gt;
            &lt;version&gt;4.10&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
         --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.plugin&lt;/groupId&gt;
            &lt;artifactId&gt;atlassian-spring-scanner-annotation&lt;/artifactId&gt;
            &lt;version&gt;${atlassian.spring.scanner.version}&lt;/version&gt;
            &lt;scope&gt;compile&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.plugin&lt;/groupId&gt;
            &lt;artifactId&gt;atlassian-spring-scanner-runtime&lt;/artifactId&gt;
            &lt;version&gt;${atlassian.spring.scanner.version}&lt;/version&gt;
            &lt;scope&gt;runtime&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.inject&lt;/groupId&gt;
            &lt;artifactId&gt;javax.inject&lt;/artifactId&gt;
            &lt;version&gt;1&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
		&lt;dependency&gt;
			&lt;groupId&gt;org.springframework&lt;/groupId&gt;
			&lt;artifactId&gt;spring-context&lt;/artifactId&gt;
			&lt;version&gt;2.5.6&lt;/version&gt;
			&lt;scope&gt;provided&lt;/scope&gt;
		&lt;/dependency&gt;
        &lt;!-- WIRED TEST RUNNER DEPENDENCIES --&gt;
        &lt;!-- 
        &lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.plugins&lt;/groupId&gt;
            &lt;artifactId&gt;atlassian-plugins-osgi-testrunner&lt;/artifactId&gt;
            &lt;version&gt;${plugin.testrunner.version}&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
         --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;javax.ws.rs&lt;/groupId&gt;
            &lt;artifactId&gt;jsr311-api&lt;/artifactId&gt;
            &lt;version&gt;1.1.1&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;
            &lt;artifactId&gt;gson&lt;/artifactId&gt;
            &lt;version&gt;2.2.2-atlassian-1&lt;/version&gt;
        &lt;/dependency&gt;
		&lt;!-- Uncomment to use TestKit in your project. Details at https://bitbucket.org/atlassian/jira-testkit --&gt;
		&lt;!-- You can read more about TestKit at https://developer.atlassian.com/display/JIRADEV/Plugin+Tutorial+-+Smarter+integration+testing+with+TestKit --&gt;
		&lt;!--
		&lt;dependency&gt;
			&lt;groupId&gt;com.atlassian.jira.tests&lt;/groupId&gt;
			&lt;artifactId&gt;jira-testkit-client&lt;/artifactId&gt;
			&lt;version&gt;${testkit.version}&lt;/version&gt;
			&lt;scope&gt;test&lt;/scope&gt;
		&lt;/dependency&gt;
		--&gt;
    &lt;/dependencies&gt;

 

To identify the Listener as a Spring bean, modify META-INF/plugin-context.xml. If this is not done, the Listener is not instantiated by JIRA in spite of being identified as a LifecycleAware listener.

&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:atlassian-scanner="http://www.atlassian.com/schema/atlassian-scanner"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.atlassian.com/schema/atlassian-scanner
        http://www.atlassian.com/schema/atlassian-scanner/atlassian-scanner.xsd"&gt;
    &lt;atlassian-scanner:scan-indexes/&gt;
	&lt;bean id="pluginListener" class="com.baddeveloper.listeners.PluginListener"/&gt;
&lt;/beans&gt;

 

In atlassian-plugin.xml:

&lt;atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2"&gt;
	&lt;plugin-info&gt;
		&lt;description&gt;${project.description}&lt;/description&gt;
		&lt;version&gt;${project.version}&lt;/version&gt;
		&lt;vendor name="${project.organization.name}" url="${project.organization.url}" /&gt;
		&lt;param name="plugin-icon"&gt;images/pluginIcon.png&lt;/param&gt;
		&lt;param name="plugin-logo"&gt;images/pluginLogo.png&lt;/param&gt;
	&lt;/plugin-info&gt;
	&lt;!-- add our i18n resource --&gt;
	&lt;resource type="i18n" name="i18n" location="jiratest" /&gt;
	&lt;!-- add our web resources --&gt;
	&lt;web-resource key="jiratest-resources" name="jiratest Web Resources"&gt;
		&lt;dependency&gt;com.atlassian.auiplugin:ajs&lt;/dependency&gt;
		&lt;resource type="download" name="jiratest.css" location="/css/jiratest.css" /&gt;
		&lt;resource type="download" name="jiratest.js" location="/js/jiratest.js" /&gt;
		&lt;resource type="download" name="images/" location="/images" /&gt;
		&lt;context&gt;jiratest&lt;/context&gt;
	&lt;/web-resource&gt;
	&lt;component key="pluginListener" class="com.baddeveloper.listeners.PluginListener" public="true"&gt;
		&lt;description&gt;Class that processes the incoming JIRA issue events.&lt;/description&gt;
	&lt;/component&gt;
&lt;/atlassian-plugin&gt;

 

And finally the listener. The ExportAsService annotation is critical.

package com.baddeveloper.listeners;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.event.user.LoginEvent;
import com.atlassian.jira.extension.JiraStartedEvent;
import com.atlassian.jira.issue.Issue;
import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.sal.api.lifecycle.LifecycleAware;


// Exporting the component as an OSGI service is the only way I could make this
// work in JIRA 7.1. The tutorials do not mention this, and it does not work
// without this.
@ExportAsService
public class PluginListener implements InitializingBean, DisposableBean, LifecycleAware
{
	private static final Logger log;
	static
	{
		log = LoggerFactory.getLogger(PluginListener.class);
		log.warn("\n\n\t#+#+# PluginListener: STATIC INITIALIZIER\n");
	}

	private EventPublisher eventPublisher;


	public PluginListener()
	{
		log.warn("\n\n\t#+#+# PluginListener: DEFAULT CONSTRUCTOR\n");
	}

	@Override
	public void afterPropertiesSet() throws Exception
	{
		log.warn("\n\n\t#+#+# PluginListener: AFTERPROPERTIESSET\n");
	}

	@Override
	public void destroy() throws Exception
	{
		log.warn("\n\n\t#+#+# PluginListener: DESTROY\n");
		if (eventPublisher != null)
			eventPublisher.unregister(this);
	}

	@Override
	public void onStart()
	{
		log.warn("\n\n\t#+#+# PluginListener: LIFECYCLEAWARE ONSTART\n");
		log.warn("\n\n\t#+#+# PluginListener: eventPublisher is " + eventPublisher + " \n");
		if (eventPublisher == null)
			eventPublisher = ComponentAccessor.getComponent(EventPublisher.class);
		log.warn("\n\n\t#+#+# PluginListener: eventPublisher is " + eventPublisher + " \n");
		if (eventPublisher != null)
			eventPublisher.register(this);
	}

	@Override
	public void onStop()
	{
		log.warn("\n\n\t#+#+# PluginListener: ONSTOP\n");
	}
	/**
	 * Receives any {@code IssueEvent}s sent by JIRA.
	 * 
	 * @param issueEvent
	 *            the IssueEvent passed to us
	 */
	@EventListener
	public void processIssueEvent(IssueEvent issueEvent)
	{
		Long eventTypeId = issueEvent.getEventTypeId();
		Issue issue = issueEvent.getIssue();
		// if it's an event we're interested in, log it
		if (eventTypeId.equals(EventType.ISSUE_CREATED_ID))
		{
			log.warn("\n\n\t#+#+# Issue {} has been created at {}.\n", issue.getKey(), issue.getCreated());
		} 
		else if (eventTypeId.equals(EventType.ISSUE_RESOLVED_ID))
		{
			log.warn("\n\n\t#+#+# Issue {} has been resolved at {}.\n", issue.getKey(), issue.getResolutionDate());
		} 
		else if (eventTypeId.equals(EventType.ISSUE_CLOSED_ID))
		{
			log.warn("\n\n\t#+#+# Issue {} has been closed at {}.\n", issue.getKey(), issue.getUpdated());
		}
		else
		{
			log.warn("\n\n\t#+#+# Unhandled event type\n");
		}
	}
 
	// This does NOT WORK. Instead use the onStart() method above.
	@EventListener
	public void processJiraStartedEvent(JiraStartedEvent jiraStartedEvent) 
	{
		log.warn("\n\n\t#+#+# PluginListener: processJiraStartedEvent\n");
		log.warn("\n\n\t#+#+# PluginListener: eventPublisher is " + eventPublisher + " \n");
		if (eventPublisher == null)
			eventPublisher = ComponentAccessor.getComponent(EventPublisher.class);
		log.warn("\n\n\t#+#+# PluginListener: eventPublisher is " + eventPublisher + " \n");
		eventPublisher.register(this);
	}
	
	@EventListener
	public void processLoginEvent(LoginEvent loginEvent)
	{
		log.warn("\n\n\t#+#+# PluginListener: processLoginEvent (" + loginEvent.getUser().getUsername() + ") \n");
	}
}
Thomas Ehardt April 18, 2016

When I do this, I get this error when trying to run:

[ERROR] atlassian-plugin.xml contains a definition of component. This is not allowed when Atlassian-Plugin-Key is set.

 

The solution to this is to comment out/remove the line in pom.xml that defines Atlassian-Plugin-Key and the corresponding attribute in atlassian-plugin.xml

Travis DePuy September 28, 2016

Great answer! Thank you for bringing this topic into the present. 

I did get a compiler complaint on the onStop method:

.... method does not override or implement a method from a supertype

I just commented it out, and I think I can use the destroy method to capture if/when the plugin is disabled/removed. 

@Thomas Ehardt I ran into that same error in another place in my plugin and followed the steps here and just imported import com.atlassian.plugin.spring.scanner.annotation.component.Scanned; and then put @Scanned before my class definition. 

Erling Haugstad October 24, 2016

I cant for the life of me get this to work, do you have a running example that i can download an try @Travis DePuy?

Roman Samorodov
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 10, 2017

Cool!

Thanks a lot smile

0 votes
Roman Samorodov
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 11, 2017

I solved this problem just restarting JIRA service.

0 votes
Travis DePuy October 25, 2016

@Erling Haugstad, putting this into its own answer to help with scrolling. 

This is the entire java file. It successfully prints the log statements when the plugin is installed. I commented out some items, but I'm not sure why. We decided to abandon this so there isn't much here, but the triggers I needed to fire on install/uninstall do work. 

I'm pretty sure I didn't have to do anything in the atlassian-plugin.xml file, it is all in this java file. 

 

package com.xmatters;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
/*import com.atlassian.plugin.event.events.PluginEnabledEvent;
import com.atlassian.plugin.event.events.PluginDisabledEvent;*/
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.util.json.JSONException;
import com.atlassian.jira.util.json.JSONObject;
import com.atlassian.jira.util.json.JSONArray;
import com.atlassian.sal.api.lifecycle.LifecycleAware;
/*
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.event.user.LoginEvent;
import com.atlassian.jira.extension.JiraStartedEvent;
import com.atlassian.jira.issue.Issue;
*/
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

@ExportAsService
@Scanned
public class xMattersStartupService implements LifecycleAware, InitializingBean, DisposableBean {
    private static final Logger log = LogManager.getLogger( "com.xmatters.xmatters" );
    private EventPublisher eventPublisher;
    public xMattersStartupService() {
    }
 
    @Override
    public void onStart() {
        log.error("\n\n\t#+#+# xMattersStartupService: LIFECYCLEAWARE ONSTART\n");
         
    }

    @Override
    public void destroy() throws Exception {
        log.error("\n\n\t#+#+# xMattersStartupService: DESTROY\n");
    }
 
    @Override
    public void afterPropertiesSet() throws Exception {
        // log.error("\n\n\t#+#+# xMattersStartupService: AFTERPROPERTIESSET\n");
    }
	public Class&lt;? extends Annotation&gt; annotationType() {
		return null;
	}
/*    @Override
    public void onStop()
    {
        log.error("\n\n\t#+#+# xMattersStartupService: ONSTOP\n");
    }*/
/*
    public void onPluginEnabled( PluginEnabledEvent event )
    {
        log.error("\n\n\t#+#+# xMattersStartupService: onPluginEnabled\n");
    }
    
    public void onPluginDisabled( PluginDisabledEvent event )
    {
        log.error("\n\n\t#+#+# xMattersStartupService: onPluginDisabled\n");
    }
*/
}
Erling Haugstad October 26, 2016

Thanks!

0 votes
Ahmed_A_ February 28, 2016

This problem is occurring for me as well in JIRA 7.1, 4 years later.

0 votes
Radek Kantor
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
May 2, 2012

Hi,

1. Did you configure log4j.properties for your plugin, enable/set the logging in some way?

2. I think, that component-importinterface="com.atlassian.event.api.EventPublisher" is not necessary.

3. Add onIssueEvent methot for test.

@EventListener
    public void onIssueEvent(IssueEvent issueEvent) {
        Long eventTypeId = issueEvent.getEventTypeId();
        
        // if it's an event we're interested in, log it
        if (eventTypeId.equals(EventType.ISSUE_DELETED_ID)) {
        	log.info("Delete issue event detected.");
        }
    }

Matthias Küspert May 2, 2012

Hi Radek,

1. no, no special log4j config for the plugin, but log messages from other parts of the plugin show up Ok

2. that's my thinking too - I just copied it from the example

3. I added an onIssueEvent() method with just a call to log.warning() and it too is not called when i.e. creating an issue

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events