Last updated Sep 20, 2024

Extending the JIRA Import plugin

This guide will help you extend the JIRA Import plugin using the Project Importer SPI. Currently, you can extend the JIRA importer plugin to include data from your plugins during project imports.

Overview

The JIRA Import plugin is used to import a JIRA project from a backup file into a JIRA instance (see documentation). One of the limitations of the JIRA Import plugin is that it cannot handle plugin data, which means that you cannot move plugin data between JIRA instances. An exception to this is custom fields, but even custom fields have limited support, e.g. the JIRA Agile sprint field might bring data across pointing to a sprint, but that sprint will not exist in the new JIRA instance.

The reason behind this limitation is that the JIRA import plugin does not have any idea of what data is stored by plugins or what the meaning and relationships with in that data are. It cannot tell what data refers to what projects or issues or even if the data is related to a project. For example, JIRA Agile is not organised around projects, but rather uses JQL to define rapid boards, etc.

The solution to this limitation is to let plugins actively participate in the import process. Read the following sections for an overview of the project import process and how your plugins can interact with the Project Importer SPI at each part of the process.

JIRA Project Importer SPI

The project import process consists of a number of steps. The Project Importer SPI allows a plugin to participate in any of these steps. The basic flow is:

  1. JIRA processes the XML, extracting information about the project and issues in the backup, and mapping configuration items, e.g. issue type ids from the source system to the values on the target system.
  2. JIRA processes the XML again, splitting it into a number of files, one for each entity type to be imported, e.g. <jiraissue><customfieldvalue>, <jiraaction>, etc.
  3. JIRA creates the project, if required.
  4. JIRA creates the issues and the issue-related data, such as comments and change history.

There are a number of basic mechanisms that your plugin needs to interact with:

  • Each data entity type (table) can be processed by the plugin, if it chooses to provide a handler for that entity type.
  • There is a collection of Mappers that the plugin can use and contribute to, to work out how data is mapped between IDs in the source and IDs in the target system.
  • The plugin can also contribute results that will appear in the user interface, summarising the results of the import, by adding results to the ProjectImportResult object.
  • Additionally, for AO data the plugin can specify the order in which the AO entities should be imported, so that they can control the rebuilding of dependencies between data.

In this document, we refer to the "JIRA Project Importer SPI" or just the "SPI" as we are mostly describing a set of interfaces that plugins will need to implement to fully participate in a project import.  However, both SPI and API points will be provided and described.

Adding to the project validation

Plugins can add to the validation that is performed before the import is done.

The process is quite complex, but in essence JIRA does a pass over the JIRA backup and gathers essential information for each project, such as the issue types and custom fields used. This information is held in the session. Once the user selects a project to import, JIRA validates the data from the backup for that project against the current system state.

The SPI described here allows plugins to join in that process.

Interface com.atlassian.jira.imports.project.ao.handler.PluggableOverviewAoEntityHandler

Defines a handler class that will be able to gather data from the backup, that can then be used for validation or other purposes. This handler will be called in the initial stages of the import before the user is presented with a list of projects, from which they select the project to import.

The plugin point for this handler is: <project-import-ao-overview-handler>.

Method Summary

  • endTable(String tableName) - void.
    • Provides the implementation an opportunity to perform some action when all rows for a table have been processed.
  • endDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document has finished being read.
  • handleEntity(java.lang.String entityName, java.util.Map<java.lang.String, java.lang.Object> attributes) - void.
    • This is the main method to implement when using this ImportEntityHandler. This method will provide the entity name and a complete map of attribute key/value pairs. The attributes may be any of String, Long, Double, Boolean or Date.
  • handlesEntity(java.lang.String entityName) - boolean.
    • Should return true if the user wishes to process this entity.
  • startDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document is starting to be read.
  • setBackupOverviewBuilder(BackupOverviewBuilder backupOverviewBuilder) - void.
    • Sets the backup overview. Plugins should use BackupOverviewBuilder.addAdditionalData(String key, String projectId, Object data) to store critical backup data for each project in the backup. Multiple entries may be added under the same project and key.

As this is a session object, this should only contain the minimum data required to ensure that the import can proceed for a project.

Interface com.atlassian.jira.imports.project.ao.PluggableValidator

Defines a class that will be called to validate that the selected project can be imported.

