Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Warning

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 !

Contents

Table of Contents

...

  1. A decent grasp of the Java language
  2. Basic knowledge about the toolchain (maven specifically)
  3. Certain Spring framework notions (injection)
  4. Know Atlassian app basics (see https://developer.atlassian.com/ for details). You will also need Atlassian SDK (see this documentation for reference)
  5. An IDE (e.g. atlassian.com/ for details). You will also need Atlassian SDK (see this documentation for reference)
  6. An IDE (e.g. Eclipse). We use JetBrains IntelliJ Idea here.

...

  1. Eclipse). We use JetBrains IntelliJ Idea here.

Setup

  1. Install the Atlassian developer kit from the terminal/command line

    Code Block
    themeEmacs
    brew tap atlassian/tap
    brew install atlassian/tap/atlassian-plugin-sdk
  2. Download your desired version of the SIL Engine from the marketplace.

  3. Add the SIL Engine to the local maven repository

    Code Block
    languagepowershell
    themeEmacs
    titleTemplate
    atlas-mvn install:install-file -Dfile=<Location of Jar from step 2> -DgroupId=com.keplerrominfo.jira.plugins -DartifactId=katl-commons -Dversion=<Version of Jar> -Dpackaging=jar
    Code Block
    languagepowershell
    themeEmacs
    titleExample
    atlas-mvn install:install-file -Dfile=/var/atlassian/katl-commons-4.1.6-r20180917150405.jar -DgroupId=com.keplerrominfo.jira.plugins -DartifactId=katl-commons -Dversion=4.1.6 -Dpackaging=jar

Configuration

Adding functionality into SIL™ means that you need to create an Atlassian add-on. Add-ons are basically JAR files containing a descriptor (atlassian-plugin.xml). So let's create one, skipping the tools that Atlassian provides, because there's too much to be customized.

...

Upon finishing your IDE wizard, you will notice that there's a pom.xml in the root of the project. Open it and replace its content with the following:

