Search code examples
jira-pluginatlassian-plugin-sdk

Jira plugin fails after adding apache kafka dependancy


I am trying to create a JIRA plugin so that whenever an Issue is created, its posted to Kafka cluster. A sample listener code works fine. However when I add Maven dependency for Kafka, the plugin fails to load with below error.

org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from URL [bundle://219.0:0/META-INF/spring/plugin-context.xml]; nested exception is org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:414)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:223)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:194)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:171)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:141)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:133)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:619)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.access$800(AbstractDelegatedExecutionApplicationContext.java:57)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$3.run(AbstractDelegatedExecutionApplicationContext.java:239)
    at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.startRefresh(AbstractDelegatedExecutionApplicationContext.java:217)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.stageOne(DependencyWaiterApplicationContextExecutor.java:224)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.refresh(DependencyWaiterApplicationContextExecutor.java:177)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:154)
    at org.eclipse.gemini.blueprint.extender.internal.activator.LifecycleManager$1.run(LifecycleManager.java:213)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve(DefaultNamespaceHandlerResolver.java:132)
    at org.eclipse.gemini.blueprint.context.support.DelegatedNamespaceHandlerResolver.resolve(DelegatedNamespaceHandlerResolver.java:55)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1361)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1352)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:178)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:98)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
    ... 20 more
2020-09-04 18:06:54,517+0530 ThreadPoolAsyncTaskExecutor::Thread 26 ERROR yogeshpitale 1031x426x4 3reejl 127.0.0.1 /rest/plugins/1.0/installed-marketplace [o.e.g.b.e.internal.support.ExtenderConfiguration] Application context refresh failed (NonValidatingOsgiBundleXmlApplicationContext(bundle=com.vz.jira.defects-plugin, config=osgibundle:/META-INF/spring/*.xml))
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from URL [bundle://219.0:0/META-INF/spring/plugin-context.xml]; nested exception is org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:414)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:223)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:194)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:171)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:141)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:133)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:619)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.access$800(AbstractDelegatedExecutionApplicationContext.java:57)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$3.run(AbstractDelegatedExecutionApplicationContext.java:239)
    at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.startRefresh(AbstractDelegatedExecutionApplicationContext.java:217)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.stageOne(DependencyWaiterApplicationContextExecutor.java:224)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.refresh(DependencyWaiterApplicationContextExecutor.java:177)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:154)
    at org.eclipse.gemini.blueprint.extender.internal.activator.LifecycleManager$1.run(LifecycleManager.java:213)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve(DefaultNamespaceHandlerResolver.java:132)
    at org.eclipse.gemini.blueprint.context.support.DelegatedNamespaceHandlerResolver.resolve(DelegatedNamespaceHandlerResolver.java:55)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1361)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1352)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:178)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:98)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
    ... 20 more
2020-09-04 18:06:54,517+0530 ThreadPoolAsyncTaskExecutor::Thread 26 ERROR yogeshpitale 1031x426x4 3reejl 127.0.0.1 /rest/plugins/1.0/installed-marketplace [o.e.g.b.e.i.dependencies.startup.DependencyWaiterApplicationContextExecutor] Unable to create application context for [com.vz.jira.defects-plugin], unsatisfied dependencies: none
org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from URL [bundle://219.0:0/META-INF/spring/plugin-context.xml]; nested exception is org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:414)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:187)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:223)
    at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:194)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:171)
    at org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext.loadBeanDefinitions(OsgiBundleXmlApplicationContext.java:141)
    at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:133)
    at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:619)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.access$800(AbstractDelegatedExecutionApplicationContext.java:57)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$3.run(AbstractDelegatedExecutionApplicationContext.java:239)
    at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.startRefresh(AbstractDelegatedExecutionApplicationContext.java:217)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.stageOne(DependencyWaiterApplicationContextExecutor.java:224)
    at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.refresh(DependencyWaiterApplicationContextExecutor.java:177)
    at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:154)
    at org.eclipse.gemini.blueprint.extender.internal.activator.LifecycleManager$1.run(LifecycleManager.java:213)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.FatalBeanException: Class [com.atlassian.plugin.spring.scanner.extension.AtlassianScannerNamespaceHandler] for namespace [http://www.atlassian.com/schema/atlassian-scanner] does not implement the [org.springframework.beans.factory.xml.NamespaceHandler] interface
    at org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve(DefaultNamespaceHandlerResolver.java:132)
    at org.eclipse.gemini.blueprint.context.support.DelegatedNamespaceHandlerResolver.resolve(DelegatedNamespaceHandlerResolver.java:55)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1361)
    at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1352)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:178)
    at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:98)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:508)
    at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:392)
    ... 20 more

Below is my pom.xml

 <?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.vz.jira</groupId>
    <artifactId>jira-defects-plugin</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <organization>
        <name>Example Company</name>
        <url>http://www.example.com/</url>
    </organization>

    <name>jira-defects-plugin</name>
    <description>This is the com.vz.jira:jira-defects-plugin plugin for Atlassian JIRA.</description>
    <packaging>atlassian-plugin</packaging>

    <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>

        <!-- 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>
        
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.10.RELEASE</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-streams -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-streams</artifactId>
            <version>2.0.1</version>
            <!-- <scope>provided</scope> -->
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <!-- <version>2.5.4.RELEASE</version> -->
            <!-- <scope>provided</scope> -->
            <version>2.0.1.RELEASE</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>

    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>jira-maven-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <extractDependencies>true</extractDependencies>
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <log4jProperties>src/aps/log4j.properties</log4jProperties>
                    <!-- Uncomment to install TestKit backdoor in JIRA. -->
                    <!-- <pluginArtifacts> <pluginArtifact> <groupId>com.atlassian.jira.tests</groupId> 
                        <artifactId>jira-testkit-plugin</artifactId> <version>${testkit.version}</version> 
                        </pluginArtifact> </pluginArtifacts> -->
                    <enableQuickReload>true</enableQuickReload>

                    <!-- See here for an explanation of default instructions: -->
                    <!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
                    <instructions>
                        <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>

                        <!-- Add package to export here -->
                        <Export-Package>
                            com.vz.jira.api,
                        </Export-Package>

                        <!-- Add package import here -->
                        <Import-Package>
                            org.springframework.osgi.*;resolution:="optional",
                            org.eclipse.gemini.blueprint.*;resolution:="optional",
                            org.springframework.kafka.*;resolution:="optional",
                            org.apache.kafka.*;resolution:="optional",
                            org.apache.kafka.clients.*;resolution:="optional",
                            org.apache.kafka.common.*;resolution:="optional",
                            *;version="0";resolution:=optional
                        </Import-Package>

                        <!-- Ensure plugin is spring powered -->
                        <Spring-Context>*</Spring-Context>
                    </instructions>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependency</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.outputDirectory}/META-INF/lib</outputDirectory>
                            <includeArtifactIds>spring-jdbc,spring-tx</includeArtifactIds>
                            <stripVersion>false</stripVersion>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>com.atlassian.plugin</groupId>
                <artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
                <version>${atlassian.spring.scanner.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>atlassian-spring-scanner</goal>
                        </goals>
                        <phase>process-classes</phase>
                    </execution>
                </executions>
                <configuration>
                    <scannedDependencies>
                        <dependency>
                            <groupId>com.atlassian.plugin</groupId>
                            <artifactId>atlassian-spring-scanner-external-jar</artifactId>
                        </dependency>
                    </scannedDependencies>
                    <verbose>false</verbose>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <properties>
        <jira.version>7.13.0</jira.version>
        <amps.version>8.0.2</amps.version>
        <plugin.testrunner.version>2.0.1</plugin.testrunner.version>
        <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
        <!-- This property ensures consistency between the key in atlassian-plugin.xml 
            and the OSGi bundle's key. -->
        <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
        <!-- TestKit version 6.x for JIRA 6.x -->
        <testkit.version>6.3.11</testkit.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

</project>

And below is my Java class.

package com.vz.jira.listeners;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Component;

import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.issue.Issue;
import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;

@Component
public class IssueCreatedResolvedListener implements InitializingBean, DisposableBean {

    private static final Logger log = LoggerFactory.getLogger(IssueCreatedResolvedListener.class);
    
    @JiraImport
    private final EventPublisher eventPublisher;
    
    @Autowired
    private KafkaTemplate<Long, String> template;
    
    @Autowired
    public IssueCreatedResolvedListener(@JiraImport EventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    
    @EventListener
    public void onIssueEvent(IssueEvent issueEvent) {
       Long eventTypeId = issueEvent.getEventTypeId();
       Issue issue = issueEvent.getIssue();
       System.out.println("Got Issue with ID:"+issue.getId());
       try{
           System.out.println("Sending message to queue");
           this.template.send("issue",issue.getId(),issue.toString());
           System.out.println("Message sent to queue");
       }catch(Exception e){
           e.printStackTrace();
       }
       if (eventTypeId.equals(EventType.ISSUE_CREATED_ID)) {
          log.info("Issue {} has been created at {}.", issue.getKey(), issue.getCreated());
       } else if (eventTypeId.equals(EventType.ISSUE_RESOLVED_ID)) {
          log.info("Issue {} has been resolved at {}.", issue.getKey(), issue.getResolutionDate());
       } else if (eventTypeId.equals(EventType.ISSUE_CLOSED_ID)) {
          log.info("Issue {} has been closed at {}.", issue.getKey(), issue.getUpdated());
       }
    }

    @Override
    public void destroy() throws Exception {
        log.info("Disabling plugin");
        eventPublisher.unregister(this);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("Enabling plugin");
        eventPublisher.register(this);      
    }
}

I tried debuugin via OSGI browser (refer to the attached image). enter image description here For some of the imported packeges, "provided by" is missing. I wonder if that is causing failure of the plugin but unable to understand how to resolve it.


Solution

  • Finally, I was able crack down the problem. Here's what I did.

    1. Removed kafka-streams dependency which wasn't really required.
    2. Kept <scope>provided</scope> for all the dependencies
    3. Since JARs need to be available at runtime. I added them to plugin>execution> configuration>includeArtifactIds so that JARs are exported to the runtime environment
    4. This is important. I Experimented with different versions of Kafka. Since the JIRA version I was using, was 8.5.4 (released couple of years back), I found versions of other JARs released during/just before the same time period & used those.
    5. I faced runtime issues for some missing classes. I added those artifact Ids in plugin>execution>configuration>includeArtifactIds

    Now my final POM looks like below.

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.vz.jira</groupId>
        <artifactId>jira-defects-plugin</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    
        <organization>
            <name>Example Company</name>
            <url>http://www.example.com/</url>
        </organization>
    
        <name>jira-defects-plugin</name>
        <description>This is the com.vz.jira:jira-defects-plugin plugin for Atlassian JIRA.</description>
        <packaging>atlassian-plugin</packaging>
    
        <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>
    
            <!-- 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>
    
            <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.1.7.RELEASE</version>
                <scope>provided</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.kafka</groupId>
                <artifactId>spring-kafka</artifactId>
                <version>2.2.8.RELEASE</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
            <dependency>
                <groupId>org.apache.kafka</groupId>
                <artifactId>kafka-clients</artifactId>
                <version>2.2.1</version>
                <scope>provided</scope>
            </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> -->
            <!-- <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> 
                <version>2.10.0</version> <scope>provided</scope> </dependency> <dependency> 
                <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> 
                <version>2.10.0</version> <scope>provided</scope> </dependency> <dependency> 
                <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> 
                <version>5.2.0.RELEASE</version> <scope>provided</scope> </dependency> -->
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>com.atlassian.maven.plugins</groupId>
                    <artifactId>jira-maven-plugin</artifactId>
                    <version>${amps.version}</version>
                    <extensions>true</extensions>
                    <configuration>
                        <extractDependencies>true</extractDependencies>
                        <productVersion>${jira.version}</productVersion>
                        <productDataVersion>${jira.version}</productDataVersion>
                        <log4jProperties>src/aps/log4j.properties</log4jProperties>
                        <!-- Uncomment to install TestKit backdoor in JIRA. -->
                        <!-- <pluginArtifacts> <pluginArtifact> <groupId>com.atlassian.jira.tests</groupId> 
                            <artifactId>jira-testkit-plugin</artifactId> <version>${testkit.version}</version> 
                            </pluginArtifact> </pluginArtifacts> -->
                        <enableQuickReload>true</enableQuickReload>
    
                        <!-- See here for an explanation of default instructions: -->
                        <!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins -->
                        <instructions>
                            <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
    
                            <!-- Add package to export here -->
                            <Export-Package>
                                com.vz.jira.api,
                            </Export-Package>
    
                            <!-- Add package import here -->
                            <Import-Package>
                                <!-- org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", 
                                    org.springframework.kafka.*;resolution:="optional", org.apache.kafka.*;resolution:="optional", 
                                    org.apache.kafka.clients.*;resolution:="optional", org.apache.kafka.common.*;resolution:="optional", -->
                                *;version="0";resolution:=optional
                            </Import-Package>
                            <DynamicImport-Package>*</DynamicImport-Package>
                            <!-- Ensure plugin is spring powered -->
                            <Spring-Context>*</Spring-Context>
                        </instructions>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>copy-dependency</id>
                            <phase>process-resources</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <outputDirectory>${project.build.outputDirectory}/META-INF/lib</outputDirectory>
                                <includeArtifactIds>spring-kafka,kafka-clients,spring-context,spring-messaging</includeArtifactIds>
                                <stripVersion>false</stripVersion>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>com.atlassian.plugin</groupId>
                    <artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
                    <version>${atlassian.spring.scanner.version}</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>atlassian-spring-scanner</goal>
                            </goals>
                            <phase>process-classes</phase>
                        </execution>
                    </executions>
                    <configuration>
                        <scannedDependencies>
                            <dependency>
                                <groupId>com.atlassian.plugin</groupId>
                                <artifactId>atlassian-spring-scanner-external-jar</artifactId>
                            </dependency>
                        </scannedDependencies>
                        <verbose>false</verbose>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    <properties>
        <jira.version>7.13.0</jira.version>
        <amps.version>8.0.2</amps.version>
        <plugin.testrunner.version>2.0.1</plugin.testrunner.version>
        <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
        <!-- This property ensures consistency between the key in atlassian-plugin.xml 
            and the OSGi bundle's key. -->
        <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
        <!-- TestKit version 6.x for JIRA 6.x -->
        <testkit.version>6.3.11</testkit.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    

    The Kafka Template related settings were done in another class file as below.

    @Scanned
    public class KafkaConfig {
    
        @Bean
        public ProducerFactory<Long, String> producerFactory() {
            return new DefaultKafkaProducerFactory<>(producerConfigs());
        }
    
        @Bean
        public Map<String, Object> producerConfigs() {
            System.out.println("Intiailizing the config");
            Map<String, Object> props = new HashMap<>();
            props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092,localhost:9093");
    
            props.put(ProducerConfig.RETRIES_CONFIG, 1);
            props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, LongSerializer.class);
            props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
            props.put(ProducerConfig.BATCH_SIZE_CONFIG, 16384);
            props.put(ProducerConfig.LINGER_MS_CONFIG, 1);
            props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 33554432);
            /*props.put(ProducerConfig.ACKS_CONFIG, "all");
            props.put(ProducerConfig.RETRIES_CONFIG, 10);*/
            return props;
        }
    
        @Bean
        public KafkaTemplate<Long, String> kafkaTemplate() {
            return new KafkaTemplate<Long, String>(producerFactory());
        }
        
    }
    

    I somehow wasn't able to get KafkaTemplate configured & autowired via Spring even after adding @Configuration annotation. Hence instantiated it manually using code below (which isnt a good way though!)

    private KafkaTemplate<Long, String> template= new KafkaConfig().kafkaTemplate();
    

    and used below code to send topic to Kafka.

    package com.vz.jira.listeners;
    
    
    import org.codehaus.jackson.map.ObjectMapper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.kafka.core.KafkaTemplate;
    import org.springframework.kafka.support.SendResult;
    import org.springframework.stereotype.Component;
    import org.springframework.util.concurrent.ListenableFuture;
    import org.springframework.util.concurrent.ListenableFutureCallback;
    
    import com.atlassian.event.api.EventListener;
    import com.atlassian.event.api.EventPublisher;
    import com.atlassian.jira.event.issue.IssueEvent;
    import com.atlassian.jira.issue.Issue;
    import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
    import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;
    import com.vz.jira.config.KafkaConfig;
    import com.vz.jira.domain.Defects;
    import com.vz.jira.util.DefectUtil;
    
    @Scanned
    @Component
    public class IssueCreatedResolvedListener implements InitializingBean, DisposableBean {
    
        private static final Logger log = LoggerFactory.getLogger(IssueCreatedResolvedListener.class);
        
        @JiraImport
        private final EventPublisher eventPublisher;
        private ObjectMapper mapper = new ObjectMapper();
        private KafkaTemplate<Long, String> template= new KafkaConfig().kafkaTemplate();
        
        @Autowired
        public IssueCreatedResolvedListener(@JiraImport EventPublisher eventPublisher) {
            this.eventPublisher = eventPublisher;
        }
        
        @EventListener
        public void onIssueEvent(IssueEvent issueEvent) {
            
        try{    
           Long eventTypeId = issueEvent.getEventTypeId();
           Issue issue = issueEvent.getIssue();
           
           if(issue!=null){
               Defects defect = DefectUtil.getDesiredIssueDetails(issue); //extract necessary details to be sent to Kafka
               ListenableFuture<SendResult<Long, String>> future=template.send("issue",issue.getId(),mapper.writeValueAsString(defect));
               future.addCallback(new ListenableFutureCallback<SendResult<Long, String>>() {
    
                @Override
                public void onSuccess(SendResult<Long, String> result) {
                    handleSuccess(eventTypeId,issue.getDescription(),result);
                }
    
                @Override
                public void onFailure(Throwable ex) {
                    try {
                        handleFailure(eventTypeId,issue.getDescription(),ex);
                    } catch (Throwable e) {
                        e.printStackTrace();
                    }
                }
           
               });
           }
        }catch (Exception e){
            e.printStackTrace();
        }
          
           
        }
        
        protected void handleFailure(Long key, String value, Throwable ex) throws Throwable {
            System.out.println("Error sending message & the exception is: {} "+ ex.getMessage());   
            throw ex;
        }
    
        protected void handleSuccess(Long key, String value, SendResult<Long, String> result) {
            System.out.println("Message Sent Successfully for key: "+key+" value "+value+" partiotion is: "+ result.getRecordMetadata().partition() );
        }
    
        @Override
        public void destroy() throws Exception {
            System.out.println("Disabling plugin");
            eventPublisher.unregister(this);
        }
    
        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("Enabling plugin");
            eventPublisher.register(this);      
        }
    }