Atlassian Spring Scanner: Plugin modules not enabled Edited

Hello,

 

When I start JIRA from the SDK, I notice my plugin is not enabled under Manage Add-Ons. Upon clicking Enable, I receive the following message:

"This add-on failed to enable. Refer to the logs for more information."

 

The logs say,

"Cannot execute atlasian-spring-scanner-runtime: plugin has an extra copy of atlassian-spring-scanner-annotation clases, perhaps embedded inside the target plugin 'com.psi.intern.jira.custom-fields'; embedding scanner-annotations is not supported since scanner version 2.0. Use 'mvn dependency:tree' and ensure the atlassian-spring-scanner-annotation depenency in your plugin has <scope>provided</scope>, not 'runtime' or 'compile', and you have NO dependency on atlassian-spring-scanner-runtime.

 

I changed these dependencies in the pom accordingly, however, I receive this message nonetheless. Uncertain where else I should look for this problem.

 

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>xxx</groupId>
    <artifactId>custom-fields</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>atlassian-plugin</packaging>

    <name>JIRA Add-On :: Custom Fields AND One Gadget</name>
    <description>This is the container plugin for all internally used custom fields for Atlassian JIRA and one gadget.</description>

    <organization>
       
    </organization>

    <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>com.atlassian.jira</groupId>
            <artifactId>jira-rest-plugin</artifactId>
            <version>7.1.0-QR20151013113456</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugins.rest</groupId>
            <artifactId>atlassian-rest-common</artifactId>
            <version>${rest.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-gadgets-plugin</artifactId>
            <version>${jira.version}</version>
            <scope>compile</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>provided</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>
        <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>
        -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.8.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-jira-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <noWebapp>true</noWebapp>
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <allowGoogleTracking>false</allowGoogleTracking>
                    <!-- 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>
                    <enableFastdev>false</enableFastdev>
                    <!-- 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.psi.intern.jira.customfield.api.*</Export-Package>
                        <!-- Add package import here -->
                        <Import-Package>org.springframework.osgi.*;resolution:="optional", org.eclipse.gemini.blueprint.*;resolution:="optional", *</Import-Package>
                        <!-- Ensure plugin is spring powered -->
                        <Spring-Context>*</Spring-Context>
                    </instructions>
                </configuration>
            </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>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <jira.version>7.3.0</jira.version>
        <amps.version>6.2.6</amps.version>
        <plugin.testrunner.version>1.2.3</plugin.testrunner.version>
        <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
        <!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. -->
        <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>
        <rest.version>3.2.14</rest.version>
    </properties>
</project>

 

Thanks!

2 answers

0 votes

Try to go to the folder of your plugin and make a clean package

atlas-mvn clean package

Thanks Alexey, that helped change the error message. Now I am getting "No qualifying bean of type [com.atlassian.jira.config.properties.ApplicationProperties] ... expected at least 1 bean which qualifies as autowire candidate for this dependency."

 

Will try to figure this out and get back to this question with an update.

Hello,

Unfortunately I can not provide feedback about the new error. I would need to see your code.

Here is the Java class for the custom field in my plugin, as well as MyPluginComponentImpl.java:

 

package com.psi.intern.jira.customfield;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.atlassian.jira.issue.customfields.impl.AbstractSingleFieldType;
import com.atlassian.jira.issue.customfields.impl.FieldValidationException;
import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
import com.atlassian.jira.issue.customfields.persistence.PersistenceFieldType;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;

import java.math.BigDecimal;

@Scanned
public class PercentageField extends AbstractSingleFieldType<BigDecimal> {
    private static final Logger log = LoggerFactory.getLogger(PercentageField.class);

    //@Autowired
    public PercentageField(@ComponentImport CustomFieldValuePersister customFieldValuePersister, @ComponentImport GenericConfigManager genericConfigManager ) {

        super(customFieldValuePersister, genericConfigManager);

    }

    @Override
    protected PersistenceFieldType getDatabaseType()
    {
        return PersistenceFieldType.TYPE_DECIMAL;
    }

    @Override
    protected Object getDbValueFromObject(final BigDecimal customFieldObject)
    {
        return getStringFromSingularObject(customFieldObject);
    }

    @Override
    protected BigDecimal getObjectFromDbValue(final Object databaseValue)
            throws FieldValidationException
    {
        return getSingularObjectFromString(String.valueOf(databaseValue));
    }

    @Override
    public String getStringFromSingularObject(final BigDecimal singularObject)
    {
        if (singularObject == null) {
            return "";
        }
        // format
        return singularObject.toString();
    }

    @Override
    public BigDecimal getSingularObjectFromString(final String string)
            throws FieldValidationException
    {
        if (string == null)
            return null;
        try
        {
            final BigDecimal decimal = new BigDecimal(string);
            // Check that we don't have too many decimal places
            if (decimal.scale() > 2)
            {
                throw new FieldValidationException(
                        "Maximum of 2 decimal places are allowed.");
            }
            return decimal.setScale(2);
        }
        catch (NumberFormatException ex)
        {
            throw new FieldValidationException("Not a valid number.");
        }
    }
}

 

package com.psi.intern.jira.customfield.impl;

import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.sal.api.ApplicationProperties;
import com.psi.intern.jira.customfield.api.MyPluginComponent;

import javax.inject.Inject;
import javax.inject.Named;

@ExportAsService ({MyPluginComponent.class})
@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent
{
    @ComponentImport
    private final ApplicationProperties applicationProperties;

    @Inject
    public MyPluginComponentImpl(final ApplicationProperties applicationProperties)
    {
        this.applicationProperties = applicationProperties;
    }

    public String getName()
    {
        if(null != applicationProperties)
        {
            return "myComponent:" + applicationProperties.getDisplayName();
        }
        
        return "myComponent";
    }
}
0 votes

Hello,

I just tried your code with your pom and everything worked fine for me. There must be something else.

and you should change //@Autowired to @Inject

Hi,

I deleted the class MyPluginComponentImpl because I wasn't using it anyway, and everything is working fine now.

 

Why should @Autowired be @Inject? What is the difference there exactly?

I had problems with autowired in constructors before. I use inject and had no ptoblems so far. Instead of scanned i use named. also the same reason. You removed the file but it does not mean you solved the problem. You can get same error later when you call your custom field.

Okay, I restored the file and changed @Autowired to @Inject, as well as @Scanned to @Named, and I get the following errors:

 

cannot find symbol

symbol class Named

 

and the same for Inject. Sorry when the solution is obvious but after a Google Search the solution for 'cannot find symbol' seems to be injecting a dependency into the constructor, which I think is exactly what I'm doing right now...?

Can you add imports to the class

import javax.inject.Inject;
import javax.inject.Named;

add dependency to the pom.xml
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
<scope>provided</scope>
</dependency>

Thanks. Now I get BUILD SUCCESSFUL with atlas-mvn install.

 

However now I receive "Plugin 'com.psi.intern.jira.custom-fields-tests' never resolved service '&myComponent' with filter '(objectClass=com.psi.intern.jira.customfield.api.MyPluginComponent)'"

Class in question:

package com.psi.intern.jira.customfield.api;

public interface MyPluginComponent
{
    String getName();
}

 

Do you have an idea where I should start looking for the problem?

I also don't understand why it's telling me the Plugin 'com.psi.intern.jira.custom-fields-tests' is not resolving. This is the plugin key in an atlassian-plugin.xml in target\test-classes. Where is that coming from?

You can delete test folder in your plugin. And run atlas-mvn clean install

Hello Alexey. Thank you for your continued support. I deleted the test folder and ran atlas-mvn clean install.

Now everything is running, I am getting a new error but this I can discuss in a different post as it is a different topic. Important for this topic, the modules of my plugin are enabled again!

Here is my code:

PercentageField.java:

package com.psi.intern.jira.customfield;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.atlassian.jira.issue.customfields.impl.AbstractSingleFieldType;
import com.atlassian.jira.issue.customfields.impl.FieldValidationException;
import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
import com.atlassian.jira.issue.customfields.persistence.PersistenceFieldType;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.beans.factory.annotation.Autowired;

import java.math.BigDecimal;

@Scanned
public class PercentageField extends AbstractSingleFieldType<BigDecimal> {
    private static final Logger log = LoggerFactory.getLogger(PercentageField.class);

    //@Inject
    public PercentageField(@ComponentImport CustomFieldValuePersister customFieldValuePersister, @ComponentImport GenericConfigManager genericConfigManager) {

        super(customFieldValuePersister, genericConfigManager);

    }

    @Override
    protected PersistenceFieldType getDatabaseType()
    {
        return PersistenceFieldType.TYPE_DECIMAL;
    }

    @Override
    protected Object getDbValueFromObject(final BigDecimal customFieldObject)
    {
        return getStringFromSingularObject(customFieldObject);
    }

    @Override
    protected BigDecimal getObjectFromDbValue(final Object databaseValue)
            throws FieldValidationException
    {
        return getSingularObjectFromString(String.valueOf(databaseValue));
    }

    @Override
    public String getStringFromSingularObject(final BigDecimal singularObject)
    {
        if (singularObject == null) {
            return "";
        }
        // format
        return singularObject.toString();
    }

    @Override
    public BigDecimal getSingularObjectFromString(final String string)
            throws FieldValidationException
    {
        if (string == null)
            return null;
        try
        {
            final BigDecimal decimal = new BigDecimal(string);
            // Check that we don't have too many decimal places
            if (decimal.scale() > 2)
            {
                throw new FieldValidationException(
                        "Maximum of 2 decimal places are allowed.");
            }
            return decimal.setScale(2);
        }
        catch (NumberFormatException ex)
        {
            throw new FieldValidationException("Not a valid number.");
        }
    }
}

MyPluginComponentImpl.java:

package com.psi.intern.jira.customfield.impl;

import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
//import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.jira.config.properties.ApplicationProperties;
import com.psi.intern.jira.customfield.api.MyPluginComponent;

import javax.inject.Inject;
import javax.inject.Named;

import org.springframework.beans.factory.annotation.Autowired;

@ExportAsService ({MyPluginComponent.class})
@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent
{
    private final ApplicationProperties applicationProperties;

    @Inject
    public MyPluginComponentImpl(final ApplicationProperties applicationProperties)
    {
        this.applicationProperties = applicationProperties;
    }

    public String getName()
    {
        if(null != applicationProperties)
        {
            return "myComponent:";
        }
        
        return "myComponent";
    }
}

 MyPluginComponent.java:

package com.psi.intern.jira.customfield.api;

public interface MyPluginComponent
{
    //String getString(String name);
}

 

As you'll notice, I had to comment out the method getString() in the MyPluginComponent interface, as well as remove the call to getDisplayName() in the MyPluginComponentImpl.java, because these methods appear not to exist in com.atlassian.jira.config.properties.ApplicationProperties and I am just having problems when I import com.atlassian.sal.api.ApplicationProperties.

I find it quite frustrating that I have to change so much from what's given in the tutorials, though. Why do the tutorials include methods that don't exist in the classes they import? Why is there a whole test folder which I should just delete?

 

In any case thank you very much for your help, I would have been stuck in the same place for a while otherwise!

Suggest an answer

Log in or Sign up to answer
Community showcase
Published Nov 27, 2018 in Portfolio for Jira

Introducing a new planning experience in Portfolio for Jira (Server/DC)

In the past, Portfolio for Jira required a high degree of detail–foresight that was unrealistic for many businesses to   have–in   order to produce a reliable long-term roadmap. We're tur...

2,930 views 19 22
Read article

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you