Expand
Code Block
languagexml
titlepom.xml
linenumberstrue
collapsetrue
 <?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>silexample</artifactId>
    <version>4.0.0</version>


    <name>silexample</name>
    <description>Example: integrating SIL routines and descriptors</description>
    <packaging>atlassian-plugin</packaging>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <katlcommons.version>4.0.0-SNAPSHOT</katlcommons.version>

        <spring.osgi.version>1.2.1</spring.osgi.version>
        <spring.version>4.1.6.RELEASE</spring.version>

        <jira.version>7.0.0</jira.version>
        <jira.data.version>7.0.0</jira.data.version>
        <jira.sal.version>3.0.0</jira.sal.version>
        <jira.event.version>2.3.5</jira.event.version>
        <atlassian.spring.scanner.version>1.2.13</atlassian.spring.scanner.version>
		<silengine.version>4.1.4</silengine.version>

        <plugin.testrunner.version>2.0.0</plugin.testrunner.version>
        <amps.version>6.1.2</amps.version>

    </properties>


    <dependencies>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-api</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>${jira.sal.version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>com.atlassian.event</groupId>
            <artifactId>atlassian-event</artifactId>
            <version>${jira.event.version}</version>
            <scope>provided</scope>
        </dependency>

        <!--
        This is for the annotations you need to indicate components and OSGI services.
        -->
        <dependency>
            <groupId>com.atlassian.plugin</groupId>
            <artifactId>atlassian-spring-scanner-annotation</artifactId>
            <version>${atlassian.spring.scanner.version}</version>
            <scope>compile</scope>
        </dependency>

        <!--
            This is the runtime code for interpreting the build time index files
        -->
        <dependency>
            <groupId>com.atlassian.plugin</groupId>
            <artifactId>atlassian-spring-scanner-runtime</artifactId>
            <version>${atlassian.spring.scanner.version}</version>
            <scope>runtime</scope>
        </dependency>


        <!-- ===================================== -->
        <!-- cPrime dependencies -->
        <!-- ===================================== -->
        <dependency>
            <groupId>com.keplerrominfo.jira.plugins</groupId>
            <artifactId>katl-commons</artifactId>
            <scope>provided</scope>
            <version>${silengine.version}</version>
        </dependency>


        <!-- ===================================== -->
        <!-- Spring & common dependencies -->
        <!-- ===================================== -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <scope>provided</scope>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.3</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
            <scope>provided</scope>
        </dependency>

        <!-- ===================================== -->
        <!-- TEST dependencies -->
        <!-- ===================================== -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.6</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-tests</artifactId>
            <version>${jira.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.plugins</groupId>
            <artifactId>atlassian-plugins-osgi-testrunner</artifactId>
            <version>${plugin.testrunner.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-func-tests</artifactId>
            <version>${jira.version}</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-simple</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>



    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>atlassian-plugin.xml</exclude>
                    <exclude>META-INF/KINFO</exclude>
                    <exclude>META-INF/KVERSION</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>atlassian-plugin.xml</include>
                    <include>META-INF/KINFO</include>
                    <include>META-INF/KVERSION</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-jira-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <compressResources>false</compressResources>

                    <useFastdevCli>false</useFastdevCli>
                    <enableFastdev>false</enableFastdev>
                    <enableDevToolbox>false</enableDevToolbox>
                    <enablePde>false</enablePde>
                    <enableQuickReload>true</enableQuickReload>
                    <quickReloadVersion>1.29</quickReloadVersion>

                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <!-- Enable here the products if you want container tests (you should) -->
                    <products>
                        <product>
                            <id>jira</id>
                            <instanceId>jira-${jira.version}</instanceId>
                            <version>${jira.version}</version>
                            <dataVersion>${jira.data.version}</dataVersion>                                                           
                            <!-- dataPath>${basedir}/src/test/resources/generated-test-resources.zip</dataPath -->
                        </product>
                    </products>
                    <pluginArtifacts>
                        <!-- installed in the product when using atlas-run or atlas-debug -->
                        <pluginArtifact>
                            <groupId>com.keplerrominfo.jira.plugins</groupId>
                            <artifactId>katl-commons</artifactId>
                            <version>${silengine.version}</version>
                        </pluginArtifact>
                    </pluginArtifacts>
                    <pluginDependencies>
                        <!-- OBR dependencies -->
                        <pluginDependency>
                            <groupId>com.keplerrominfo.jira.plugins</groupId>
                            <artifactId>katl-commons</artifactId>
                        </pluginDependency>
                    </pluginDependencies>
                    <testGroups>
                        <testGroup>
                            <id>wired-jira-integration</id>
                            <productIds>
                                <productId>jira-test</productId>
                            </productIds>
                            <includes>
                                <include>it/**/*WiredTest.java</include>
                            </includes>
                        </testGroup>
                    </testGroups>
                    <testBundleExcludes>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo.jira.plugins</groupId>
                            <artifactId>katl-commons</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>sil</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>stl</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>sil-remote-client</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>sil-sms-client</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>kepler-util</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>com.keplerrominfo</groupId>
                            <artifactId>kepler-refapp</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>xalan</groupId>
                            <artifactId>xalan</artifactId>
                        </testBundleExclude>
                        <testBundleExclude>
                            <groupId>xml-apis</groupId>
                            <artifactId>xml-apis</artifactId>
                        </testBundleExclude>
                    </testBundleExcludes>
                    <instructions>
                        <Bundle-Name>SIL Example</Bundle-Name>
                        <Bundle-SymbolicName>com.mycompany.silexample</Bundle-SymbolicName>
                        <Atlassian-Plugin-Key>com.mycompany.silexample</Atlassian-Plugin-Key>
                        <Spring-Context>*</Spring-Context>
                        <Export-Package>
                            !net.java.ao,
                            !com.atlassian,
                            com.mycompany.silexample.*
                        </Export-Package>
                        <Import-Package>
                            com.keplerrominfo.refapp.launcher;resolution:="optional",
                            com.keplerrominfo.refapp.launcher.*;resolution:="optional",
                            org.springframework.osgi.*;resolution:="optional",
                            org.eclipse.gemini.blueprint.*;resolution:="optional",
                            com.atlassian.plugin.osgi.bridge.external,
                            *;resolution:="optional"
                        </Import-Package>
                    </instructions>
                    <skipManifestValidation>true</skipManifestValidation>
                    <systemPropertyVariables>
                        <webdriver.browser>htmlunit</webdriver.browser>
                        <!--
                        You can also use chrome if you have it installed.
                        You'll also need a webDriver for Chrome, which you can get from
                        https://sites.google.com/a/chromium.org/chromedriver/downloads and place it somewhere on the disk.
                        Then just comment the htmlunit driver above and uncomment below.
                        -->
                        <!--<webdriver.browser>chrome</webdriver.browser>-->
                        <!--<webdriver.chrome.driver>D:/tools/chromedriver.exe</webdriver.chrome.driver>-->
                    </systemPropertyVariables>
                </configuration>
            </plugin>
            <plugin>
                <groupId>com.atlassian.plugin</groupId>
                <artifactId>atlassian-spring-scanner-maven-plugin</artifactId>
                <version>${atlassian.spring.scanner.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>atlassian-spring-scanner</goal>
                        </goals>
                        <!-- process-classes seems to be skipped if you are using scala
                             so perhaps use prepare-package -->
                        <phase>process-classes</phase>
                    </execution>
                </executions>
                <configuration>
                    <includeExclude>+com.mycompany.*</includeExclude>
                    <verbose>true</verbose>
                    <scannedDependencies>
                        <!--
                        - Add here the scanned dependencies, if any
                        <dependency>
                            <groupId>com.somegroup</groupId>
                            <artifactId>artifact.id</artifactId>
                        </dependency>
                         -->
                    </scannedDependencies>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <reporting>
        <plugins>
            <plugin>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>LATEST</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.4.3</version>
                <configuration>
                    <argLine>-Xms128m -Xmx256m</argLine>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>LATEST</version>
            </plugin>
        </plugins>
    </reporting>


</project>
Note
titleSIL Version
The property <sil.version> will need to be updated to the version installed in the Setup section


You will notice that everything revolves around two maven plugins:

...

In the resources directory, create a META-INF/spring directory and add a spring.xml file.

...

Code Block
languagexml
titlespring.xml
linenumberstrue
collapsetrue
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:atlassian-scanner="http://www.atlassian.com/schema/atlassian-scanner"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
        http://www.atlassian.com/schema/atlassian-scanner
        http://www.atlassian.com/schema/atlassian-scanner/atlassian-scanner.xsd">
    <atlassian-scanner:scan-indexes />

    <!--
     - Please un-comment this to enable active object support in your plugin
     -
    <bean class="com.atlassian.activeobjects.external.TransactionalAnnotationProcessor">
        <constructor-arg ref="activeObjects"/>
    </bean>
    -->
</beans>


This adds support for bean scanning enabling you to annotate your components with stereotypes. 

...

Open the editor, and create a new java class named ExamplePluginInfoService in the com.mycompany.silexample package.

...

Code Block
languagejava
titleExamplePluginInfoService.java
linenumberstrue
collapsetrue
/*
 * File: ExamplePluginInfoService.java
 * Created by rdumitriu on 13.12.2016.
 */
package com.mycompany.silexample;

import javax.annotation.Nonnull;

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

/**
 * The plugin info service
 *
 * @author Radu Dumitriu (rdumitriu@gmail.com)
 * @version 1.0
 * @since 1.0
 */
@Component
public class ExamplePluginInfoService implements PluginInfoService {
    @Nonnull
    @Override
    public String getKey() {
        return "com.mycompany.silexample";
    }
    
    @Nonnull
    @Override
    public String getName() {
        return "SIL Example";
    }
}


The bean implementation is straightforward. This is a local component, not exported to the outside world, this is what the @Component annotation is used for. 

Now, register certain routines and CF descriptors, and you can do that at launch time of the plugin. The start-stop sequence of plugins in complicated enough therefore we use our own Launcher class, abstracting all those events in such a way that you will know that whatever you do, it is safe to be done. So, extend / implement com.keplerrominfo.refapp.launcher.AbstractPluginLauncher: create an ExampleLauncher class within the same package (com.mycompany.silexample). Override doAtLaunch() and doAtStop() members.

Expand
Code Block
languagejava
titleExampleLauncher.java
linenumberstrue
collapsetrue
 /*
 * File: ExampleLauncher.java
 * Created by rdumitriu on 12.12.2016.
 */
package com.mycompany.silexample;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.fields.CustomField;
import com.atlassian.jira.project.ProjectManager;
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.jira.commons.ivm.*;
import com.keplerrominfo.refapp.config.*;
import com.keplerrominfo.refapp.config.impl.PluginConfigurationServiceImpl;
import com.keplerrominfo.refapp.launcher.AbstractPluginLauncher;
import com.keplerrominfo.sil.lang.RoutineRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * This is a standard launcher for a cPrime add-on. We respect here the life-cycle of the add-on within the whole JIRA,
 * so make sure you follow it.
 *
 * @author Radu Dumitriu (rdumitriu@gmail.com)
 * @version 1.0
 * @since 1.0
 */
@Component
@ExportAsService(LifecycleAware.class)
public class ExampleLauncher extends AbstractPluginLauncher {
    private static final Log LOG = LogFactory.getLog(ExampleLauncher.class);
    
    private final CustomFieldDescriptorRegistry customFieldDescriptorRegistry;
    private final ProjectManager projectManager;
    
    @Autowired
    public ExampleLauncher(@ComponentImport EventPublisher eventPublisher,
                           //this is the local component providing info about the add-on (ExamplePluginInfoService)
                           PluginInfoService pluginInfoService,
                           //do not remove this, it will generate the correct component-import
                           @ComponentImport CommonPluginConfigurationService commonPluginConfigurationService,
                           @ComponentImport HostConfigurationProvider hostConfigurationProvider,
                           @ClasspathComponent PluginConfigurationServiceImpl pluginConfigurationService,
                           @ComponentImport CustomFieldDescriptorRegistry customFieldDescriptorRegistry,
                           @ComponentImport ProjectManager projectManager) {
        super(eventPublisher, pluginInfoService, hostConfigurationProvider, pluginConfigurationService);
        this.customFieldDescriptorRegistry = customFieldDescriptorRegistry;
        this.projectManager = projectManager;
    }
    
    
    @Override
    public void doAtLaunch() {
        super.doAtLaunch();
    
//HERE:: will do more at launch
    }
    
    @Override
    public void doAtStop() {
//HERE:: will do cleanup
        super.doAtStop();
    }
}


We have commented where you should put your own code. You should also note the beans annotated as @ComponentImport, as well as the local bean pluginConfigurationService which offers a separate space to hold configuration values (key-values pairs), local to this plugin.

...