Make it more complicated
Actually if we want to use JCMA gateway service we need to create a listener of the AppCloudMigrationListener class.
Let's do it.
First change our interface src/main/java/ru/matveev/alexey/atlassian/tutorial/api/MyPluginComponent.java
public interface MyPluginComponent extends AppCloudMigrationListener
{
String getName();
}
We extended our interface from AppCloudMigrationListener
Now we need to register our AppCloudMigrationListener.
Change src/main/java/ru/matveev/alexey/atlassian/tutorial/impl/MyPluginComponentImpl.java to the following code:
@Slf4j
@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent, DisposableBean
{
public MyPluginComponentImpl() {
JCMAAccessor.getJCMAGateway().registerListener(this);
log.error("Listener registered");
}
public String getName()
{
if(null != JCMAAccessor.getJCMAGateway())
{
return "myComponent:" + JCMAAccessor.getJCMAGateway().getClass().getName();
}
return "myComponent";
}
@Override
public void onStartAppMigration(String transferId, MigrationDetails migrationDetails) {
// Retrieving ID mappings from the migration.
// A complete guide can be found at https://developer.atlassian.com/platform/app-migration/getting-started/mappings/
try {
log.info("Migration context summary: " + new ObjectMapper().writeValueAsString(migrationDetails));
PaginatedMapping paginatedMapping = JCMAAccessor.getJCMAGateway().getPaginatedMapping(transferId, "identity:user", 5);
while (paginatedMapping.next()) {
Map<String, String> mappings = paginatedMapping.getMapping();
log.info("mappings = {}", new ObjectMapper().writeValueAsString(mappings));
}
} catch (IOException e) {
log.error("Error retrieving migration mappings", e);
}
// You can also upload one or more files to the cloud. You'll be able to retrieve them through Atlassian Connect
try {
OutputStream firstDataStream = JCMAAccessor.getJCMAGateway().createAppData(transferId);
// You can even upload big files in here
firstDataStream.write("Your binary data goes here".getBytes());
firstDataStream.close();
// You can also apply labels to distinguish files or to add meta data to support your import process
OutputStream secondDataStream = JCMAAccessor.getJCMAGateway().createAppData(transferId, "some-optional-label");
secondDataStream.write("more bytes".getBytes());
secondDataStream.close();
} catch (IOException e) {
log.error("Error uploading files to the cloud", e);
}
}
@Override
public String getCloudAppKey() {
return "my-cloud-app-key";
}
@Override
public String getServerAppKey() {
return "my-server-app-key";
}
/**
* Declare what categories of data your app handles.
*/
@Override
public Set<AccessScope> getDataAccessScopes() {
return Stream.of(AccessScope.APP_DATA_OTHER, AccessScope.PRODUCT_DATA_OTHER, AccessScope.MIGRATION_TRACING_IDENTITY)
.collect(Collectors.toCollection(HashSet::new));
}
@Override
public void destroy() {
JCMAAccessor.getJCMAGateway().deregisterListener(this);
log.error("Listener deregistered");
}
I made MyPluginComponentImpl as a listener.
Let's have a look at the constructor:
public MyPluginComponentImpl() {
JCMAAccessor.getJCMAGateway().registerListener(this);
log.error("Listener registered");
}
I register our listener. And I deregister this listener in the destroy method:
@Override
public void destroy() {
JCMAAccessor.getJCMAGateway().deregisterListener(this);
log.error("Listener deregistered");
}
All other methods are the implementations of the methods from the AppCloudMigrationListener interface. Implementation of those modules is not important for this tutorial.
Run app again
If we run our app again. Our app will be disabled with the following error:
Caused by: java.lang.NoClassDefFoundError: com/atlassian/migration/app/AppCloudMigrationListener
It is correct. AppCloudMigrationListener belongs to JCMA and JCMA is not installed. You can find the complete code for this part under the v.3 tag.
Fix our code
First of all let's make our AppCloudMigrationListener a separate class.
src/main/java/ru/matveev/alexey/atlassian/tutorial/MyAppCloudMigrationListener.java:
@Slf4j
public class MyAppCloudMigrationListener implements AppCloudMigrationListener {
@Override
public void onStartAppMigration(String transferId, MigrationDetails migrationDetails) {
// Retrieving ID mappings from the migration.
// A complete guide can be found at https://developer.atlassian.com/platform/app-migration/getting-started/mappings/
try {
log.info("Migration context summary: " + new ObjectMapper().writeValueAsString(migrationDetails));
PaginatedMapping paginatedMapping = JCMAAccessor.getJCMAGateway().getPaginatedMapping(transferId, "identity:user", 5);
while (paginatedMapping.next()) {
Map<String, String> mappings = paginatedMapping.getMapping();
log.info("mappings = {}", new ObjectMapper().writeValueAsString(mappings));
}
} catch (IOException e) {
log.error("Error retrieving migration mappings", e);
}
// You can also upload one or more files to the cloud. You'll be able to retrieve them through Atlassian Connect
try {
OutputStream firstDataStream = JCMAAccessor.getJCMAGateway().createAppData(transferId);
// You can even upload big files in here
firstDataStream.write("Your binary data goes here".getBytes());
firstDataStream.close();
// You can also apply labels to distinguish files or to add meta data to support your import process
OutputStream secondDataStream = JCMAAccessor.getJCMAGateway().createAppData(transferId, "some-optional-label");
secondDataStream.write("more bytes".getBytes());
secondDataStream.close();
} catch (IOException e) {
log.error("Error uploading files to the cloud", e);
}
}
@Override
public String getCloudAppKey() {
return "my-cloud-app-key";
}
@Override
public String getServerAppKey() {
return "my-server-app-key";
}
/**
* Declare what categories of data your app handles.
*/
@Override
public Set<AccessScope> getDataAccessScopes() {
return Stream.of(AccessScope.APP_DATA_OTHER, AccessScope.PRODUCT_DATA_OTHER, AccessScope.MIGRATION_TRACING_IDENTITY)
.collect(Collectors.toCollection(HashSet::new));
}
}
Now we need to remove "extends AppCloudMigrationListener" from src/main/java/ru/matveev/alexey/atlassian/tutorial/api/MyPluginComponent.java:
public interface MyPluginComponent
{
String getName();
}
Now let's create the src/main/java/ru/matveev/alexey/atlassian/tutorial/ListenerManager.java file. ListenerManager will register and deregister MyAppCloudMigrationListener:
@Slf4j
public class ListenerManager {
static AppCloudMigrationListener appCloudMigrationListener = null;
public static void registerListener() {
appCloudMigrationListener = new MyAppCloudMigrationListener();
JCMAAccessor.getJCMAGateway().registerListener(appCloudMigrationListener);
log.error("Listener registered");
}
public static void deregisterListener() {
appCloudMigrationListener = new MyAppCloudMigrationListener();
JCMAAccessor.getJCMAGateway().deregisterListener(appCloudMigrationListener);
log.error("Listener deregistered");
}
}
We have two methods here: to register our MyAppCloudMigrationListener and deregister it. Also we throw a log message upon registering and deregistering this listener.
Now we need to change src/main/java/ru/matveev/alexey/atlassian/tutorial/impl/MyPluginComponentImpl.java to this one:
@Slf4j
@Named ("myPluginComponent")
public class MyPluginComponentImpl implements MyPluginComponent, DisposableBean {
public MyPluginComponentImpl() {
if (JCMAAccessor.getJCMAGateway() != null) {
ListenerManager.registerListener();
}
else {
log.error("Listener not registered");
}
}
public String getName()
{
if(null != JCMAAccessor.getJCMAGateway())
{
return "myComponent:" + JCMAAccessor.getJCMAGateway().getClass().getName();
}
return "myComponent";
}
@Override
public void destroy() {
ListenerManager.deregisterListener();
}
}
In the constructor we check if JCMA is installed and if installed we register our listener with the ListenerManager class. In the destroy method we deregister our listener with the Listener Manager class. And the getName is left unchanged.
Now let's run our app
Run app again
If we install our application our app will be enabled and we will have these logs in the log file:
2020-08-31 15:58:42,907+0300 ThreadPoolAsyncTaskExecutor::Thread 64 ERROR admin 958x12356x1 28lz3m 0:0:0:0:0:0:0:1 /rest/plugins/1.0/com.atlassian.jira.migration.jira-migration-plugin-key [r.m.a.a.tutorial.impl.MyPluginComponentImpl] Listener not registered
Which is correct because JCMA is absent. Now let's install JCMA and then install our app. We will see the following logs:
2020-08-31 16:00:29,104+0300 http-nio-2990-exec-9 ERROR admin 960x12417x1 28lz3m 0:0:0:0:0:0:0:1 /rest/plugins/1.0/com.atlassian.jira.migration.jira-migration-plugin-key [r.m.a.atlassian.tutorial.ListenerManager] Listener registered
The listener has been registered. Well, that is what we expected.
Let's disable our app, we will see the following message:
2020-08-31 16:01:38,078+0300 http-nio-2990-exec-1 ERROR admin 961x12427x1 28lz3m 0:0:0:0:0:0:0:1 /rest/plugins/1.0/ru.matveev.alexey.atlassian.tutorial.third-party-dependency-tutorial-key [r.m.a.atlassian.tutorial.ListenerManager] Listener deregistered
Again correct. The listener was deregistered.
Now enable our app and disable JCMA:
2020-08-31 16:03:13,050+0300 ThreadPoolAsyncTaskExecutor::Thread 68 ERROR admin 963x12437x1 28lz3m 0:0:0:0:0:0:0:1 /rest/plugins/1.0/ru.matveev.alexey.atlassian.tutorial.third-party-dependency-tutorial-key [r.m.a.a.tutorial.impl.MyPluginComponentImpl] Listener not registered
Again correct. The listener was deregisterd.
Now if you enable JCMA, we expect that our listener should be enabled but it will not be enabled. To fix it we need to create listeners for PluginInstalledEvent and PluginEnabledEvent.
src/main/java/ru/matveev/alexey/atlassian/tutorial/PluginListener.java:
@Slf4j
public class PluginListener implements InitializingBean, DisposableBean {
private final EventPublisher eventPublisher;
public PluginListener(@ComponentImport EventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
@EventListener
public void onPluginInstalledEvent(PluginInstalledEvent pluginInstalledEvent) {
ListenerManager.registerListener();
}
@EventListener
public void onPluginEnabledEvent(PluginEnabledEvent pluginEnabledEvent) {
ListenerManager.registerListener();
}
@Override
public void destroy() throws Exception {
this.eventPublisher.unregister(this);
}
@Override
public void afterPropertiesSet() throws Exception {
this.eventPublisher.register(this);
}
}
The code is simple. If PluginInstalledEvent and PluginEnabledEvent are triggered we register our listener. And now if we enable JCMA, our listener will be registered.
You can see the code for this part under v.4 tag.
That is all for the article. We managed to enable our app if our dependant app is absent.
Alexey Matveev
software developer
MagicButtonLabs
Philippines
1,575 accepted answers
0 comments