And this happens when you try to inject a JIRA component in the constructor. Following is a typical example.
[INFO] [talledLocalContainer] Caused by: org.osgi.framework.BundleException: Unresolved constraint in bundle com.jtricks.component-tricks-tests [105]: Unable to resolve 105.0: missing requirement [105.0] package; (package=com.jtricks)
[INFO] [talledLocalContainer] at org.apache.felix.framework.Felix.resolveBundle(Felix.java:3409)
[INFO] [talledLocalContainer] at org.apache.felix.framework.Felix.startBundle(Felix.java:1709)
[INFO] [talledLocalContainer] at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:905)
[INFO] [talledLocalContainer] at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:892)
[INFO] [talledLocalContainer] at com.atlassian.plugin.osgi.factory.OsgiPlugin.enableInternal(OsgiPlugin.java:417)
[INFO] [talledLocalContainer] ... 53 more
[INFO] [talledLocalContainer] Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.atlassian.sal.api.user.UserManager] is defined: Unsatisfied dependency of type [interface com.atlassian.sal.api.user.UserManager]: expected at least 1 matching bean
[INFO] [talledLocalContainer] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:613)
[INFO] [talledLocalContainer] at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:622)
[INFO] [talledLocalContainer] at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:584)
Fortunately, the fix is a simple one. Atlassian gives the component-import plugin module. For the above error, all you need to do is to add the following in the atlassian-plugin.xml file.
<component-import key="userManager" interface="com.atlassian.sal.api.user.UserManager"/>
You would imagine the same will work for every component, Wouldn't you? Unfortunately, that is not the case!
Here is an example: JiraDurationUtils.
[INFO] [talledLocalContainer] Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.atlassian.jira.util.JiraDurationUtils] is defined: Unsatisfied dependency of type [class com.atlassian.jira.util.JiraDurationUtils]: expected at least 1 matching bean
[INFO] [talledLocalContainer] at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:613)
Naturally, one would add the component-import tag for the class. I added the following:
<component-import key="jiraDurationUtils" interface="com.atlassian.jira.util.JiraDurationUtils" />
This fixed the above error but along came another!
[INFO] [talledLocalContainer] 2013-07-12 00:11:44,650 pool-5-thread-3 ERROR admin 11x1420x1 xq2dkb 127.0.0.1 /rest/plugins/1.0/ [plugin.osgi.factory.OsgiPlugin] Never resolved service '&jiraDurationUtils' for plugin 'com.jtricks.component-tricks' with filter (object.atlassian.jira.util.JiraDurationUtils)
So, how is JiraDurationUtils different from other components? If you look at the JIRA source, ContainerRegistrar class to be exact, you will see that the JiraDurationUtils component is registered as an internal class.
register.implementation(INTERNAL, JiraDurationUtils.class);
And that is exactly what causes the new exception.
Fortunately, the fix is again an easy one. Instead of using component-import in this case, assume JiraDurationUtils as a class in your plugin (which is of course imported via jira-api dependency). To make it available for constructor injection, define the class as a component in the atlassian-plugin.xml. Int this case, the definition will be as follows:
<component key="jiraDurationUtils" name="JIRA Duration Utils" class="com.atlassian.jira.util.JiraDurationUtils">
</component>
That's all. Everything will work fine and you don't need the component-import anymore.
Want another example? Take ComponentLocator interface. Instead of
<component-import key="componentLocator" interface="com.atlassian.jira.util.ComponentLocator" />
you will end up adding the following:
<component key="componentLocator" name="Component Locator" class="com.atlassian.jira.util.JiraComponentLocator">
<interface>com.atlassian.jira.util.ComponentLocator</interface>
</component>
Attached is a sample listener code for you to try it out. Till the next one, enjoy the recipes in JIRA 5.x Development cookbook ;)

component-tricks.zip |