Spring autowiring issues when extending ViewBuildResults

I have a very simple class extending com.atlassian.bamboo.build.ViewBuildResults, and an <xwork> section in my atlassian-plugin.xml corresponding to it. When Bamboo starts up, I see the following error, and none of the xworks functionality is enabled.

[INFO] [talledLocalContainer] org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'com.myplugin.ViewReport': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field:
private com.atlassian.bamboo.storage.StorageLocationService com.atlassian.bamboo.build.ViewBuildResults.storageLocationService; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
[com.atlassian.bamboo.storage.StorageLocationService] found for dependency: expected at least 1 bean
which qualifies as autowire candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}

I’ve checked under the plugin OSGi listings in localhost:6990/bamboo/plugins/servlet/upm/osgi, and confirmed that my plugin does import the com.atlassian.bamboo.storage package, and if I add it as an explicit required dependency in Import-Package, it is not detected as missing. However, at least one bean from the package is clearly still not found. There are no other errors earlier in the log output.

What could cause the StorageLocationService bean to be missing like this? What are some other steps I could take to diagnose the issue?

Hello, the fact you imported com.atlassian.bamboo.storage package means you got the class definitions but ViewBuildResults class has an dependency on StorageLocationService component which is not imported by your plugin.

I recommend adding

<component-import key="storageLocationService" interface="com.atlassian.bamboo.storage.StorageLocationService"/>

to your atlassian-plugin.xml to instruct container that your plugin depends on StorageLocationService bean.

That being said. It should solve the problem with this particular dependency, but ViewBuildResults is not an API and it can depend on other components, including the ones which are not exported to plugins’ container. If that’s the case you won’t be able to provide a viable workaround (as the one mentioned) for that.

Cheers,
Grzegorz Lewandowski

Hi,

I am using Spring Scanner version 2, which doesn’t allow component-import statements; it only works with annotations. So, that workaround isn’t possible. Is there an equivalent for version 2?

What would cause StorageLocationService not be exported into my plugin’s container? It seems to be imported correctly for several other plugins, including the official Clover plugin. These have similar classes that inherit from ViewBuildResults and are able to autowire. I don’t see anything the Clover plugin’s pom.xml or atlassian-plugin.xml that is related to the missing class.

I also just tried to instantiate a StorageLocationServiceImpl programmatically in my task plugin’s code; the class was found there. The only issue, apparently, is that Spring doesn’t find the class.

You’ll need to annotate your declaration of StorageLocationService with @ComponentImport as well adding the package to your Import-Package to your pom.xml.

The issue is that I don’t have a declaration of StorageLocationService to annotate — the field that gets autowired is in ViewBuildResults which I have no control over.

Adding that class specifically (the whole string com.atlassian.bamboo.storage.StorageLocationService) to Import-Package didn’t affect anything. I did however find that if I create an extra empty constructor annotated with @Autowired and with a @ComponentImport-annotated StorageLocationService argument (see below), Spring will be induced to load StorageLocationService, and is able to wire ViewBuildResults. This solves my problem for the time being, but I’m still wondering why Spring isn’t picking up that dependency on its own given that the Bamboo core libraries need it… I have a feeling this indicates deeper problems that are going to come up later.

@Autowired
public ViewReport(@ComponentImport StorageLocationService storageLocationService) {
}

This is exactly the same thing. Spring scanner is able to wire the classes in your code, but it’s not able import beans from core. The are some which are injected always, StorageLocationService is not one of them.

The way you used is totally equivalent to the solution I proposed.

There’s also a common practice to create a class which lists all components to import, instead of the solution you’ve chosen

@Scanned
class  Imports {
@ComponentImport
StorageLocationService storageLocationServic
}

There are people who favors this one, but it’s easier to lost track in unused dependencies this way.

Additionally, I rather recommend using @BambooImport instead of @ComponentImport to instrument Bamboo’s container that this bean should be located in core only, otherwise you may bump into some problems (but not necessarily, it’s hard to list all possible use cases) when using your plugin on remote agents.

1 Like

Thanks, that’s roughly what I ended up using. @Scanned doesn’t exist for me (maybe a scanner v1 thing?), but I did this:

@Component
public class Imports {
    @BambooImport
    StorageLocationService storageLocationService;
}