SIL support for nFeed plugin

Looking for the documentation on the newest versions of SIL Engine and the Simple Issue Language for Jira 8 for Server/Data Center? Click here !


Please note that NFeed support is an experimental feature which can harm your nFeed plugin if not used properly (values on set do really need to exist in the database!). This might change in the future when we revisit this in our roadmap.

Introduction

This plugin is used to offer Simple Issue Language™ (SIL™) support for the SQL Feed Custom Field offered by the nFeed Jira plugin. The nFeed plugin connects your data to Jira issues by creating custom fields with data from remote files, web services and databases, multi-selecting values from your data sources, creating infinite cascading selections, integrating the notion of conditional requests within Jira's custom fields. This page show you how this custom field is handled by our plugins and especially by SIL™.

Solution

First of all we need to create a CF Descriptor that allow us to handle the values that the nFeed Custom Field accepts and returns:

NFeedCFDescriptor
/* 
 * File: NFeedCFDescriptor.java
 */
package com.keplerrominfo.jira.plugins.keplernfeed.nfeedcf;

import java.util.Collection;
import java.util.List;

import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.fields.CustomField;
import com.keplerrominfo.common.util.MutableString;
import com.keplerrominfo.jira.commons.ivm.AbstractCustomFieldDescriptor;
import com.keplerrominfo.sil.lang.*;
import com.keplerrominfo.sil.lang.type.TypeInst;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The nFeed CF descriptor for nFeed plugin
 *
 * @author Paul Talpiga (paul.talpiga@kepler-rominfo.com)
 * @author Florin Manaila (florin.manaila@kepler-rominfo.com)
 * @author Alexandru Iacob (alexandru.iacob@kepler-rominfo.com)
	* @author Ana-Maria Gologan (ana.gologan@kepler-rominfo.com)
 * @since 1.0
 * @version 4.0.0
 */
public class NFeedCFDescriptor extends AbstractCustomFieldDescriptor<Object, KeyableArraySILObject<MutableString>> {
   
   private static final Log LOG = LogFactory.getLog(NFeedCFDescriptor.class);
   
   /**
    * Constructor
    * @param issue
    * @param customField
    */
   public NFeedCFDescriptor(Issue issue, CustomField customField) {
      super(issue, customField);
   }

   /**
    * @see com.keplerrominfo.jira.commons.ivm.FieldDescriptor#getType()
    */
   @Override
   public SILType<KeyableArraySILObject<MutableString>> getType() {
      return TypeInst.STRING_ARR;
   }
   
   /**
    * @see com.keplerrominfo.jira.commons.ivm.FieldDescriptor
    * #toJiraValue(com.keplerrominfo.sil.lang.value.SILValue)
    */
   @Override
   public Object toJiraValue(SILContext ctx, SILValue<KeyableArraySILObject<MutableString>> value) {
      ClassLoader nfCl = getCustomField().getCustomFieldType().getClass().getClassLoader();
      Object ret;
      try {
         Class clazz = nfCl.loadClass("com.valiantys.jira.plugins.sql.customfield.SQLFeedContent");
         ret = clazz.getConstructor().newInstance();
         clazz.getDeclaredMethod("setValues", List.class).invoke(ret, value.toStringArray());
      } catch (Exception e) {
         String msg = String.format("Could not convert %s to nFeed value", 
                              value.toString());
         LOG.error(msg, e);
         throw new SILException(msg, e);
      }
      return ret;
   }
   
   /**
    * @see com.keplerrominfo.jira.commons.ivm.FieldDescriptor
    * #toSILValue(java.lang.Object)
    */
   @Override
   public SILValue<KeyableArraySILObject<MutableString>> toSILValue(SILContext ctx, Object value) {
        SILValue<KeyableArraySILObject<MutableString>> ret = SILValueFactory.stringArray((Collection<String>)value);
      if(LOG.isDebugEnabled()){
         LOG.debug(String.format("Translated (JIRA) %s to (SIL) %s", 
                           value, ret.toString()));
      }
      return ret;
   }
   
}

 

This class extends the abstract class AbstractCustomFieldDescriptor form the SIL Engine™ (former katl-commons) plugin. Due to the fact that the nFeed CF accepts only SQLFeedContent objects, in order to set the value of this custom field we need to get this type through Java Reflection. The toJiraValue() method is overridden in order to provide a converter from the SIL™ Value to the objects that the nFeed CF supports.

The values returned by the nFeed CFs are in fact Collection<String> and this is why we are able to create the SIL™ value for this CF type in a simple manner only by calling the SILValue constructor.


Then, we need to register the CF Descriptor. For that, we need our own launcher.

KeplerNFeedLauncher
/*
 * File: KeplerNFeedLauncher
 */
package com.keplerrominfo.jira.plugins.keplernfeed.admin;


import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.plugin.spring.scanner.annotation.component.ClasspathComponent;
import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.atlassian.sal.api.lifecycle.LifecycleAware;
import com.keplerrominfo.common.util.MutableString;
import com.keplerrominfo.jira.commons.hostapp.JMUserServices;
import com.keplerrominfo.jira.commons.ivm.*;
import com.keplerrominfo.jira.plugins.keplernfeed.nfeedcf.NFeedCFDescriptor;
import com.keplerrominfo.refapp.config.*;
import com.keplerrominfo.refapp.config.impl.PluginConfigurationServiceImpl;
import com.keplerrominfo.refapp.launcher.AbstractPluginLauncher;
import com.keplerrominfo.sil.lang.KeyableArraySILObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * The launcher for Kepler nFeed plugin
 *
 * @author Ana-Maria Gologan (ana.gologan@kepler-rominfo.com)
 * @version 1.0
 * @since 4.0.0
 */
@Component
@ExportAsService(LifecycleAware.class)
public class KeplerNFeedLauncher extends AbstractPluginLauncher {
    
    private static final String NFEED_CF_KEY =
            "com.valiantys.jira.plugins.SQLFeed:com.valiantys.jira.plugins.sqlfeed.customfield.type";

    private final CustomFieldDescriptorRegistry customFieldDescriptorRegistry;
    
    @Autowired
    public KeplerNFeedLauncher(PluginInfoService pluginInfoService,
                               @ClasspathComponent PluginConfigurationServiceImpl pluginConfigurationService,
                               @ComponentImport EventPublisher eventPublisher,
                               @ComponentImport HostConfigurationProvider hostConfigurationProvider,
                               // needed by UserIsAdministratorCondition from commons. DON'T REMOVE
                               @ComponentImport JMUserServices userServices,
                               // needed by PluginConfigurationServiceImpl above. DON'T REMOVE
                               @ComponentImport CommonPluginConfigurationService commonPluginConfigurationService,
                               @ComponentImport final CustomFieldDescriptorRegistry customFieldDescriptorRegistry) {
        super(eventPublisher, pluginInfoService, hostConfigurationProvider, pluginConfigurationService);
        this.customFieldDescriptorRegistry = customFieldDescriptorRegistry;
    }
    
    @Override
    public void doAtLaunch() {
        super.doAtLaunch();
        customFieldDescriptorRegistry.register(NFEED_CF_KEY, NFEED_CFDF);
    }
    
    @Override
    public void doAtStop() {
        customFieldDescriptorRegistry.unregister(NFEED_CF_KEY);
        super.doAtStop();
    }
    
    private static final CustomFieldDescriptorFactory<Object, KeyableArraySILObject<MutableString>> NFEED_CFDF =
            new AbstractCustomFieldDescriptorFactory<Object, KeyableArraySILObject<MutableString>>(
                    "sil.adapters.cf.nfeed",
                    "nFeed -> string[]",
                    "Translates to an array of string values") {
                @Override
                public FieldDescriptor<Object, KeyableArraySILObject<MutableString>> createDescriptor(Issue issue,
                                                                                                      CustomField cf) {
                    return new NFeedCFDescriptor(issue, cf);
                }
            };
}

The line 58 registers the nFeed CF into our CF translators map.


Also, we need to declare certain basic information about what we bring into SIL™.

KeplerNFeedPluginInfoService
 /*
 * File: KeplerNFeedPluginInfoService
 */
package com.keplerrominfo.jira.plugins.keplernfeed.admin;

import javax.annotation.Nonnull;

import com.keplerrominfo.refapp.config.PluginInfoService;
import org.springframework.stereotype.Component;

/**
 * The Info Service for Kepler nFeed plugin
 *
 * @author Ana-Maria Gologan (ana.gologan@kepler-rominfo.com)
 * @version 1.0
 * @since 4.0.0
 */
@Component
public class KeplerNFeedPluginInfoService implements PluginInfoService {
    
    public static final String PLUGIN_NAME = "Kepler SIL nFeed Support";
    public static final String PLUGIN_KEY = "com.keplerrominfo.jira.plugins.keplernfeed";
    
    @Nonnull
    public String getKey() {
        return PLUGIN_KEY;
    }
    
    @Nonnull
    public String getName() {
        return PLUGIN_NAME;
    }
}



That is all. You can use this CF in your SIL™ programs by typing the name of your nFeed custom field - like any other Jira CF accepted by SIL™.

Example
string[] array = nFeed; //where nFeed is your CF name !
return array[0];

Binaries and Sources

Ok, now that you read it through, here they are: the binary (jar) and the sources, packed as zip: