Missed Team ’24? Catch up on announcements here.

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

Custom macro doesn't show in Macro Browser

Qing LIANG March 31, 2016

Hello,

 

I am following the official tutorial about writing a custom macro for confluence version 5.9.7. The only thing I changed from the tutorial is the plugin name and the package name which you can see in the file I attached here. After running altas-run.bat, I can find in the system configuration page in Confluence that the plugin is successfully installed and enabled, with both the two modules. But when I tried to insert this macro in a page, I couldn't find it in the macro browser. 

 

I then followed another tutorial writing a plugin with JSON in exactly the same step, everything goes fine as the previous one but still can not find the macro in the macro browser. I have spent hours on this issue and got really fed up.

 

The ExampleMacro.java is under the package com.example.api. Same code as written in the tutorial. Below is the plugin description file and pom.

 

<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>
    
    <xhtml-macro name="my-confluence-plugin" class="com.example.api.ExampleMacro" key="my-macro">
        <parameters/>
        <category name="navigation" />
    </xhtml-macro>
    <!-- add our i18n resource -->
    <resource type="i18n" name="i18n" location="my-confluence-plugin"/>
    
    <!-- add our web resources -->
    <web-resource key="my-confluence-plugin-resources" name="my-confluence-plugin Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        
        <resource type="download" name="my-confluence-plugin.css" location="/css/my-confluence-plugin.css"/>
        <resource type="download" name="my-confluence-plugin.js" location="/js/my-confluence-plugin.js"/>
        <resource type="download" name="images/" location="/images"/>
        <context>my-confluence-plugin</context>
    </web-resource>
    
</atlassian-plugin>
<?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.example</groupId>
    <artifactId>my-confluence-plugin</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <organization>
        <name>Example</name>
        <url>http://www.example.com/</url>
    </organization>
    <name>my-confluence-plugin</name>
    <description>This is the com.amundi:my-confluence-plugin plugin for Atlassian Confluence.</description>
    <packaging>atlassian-plugin</packaging>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence</artifactId>
            <version>${confluence.version}</version>
            <scope>provided</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>
        <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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-confluence-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>${confluence.version}</productVersion>
                    <productDataVersion>${confluence.data.version}</productDataVersion>
                    <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.example.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>1.2.6</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>
        <confluence.version>5.9.7</confluence.version>
        <confluence.data.version>5.9.7</confluence.data.version>
        <amps.version>6.2.3</amps.version>
        <plugin.testrunner.version>1.2.3</plugin.testrunner.version>
        <atlassian.spring.scanner.version>1.2.6</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>
    </properties>
</project>

 

I found the following ERROR message about creating the bean for my custom macro class : 

 

...
[INFO] [talledLocalContainer] 2016-03-31 14:00:29,799 WARN [UpmScheduler:thread-2] [atlassian.upm.pac.PacClientImpl] fetchMpacAppInfo Error when querying application info from MPAC: com.atlassian.marketplace.client.MpacException: java.net.UnknownHostException: marketplace.atlassian.com
[INFO] [talledLocalContainer] 2016-03-31 14:00:32,829 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-31 14:00:32,830 WARN [AtlassianEvent::CustomizableThreadFactory-1] [impl.macro.metadata.AllMacroMetadataCache] lambda$loadMacroMetadata$1 Failed to make metadata for module 'com.example.my-confluence-plugin:my-macro': Error creating bean with name 'com.example.api.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.api.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: {}
...

6 answers

1 accepted

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

2 votes
Answer accepted
Qing LIANG March 31, 2016

I just made some search and found out the problem. 

As answered by @Jon Mort (Adaptavist) in this question , the official tutorial code is in fact out of date. As I am using the SDK version 6.2.4(latest),  Atlassian Spring Scanner is used instead of <component> and <component-import>.

 

