I added the following plugins to backend/pom.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>write-project-properties</goal>
</goals>
<configuration>
<outputFile>${project.build.outputDirectory}/maven.properties</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>copy frontend files to resources</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources/frontend</outputDirectory>
<overwrite>true</overwrite>
<resources>
<resource>
<directory>../frontend/dist</directory>
<includes>
<include>*.*</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
properties-maven-plugin creates a file called maven.properties which contains all maven properties. I need the atlassian.plugin.key property to call the web resource correctly. I will show you later how I use it.
maven-resources-plugin takes JavaScript files from frontend/dist and places these files into backend/resources/frontend.
Next I created a web section called React and two web items called Form and Dynamic Table. I created two servlets and called these servlets from the Form and Dynamic Table web item. Here are the lines from atlassian-plugin.xml:
<servlet name="Form Servlet" i18n-name-key="form-servlet.name" key="form-servlet" class="react.atlaskit.tutorial.servlet.FormServlet">
<description key="form-servlet.description">The Form Servlet Plugin</description>
<url-pattern>/form</url-pattern>
</servlet>
<servlet name="Dynamic Table Servlet" i18n-name-key="dynamic-table-servlet.name" key="dynamic-table-servlet" class="react.atlaskit.tutorial.servlet.DynamicTableServlet">
<description key="dynamic-table-servlet.description">The Dynamic Table Servlet Plugin</description>
<url-pattern>/dynamictable</url-pattern>
</servlet>
<web-section name="React Plugin" i18n-name-key="react.name" key="react" location="admin_plugins_menu" weight="1000">
<description key="react.description">React Plugin</description>
<label key="react.label"/>
</web-section>
<web-item name="from web item" i18n-name-key="form.name" key="form" section="admin_plugins_menu/react" weight="1000">
<description key="form.description">Form</description>
<label key="form.label"/>
<link linkId="configuration-link">/plugins/servlet/form</link>
</web-item>
<web-item name="dynamic table web item" i18n-name-key="dynamictable.name" key="dynamictable" section="admin_plugins_menu/react" weight="1000">
<description key="dynamictable.description">Dynamic Table</description>
<label key="dynamictable.label"/>
<link linkId="configuration-link">/plugins/servlet/dynamictable</link>
</web-item>
So we have our menu items which call our servlets. Now let's have a look at the servlets.
FormServlet.java:
public class FormServlet extends HttpServlet{
private static final Logger log = LoggerFactory.getLogger(FormServlet.class);
private final ResourceService resourceService;
private final SoyTemplateRenderer soyTemplateRenderer;
public FormServlet(@ComponentImport SoyTemplateRenderer soyTemplateRenderer, ResourceService resourceService) {
this.resourceService = resourceService;
this.soyTemplateRenderer = soyTemplateRenderer;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
String pluginKey = this.resourceService.getProperty("atlassian.plugin.key");
Map<String, Object> map = new HashMap<>();
map.put("contextPath", req.getContextPath());
String html = soyTemplateRenderer.render(pluginKey + ":jira-react-atlaskit-resources", "servlet.ui.form", map);
resp.setContentType("text/html");
resp.getWriter().write(html);
resp.getWriter().close(); }
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
StringBuffer jb = new StringBuffer();
String line = null;
try {
BufferedReader reader = req.getReader();
while ((line = reader.readLine()) != null)
jb.append(line);
} catch (Exception e) { /*report an error*/ }
log.info(String.format("Post Data: %s", jb.toString()));
String pluginKey = this.resourceService.getProperty("atlassian.plugin.key");
Map<String, Object> map = new HashMap<>();
map.put("contextPath", req.getContextPath());
String html = soyTemplateRenderer.render(pluginKey + ":jira-react-atlaskit-resources", "servlet.ui.form", map);
resp.setContentType("text/html");
resp.getWriter().write(html);
resp.getWriter().close();
}
}
I define two fields resourceService and soyTemplateRenderer and initialize these fields in the constructor. resourceService is my bean to read maven.properties file. soyTemplateRenderer is a Jira bean to call soy templates.
In the doGet method I get the atlassian.plugin.key property and contextPath. As I said above I need the context path to call this servlet from the Form element on the onsubmit event. Context path contains prefix of you Atlassian Jira url. In my case /jira. Then I pass contextPath as a parameter to my soy template and call servlet.ui.form soy template.
Here is the soy file:
{namespace servlet.ui}
/**
* This template is needed to draw the form page.
*/
{template .form}
{@param contextPath: string}
{webResourceManager_requireResourcesForContext('form')}
<html>
<head>
<meta charset="utf-8"/>
<meta name="decorator" content="atl.admin">
<meta name="admin.active.section" content="admin_plugins_menu/react">
<title>Form Page</title>
</head>
<body>
<div class="field-group hidden">
<input class="text" type="text" id="contextPath" name="contextPath" value="{$contextPath}">
</div>
<div id="maincontainer">
<div id="container">
</div>
</div>
</body>
</html>
{/template}
/**
* This template is needed to draw the dynamic table page.
*/
{template .dynamictable}
{webResourceManager_requireResourcesForContext('dynamictable')}
<html>
<head>
<meta charset="utf-8"/>
<meta name="decorator" content="atl.admin">
<meta name="admin.active.section" content="admin_plugins_menu/react">
<title>Dynamic Table Page</title>
</head>
<body>
<div id="maincontainer">
<div id="container">
</div>
</div>
</body>
</html>
{/template}
As you can see the templates are quite simple. All the templates do is to call required atlassian-plugin.xml web resources and define the div container where react will place all our UI elements.
This soy file was added into atlassian-plugin.xml in the web resource section:
<web-resource key="jira-react-atlaskit-resources" name="jira-react-atlaskit Web Resources">
...
<resource type="soy" name="soyui" location="/templates/servlets.soy"/>
...
<context>jira-react-atlaskit</context>
</web-resource>
And that is all what should be done to use React and Atlaskit in your Atlassian apps.
Alexey Matveev
software developer
MagicButtonLabs
Philippines
1,574 accepted answers
4 comments