You are not logged in. Click here to log in.

codebeamer Application Lifecycle Management (ALM)

Search In Project

Search inClear

Tags:  not added yet



  • Example code shown on or published on GitHub is provided as is!
  • Intland Software will not guarantee that any example code will work with current or future codeBeamer versions and specific Jira/Plugin versions
  • Intland Software will not provide support for the example code or any @CustomField implementations!


Add support for special JIRA custom fields to the JIRA synchronization

The Synchronization of Trackers with Atlassian JIRA© by default only supports

  • JIRA Core/Custom Fields
  • JIRA Agile fields: Sprint and Epic Link,
  • JIRA Portfolio's Team field,


JIRA custom fields defined by JIRA Add-Ons and Extensions cannot be synchronized by default, because in the field schema provided by the JIRA REST API, the custom field type is identified by an opaque identifier.


E.g. Checklist for Jira:

"customfield_10201": {
   "required": false,
   "schema": {
       "type": "array",
       "items": "checklist-item",
       "custom": "com.okapya.jira.checklist:checklist",
       "customId": 10201
   },
   "name": "DoD",
   ...

You need additional knowledge about each specific custom field, e.g. Modifying checklists using a REST API, that is not available via the JIRA REST API itself, in order to

  • Map the JIRA custom field to an appropriate codeBeamer tracker field
  • Convert JIRA custom field values to appropriate codeBeamer field values and vice versa.
  • Convert JIRA custom field changelog entries into appropriate codeBeamer history entries.


Custom field extensions

In codeBeamer 21.05 (Dorothy) and newer, you can add support for specific JIRA custom fields to the Synchronization of Trackers with Atlassian JIRA© via the @CustomField extension framework.

These extensions are completely annotation based.

Example Code

A fully featured example adding support for Checklist for Jira fields can be found at https://github.com/intland/cb-jira-checklist

Development environment

Developing own/custom field extensions in Java requires a Java 8 or newer Standard Edition (SE) Java Development Kit (JDK).

To setup the development environment you can adopt the Gradle build script published with the example and follow the instructions to configure your IDE.

Alternatively you can setup the build manually by adding frameworks/libraries:

Framework/Library Component Version
org.apache.commons commons-lang3 3.4
com.fasterxml.jackson.core jackson-core 2.9.x
com.fasterxml.jackson.core jackson-databind 2.9.x
org.springframework spring-core 5.2.x
org.springframework spring-context 5.2.x
org.springframework spring-beans 5.2.x


You also need cb.jar from the directory ~/CB-../tomcat/webapps/cb/WEB-INF/lib of the CodeBeamer 2021 or newer installation, you want to develop new @CustomField extensions for.

@CustomField

A Custom field extension is a Java class, that must be annotated as @CustomField and @Component.

The package of your extension class must be a sub-package of com.intland.codebeamer, in order to be automatically detected and deployed by the Component scan during CodeBeamer startup.

The @CustomField annotation has the following attributes:

  • value
    the identifier of the JIRA custom field. Can be ommitted, if the identifier of the JIRA custom field is also used as the Component identifier.
  • type
    the mapped codeBeamer field type.
  • of
    an optional field type qualifier. E.g. a Wikitext representation of a Checklist.



If the JIRA custom field has a primitive type, e.g. string, that is compatible with the mapped codeBeamer field type, e.g. Text, then an empty class with only the annotations is already sufficient.


For example: Picker Color Custom Field:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.intland.codebeamer.controller.jira.CustomField;

@Component("ru.andreymarkelov.atlas.plugins.colorfields:picker-color-custom-field")
@CustomField(type = "Color")
public class ColorPickerField {
}


But with more complex field types, you may also need to add support for specific aspects of custom field schema and value handling:

  • Convert the JIRA custom field schema into the appropriate codeBeamer tracker field definition
  • Convert JIRA custom fields values into appropriate codeBeamer field values and vice versa.
  • Convert JIRA custom field changelog entries into appropriate codeBeamer history entries.


For example: Checklist for Jira:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.intland.codebeamer.controller.jira.CustomField;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {
  ...
}


All methods of @CustomField extensions must be


Context specific information required by a method, must be declared as parameters.

Only the parameter type is relevant, not the parameter name or position.


@MetaData

If you need to make adjustments to the JIRA custom field schema mapping, then your @CustomField extension must also provide a @CustomField.MetaData method.

The method can have any name and return type and can request the following context information via parameters of the appropriate type:


For example: Checklist for Jira:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.ObjectNode;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.controller.jira.JiraImportController;
import com.intland.codebeamer.persistence.dto.TrackerLayoutLabelDto;


@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

   @CustomField.MetaData
   public void setMetaData(TrackerLayoutLabelDto field, ObjectNode metaData, JiraImportController controller) {
    // In the JIRA REST-API schema, a checklist field is an array of checklist items, but in CB it's a single value WIKI field'
    field.setMultipleSelection(Boolean.FALSE);

    // The default value (options) of a checklist, are available in the "allowedValues" property. Only status ID is available with Meta API's. The status name is not.'
    field.setDefaultValue(importChecklist(null, metaData.remove("allowedValues"), controller));

    // Place checklist on own row with full width
    field.setBreakRow(Boolean.TRUE);
    field.setColspan(Integer.valueOf(3));
   }
}
@ImportFieldValue