Plugin developers need to understand the user flow here:

  1. The user selects a project to import.
  2. JIRA will then validate if the plugin can be imported. PluggableValidator implementations will be called during this stage.
  3. The user can then respond to errors by configuring items in JIRA.
  4. The user can then redo the validation step or select a different project to import.

The plugin point for this handler is: <project-import-validator>.

Method Summary

  • validate(BackupProject backupProject, I18nHelper i18n)  - ValidationMessage.
    • The backupProject will contain any data contributed by the PluggableOverviewAoEntityHandler. See Collection<Object> BackupProject.getAdditionalData(String key).

Importing data

Interface com.atlassian.jira.imports.project.ao.handler.PluggableImportAoEntityHandler

Defines a handler class that will be able to perform some operation, given an AO entity name and the entities attributes.

There are two plugin points that both use this handler, <ao-preimport-handler> and <ao-import-handler>:

  • <ao-preimport-handler>, will be called during the first import stage when the activeobjects.xml is split into files for each entity type. Plugins can gather data at this stage and store that, using instances of AbstractMapper, for use in the actual import stage.
  • <ao-import-handler>, will be called after the OfBiz data has been installed. This is the time when plugins would normally import the required data into the database.

Method Summary

  • endTable(String tableName) - void.
    • Provides the implementation an opportunity to perform some action when all rows for a table have been processed.
  • endDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document has finished being read.
  • handleEntity(java.lang.String entityName, java.util.Map<java.lang.String, java.lang.Object> attributes) - void.
    • This is the main method to implement when using this ImportEntityHandler. This method will provide the entity name and a complete map of attribute key/value pairs. The attributes may be any of String, Long, Double, Boolean or Date.
  • handlesEntity(java.lang.String entityName) - boolean.
    • Should return true if the user wishes to process this entity.
  • getEntityWeight(java.lang.String entityName) - long.
    • Return the weight for this entity. If this handler is does not handle the entity or care about its ordering it should return com.atlassian.jira.imports.project.ao.handler.PluggableImportAoEntityHandler.WEIGHT_NONE. This is ignored (not called) during the pre-import stage where the entities are supplied in the backup XML order.
  • startDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document is starting to be read.
  • setProjectImportMapper(ImportMapper projectImportMapper) - void.
    • A setter for the ImportMapper. This will be injected when the instance is created.
  • setProjectImportResults(ProjectImportResults projectImportResults) - void.
    • A setter for the ProjectImportResults.  This will be injected when the instance is created.
  • setBackupSystemInformation(BackupSystemInformation backupSystemInformation) - void.
    • A setter for the BackupSystemInformation.  This will be injected when the instance is created.
  • setBackupProject(BackupProject backupProject) - void.
    • A setter for the BackupProject. This will be injected when the instance is created.

Interface com.atlassian.jira.imports.project.handler.PluggableImportOfBizEntityHandler

Defines a handler class that will be able to perform some operation, given an OfBiz entity name and the entities attributes.

There are two plugin points that both use this handler, <ofbiz-preimport-handler> and <ofbiz-import-handler>:

  • <ofbiz-preimport-handler> will be called during the first import stage when the entities.xml is split into files for each entity type. Plugins can gather data at this stage and store that, using instances of AbstractMapper, for use in the actual import stage.
  • <ofbiz-import-handler> will be called as JIRA imports the OfBiz Data. Plugins may need to import some data during this stage, e.g. entity properties.

Method Summary

  • endDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document has finished being read.
  • handleEntity(java.lang.String entityName, java.util.Map<java.lang.String, java.lang.Object> attributes) - void.
    • This is the main method to implement when using this ImportEntityHandler. This method will provide the entity name and a complete map of attribute key/value pairs.
  • handlesEntity(java.lang.String entityName) - boolean.
    • Should return true if the user wishes to process this entity.
  • startDocument() - void.
    • Provides the implementation an opportunity to perform some action when the document is starting to be read.
  • setProjectImportMapper(ImportMapper projectImportMapper) - void.
    • A setter for the ImportMapper.  This will be injected when the instance is created.
  • setProjectImportResults(ProjectImportResults projectImportResults) - void.
    • A setter for the ProjectImportResults.  This will be injected when the instance is created.
  • setBackupSystemInformation(BackupSystemInformation backupSystemInformation) - void.
    • A setter for the BackupSystemInformation.  This will be injected when the instance is created.
  • setBackupProject(BackupProject backupProject) - void.
    • A setter for the BackupProject.  This will be injected when the instance is created.

