Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

How to make confluence dependency injenction work

Johan March 7, 2016

I've tried to build the example confluence macro following this page:

https://developer.atlassian.com/confdev/tutorials/macro-tutorials-for-confluence/creating-a-new-confluence-macro

I am using atlas SDK v6.2.3 and JRE 1.8.0_74 (64 bit).

I have tried numerous times, but reach a dead end. Following the example I get the following problem when running confluence with the plugin (atlas-run):

[INFO] [talledLocalContainer] 2016-03-08 07:52:05,984 ERROR [AtlassianEvent::CustomizableThreadFactory-1] [atlassian.plugin.module.PrefixDelegatingModuleFactory] createModule Detected an error instantiating the module via Spring. This usually means that you haven't created a <component-import> for the interface you're trying to use. https://developer.atlassian.com/x/TAEr for more details.
[INFO] [talledLocalContainer] 2016-03-08 07:52:05,985 WARN [AtlassianEvent::CustomizableThreadFactory-1] [impl.macro.metadata.AllMacroMetadataCache] lambda$loadMacroMetadata$1 Failed to make metadata for module 'com.example.plugins.tutorial.confluence.tutorial-confluence-macro-demo:my-macro': Error creating bean with name 'com.example.plugins.tutorial.confluence.ExampleMacro': Unsatisfied dependency expressed through constructor argument with index 0 of type [com.atlassian.confluence.xhtml.api.XhtmlContent]: : No qualifying bean of type [com.atlassian.confluence.xhtml.api.XhtmlContent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.atlassian.confluence.xhtml.api.XhtmlContent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
[INFO] [talledLocalContainer] org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.plugins.tutorial.confluence.ExampleMacro': Unsatisfied dependency expressed through constructor argument with index 0 of type [com.atlassian.confluence.xhtml.api.XhtmlContent]: : No qualifying bean of type [com.atlassian.confluence.xhtml.api.XhtmlContent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.atlassian.confluence.xhtml.api.XhtmlContent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

So I try to follow the instructions here: https://developer.atlassian.com/docs/faq/troubleshooting/unsatisfieddependencyexception-error-creating-bean-with-name

Adding the following to atlassian-plugin.xml:

<component-import key="xhtmlContent" interface="com.atlassian.confluence.xhtml.api.XhtmlContent"/>

....I get the following error when running atlas-package (after atlas-mvn eclipse:eclipse):

[ERROR] Failed to execute goal com.atlassian.maven.plugins:maven-confluence-plugin:6.2.2:validate-manifest (default-validate-manifest) on project tutorial-confluence-macro-demo:
[ERROR]
[ERROR] atlassian-plugin.xml contains a definition of component-import. This is not allowed when Atlassian-Plugin-Key is set.
[ERROR]

 

Since the plugin is of version 2 I expected the constructor injection to work, but I cannot figure this out.

How do I resolve this?

Thanks.

3 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

8 votes
Answer accepted
Johan March 9, 2016

Thanks a lot Jon!

I added the annotations and the build worked. However, I still got a run-time error saying that there was no qualifying bean:

Unsatisfied dependency expressed through constructor argument with index 0 of type [com.atlassian.confluence.xhtml.api.XhtmlContent]: : No qualifying bean of type [com.atlassian.confluence.xhtml.api.XhtmlContent] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. 

 

So I tried to adding things to plugin-context.xml (not really understanding what I was doing), but it didn't help. Finally I stumbled upon som open source code in Bitbucket, which led me to the solution:

  1. Your class will need an @Scanned annotation (not @Component)

    Your second and third points still valid:
     
  2. Your constructor will need an @Autowired annotation

  3. The XhtmlContent argument in the constructor will need an @ComponentImport annotation

Now it worked!

 

If anyone can point to a reference where this is explained (not just a "general" reference, but an explanation), it would probably help the community a lot. For example, I did play around with annotations before asking the community, but since I didn't find any source on the Internet explaining this I wasn't able to find the right combination.

 

For other's who read this thread:

This is how the main class now looks:

...
@Scanned
public class ExampleMacro implements Macro {
	private final XhtmlContent xhtmlUtils;
	
	@Autowired
	public ExampleMacro(@ComponentImport XhtmlContent xhtmlUtils) {
		this.xhtmlUtils = xhtmlUtils;
	}
...

 

My (auto-generated) plugin-context.xml looks like this (and needed no changes):

&lt;?xml version="1.0" encoding="UTF-8"?&gt;

&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;/beans&gt;

 

This source helped me:

https://bitbucket.org/atlassian/confluence-chart-plugin/pull-requests/10/condev-34757-transformless-plugin/diff

 

3 votes
Jon Bevan [Adaptavist]
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.
March 7, 2016

So you'll need to make sure you have a valid src/main/resources/META-INF/spring/plugin-context.xml which should have been generated for you by the atlas-create-confluence-plugin command.

Additionally, on your class you need a number of Spring Scanner annotations:

  1. Your class will need an @Component annotation
  2. Your constructor will need an @Autowired annotation
  3. The XhtmlContent argument in the constructor will need an @ComponentImport annotation

Once those annotations have been added, you can remove the <component-import> from your atlassian-plugin.xml and recompile.

0 votes
BKippelt June 1, 2016

I have a similar Problem.

@Scanned
@Path("/test")
public class TestRest {
    private static final Logger log = LoggerFactory.getLogger(Test.class);

    @Autowired
    public TestRest(@ComponentImport ApplicationLinkService appLinkService) {
    }
}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.atlassian.applinks.api.ApplicationLinkService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport(value=)}

 

 

BKippelt June 1, 2016

Next ERROR:

Caused by: java.lang.NoClassDefFoundError: com/atlassian/plugin/spring/scanner/ProductFilter

Tunahan Coban November 14, 2016

I have the same problem.. Do you know a solution for this?

Alexander Botor November 16, 2016

Hi,

for my plugin, this works. Check your pom for:

&lt;dependency&gt;
            &lt;groupId&gt;com.atlassian.plugins.rest&lt;/groupId&gt;
            &lt;artifactId&gt;atlassian-rest-common&lt;/artifactId&gt;
            &lt;version&gt;1.0.2&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&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;

 

...and

&lt;properties&gt;
        &lt;confluence.version&gt;5.7.4&lt;/confluence.version&gt;
        &lt;confluence.data.version&gt;5.7.4&lt;/confluence.data.version&gt;
        &lt;amps.version&gt;6.2.6&lt;/amps.version&gt;
        &lt;plugin.testrunner.version&gt;1.2.3&lt;/plugin.testrunner.version&gt;
        &lt;atlassian.spring.scanner.version&gt;1.2.13&lt;/atlassian.spring.scanner.version&gt;
        &lt;!-- This key is used to keep the consistency between the key in atlassian-plugin.xml and the key to generate bundle. --&gt;
        &lt;atlassian.plugin.key&gt;${project.groupId}.${project.artifactId}&lt;/atlassian.plugin.key&gt;
    &lt;/properties&gt;

Maybe you want to develop to another version, but this is just what works for me. smile

 

My main class looks like this:

@Named("istabsolutegal") //do I need this?
@Path("/getRestrictedPagesForUser")
public class DeactivatedUserViewRestrictions {
    @ComponentImport
    private final PageManager pageManager;
    @ComponentImport
    private final SpaceManager spaceManager;
    @ComponentImport
    private final UserAccessor userAccessor;
    
    private static final Logger log = LoggerFactory.getLogger(DeactivatedUserViewRestrictions.class);
    @Inject
    public DeactivatedUserViewRestrictions(PageManager pageManager, UserAccessor userAccessor, SpaceManager spaceManager) {
        this.pageManager = pageManager;
        this.userAccessor = userAccessor;
        this.spaceManager = spaceManager;
        log.debug("constructed");
    }
    @GET
    @AnonymousAllowed
    @Produces({ MediaType.APPLICATION_JSON })
    public Response getMessage(@QueryParam("user") String userName) {
	...
	}
}

 

By the way: Maybe you want to set your logger to:

private static final Logger log = LoggerFactory.getLogger(TestRest.class);

 

 

Alexander Botor November 16, 2016

I am not sure what of that is really necessary to get the rest plugin working with constructor based injection, but it works. smile

TAGS
AUG Leaders

Atlassian Community Events