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 |
---|
...
- A decent grasp of the Java language
- Basic knowledge about the toolchain (maven specifically)
- Certain Spring framework notions (injection)
- Know Atlassian app basics (see https://developer.atlassian.com/ for details). You will also need Atlassian SDK (see this documentation for reference)
- An IDE (e.g. Eclipse). We use JetBrains IntelliJ Idea here.
...
- )
- An IDE (e.g. Eclipse). We use JetBrains IntelliJ Idea here.
Setup
Install the Atlassian developer kit from the terminal/command line
Code Block theme Emacs brew tap atlassian/tap brew install atlassian/tap/atlassian-plugin-sdk
Download your desired version of the SIL Engine from the marketplace.
Add the SIL Engine to the local maven repository
Code Block language powershell theme Emacs title Template 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 language powershell theme Emacs title Example 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:
...
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<?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 | ||
---|---|---|
| ||
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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
<?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 | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
/*
* 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 | ||||||||
| ||||||||
/*
* 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.
...