Class com.atlassian.jira.imports.project.mapper.AbstractMapper

Plugins should provide a concrete implementation of this class when they need to map old values from the source system to new values in the destination and/or to flag values as required.  They can, of course, extend it to provide any additional data they required to help in completing the import process. A general purpose mapper, SimpleProjectImportIdMapperImpl, is provided for use in trivial cases.

Instances of the mapper are available by calling the getPluggableMapper(String mapperKey) on the ImportMapper.

Method Summary

  • clearMappedValues() - void.
    • This will clear any mapped data that may have been entered into the mappers.
  • flagValueAsRequired(java.lang.String oldId) - void.
    • This is an internal method for use by Mappers extending AbstractMapper and should not be called from other classes.
  • getAllMappedIds() - java.util.Collection<java.lang.String>.
    • Returns a Collection of all the new IDs that are mapped to.
  • getDisplayName(java.lang.String oldId) - java.lang.String.
    • Returns a display name for the given old ID.
  • getKey(java.lang.String oldId) - java.lang.String.
    • Returns the registered "key" for the given old ID, or null if none is registered.
  • getMappedId(java.lang.String oldId) - java.lang.String.
    • Retrieves a String that corresponds to the id in the target JIRA system, null if none has been mapped.
  • getRegisteredOldIds() - java.util.Collection<java.lang.String>.
    • Returns a collection of ID's as String objects identifying objects from the import file that are required for the import.
  • getRequiredOldIds() - java.util.Collection<java.lang.String>.
    • Returns a collection of ID's as String objects identifying all objects of the appropriate type found in the import file.
  • getValuesFromImport() - java.util.Collection<IdKeyPair>.
    • Deprecated. Use getRegisteredOldIds()
  • mapValue(java.lang.String oldId, java.lang.String newId) - void.
    • This method maps a value from the backup system to a valid value in the target system.
  • registerOldValue(java.lang.String oldId, java.lang.String oldKey) - void.
    • This is an internal method for use by Mappers extending AbstractMapper and should not be called from other classes.

Interface com.atlassian.jira.imports.project.handler.PluggableImportRunnable

There are two plugin points that both use this handler, <preimport-handler> and <postimport-handler>:

  • <preimport-handler> defines a handler class that will be called after the project object is created, but before any configuration or issue data is imported. Plugins can use this handler to examine the state of the target system before the import begins.
  • <postimport-handler> defines a handler class that will be called after the all data is imported into the destination system.

Method Summary

  • run() - void.
    • Provides the implementation an opportunity to perform some action.
  • setImportMapper(ImportMapper projectImportMapper) - void.
    • A setter for the ImportMapper.  This will be injected when the instance is created.
  • setProjectImportResults(ProjectImportResults projectImportResults) - void.
    • A setter for the ProjectImportResults.  This will be injected when the instance is created.
  • setBackupSystemInformation(BackupSystemInformation backupSystemInformation) - void.
    • A setter for the BackupSystemInformation.  This will be injected when the instance is created.
  • **setBackupProject(BackupProject backupProject) - void. **
    • A setter for the BackupProject.  This will be injected when the instance is created.

Plugin custom field types

Interface com.atlassian.jira.imports.project.customfield.ProjectImportableCustomField

Any plugins that provide custom field types that need to be migrated need to implement the ProjectImportableCustomField interface. This interface requires the class to provide a ProjectCustomFieldImporter that will look after any data translation required during the archiving process.

Method Summary

  • getProjectImporter() - ProjectCustomFieldImporter.
    • Returns the object that will perform the actual project import functions for the custom field type.

Interface com.atlassian.jira.imports.project.customfield.ProjectCustomFieldImporter

Plugins also need to supply or use a current JIRA implementation of this interface to actually map the imported data as required.

Method Summary

  • canMapImportValue(ProjectImportMapper projectImportMapper, ExternalCustomFieldValue customFieldValue, FieldConfig fieldConfig, I18nHelper i18n) - MessageSet.
    • The custom field needs to determine if the provided custom field value, in the context of the config and project import mapper, is a valid value that can be imported.
  • getMappedImportValue(ProjectImportMapper projectImportMapper, ExternalCustomFieldValue customFieldValue, FieldConfig fieldConfig) - ProjectCustomFieldImporter.MappedCustomFieldValue.
    • The custom field needs to determine what the "mapped" value will be for the provided custom field value and return this new string representation of the value.