In order to convert the JIRA custom field value, e.g. Array of checklist items, into the appropriate codeBeamer field value, e.g. Wiki markup, your @CustomField extension must provide a @CustomField.ImportFieldValue method.

The method must return the passed in JIRA field value (provided in it's JSON form) converted into the codeBeamer @CustomField.type.

If the Jira custom field value might contain 4-byte UTF-8 characters, it is necessary to pass such Strings through JiraImportController.check4ByteChars(string)!

The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA import
  • ProjectConfiguration or ProjectDto, to receive the target project of the JIRA import
  • JiraTrackerSyncConfig, to receive the target tracker of the JIRA import
  • TrackerItemDto, to receive the target tracker item, where the custom field value should be stored into
  • TrackerLayoutLabelDto, to receive the target field, whose value to import
  • JsonNode, to receive the Jira custom field value (as provided by the JIRA REST API) to import
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • ImportStatistics, to receive the current import statistics
  • TrackerItemFieldHandler, to receive the current tracker item field handler



For example: Checklist for Jira:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.JsonNode;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.controller.jira.JiraImportController;
import com.intland.codebeamer.controller.jira.JiraTrackerSyncConfig;
import com.intland.codebeamer.wiki.plugins.ChecklistPlugin;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

   /**
    * Convert a <a href="https://okapya.atlassian.net/wiki/spaces/CHKDOC/pages/270172389/Modifying+Checklists+using+a+REST+API">Checklist for JIRA<a>
    * into a {@link ChecklistPlugin} body
    * @param tracker is the JIRA tracker sync configuration
    * @param checklist is the checklist as returned from Jira
    * @param controller to {@link JiraImportController#check4ByteChars(String)}
    * @return the checklist converted into a {@link ChecklistPlugin} body
    */
   public JsonNode jira2cb(JiraTrackerSyncConfig tracker, JsonNode checklist, JiraImportController controller) {
      ...
   }

   @CustomField.ImportFieldValue
   public String importChecklist(JiraTrackerSyncConfig tracker, JsonNode checklist, JiraImportController controller) {
    return ChecklistPlugin.wrapChecklist(jira2cb(tracker, checklist, controller));
   }

}

The Checklist for Jira extension converts the Array of checklist items into an array of Checklist Plugin items (via jira2cb()) and wraps them into Checklist Plugin markup. The Checklist Plugin is also a custom codeBeamer Wiki Plugin, whose example source code is attached.

