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
Community moderators have prevented the ability to post new answers.
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>
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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:
<dependencies> <dependency> <groupId>com.atlassian.jira</groupId> <artifactId>jira-api</artifactId> <version>${jira.version}</version> <scope>provided</scope> </dependency> <!-- Add dependency on jira-core if you want access to JIRA implementation classes as well as the sanctioned API. --> <!-- This is not normally recommended, but may be required eg when migrating a plugin originally developed against JIRA 4.x --> <dependency> <groupId>com.atlassian.jira</groupId> <artifactId>jira-core</artifactId> <version>${jira.version}</version> <scope>provided</scope> </dependency> <!-- <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> --> <dependency> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-annotation</artifactId> <version>${atlassian.spring.scanner.version}</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.atlassian.plugin</groupId> <artifactId>atlassian-spring-scanner-runtime</artifactId> <version>${atlassian.spring.scanner.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>2.5.6</version> <scope>provided</scope> </dependency> <!-- WIRED TEST RUNNER DEPENDENCIES --> <!-- <dependency> <groupId>com.atlassian.plugins</groupId> <artifactId>atlassian-plugins-osgi-testrunner</artifactId> <version>${plugin.testrunner.version}</version> <scope>test</scope> </dependency> --> <dependency> <groupId>javax.ws.rs</groupId> <artifactId>jsr311-api</artifactId> <version>1.1.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.2.2-atlassian-1</version> </dependency> <!-- Uncomment to use TestKit in your project. Details at https://bitbucket.org/atlassian/jira-testkit --> <!-- You can read more about TestKit at https://developer.atlassian.com/display/JIRADEV/Plugin+Tutorial+-+Smarter+integration+testing+with+TestKit --> <!-- <dependency> <groupId>com.atlassian.jira.tests</groupId> <artifactId>jira-testkit-client</artifactId> <version>${testkit.version}</version> <scope>test</scope> </dependency> --> </dependencies>
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.
<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"> <atlassian-scanner:scan-indexes/> <bean id="pluginListener" class="com.baddeveloper.listeners.PluginListener"/> </beans>
In atlassian-plugin.xml:
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2"> <plugin-info> <description>${project.description}</description> <version>${project.version}</version> <vendor name="${project.organization.name}" url="${project.organization.url}" /> <param name="plugin-icon">images/pluginIcon.png</param> <param name="plugin-logo">images/pluginLogo.png</param> </plugin-info> <!-- add our i18n resource --> <resource type="i18n" name="i18n" location="jiratest" /> <!-- add our web resources --> <web-resource key="jiratest-resources" name="jiratest Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <resource type="download" name="jiratest.css" location="/css/jiratest.css" /> <resource type="download" name="jiratest.js" location="/js/jiratest.js" /> <resource type="download" name="images/" location="/images" /> <context>jiratest</context> </web-resource> <component key="pluginListener" class="com.baddeveloper.listeners.PluginListener" public="true"> <description>Class that processes the incoming JIRA issue events.</description> </component> </atlassian-plugin>
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"); } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Cool!
Thanks a lot
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
I solved this problem just restarting JIRA service.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@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<? extends Annotation> 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"); } */ }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi,
1. Did you configure log4j.properties for your plugin, enable/set the logging in some way?
2. I think, that component-import
interface
=
"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."); } }
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.