6. 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?
9. Quick departure - versioning
Easy - Dependency Management POM
Custom - Specify versions per module
more info: http://bit.ly/platform-versions
10. 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/
11. 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
12. 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
13. 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"
14. 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 entity
MessageEntity m = response.getEntity(MessageEntity.class);
15. 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
16.
17. 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?
24. 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>
27. 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?
32. 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>
33. 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();
}
}
}
34. 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>
35.
36.
37.
38.
39. 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?
42. 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()
);
}
}
43.
44. 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?
46. Depending on SAL ...
<dependency>
<groupId>com.atlassian.sal</groupId>
<artifactId>sal-api</artifactId>
<version>${sal.version}</version>
<scope>provided</scope>
</dependency>
47. Depending on SAL ...
<atlassian-plugin>
<component-import
key="[someSalComponent]"
interface="com.atlassian.sal.api.[someSalComponent]" />
</atlassian-plugin>
AuthenticationControlle
r
LoginUriProvider
ComponentLocator
I18nResolver
LocaleResolver
RequestFactory
SearchProvider
PluginUpgradeManage
r
UserManager
ApplicationProperties
PluginSettingsFactory
ProjectManager
PluginScheduler
More!
48. 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>
49. 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)
50. 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>
51. Using SAL ... job scheduling
// simple very-contrived job
public class MyPluginJob implements PluginJob {
public void execute(Map<String, Object> data) {
int count = data.get("jobExecutionCount");
count++;
data.put("jobExecutionCount", count);
}
}
52. 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
);
}
}