Please also note, that Checklist for Jira uses it's own Markdown syntax, so checklist item names must be converted from this special markdown to codeBeamer Wiki Markup as well (this happens in jira2cb() and is not shown here, but the example source code is attached).

If the JIRA field value would contain standard JIRA Wiki markup, your extension should use JiraImportController.convertWikiText(String text, String dflt), to convert it into codeBeamer Wiki Markup.

@ImportFieldHistoryValue

In order to also import the JIRA issue changelog, the JIRA changelog entries, must be converted into appropriate codeBeamer TrackerItemHistoryConfigurations.

For a single value field, e.g. status:

{
  "from"       : "10000",
  "fromString" : "To Do",
  "to"         : "3",
  "toString"   : "In Progress"
}

the TrackerItemHistoryConfiguration's change op should be Set and the oldValue and newValue should be the old and new status.


For multiple (array) value fields, e.g. assignee:

{
   "from"       : null,
   "fromString" : null,
   "to"         : "lzoltan",
   "toString"   : "Luspai Zoltan"
}

JIRA typically provides multiple changelog entries for each

  • added (from: null and to: not null) and
  • removed (from: not null and to : null)

value. In this case, the resulting TrackerItemHistoryConfiguration's change op should be Add (the newValue) or Remove (the oldValue).



But each JIRA custom field is free to write into the JIRA changelog whatever it wants!


E.g. Checklist for Jira, although the JIRA field is defined to be an Array of checklist items, only provides a single changelog entry, but that can contain any number of checklist changes, in form of a multi-line string, where an x) at the start of a new line marks the begin of the x.th change.

{
    "from"      : null,
    "fromString": "1) [Checked] Deploy to staging server\n\r2) [Checked] Pull request",
    "to"        : null,
    "toString"  : "1) [Status changed, Unchecked] (In Progress) Deploy to staging server\n\r2) [Unchecked] Pull request"
}


In order to decode the JIRA custom field changelog

  • "from" and "fromString" into the codeBeamer TrackerItemHistoryConfiguration's oldValue
  • "to" and "toString" into the codeBeamer TrackerItemHistoryConfiguration's newValue

a @CustomField extension can provide a @CustomField.ImportFieldHistoryValuemethod, that is called twice per JIRA changelog entry

  1. with "from" and "fromString" to get the TrackerItemHistoryConfiguration's oldValue
  2. with "to" and "toString" to get the codeBeamer TrackerItemHistoryConfiguration's newValue


If the Jira change log string value might contain 4-byte UTF-8 characters, it is necessary to pass such Strings through JiraImportController.check4ByteChars(string)!


The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA import
  • ProjectConfiguration or ProjectDto, to receive the target project of the JIRA import
  • JiraTrackerSyncConfig, to receive the target tracker of the JIRA import
  • TrackerItemDto, to receive the target tracker item, where the custom field value should be stored into
  • TrackerLayoutLabelDto, to receive the target field, whose value to import
  • JsonNode, to receive the "from"/"to" JSON value of the changelog entry to import
  • String, to receive the "fromString"/"toString" String value of the changelog entry to import
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • ImportStatistics, to receive the current import statistics
  • TrackerItemFieldHandler, to receive the current tracker item field handler



For example: Checklist for Jira:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.controller.jira.JiraImportController;
import com.intland.codebeamer.wiki.plugins.ChecklistPlugin;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

    /**
     * Parse the information about JIRA checklist changes from a JIRA issue changelog <code>fromString</code> or <code>toString</code>
     * @param lines is a <code>fromString</code> or <code>toString</code>, that can contain multiple lines of checklist changes, one line per changed checklist item
     * @param controller to {@link JiraImportController#check4ByteChars(String)}, or null
     * @return a Map of the parsed checklist item changes
     */
    @CustomField.ImportFieldHistoryValue
    public Map<Integer,Change> getItemChanges(String lines, JiraImportController controller) {
        Map<Integer,Change> result = ...
        return result;
    }

}

