But then, this is a well documented error and even we have attempted documenting it here - to save hours of debugging and, of course, to provide a good night's sleep!!
With the latest Atlassian Plugin SDK though, it seems the nightmares are back. And I have seen a rising number of questions about this error in Atlassian Answers and other forums. Are you one of those who ran into the same? If so, don't blame it on Atlassian or the plugin SDK. You are just ignorant, as I was for a good few hours, about the Atlassian Spring Scanner libraries.
What does it mean though?
Once you have the spring scanner plugin, and related dependencies, added in the pom.xml, it will not look for component definitions in the atlassian-plugin.xml and will, instead, look for scanner annotations. Even the dependency injection requires the special annotations in the code.
And yes, spring scanner plugin is enabled by default in the pom.xml when you create the plugin skeleton, by running atlas-create-jira-plugin, using the latest plugin SDK. That means you will run into NoSuchBeanDefinitionException when you inject components in the constructor without using the required annotations. Or when you use component-import definitions inside the atlassian-plugin.xml. Or when you use public="true" to export one of your components. And so on..
For example, let is create a simple skeleton plugin using the new Plugin SDK (6.1.2 as of now) and add a webwork module definition.
<webwork1 key="plugin-scanner-action" name="Plugin Scanner Action" i18n-name-key="plugin-scanner-action.name">
<description key="plugin-scanner-action.description">Demos how the scanner annotations work</description>
<actions>
<action name="com.jtricks.jira.webwork.PluginScannerDemo" alias="PluginScannerDemo">
<view name="success">/templates/plugin-scanner-action/pluginscannerdemo/success.vm</view>
</action>
</actions>
</webwork1>
In the action class, we can keep it simple by injecting the ever popular JiraAuthenticationContext interface, as shown below:
public class PluginScannerDemo extends JiraWebActionSupport {
private static final Logger log = LoggerFactory.getLogger(PluginScannerDemo.class);
private final JiraAuthenticationContext authContext;
private String currentUser;
public PluginScannerDemo(JiraAuthenticationContext authContext) {
super();
this.authContext = authContext;
}
@Override
protected String doExecute() throws Exception {
if (this.authContext.getLoggedInUser() != null) {
this.currentUser = this.authContext.getLoggedInUser().getDisplayName();
} else {
this.currentUser = "Anonymous";
}
return super.doExecute();
}
public String getCurrentUser() {
return currentUser;
}
}
And we can create a success view that prints the currentUser.
<h1>Welcome $!currentUser!</h1>
It looks very simple but you will run into the, you guessed it right, NoSuchBeanDefinitionException when this code is executed.
[INFO] [talledLocalContainer] 2016-01-26 10:52:19,659 http-nio-2990-exec-7 ERROR admin 652x237x3 zb3hwx 0:0:0:0:0:0:0:1 /secure/PluginScannerDemo.jspa [c.a.j.web.dispatcher.JiraWebworkActionDispatcher] Exception thrown from action 'PluginScannerDemo', returning 404
[INFO] [talledLocalContainer] org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.jtricks.jira.webwork.PluginScannerDemo': Unsatisfied dependency expressed through constructor argument with index 0 of type [com.atlassian.jira.security.JiraAuthenticationContext]: : No qualifying bean of type [com.atlassian.jira.security.JiraAuthenticationContext] 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.jira.security.JiraAuthenticationContext] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
We know that JiraAuthenticationContext is a public component and doesn't need a component-import definition. Even if we add it in the atlassian-plugin.xml, as described in one of our earlier tips, that is not going to help with this error.
This is where we need to start adding the new scanner annotations. Following are the important ones.
- @ExportAsService: Replaces <component ... public="true"> and exposes the class as a OSGi service.
- @Named (JSR) or @Component (Spring): Replaces <component ...> definition.
- @Autowired or @Inject: Used for dependency injection
- @ComponentImport: Replaces <component-import ...> definition.
- @ClasspathComponent: Defined a component whose class is contained in some other library.
- @ModuleType: Replaces <module-type.. > definition.
And you will find more annotations and finer details at https://bitbucket.org/atlassian/atlassian-spring-scanner.
Our modified code, with all these annotations, will look like this:
@Named ("PluginScannerDemo")
public class PluginScannerDemo extends JiraWebActionSupport {
private static final Logger log = LoggerFactory.getLogger(PluginScannerDemo.class);
@ComponentImport
private final JiraAuthenticationContext authContext;
private String currentUser;
@Inject
public PluginScannerDemo(JiraAuthenticationContext authContext) {
super();
this.authContext = authContext;
}
@Override
protected String doExecute() throws Exception {
if (this.authContext.getLoggedInUser() != null) {
this.currentUser = this.authContext.getLoggedInUser().getDisplayName();
} else {
this.currentUser = "Anonymous";
}
return super.doExecute();
}
public String getCurrentUser() {
return currentUser;
}
}
And, voilà, Everything works as expected!
You can find the full code attached at the end of this blog.
But, if you want to stick with old style, maybe because you are upgrading a huge plugin, you can do that by removing the Spring Scanner plugin and its dependencies from the pom.xml. As simple as that! You can find the source code, for the above example, without the spring scanner attached as plugin-scanner-demo-old-style.zip.
Note that Spring Scanner is recommended because the code with annotations load significantly faster than traditional P2 plugins that require runtime transformation. As per Atlassian, local tests show plugins that took 5 seconds to load now load in under 2 seconds. Being more explicit about the code, using annotations, is indeed a fair trade-off, if that is the case.
Hope you enjoyed this tip. You can read more such tips in the new version of JIRA Development Cookbook, that is coming out soon. Stay tuned!

plugin-scanner-demo.zip |

plugin-scanner-demo-old-style.zip |