56
Understanding the Atlassian Platform Tim Pettersen, Atlassian

AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Embed Size (px)

Citation preview

Page 1: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Understanding the Atlassian Platform

Tim Pettersen, Atlassian

Page 2: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 3: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

AtlassianPluginDevelopmentPlatform

Targeted at You!FeaturesDedicated TeamDocumentationFocus on Backwards Compatibility

Page 4: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Overview...

Page 6: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Have you ever?

... needed to communicate with a remote application?

... wanted an easy way to provide JSON data for an AJAXy UI?

... wanted to expose your plugin's data via web services?

Page 7: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

REST Atlassian

Plugin

Page 8: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on REST ...

<dependency> <groupId>com.atlassian.plugins.rest</groupId> <artifactId>atlassian-rest-common</artifactId> <version>${rest.version}</version> <scope>provided</scope></dependency><dependency> <groupId>com.atlassian.plugins.rest</groupId> <artifactId>atlassian-rest-module</artifactId> <version>${rest.version}</version> <scope>provided</scope></dependency><dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.1</version> <scope>provided</scope></dependency>

Atlassian annotations & utilities

JAX-RS annotations & jersey

JAXB annotations

Page 9: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Quick departure - versioning

Easy - Dependency Management POM

Custom - Specify versions per module

more info: http://bit.ly/platform-versions

Page 10: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on REST ...

<atlassian-plugin>

<rest key="some-key" path="/some-path" version="1.0" description="Initialise REST resources for my plugin" />

<component-import key="restUrlBuilder" interface="com.atlassian.plugins.rest.common.util.RestUrlBuilder" />

</atlassian-plugin>

http://jira.atlassian.com/REST/some-path/1.0/

Page 11: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using REST ... creating a resource

@Path("/hello/")@Consumes(APPLICATION_XML, APPLICATION_JSON)@Produces(APPLICATION_XML, APPLICATION_JSON)public class MyResource {

@Path("/my-name-is/{name}") @GET @AnonymousAllowed public Response hello(@PathParam("name") String name) { return ok( new MessageEntity("Hello, " + name + "!") ); }}

http://.../REST/some-path/1.0/hello

http://.../REST/some-path/1.0/hello/my-name-is/tim

Page 12: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using REST ... creating an entity

@XmlAccessorType(FIELD)@XmlRootElement(name = "message")public class MessageEntity { private String body; public MessageEntity(String body) { this.body = body; }}

http://.../REST/some-path/1.0/hello/my-name-is/Tim.xml<message> <body>Hello, Tim!</body></message>

http://.../REST/some-path/1.0/hello/my-name-is/Tim.json{body:"Hello, world!"}

Poster: http://bit.ly/poster-ff

Page 13: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using REST ... generating a resource URL

public class MyClient {

public void doGet() { String baseUrl = "http://jira.atlassian.com"; String restUrl = restUrlBuilder .getUrlFor(baseUrl, MyResource.class) .hello("Tim") .toString(); ... }}

// generated restUrl = "http://.../REST/some-path/1.0/hello/my-name-is/Tim"

Page 14: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using REST ... unmarshalling an entity Request request = requestFactory .createRequest(MethodType.GET, restUrl);

request.execute(new ResponseHandler<Response>() { public void handle(Response response) { if (response.isSuccessful()) {

} } });

// unmarshall the entityMessageEntity m = response.getEntity(MessageEntity.class);

Page 15: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using REST ... marshalling an entity

Request request = requestFactory.createRequest(PUT, restUrl); MessageEntity entity = new MessageEntity("some-value"); request.setEntity(entity); request.execute();

@PUT public Response submit(MessageEntity message) { System.out.println("Received: " + message.getBody()); }

Client

Server

Page 16: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 17: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Have you ever?

... wanted to launch a modal dialog from your plugin?

... wanted to add rich tooltips to your content?

... wished someone else would write your CSS and javascript for you?

... wanted your plugin to look and behave a little bit more like the host application?

