How to find Spring beans from another plugin

Eduard Grinchenko October 18, 2018

I have a base plugin A based on atlassian-spring-scanner 2+ and I have additional plugins B, C, D, etc. based on atlassian-spring-scanner 2+ too.

In all of the additional plugins(B, C, D,..) I have spring @Component implemented some interface from A. 

I need dynamically find those components from plugin A. How can I achieve that?

I've tried to use:

Map<String, CustomLogicProvider> beansOfType = beanFactory.getBeansOfType(CustomLogicProvider.class)

but it founds only components from plugin A. It looks like I need somehow merge OSGi classloaders from all of those plugins or something else, but I can't find a way to do that.

1 answer

1 accepted

1 vote
Answer accepted
m@
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
October 22, 2018

Hello Eduard,

You need to mark the components in your base module with @ExportAsService (or @ExportAsDevService if you only want it work in dev mode). See the code here.

After doing that the @Component annotation should allow you to import them in your other modules.  You can simply auto wire them to your constructor at this point, there is no need to use getBeansOfType.

There are other annotations that can be used to optionally import components based on the product, take a look here.

Cheers

Matt

Eduard Grinchenko October 22, 2018

Thank you, the question is a bit about another thing. Let's imagine that my base module is already installed and I need to add other modules, where that @Component's are implemented.

And during a call to some method in base modules, It should understand what extensions(other modules) are installed and find the actual bean in them. I have an interface 

CustomLogicProvider

in a base module, but it's implemented in others. And I'm trying to find all implemented beans from others by calling 

getBeansOfType(CustomLogicProvider.class)
m@
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
October 22, 2018

Right.  I think you need ModuleTypes.

1) See the example here in the section Expose Module Types Differently via @ModuleType.

This example defines a ModuleDescriptorFactory which defines a module type that other plugins can use to implement.  It also defines a ModuleDescriptor which is responsible for creating instances of your module after the plugin system reads it from the descriptor.

So, a) In your base module define a module factory that creates a module type for your logic provider.  

b) define a module descriptor for the container to contain the module and logic to instantiate it from the information in the descriptor.  This will be used to create the specific instances of your module in step #2.

The example in spring scanner is defining a module type "basic", you can call it what you want.

It may also be useful to read the deprecated tutorial on the module type module.  Parts of this tutorial have been replaced with the approach above but gives some examples on how to implement the module descriptor.

2) In your other plugins, define a module in the plugin descriptor using the key defined by the module factory ("basic" from the example).  You could pass in a class name or something as a parameter that can be used by the module descriptor to create the instance you desire.

3) You can inject a PluginAccessor in to any code in any plugin.  This class has a method called getEnabledModulesByClass which can be used to find and implementations of this module.

---

There is a bit of overhead here but its abstracting away the lifecycle and management of the beans that gets really complicated when you consider installing/uninstalling and enabling/disabling plugins.

Eduard Grinchenko October 23, 2018

Thank you, Matthew, very helpful! Will try it today.

Eduard Grinchenko October 24, 2018

@m_ As I understand right, instances of modules will be created inside ModuleDiscriptor - it means in runtime in plugin A.

Is it possible to make module instances Spring components? How can I inject something specific to that instances? Idea is to inject some components from 3rd party services which shouldn't be in plugin A dependencies.

ModuleDiscriptor should be only one for each ModuleType, or it's possible to extend it for each sub-modules for that reasons?

Eduard Grinchenko October 31, 2018

I've found a solution to my last question.

That code correctly finds beans, which are created by another plugin and registered as module type in atlassian-plugin.xml

@Component
public class CustomLogicDescriptor extends AbstractModuleDescriptor<CustomLogicProvider> {
@Autowired
public CustomLogicDescriptor(@ComponentImport ModuleFactory moduleFactory) {
super(moduleFactory);
}

@Override
public CustomLogicProvider getModule() {
return moduleFactory.createModule(this.moduleClassName, this);
}
}
Adrien Ragot (Requirement Yogi)
Contributor
April 17, 2024

I just want to add that, using this method, `pluginAccessor.getEnabledModulesByClass()` should be used. `listableBeanfactory.getBeansOfType(CustomLogicProvider.class)` won't return the beans. Thank you very for posting your resolution, Eduard, it helped!

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events