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: {} ...
Community moderators have prevented the ability to post new answers.
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.
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<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException { String body = conversionContext.getEntity().getBodyAsString(); final List<MacroDefinition> macros = new ArrayList<MacroDefinition>(); 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("<p>"); if (!macros.isEmpty()) { builder.append("<table width=\"50%\">"); builder.append("<tr><th>Macro Name</th><th>Has Body?</th></tr>"); for (MacroDefinition defn : macros) { builder.append("<tr>"); builder.append("<td>").append(defn.getName()).append("</td><td>").append(defn.hasBody()).append("</td>"); builder.append("</tr>"); } builder.append("</table>"); } else { builder.append("You've done built yourself a macro! Nice work."); } builder.append("</p>"); 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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Well, I made it work.
From pom.xml, remove
<Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key> <enableQuickReload>true</enableQuickReload> <enableFastdev>false</enableFastdev>
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
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:atlassian-scanner="http://www.atlassian.com/schema/atlassian-scanner" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.atlassian.com/schema/atlassian-scanner http://www.atlassian.com/schema/atlassian-scanner/atlassian-scanner.xsd"> <atlassian-scanner:scan-indexes/> </beans>
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You are welcome, thanks for posting your solution
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Try adding
<component-import key="xhtmlContent"> <interface>com.atlassian.confluence.xhtml.api.XhtmlContent</interface> </component-import>
in the atlassian-plugin.xml and place back as well the
<!-- import from the product container --> <component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" />
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@Panos Karampis please see the log message above. Why does it complains that no qualifying bean of type XhtmlContent is found?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
@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.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
With first glance both xml look fine. Did you check for any exceptions in the log files? (both catalina.out and atlassian-confluence.log)
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Community moderators have prevented the ability to post new answers.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.