Page 18: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

User Interface Atlassian

Plugin

Page 19: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on AUI ...

public class MyServlet extends HttpServlet { public void doGet() { webResourceManager .requireResource("com.atlassian.auiplugin:ajs"); } }

<html> <head> #webResourceManager.getRequiredResources() </head> <body ... /></html>

Page 20: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on AUI ...

<atlassian-plugin> <web-resource key="some-web-resource"> <resource name="my-script.js" ... /> <resource name="my-styles.css" ... /> <dependency>com.atlassian.auiplugin:ajs</dependency> </web-resource></atlassian-plugin>

public class MyServlet extends HttpServlet { public void doGet() { webResourceManager .requireResource("com.my.plugin:some-web-resource"); } }

Page 21: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using AUI ... dropdowns

Page 22: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using AUI ... dropdowns

<script type="text/javascript"> // create drop-down AJS.$("#my-dropdown") .dropDown("standard", {alignment: "right"});</script>

<!-- the list to display as a dropdown --><ul id="my-dropdown"> <li class="paper-clip">Item One</li> <li class="clock">Item Two</li> <li class="envelope">Item Three</li> </ul>

Page 23: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using AUI ... tooltips

Page 24: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using AUI ... tooltips

<script type="text/javascript"> // assign URL for retrieving AJAX content var contentUrl = AJS.params.baseUrl + "/rest/myplugin/1.0/tooltip"; // bind hovers to DOM elements AJS.InlineDialog(".tooltip-link", "tooltip", contentUrl, { onHover: true, width: 300, cacheContent: true });</script>

<!-- an anchor that the tooltip will be applied to --><a class='.hover-link'>Hover over me!</a>

Page 25: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using AUI ... more!

Page 26: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 27: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Have you ever?

... wanted to include a piece of javascript or CSS on every page in Confluence?

... wanted to make your plugin, pluggable?

... wanted to build a single plugin that can be deployed in multiple applications?

Page 28: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Plugins Framework Atlassian

Page 29: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on plugins ...

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

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

<atlassian-plugin> <!-- Unnecessary! --> <component-import ... /></atlassian-plugin>

Page 30: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using plugins ... web-resource contexts

<atlassian-plugin> <web-resource key="some-web-resource"> <context>atl.general</context> <resource name="my-script.js" ... /> <resource name="my-styles.css" ... /> </web-resource></atlassian-plugin>

atl.general

atl.admin

atl.userprofile

Page 31: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Custom Plugin Modules

Page 32: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using plugins ... custom module types

public class MyDescriptor extends AbstractModuleDescriptor<MyModule> {

public void init(Plugin plugin, Element element);

public MyModule getModule() { // initialize the module specified by the descriptor's class attribute return moduleFactory.createModule(moduleClassName, this); }

}<atlassian-plugin> <module-type key="my-descriptor" class="com.myplugin.MyDescriptor" /> <my-descriptor class="com.myplugin.ModuleOne" /> <my-descriptor class="com.myplugin.ModuleTwo" />

</atlassian-plugin>

Page 33: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using plugins ... custom module types

public class MyService {

private PluginAccessor pluginAccessor;

public MyService(PluginAccessor pluginAccessor) { this.pluginAccessor = pluginAccessor; } public void executeInstalledModules() { for (MyModule module : pluginAccessor.getEnabledModulesByClass(MyModule.class)) { module.execute(); } }

}

Page 34: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using plugins ... descriptor application scopes<atlassian-plugin>

<web-resource key="some-web-resource" application="confluence"> <context>atl.general</context> <resource name="page-integration.js" ... /> </web-resource> <web-resource key="some-web-resource" application="jira"> <context>atl.general</context> <resource name="issue-integration.js" ... /> </web-resource>

<!-- depends on Confluence's API --> <component key="some-component" application="confluence" class="com.myplugin.PageHandler" /> <!-- depends on JIRA's API --> <component key="some-component" application="jira" class="com.myplugin.IssueHandler" /> </atlassian-plugin>

Page 35: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 36: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 37: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 38: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 39: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Have you ever?

... needed to render HTML outside of a JIRA or Confluence action?

... needed to pass back rendered HTML to use in an AJAX UI?

... wished you could use something a little more modern than Velocity 1.4 in JIRA?

Page 40: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Template Renderer Atlassian

Page 41: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on ATR ...

<dependency> <groupId>com.atlassian.templaterenderer</groupId> <artifactId>atlassian-template-renderer-api</artifactId> <version>${template.renderer.version}</version> <scope>provided</scope></dependency>

<atlassian-plugin> <component-import key="templateRenderer" interface="com.atlassian.templaterenderer.TemplateRenderer" /></atlassian-plugin>

Page 42: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using ATR ...

public class MyServlet extends HttpServlet {

private TemplateRenderer templateRenderer; // constructor-injected

public void doGet(HttpServletRequest req, HttpServletResponse resp) { Map<String, Object> context = createContext(); templateRenderer.render("templates/view.vm", context, resp.getWriter() ); } }

Page 43: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 44: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Have you ever?

... needed to persist some simple data for your plugin?

... wanted to i18n your plugin?

... needed to write an upgrade task?

... wanted to schedule a recurring job?

Page 45: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Shared Application Layer Atlassian

Plugin

Page 46: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on SAL ...

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

Page 47: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Depending on SAL ...

<atlassian-plugin>

<component-import key="[someSalComponent]" interface="com.atlassian.sal.api.[someSalComponent]" />

</atlassian-plugin>

AuthenticationControllerLoginUriProviderComponentLocatorI18nResolverLocaleResolverRequestFactorySearchProvider

PluginUpgradeManagerUserManagerApplicationPropertiesPluginSettingsFactoryProjectManagerPluginSchedulerMore!

Page 48: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using SAL ... upgrade tasks

public interface PluginUpgradeTask { int getNumber(); String getShortDescription(); Collection<Message> doUpgrade(); String getPluginKey();}

<atlassian-plugin> <component key="my-upgrade-task" public="true" class="com.myplugin.MyUpgradeTask"> <interface>com.atlassian.sal.api.upgrade.PluginUpgradeTask</interface> </component></atlassian-plugin>

Page 49: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using SAL ... i18n

<atlassian-plugin>

<component-import key="i18nResolver" interface="com.atlassian.sal.api.message.I18nResolver" />

<resource key="my-i18n" type="i18n" location="i18n" />

</atlassian-plugin>

hello.world = Hello World! (i18n.properties)

hello.world: Hola a todos! (i18n_es.properties)

hello.world Hallo Welt! (i18n_de.properties)

Page 50: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using SAL ... i18n

Map<String, Object> context = new HashMap<String, Object>();context.put("i18n", i18nResolver);templateRenderer.render("templates/view.vm", context, writer);

<html> <head> <title>#i18n.getText('hello.world')</title> </head> <body ... /></html>

Page 51: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using SAL ... job scheduling

// simple very-contrived jobpublic class MyPluginJob implements PluginJob {

public void execute(Map<String, Object> data) { int count = data.get("jobExecutionCount"); count++; data.put("jobExecutionCount", count); }

}

Page 52: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Using SAL ... job scheduling

public class MyComponent implements LifecycleAware { private PluginScheduler scheduler; // autowired

public void onStart() { scheduler.scheduleJob( "my-plugin-job", MyPluginJob.class, new HashMap<String, Object>(), new Date(), 10000 // milliseconds );

}}

Page 53: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Targeted at You!FeaturesDedicated TeamDocumentationBackwards Compatibility

Open Source :D

Page 54: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Go use it!

Shared Application Layer

Unified Application Links Template Renderer

Plugins Framework

Atlassian User Interface REST

Page 55: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
Page 56: AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

Questions?

more info - http://bit.ly/best-thing-ever