Change our app
Now we need to add support for modules to our calculator.
First uncomment the jira-core dependency in the pom.xml file.
<dependency>
<groupId>com.atlassian.jira</groupId>
<artifactId>jira-core</artifactId>
<version>${jira.version}</version>
<scope>provided</scope>
</dependency>
Then add com.atlassian.plugin.osgi.bridge.external to Import-Package in the pom.xml file
<Import-Package>org.springframework.osgi.*;resolution:="optional", com.atlassian.plugin.osgi.bridge.external, org.eclipse.gemini.blueprint.*;resolution:="optional", *</Import-Package>
Next add our module descriptor.
src/main/java/ru/matveev/alexey/atlassian/calculator/module/OperationModuleDescriptor.java
public class OperationModuleDescriptor extends AbstractModuleDescriptor<Operation>
{
public OperationModuleDescriptor(final @ComponentImport ModuleFactory moduleFactory)
{
super(moduleFactory);
}
@Override
public Operation getModule()
{
return moduleFactory.createModule(moduleClassName, this);
}
}
As you can see we created this module descriptor as AbstractModuleDescriptor of the Operation interface type.
Next, we need to register our module descriptor.
src/main/java/ru/matveev/alexey/atlassian/calculator/module/BasicModuleTypeFactory.java
@ModuleType(ListableModuleDescriptorFactory.class)
@Named
public class BasicModuleTypeFactory extends
SingleModuleDescriptorFactory<OperationModuleDescriptor>
{
@Autowired
public BasicModuleTypeFactory(HostContainer hostContainer)
{
super(hostContainer, "calculatorOperation", OperationModuleDescriptor.class);
}
}
As you can see We registered our module descriptor under the calculatorOperation name.
Done.
Now apps can define calculatorOperation in their atlassian-plugin.xml file.
But let's also change our CalculatorService.
When we need to look for operations in modules of external apps.
I changed src/main/java/ru/matveev/alexey/atlassian/calculator/service/CalculatorService.java to this one:
@Named
public class CalculatorService {
private final SumOperation sumOperation;
private final PluginAccessor pluginAccessor;
public CalculatorService(final @ComponentImport PluginAccessor pluginAccessor, SumOperation sumOperation) {
this.sumOperation = sumOperation;
this.pluginAccessor = pluginAccessor;
}
public int calculate(String operation, int val1, int val2) throws OperationNotFoundException {
if (operation.equals(sumOperation.getName())) {
return sumOperation.calculate(val1, val2);
}
Operation operationModule = this.getModuleForOperationName(operation);
if (operationModule != null) {
return operationModule.calculate(val1, val2);
}
throw new OperationNotFoundException(String.format("Operation %s not found", operation));
}
private Operation getModuleForOperationName(String operationName) {
List<OperationModuleDescriptor> operationModuleDescriptors =
pluginAccessor.getEnabledModuleDescriptorsByClass(OperationModuleDescriptor.class);
for (OperationModuleDescriptor operationModuleDescriptor : operationModuleDescriptors)
{
if (operationName.equals(operationModuleDescriptor.getModule().getName())) {
return operationModuleDescriptor.getModule();
}
}
return null;
}
}
I added getModuleForOperationName which returns a reference to a module in an external app if the operation name we want to execute equals to the name of the operation provided by this module.
And then I added these lines to the calculate method:
Operation operationModule = this.getModuleForOperationName(operation);
if (operationModule != null) {
return operationModule.calculate(val1, val2);
}
First, we look for a module in another app which provides implementation of the operation we need and then we run the calculate method on the found module.
That is all with the calculator app. Now let's write an extension to our app.
Calculator-extension
I created an ordinary plugin from Atlassian SDK.
I added dependency to the jar file of the calculator app. I need it to use the Operation interface defined in the calculator app.
<dependency>
<groupId>ru.matveev.alexey.atlassian</groupId>
<artifactId>calculator</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>system</scope>
<systemPath>${project.basedir}/../calculator/target/calculator-1.0.0-SNAPSHOT.jar</systemPath>
</dependency>
Then I created the minus operation.
src/main/java/ru/matveev/alexey/atlassian/operation/MinusOperation.java
public class MinusOperation implements Operation {
@Override
public String getName() {
return "minus";
}
@Override
public int calculate(int val1, int val2) {
return val1 - val2;
}
}
As you can see I implemented this class from the Operation interface and defined two methods: getName (returns the "minus" value) and calculate.
And now I can add calculatorOperation module to the atlassian-plugin.xml file.
src/main/resources/atlassian-plugin.xml
<calculatorOperation key="minus-operation" class="ru.matveev.alexey.atlassian.operation.MinusOperation"/>
And that is it. Install this app to the same Jira instance where the Calculator app is installed.
Test extension app
I installed our two apps to the same Jira instance.
And now I will execute our http://localhost:2990/jira/rest/calculator/1.0/calculate REST endpoint with the following request body:
{"operation": "minus", "value1": 1, "value2" : 3}
As you can see I am trying to use the minus operation from our calculator-extension app. And here is the result:
And the result is -2 as expected. We did what we wanted. We added a new operation in an external app for our calculator app. Now other apps can extend our calculator with their own operations.
You can find the final code here.
Alexey Matveev
software developer
MagicButtonLabs
Philippines
1,575 accepted answers
0 comments