parses the changelog item string into a Map of Changes per Change ID. This code is quite complex and not shown here, but the example source code is attached.


Please note, that this would not be a valid oldValue or newValue for a codeBeamer Checklist history entry, because a Checklist field is a single value Wikitext field, so the oldValue and newValue must be Checklist Wiki markup!



@ImportFieldChange

In order to convert an oldValueObject and a newValueObject (as provided by @ImportFieldHistoryValue) into an appropriate TrackerItemHistoryConfiguration, a @CustomField extension can provide a @CustomField.ImportFieldChange method.

The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA import
  • ProjectConfiguration or ProjectDto, to receive the target project of the JIRA import
  • JiraTrackerSyncConfig, to receive the target tracker of the JIRA import
  • TrackerItemDto, to receive the target tracker item, where the custom field value should be stored into
  • TrackerLayoutLabelDto, to receive the target field, whose value to import
  • TrackerItemHistoryConfiguration, to receive the current tracker item history configuration to import
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • ImportStatistics, to receive the current import statistics
  • TrackerItemFieldHandler, to receive the current tracker item field handler


The method should process the specified TrackerItemHistoryConfiguration,

  • whose oldValueObject and newValueObject are the values provided by @ImportFieldHistoryValue
  • and whose op is initialized to Set
  • and optionally return it.



For example: Checklist for Jira


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.JsonNode;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.controller.jira.JiraImportController;
import com.intland.codebeamer.controller.jira.JiraTrackerSyncConfig;
import com.intland.codebeamer.manager.util.ImportStatistics;
import com.intland.codebeamer.manager.util.ImporterSupport;
import com.intland.codebeamer.manager.util.TrackerItemHistoryConfiguration;
import com.intland.codebeamer.persistence.dto.TrackerItemDto;
import com.intland.codebeamer.persistence.dto.TrackerLayoutLabelDto;
import com.intland.codebeamer.wiki.plugins.ChecklistPlugin;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

    public static Map<Integer,Change> getChanges(Object value) {
        return value instanceof Map ? (Map)value : null;
    }

    /**
     * Convert the specified incremental checklist change into an appropriate Checklist Wikitext change
     * @param tracker is the tracker sync configuration
     * @param item is the tracker item to import
     * @param fieldChange is the incremental custom checklist field change
     * @param importer is the current import cache/support
     * @param statistic are the current import statistics
     */
    @CustomField.ImportFieldChange
    public void buildTrackerItemHistoryConfiguration(JiraTrackerSyncConfig tracker, TrackerItemDto item, TrackerItemHistoryConfiguration fieldChange, ImporterSupport importer, ImportStatistics statistic) {
        TrackerLayoutLabelDto field;
        Map<Integer,Change>      newValues;

        if (fieldChange != null && (field = fieldChange.getField()) != null
                                && (newValues = getChanges(fieldChange.getNewValueObject())) != null && newValues.size() > 0) {
            Map<Integer,Change> oldValues = getChanges(fieldChange.getOldValueObject());

            fieldChange.setOldValueObject(null);
            fieldChange.setNewValueObject(null);

            JsonNode  oldItems = getChecklist(tracker, item, field, importer, statistic);
            Checklist modified = new Checklist(oldItems.deepCopy());

            for (Map.Entry<Integer,Change> change : newValues.entrySet()) {
                Change newItem = change.getValue();
                Change oldItem = oldValues.get(change.getKey());

                if (newItem.wasAdded()) {
                    newItem.apply(tracker, modified.addItem());
                } else if (newItem.wasRemoved()) {
                    modified.removeItem(StringUtils.defaultString(oldItem != null ? oldItem.getName() : null,  newItem.getName()));
                } else if (newItem.wasReordered()) {
                    modified.reorderItems();
                } else {
                    // We must NOT add the item here, if no such item exists (any more), because it was most probably removed locally and MUST remain removed !
                    ObjectNode node = modified.getItem(StringUtils.defaultString(newItem.wasRenamed() && oldItem != null ? oldItem.getName() : null,  newItem.getName()));
                    if (node != null) {
                        if (!newItem.isHeader() && oldItem != null && oldItem.isHeader()) {
                            node.remove(HEADER);
                        }

                        newItem.apply(tracker, node);
                    }
                }
            }

            modified.applyOrder(getChecklist(tracker, item, field));

            fieldChange.setOldValue(ChecklistPlugin.wrapChecklist(oldItems));
            fieldChange.setNewValue(ChecklistPlugin.wrapChecklist(modified.getItems()));

            setChecklist(item, field, modified.getItems(), importer);
        }
    }

}

In order to convert incremental changes to the Checklist for Jira into the appropriate oldValue and newValue of the codeBeamer Checklist Wiki field before and after the change, the ChecklistForJira extension needs to maintain a cache of checklist values per field.


But since a custom field extension must be stateless, this cache must be stored in the import context, represented by the ImporterSupport instance:

  • getChecklist(tracker, item, field, importer, statistic)
  • setChecklist(item, field, checklist, importer)

The full example source code is attached).


@ResolveUpdateConflicts

Update conflicts can only occur upon the import of changes for an already existing target item, if the same field value was modified in JIRA and an in codeBeamer concurrently.
By default, conflict resolution works as follows:

  • For single value fields, e.g. status
    • The new JIRA value has precedence
    • Any local field changes will be discarded
  • For multiple value fields, e.g. assignee
    • The typically incremental JIRA changes will be applied to the target codeBeamer field, e.g. Add user "Zoltan Luspai".
      • If there are conflicting local changes, they will be discarded
    • Remaining (non-conflicting) local changes will be exported to JIRA (if bi-directional synchronization)


If a @CustomField extension wants to get involved in/take control of conflict resolution, it can provide a @CustomField.ResolveUpdateConflictsmethod.


The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA import
  • ProjectConfiguration or ProjectDto, to receive the target project of the JIRA import
  • JiraTrackerSyncConfig, to receive the target tracker of the JIRA import
  • TrackerItemDto, to receive the target tracker item, whose import is finished
  • TrackerLayoutLabelDto, to receive the target field, whose value was imported
  • List<IdentifiableFieldValueDto>, to receive the local field updates to be applied remotely
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • TrackerItemFieldHandler, to receive the current tracker item field handler



For example: Checklist for Jira does not @ResolveUpdateConflicts

  • In case of concurrent modifications, local changes to the target Checklist Wiki field will be overwritten.
@ImportFinished

If a @CustomField extension wants to get called, after the import processing of an item is finished, it can provide a @CustomField.ImportFinished method.

The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA import
  • ProjectConfiguration or ProjectDto, to receive the target project of the JIRA import
  • JiraTrackerSyncConfig, to receive the target tracker of the JIRA import
  • TrackerItemDto, to receive the target tracker item, whose import is finished
  • TrackerLayoutLabelDto, to receive the target field, whose value was imported
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • ImportStatistics, to receive the current import statistics
  • TrackerItemFieldHandler, to receive the current tracker item field handler


Please note, that this method will get called once per imported item and per item field, where the @CustomField extension is associated with.



For example: Checklist for Jira


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.JsonNode;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.manager.util.ImporterSupport;
import com.intland.codebeamer.persistence.dto.TrackerItemDto;
import com.intland.codebeamer.persistence.dto.TrackerLayoutLabelDto;
import com.intland.codebeamer.wiki.plugins.ChecklistPlugin;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

    /**
     * Remove the cached checklist for the specified field of the specified item, after the item import is finished
     * @param item is the current tracker item to import
     * @param field is the checklist field
     * @param importer is the current import cache/support
     */
    @CustomField.ImportFinished
    public void resetChecklist(TrackerItemDto item, TrackerLayoutLabelDto field, ImporterSupport importer) {
        Map<Integer,JsonNode> checklists = getChecklists(importer, false);
        if (checklists != null) {
            if (field != null) {
                checklists.remove(field.getId());
            } else {
                checklists.clear();
            }
        }
    }

}