Contributing results

Each import handler is injected with the ProjectImportResults object. Plugins can add results to this using the following:

  • ProjectImportResult
    • void addResult(long count, String msgKey) 
      Add an import result.
      This method is provided to enable plugins to supply counts of items imported.
      Plugins should try to display only a small amount of the most relevant information so as not to overwhelm the user and clutter the result

A setter for the BackupProject.  This will be injected when the instance is created.

Plugin XML

This section describes the key elements for your plugin descriptor, if you are using the Project Importer SPI. An example of the plugin XML is shown below:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<atlassian-plugin >

    <project-import-ao-overview-handler key="myplugin-ao-overview-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyReferenceAoOverview" />
    <project-import-validator key="myplugin-import-validator" class="com.atlassian.jira.dev.myplugin.imports.project.MyProjectImportValidator" />
    <project-preimport-handler key="myplugin-preimport-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyPreImportPluginModule" />
    <project-postimport-handler key="myplugin-postimport-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyPostImportPluginModule" />
    <project-ao-preimport-handler key="myplugin-ao-preimport-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyAoPreImport" />
    <project-ao-import-handler key="myplugin-ao-import-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyAoImport" />
    <project-ofbiz-preimport-handler key="myplugin-ofbiz-preimport-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyOfBizPreImport" />
    <project-ofbiz-import-handler key="myplugin-ofbiz-import-handler" class="com.atlassian.jira.dev.myplugin.imports.project.MyOfBizImport" />

</atlassian-plugin>

For the definition of the contents of these elements, see below:

Element

Description

<project-import-ao-overview-handler>

This block defines a handler that will be called while JIRA gathers data for al projects in the backup before the import starts.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableOverviewAoEntityHandler.

Attribute: class.

Element

Description

<project-import-validator>

This block defines a module that can validate that a selected project is OK to import.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableValidator.

Attribute: class.

Element

Description

<project-preimport-handler>

This block defines a handler that will be called before the import of data commences.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableImportRunnable.

Attribute: class.

Element

Description

<project-postimport-handler>

This block defines a handler that will be called after the import of data is complete.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: class.

 

An implementation of PluggableImportRunnable.

Attribute: class.

Element

Description

<project-ofbiz-preimport-handler>

This block defines a handler that will be called for each data row for OfBiz tables that are handled.

It is called during the preprocessing of the data, when all "entities.xml" entry from the backup zip file is processed in one pass and the entity types (tables) are split into separate files. Entities are processed in the order they are contained in the backup, which is alphabetical order.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableImportOfBizEntityHandler.

Attribute: class.

Element

Description

<project-ao-preimport-handler>

This block defines a handler that will be called for each data row for Active Objects tables that are handled.

It is called during the preprocessing of the data, when all "activeobjects.xml" entry from the backup zip file is processed in one pass and the entity types (tables) are split into separate files. Entities are processed in the order they are contained in the backup, which is alphabetical order.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableImportAoEntityHandler.

Attribute: class.

Element

Description

<project-ofbiz-import-handler>

This block defines a handler that will be called for each data row for OfBiz tables that are handled.

It is called during the import of the data. The import of the data is actually performed by JIRA and generally plugins would not need to do anything at this time, but they can observe the data if they wish.

The ordering of the data is controlled by JIRA and is undefined and may change from one JIRA to another.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableImportOfBizEntityHandler.

Attribute: class.

Element

Description

<project-ao-import-handler>

This block defines a handler that will be called for each data row for Active Objects tables that are handled.

It is called during the import of the data. Plugins should import their data at this time. 

The ordering of the data is determined by the results of the calls to getEntityWeight(String entityName) with lowest values processed first. Plugin developers may cooperate to weight entities appropriately so plugin A's data is imported before plugin-B's data. Entities with lower weights are imported before entities with higher weights.

 

The key of this handler definition.  Must be unique within the plugin.

Attribute: key.

 

An implementation of PluggableImportAoEntityHandler.

Attribute: class.

Notes

  • Active Object and Property set data is imported after JIRA Configuration data and JIRA Issues (the "jiraissue" entity).
  • Property set data is pre-processed into a joined format, joining <property-entry> and <property-????> data, i.e. an <OSPropertyString> entry will also have the entityName and propertyKey attributes.

Rate this page: