Getting Started

That sounds like some simple features to implement so let's get to it.

First install the Atlassian SDK and Oracle Java JDK 8 on your system.

After the installation all the atlas-* commands and java command should be on the PATH variable.

Let's do a short version check of the installed Atlassian SDK tools. I am using a Mac so the output will look a little different if you use another OS.

atlas-version
ATLAS Version:    6.1.0
ATLAS Home:       /Applications/Atlassian/atlassian-plugin-sdk-6.1.0
ATLAS Scripts:    /Applications/Atlassian/atlassian-plugin-sdk-6.1.0/bin
ATLAS Maven Home: /Applications/Atlassian/atlassian-plugin-sdk-6.1.0/apache-maven-3.2.1
AMPS Version:     6.1.2
--------
Executing: /Applications/Atlassian/atlassian-plugin-sdk-6.1.0/apache-maven-3.2.1/bin/mvn --version -gs /Applications/Atlassian/atlassian-plugin-sdk-6.1.0/apache-maven-3.2.1/conf/settings.xml
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=256M; support was removed in 8.0
Apache Maven 3.2.1 (ea8b2b07643dbb1b84b6d16e1f08391b666bc1e9; 2014-02-14T18:37:52+01:00)
Maven home: /Applications/Atlassian/atlassian-plugin-sdk-6.1.0/apache-maven-3.2.1
Java version: 1.8.0_45, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/jre
Default locale: de_DE, platform encoding: UTF-8
OS name: "mac os x", version: "10.10.5", arch: "x86_64", family: "mac"

Now check if Java is correctly installed.

java -version
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

Ok once you got all that set-up we can start.

Creating the base project

We will now create the JIRA Plugin via the Atlassian SDK command atlas-create-jira-plugin. Change to a folder where you want to create your Plugin.

You will be asked to specify a groupId, artifactId, version and package. You can see my choices below.

atlas-create-jira-plugin
...
Define value for groupId: : io.codeclou
Define value for artifactId: : kitchen-duty-plugin
Define value for version:  1.0.0-SNAPSHOT: :
Define value for package:  io.codeclou: : io.codeclou.kitchen.duty
....
Confirm
Y
...

Now the command should have created a folder called like the artifactId, in our case kitchen-duty-plugin.

How do JIRA Plugins work?
JIRA Plugins are OSGi bundles.
JIRA starts an OSGi Service Plattform during startup (e.g. Apache felix).
By providing information in pom.xml and custom packaging via atlas-package the SDK builds an OSGi Plugin bundle in form of a jar file.
JIRA Plugins can therefore be installed into a running JIRA instance. Once installed they are executed on JIRA startup too.

Open Project in Java IDE

Use your favorite Java IDE to open the project. I use JetBrains IntelliJ IDEA since it is just awesome. If you want to use Eclipse it is fine with me but all the screenshots will be done with IntelliJ.

Once you have it opened we need to create 3 files for better usage with git and to tell the IDE how we want our indentation.

Create .editorconfig and add statements for line endings, charset and tab indentation. Since IntelliJ is awesome EditorConfig Plugin is already installed. If you are using another IDE get the plugin now.

.editorconfig
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8

[*.html]
indent_style = space
indent_size = 4

[*.js]
indent_style = space
indent_size = 4

[*.css]
indent_style = space
indent_size = 4

[*.xml]
indent_style = space
indent_size = 4

[*.java]
indent_style = space
indent_size = 4

[*.vm]
indent_style = space
indent_size = 4

Create .gitignore and add ignore statements for files we do not want to commit.

.gitignore
.idea/
*.iml
*.log
target/

Create .gitattributes and set line endings to unix.

.gitattributes
*.svg eol=lf
*.html eol=lf
*.js eol=lf
*.css eol=lf
*.scss eol=lf
*.json eol=lf
*.sh eol=lf
*.yml eol=lf
*.md eol=lf
*.txt eol=lf
*.java eol=lf
*.properties  eol=lf
*.xml eol=lf

Edit pom.xml and add line <version>3.3</version> to maven-compiler-plugin.

pom.xml
<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.3</version>
    <configuration>
        <source>1.7</source>
        <target>1.7</target>
    </configuration>
</plugin>

Also add in the pom.xml right before the closing </dependencies>.

pom.xml
<dependencies>
   ...
   <dependency>
       <groupId>com.atlassian.sal</groupId>
       <artifactId>sal-api</artifactId>
       <version>3.0.3</version>
       <scope>provided</scope>
   </dependency>
</dependencies>

Now open Preferences in IntelliJ and set the built-in maven of the SDK together with the settings.xml

(Tip: By calling atlas-version all paths including home directory of SDK are printed)

Since we need the ATLAS_HOME environment variable to get maven inside IntelliJ to work we set that as well.

Now you can click on the Maven-Tab on the right and click compile and you should have a successful build.

If you do a Build > Make Project now and open the MyPluginComponentImpl.java you should have no errors.

You have completed Step 0. Good Job! You can check out this step's solution on GitHub

Further Reading: OSGi

You can skip this chapter if you already know OSGi or if you are not interested in the deep internals of imports and exports beetween OSGi modules. The next Step of the tutorial will build on this knowledge.

As you opened MyPluginComponentImpl.java already you might have seen some annotations like @ExportAsService and @ComponentImport.

There is also an important passage of maven-jira-plugin in the pom.xml that contains the following lines.

pom.xml
...
<artifactId>maven-jira-plugin</artifactId>
...
<instructions>
    <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
    <Export-Package>
       io.codeclou.kitchen.duty.api,
    </Export-Package>
    <Import-Package>
       org.springframework.osgi.*;resolution:="optional",
       org.eclipse.gemini.blueprint.*;resolution:="optional",
       *
   </Import-Package>
   <Spring-Context>*</Spring-Context>
</instructions>

We will now dive into unterstanding what all that means. First you should read the Wikipedia Article on OSGi.

Services (OSGi)

Each bundle can publish and find interfaces and therefore use functionality from other bundles. JIRA itself is composed of some OSGi bundles itself. Therefore we need to tell the Atlassian SDK which JIRA interfaces we want to use (the OSGi framework then finds the right bundle for us automatically).

Life-Cycle (OSGi)

We can install, start and stop OSGi bundles during runtime. That means, while JIRA is running our plugin can be installed, started and stopped.

Bundles (OSGi)

Bundles (also called modules) only share Interfaces with other modules. You want to hide your implementation. The Atlassian SDK creates a jar file with a custom MANIFEST.mf from the Import/Export-Package instructions of pom.xml and the Atlassian Spring Scanner Annotations e.g. @ComponentImport.

We run the package command, so that the jar file is build.

atlas-package
...
[INFO] Building jar: /Users/../kitchen-duty-plugin/target/kitchen-duty-plugin-1.0.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

We can now inspect the MANIFEST.mf file (I have shortened it a little).

cat target/classes/MANIFEST.md
Manifest-Version: 1.0
Atlassian-Build-Date: 2015-12-15T18:43:43+0100
Atlassian-Plugin-Key: io.codeclou.kitchen-duty-plugin
Bundle-Name: kitchen-duty-plugin
Bundle-SymbolicName: io.codeclou.kitchen-duty-plugin
Bundle-Vendor: codeclou UG
Bundle-Version: 1.0.0.SNAPSHOT
Export-Package: io.codeclou.kitchen.duty.api;version="1.0.0"
Import-Package: org.springframework.osgi.service.exporter.support;resolution:=optional,
org.springframework.osgi.service.importer.support;resolution:=optional,
....
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Spring-Context: *

We see Export-Package and Import-Package statements with the values we expected from pom.xml and Annotations

Summary


@ExportAsService is used to export our interface (e.g. MyPluginComponent.java) so that other OSGi bundles can use it as Spring Bean. Maybe you want to develop a base plugin and use its functionality from a second plugin, then you know now how to do it.


@ComponentImport is used to import functionality from other OSGi bundles Spring Context such as JIRAs ApplicationProperties.


These annotations are used because JIRA uses Spring together with OSGi and these annotations help to make beans available as Spring Beans and to get them from the Spring Context (even if it is a spring context from other bundle).


If we want to do something low-level with OSGi imports and exports we can do that in the pom.xml with <Import-Package> and <Export-Package>

If you want to dive even deeper into OSGi and Atlassian read about Atlassian Spring Scanner, watch the Apache Felix Introduction Video or do the Apache Felix Tutorials.