The ChecklistForJira extension needs to reset its cache of checklist values per field after each item.

@ExportFieldValue

In order to convert the codeBeamer field value, e.g. Wiki markup, into the appropriate JIRA custom field value, e.g. Array of checklist items, your @CustomField extension must provide a @CustomField.ExportFieldValue method.

The method must return the specified codeBeamer field value, e.g. Wikitext String, converted into the appropriate JIRA custom field value, e.g. Array of checklist items.

The method has access to the following context information via parameters of the appropriate type:

  • JiraImportController, to receive the controller of the current JIRA export
  • ProjectConfiguration or ProjectDto, to receive the source project of the JIRA export
  • JiraTrackerSyncConfig, to receive the source tracker of the JIRA export
  • TrackerItemDto, to receive the source tracker item, where the custom field value comes from
  • TrackerLayoutLabelDto, to receive the source field, where the value comes from
  • @CustomField.type, to receive the source field value to export
  • JiraRestClient, to receive the REST API of the remote JIRA instance to import from
  • ImporterSupport, to receive the current importer
  • TrackerItemFieldHandler, to receive the current tracker item field handler



For example: Checklist for Jira:


package com.intland.codebeamer.controller.jira;

import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.node.JsonNode;

import com.intland.codebeamer.controller.jira.CustomField;
import com.intland.codebeamer.controller.jira.JiraImportController;
import com.intland.codebeamer.controller.jira.JiraTrackerSyncConfig;
import com.intland.codebeamer.persistence.dto.TrackerLayoutLabelDto;
import com.intland.codebeamer.wiki.plugins.ChecklistPlugin;

@Component("com.okapya.jira.checklist:checklist")
@CustomField(type="WikiText", of="Checklist")
public class ChecklistForJiraField {

   /**
    * Convert a {@link ChecklistPlugin} body into a
    * <a href="https://okapya.atlassian.net/wiki/spaces/CHKDOC/pages/270172389/Modifying+Checklists+using+a+REST+API">Checklist for JIRA<a>
    * @param tracker is the JIRA tracker sync configuration
    * @param checklist is the {@link ChecklistPlugin} body
    * @return the converted {@link ChecklistPlugin} body
    */
   public JsonNode cb2jira(JiraTrackerSyncConfig tracker, JsonNode checklist) {
      ...
   }

   @CustomField.ExportFieldValue
   public JsonNode exportChecklist(JiraTrackerSyncConfig tracker, String markup) {
    return cb2jira(tracker, ChecklistPlugin.unwrap(markup));
   }

}

Upon import, the Checklist for Jira extension converts the Array of checklist items into an array of Checklist Plugin items (via jira2cb()) and wraps them into Checklist Plugin markup.
Upon export, the Checklist Plugin body is unwrapped and then converted into an Array of checklist items (via cb2jira()). This will also convert codeBeamer Wiki Markup back into the special Checklist for Jira Markdown syntax (see attached) example source code).

If the JIRA field value would use standard JIRA Wiki markup, your extension should use JiraImportController.exportWikiText(String markup, JiraRestClient restApi, to convert codeBeamer Wiki Markup into JIRA Wiki markup.

Deployment

To deploy your custom field extension(s), the Java code must be compiled and the resulting *.class file(s) must be uploaded to the appropriate sub-directory under ~/CB-.../tomcat/webapps/cb/WEB-INF/classes/..., where the sub-directory is the equivalent of the extension's package name.

If you have multiple custom field extensions (along with custom Wiki Plugins, Workflow Actions, etc.), you may choose to pack all your custom classes (along with your custom language resource files) into one Java archive (*.jar) and put that into ~/CB-.../tomcat/webapps/cb/WEB-INF/lib.

Finally you have to restart CodeBeamer, for your newly deployed classes to get loaded.