Then I found the answer by @Brad Baker in this question, and tried to add the annotations @Scanned (can't be omitted) to my macro class and @ComponentImport  to the parameter of type XhtmlContent in my constructor indicating the scanner to find this component from outside and make it available to me. Also I removed the <component-import> elements in my atlassian-plugin.xml as they are no longer used in fact by my version of SDK. And no need to remove the <Atlassian-Plugin-Key> declaration in the pom then. 

 

I don't know from which version the SDK no longer support <component-import> and <component> declarations in the atlassian-plugin.xml. But the the SDK I am using already made changes to respect the instructions in the description for atlassian-spring-scanner of version 1.x. @Panos Karampis I haven't tried your solution yet, but I think it will work for me too. 

 

Overall, if you are using the latest SDK and following the current tutorial by Confluence, then the only thing you need to change to make your macro really work is remove <component-import> and <component> and add the necessary annotations in your java code.

 

 

 

noobschoolbus
I'm New Here
I'm New Here
Those new to the Atlassian Community have posted less than three times. Give them a warm welcome!
July 11, 2016

Thanks @Qing LIANG this answer worked for me in the atlassian-plugin-sdk-6.1.0, but it would have been easier to see the tutorial example updated so here's what it should look like:

package com.example.plugins.tutorial.confluence;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.confluence.xhtml.api.XhtmlContent;
import com.atlassian.confluence.content.render.xhtml.XhtmlException;
import com.atlassian.confluence.xhtml.api.MacroDefinition;
import com.atlassian.confluence.xhtml.api.MacroDefinitionHandler;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;

@Named ("ExampleMacro")
public class ExampleMacro implements Macro {
	
	@ComponentImport
	private final XhtmlContent xhtmlUtils;

	@Inject
	public ExampleMacro(XhtmlContent xhtmlUtils) 
	{
		this.xhtmlUtils = xhtmlUtils;	
	}

	@Override
	public String execute(Map&lt;String, String&gt; parameters, String bodyContent, ConversionContext conversionContext)
			throws MacroExecutionException {
        String body = conversionContext.getEntity().getBodyAsString();
        final List&lt;MacroDefinition&gt; macros = new ArrayList&lt;MacroDefinition&gt;();
        try
        {
            xhtmlUtils.handleMacroDefinitions(body, conversionContext, new MacroDefinitionHandler()
            {
                @Override
                public void handle(MacroDefinition macroDefinition)
                {
                    macros.add(macroDefinition);
                }
            });
        }
        catch (XhtmlException e)
        {
            throw new MacroExecutionException(e);
        }
        StringBuilder builder = new StringBuilder();
        builder.append("&lt;p&gt;");
        if (!macros.isEmpty())
        {
            builder.append("&lt;table width=\"50%\"&gt;");
            builder.append("&lt;tr&gt;&lt;th&gt;Macro Name&lt;/th&gt;&lt;th&gt;Has Body?&lt;/th&gt;&lt;/tr&gt;");
            for (MacroDefinition defn : macros)
            {
                builder.append("&lt;tr&gt;");
                builder.append("&lt;td&gt;").append(defn.getName()).append("&lt;/td&gt;&lt;td&gt;").append(defn.hasBody()).append("&lt;/td&gt;");
                builder.append("&lt;/tr&gt;");
            }
            builder.append("&lt;/table&gt;");
        }
        else
        {
            builder.append("You've done built yourself a macro! Nice work.");
        }
        builder.append("&lt;/p&gt;");
        return builder.toString();	
	}

	@Override
	public BodyType getBodyType() {
		return BodyType.NONE;
	}

	@Override
	public OutputType getOutputType() {
		 return OutputType.BLOCK;
	}
}

Note the additional imports and annotations 

With the atlassian-plugin.xml and pom.xml file being the same as that in the tutorial. This is when the project is created from the skeleton eg "atlas-create-confluence-plugin" which now defaults to using the spring scanner.

0 votes
Panos
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 31, 2016

Well, I made it work.

  • You are using newer atlassian-plugin.xml with outdated code
  • Something is wrong with <Atlassian-Plugin-Key>, at least with my sdk.

From pom.xml, remove

&lt;Atlassian-Plugin-Key&gt;${atlassian.plugin.key}&lt;/Atlassian-Plugin-Key&gt;
&lt;enableQuickReload&gt;true&lt;/enableQuickReload&gt;
&lt;enableFastdev&gt;false&lt;/enableFastdev&gt;

Leave your atlassian-plugin.xml as is but change the ExampleMacro.java as

@ComponentImport
private final XhtmlContent xhtmlUtils;
@Inject
public ExampleMacro(XhtmlContent xhtmlUtils)
{
 this.xhtmlUtils = xhtmlUtils;
}

Add the following

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

in the src/main/resources/META-INF/spring/plugin-context.xml file.

 

I always remove the atlassian-plugin-key, it doesn't work for me. I suspected that i have old sdk. If someone knows anything else, I'd like to know smile

Qing LIANG March 31, 2016

Thanks very much for your help!

Panos
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 31, 2016

You are welcome, thanks for posting your solution

0 votes
Panos
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 31, 2016

Try adding 

 

&lt;component-import key="xhtmlContent"&gt;
    &lt;interface&gt;com.atlassian.confluence.xhtml.api.XhtmlContent&lt;/interface&gt;
&lt;/component-import&gt;

in the atlassian-plugin.xml and place back as well the 

&lt;!-- import from the product container --&gt;
    &lt;component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" /&gt;
Qing LIANG March 31, 2016

I got this error then : 

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

Qing LIANG March 31, 2016

After adding the two imports you mentioned and commented the <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key> element in the <instructions> element within the pom, the project can be successfully compiled and archived. But when I reinstall the plugin, the same complain came out. Still can not create a bean for the macro class. 

0 votes
Qing LIANG March 31, 2016

@Panos Karampis please see the log message above. Why does it complains that no qualifying bean of type XhtmlContent is found?

0 votes
Qing LIANG March 31, 2016

@Panos Karampis Sorry I couldn't find any logs, except the atlassian-analytics.log, in the project target folder, neither the target/container folder. The logs are shown in the console when running atlas-run.bat. I will attach the console log later after a clean run. 

0 votes
Panos
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 31, 2016

With first glance both xml look fine. Did you check for any exceptions in the log files? (both catalina.out and atlassian-confluence.log)

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

TAGS
AUG Leaders

Atlassian Community Events