NoSuchBeanDefinitionException is a notorious exception in the JIRA Development world. Some even have had nightmares about it I hear! 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. "Atlassian Spring Scanner is a set of libraries that make plugins faster to load and easier to develop.". It scans the source code to find special annotations and creates special index files in the JAR's META-INF directory. These index files are read at runtime to create the necessary Spring components and OSGI services. 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.
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!
30 Comments
John Strzempa
2/16/2016 06:11:04 pm
How does this work when you're extending a class. I'm trying to extend the CreateWorklog and I'm getting this error. Do I have to use the @ComponentImport and initialize the object in my class even if I'm not using the object in my code? Like below for the worklogService?
Reply
John
2/17/2016 12:50:34 am
Can you explain how to do this with JiraDurationUtils. I keep getting this
Reply
3/10/2016 05:58:52 pm
Appreciate the writeup! An absolute abysmal lack of documentation on this from ATL.
Reply
J-Tricks
3/12/2016 10:59:56 am
Anything in the constructor needs the @ComponentImport annotation and the class needs a @Scanned annotation.
Reply
Tom McCann
3/13/2016 05:10:46 am
Hi Jobin, Thank you so much for this article. I had been banging my head against a brick wall for a week trying to solve this. Nobody on Atlassian Answers had responded with anything useful and I was about to give up. You have saved the day. I not only have a solution for Jira 7 but also an approach that will work on Jira 6.
Reply
J-Tricks
3/13/2016 10:00:16 am
Glad you like the book. Yes, on target for the next version :)
Reply
Lorand
5/26/2016 04:39:57 am
Hi Jobin,
Reply
Ahmad A
8/25/2016 04:07:50 pm
I wish your new book was out now (still less than 2 weeks). It is so needed especially with all the tutorials on Atlassian's site being so outdated.
Reply
J-Tricks
8/25/2016 10:01:19 pm
The wait is almost over :)
Reply
Ahmad A
8/26/2016 08:11:48 am
Will there be a bundle price for purchasing both the book and Kindle version? That would be cool.
J-Tricks
8/26/2016 09:14:14 am
I guess there is a bundle price. Will keep you posted.
Ahmad A
8/28/2016 03:23:57 pm
I have a plugin that is listening for closed events in Jira 7.1.x. The plugin was working fine in jira 6.2.x.
Reply
J Reinhard
9/1/2016 11:20:46 am
This saved me, thank you!
Reply
Todd
9/2/2016 11:49:30 pm
I'm obviously still missing something. I am having a hell of a time with JiraDurationUtils.
Reply
J-Tricks
9/4/2016 09:02:58 pm
JiraDurationUtils is an internal component. It cannot be imported just like that. See http://www.j-tricks.com/tutorials/component-import-gotchas-nosuchbeandefinitionexception for some details on that.
Reply
Todd
9/8/2016 02:05:56 pm
Is it your recommendation that I disable the use of the Spring Scanner?
Todd
9/8/2016 02:07:15 pm
Otherwise, is there an alternative library that formats the duration in the same manner as Agile/Time Tracking.
Todd
9/8/2016 05:12:37 pm
It would turn out that the JiraDurationUtils::PrettyDurationFormatter is a public component and worked for me.
Reply
J-Tricks
9/8/2016 09:43:23 pm
Awesome. Thanks for providing the detail here. I am sure someone else will benefit from it too.
Reply
Todd
10/25/2016 10:54:41 pm
Hey J-Tricks. me again.
Reply
J-Tricks
10/26/2016 10:07:07 pm
If the component is internal to your plugin, you can inject it without a ComponentImport. Just do plain constructor injection, without the annotation.
Reply
sameer_v
9/20/2017 12:42:47 pm
Awesome! Was struggling with this issue..removing the @ComponentImport for the class inside the same plugin fixed it.
Matt Doar
12/14/2016 04:57:44 pm
Ai, ai, ai! Also I needed to add
Reply
Dan Greenthumb
1/20/2017 09:36:26 am
Nearly one year has passed since this was published and still Atlassian didn't manage to update their docs. I'm not very pleased to put this in a polite way -.-
Reply
Mike
1/26/2017 04:56:27 am
Guys, thank you so much!! It's awesome!
Reply
Alexej Geldt
1/27/2017 05:35:26 am
And, voilà, Everything works as expected!
Reply
J-Tricks
1/29/2017 03:01:01 pm
There are just so many use cases that can fail. Which component are you trying to import and what is the error?
Reply
Bastian
3/9/2017 09:19:31 am
Hi,
Reply
peter
1/20/2019 11:19:48 am
nice to read thisi article........ but not worth the characters on the screen - the nightmare is worse. This morning my servlet plugin worked and in the afternoon no more..... why - I did not change a single line of code. I am fighting now since two weeks an I have to tell my customer it is no more possible to develop with atlassian - to worse their shred of code. I develop software since 30 years in the field of internet development and technical informatics. the atlassian mess is by far the worst I have ever seen in my career... is this the end of atlassian ? and the nightmare goes on
Reply
J-Tricks
1/21/2019 11:20:52 am
Not sure what the issue is. Something must have changed for sure. If it is not your code, did you check the JIRA version? How about Atlassian Plugin SDK version? Or the spring scanner version that is being used? There must be some variable somewhere.
Reply
Your comment will be posted after it is approved.
Leave a Reply. |
AuthorJobin Kuruvilla - Works in Adaptavist as Head of DevOps Professional Services. Categories
All
Archives
October 2016
|