Плагин будет представлять собой вкладку в административной части проекта, через которую и будем осуществлять работу с базой данных.
Плагин буду делать для джира 4.4.4. Для начала создадим пустой проект. Проект можно создать с помощью Atlassian SDK, а затем открыть в любимой IDE. В данном случае я буду работать с Netbeans. Файловая структура проекта будет выглядеть следующим образом:

Создадим страничку, на которой будет находиться наш функционал. В джире есть несколько механизмов для создания страниц – report, webwork actions, servlet, для решения этой задачи выберем webwork actions. В момент знакомства с JIRA в этом механизме все было предельно просто, понятно и знакомо (по сути это тот же Apache Struts). Создадим новый класс MyAction.java, унаследованный от класса JiraWebActionSupport. И создадим velocity шаблон succeess.vm такого содержания:
Теперь осталось добавить следующий элемент в atlassian-plugin.xml для создания новой страницы:
И при переходе по ссылке http://myserver/secure/action.jspa мы увидим созданную нами страницу:
Теперь сделаем так, чтоб эта страничка стала такой же, как вкладка в админской части проекта.
Для начала сделаем саму ссылку:

Чтоб ссылка выглядела отдельной группой, добавим в atlassian-plugin.xml новую секцию (web-section). Затем в эту секцию уже добавим саму ссылку (web-item):
Но при клике по ссылке мы все равно будем получать страницу без оформления, чтобы это исправить внесем изменения в MyAction.java. На самом деле при переходе по ссылке http://myserver/secure/action.jspa вызывается метод execute() и возвращаемое им значение –это success поэтому-то мы именно так описали наш action в atlassian-plugin.xml. Так мы будем явно передавать, какой проект сейчас активен, и добавлять «шапку» проекта к вкладке.
Но так передается только html код без css. Чтоб добавить css к странице допишем в atlassian-plugin.xml
Здесь мы добавили зависимость dependency для самих ресурсов и объявили через context то, как мы можем получить доступ к этим ресурсам через менеджера.
Тогда velocity шаблон приведем к такому виду:
В результате получим страницу:

Добавим функционал работы с базой данных. Пользоваться будем Active Objects (дальше AO). Для начала создадим пакет logic. В нем опишем объект, с которым будем работать, пусть это будет элементарный Student:
В пакете entity опишем наш интерфейс-сущность, который будем хранить в БД. Пусть для примера в базе еще будем хранить время создания этой записи:
Добавим теперь наш AO в atlassian-plugin.xml:
Подробнее про подключение AO к плагину можно почитать тут.
Теперь разберемся с взаимодействием нашего приложения с базой данных. Определим интерфейс StudentDAO из пакета DAO, содержащий набор необходимых методов:
Теперь создадим класс DAOFactory в пакете DAO, к которому будем обращаться за нашими реализациями DAO, от которых и будем вызывать необходимые нам методы:
Добавим в atlassian-plugin.xml нашу DAOFactory. Это необходимо чтобы избежать проблем с обращением к AO. Кому интересно: рассуждение на тему Active Objects injection.
Собственно, все необходимое для работы с базой данных мы описали и реализовали, осталось теперь добавить функционал в MyAction:
И в шаблоне velocity привести тег body к такому виду:
В общем, запускаем, тестируем, получаем:

На этом на первый раз все, в следующей статье расскажу:
Весь код плагина на GitHub
Продолжение: придаём нашему плагину нормальный внешний вид.

Больше проектов:
Как за 5233 человеко-часа создать софт для микротомографа
SDK для внедрения поддержки электронных книг в формате FB2
Управление доступом к электронным документам. От DefView до Vivaldi
Интегрируем две системы видеонаблюдения: Axxon Next и SureView
Подробнее о разработке софта рентгеновского томографа
«Сфера»: как мониторить миллиарды киловатт-часов
Разработка простого плагина для JIRA для работы с базой данных
В помощь DevOps: сборщик прошивок для сетевых устройств на Debian за 1008 часов
Автообновление службы Windows через AWS для бедных
Плагин буду делать для джира 4.4.4. Для начала создадим пустой проект. Проект можно создать с помощью Atlassian SDK, а затем открыть в любимой IDE. В данном случае я буду работать с Netbeans. Файловая структура проекта будет выглядеть следующим образом:

Создадим страничку, на которой будет находиться наш функционал. В джире есть несколько механизмов для создания страниц – report, webwork actions, servlet, для решения этой задачи выберем webwork actions. В момент знакомства с JIRA в этом механизме все было предельно просто, понятно и знакомо (по сути это тот же Apache Struts). Создадим новый класс MyAction.java, унаследованный от класса JiraWebActionSupport. И создадим velocity шаблон succeess.vm такого содержания:
MyAction template
Теперь осталось добавить следующий элемент в atlassian-plugin.xml для создания новой страницы:
<webwork1 key="actions" name="MyActions">
<actions>
<action name="com.edsd.jira.plugins.simpleplugin.action.MyAction" alias="action" roles-required="admin">
<view name="success">/myaction/success.vm</view>
</action>
</actions>
</webwork1>
И при переходе по ссылке http://myserver/secure/action.jspa мы увидим созданную нами страницу:

Теперь сделаем так, чтоб эта страничка стала такой же, как вкладка в админской части проекта.
Для начала сделаем саму ссылку:

Чтоб ссылка выглядела отдельной группой, добавим в atlassian-plugin.xml новую секцию (web-section). Затем в эту секцию уже добавим саму ссылку (web-item):
<web-section key="my_section" name="MySection" location="atl.jira.proj.config" weight="50"/>
<web-item key="my_item_link" name="MyTab" section="atl.jira.proj.config/my_section" weight="10">
<label key="MyLink" />
<link linkId="my_item_link">/secure/action.jspa</link>
</web-item>
Но при клике по ссылке мы все равно будем получать страницу без оформления, чтобы это исправить внесем изменения в MyAction.java. На самом деле при переходе по ссылке http://myserver/secure/action.jspa вызывается метод execute() и возвращаемое им значение –это success поэтому-то мы именно так описали наш action в atlassian-plugin.xml. Так мы будем явно передавать, какой проект сейчас активен, и добавлять «шапку» проекта к вкладке.
public class MyAction extends JiraWebActionSupport {
private Project project;
@Override
public String execute() throws Exception {
project = getSelectedProjectObject();
//добавление шапки проекта к вкладке
request.setAttribute("com.atlassian.jira.projectconfig.util.ServletRequestProjectConfigRequestCache:project", project);
return super.execute();
}
}
Но так передается только html код без css. Чтоб добавить css к странице допишем в atlassian-plugin.xml
<web-resource key="my-resources">
<dependency>com.atlassian.jira.jira-project-config-plugin:project-config-global</dependency>
<context>my-resources</context>
</web-resource>
Здесь мы добавили зависимость dependency для самих ресурсов и объявили через context то, как мы можем получить доступ к этим ресурсам через менеджера.
Тогда velocity шаблон приведем к такому виду:
<!DOCTYPE HTML>
<html>
<head>
<title>MyTestPage</title>
<meta name="decorator" content="atl.admin"/>
<meta name="projectKey" content="$project.getKey()"/>
<meta name="projectId" content="$project.getId()"/>
<meta name="admin.active.tab" content="my_item_link"/>
<meta name="admin.active.section" content="atl.jira.proj.config"/>
$webResourceManager.requireResourcesForContext("my-resources")
</head>
<body>
MyAction template
</body>
</html>
В результате получим страницу:

Добавим функционал работы с базой данных. Пользоваться будем Active Objects (дальше AO). Для начала создадим пакет logic. В нем опишем объект, с которым будем работать, пусть это будет элементарный Student:
public interface Student {
public void setName(String name);
public String getName();
}
В пакете entity опишем наш интерфейс-сущность, который будем хранить в БД. Пусть для примера в базе еще будем хранить время создания этой записи:
import java.util.Date;
import net.java.ao.Entity;
public interface StudentEntity extends Entity, Student {
public Date getCreated();
public void setCreated(Date created);
}
Добавим теперь наш AO в atlassian-plugin.xml:
<component-import key="ao" name="Active Objects service" interface="com.atlassian.activeobjects.external.ActiveObjects">
<description>Component to access Active Objects functionality from the plugin</description>
</component-import>
<ao key="ao-module">
<entity>com.edsd.jira.plugins.simpleplugin.entity.StudentEntity</entity>
</ao>
Подробнее про подключение AO к плагину можно почитать тут.
Теперь разберемся с взаимодействием нашего приложения с базой данных. Определим интерфейс StudentDAO из пакета DAO, содержащий набор необходимых методов:
public interface StudentDAO {
public StudentEntity addStudent(Student student) throws Exception;
public StudentEntity[] getStudents() throws Exception;
}
Реализацию интерфейса сделаем в пакете DAO.Impl в классе StudentDAOImpl.
public class StudentDAOImpl implements StudentDAO {
private final ActiveObjects ao;
public StudentDAOImpl(ActiveObjects ao) {
this.ao = ao;
}
@Override
public StudentEntity addStudent(final Student student) throws Exception {
return ao.executeInTransaction(new TransactionCallback<StudentEntity>() {
@Override
public StudentEntity doInTransaction() {
StudentEntity entity = ao.create(StudentEntity.class);
entity.setName(student.getName());
entity.setCreated(new Date(System.currentTimeMillis()));
entity.save();
return entity;
}
});
}
@Override
public StudentEntity[] getStudents() throws Exception {
return ao.executeInTransaction(new TransactionCallback<StudentEntity[]>() {
@Override
public StudentEntity[] doInTransaction() {
return ao.find(StudentEntity.class);
}
});
}
}
Теперь создадим класс DAOFactory в пакете DAO, к которому будем обращаться за нашими реализациями DAO, от которых и будем вызывать необходимые нам методы:
public class DAOFactory {
private static StudentDAO studentDAO = null;
private static DAOFactory instance = null;
private static ActiveObjects ao;
public DAOFactory(ActiveObjects ao) {
DAOFactory.ao = ao;
}
public static synchronized DAOFactory getInstance() {
if (instance == null) {
instance = new DAOFactory(ao);
}
return instance;
}
public StudentDAO getStudentDAO() {
if (studentDAO == null) {
studentDAO = new StudentDAOImpl(ao);
}
return studentDAO;
}
}
Добавим в atlassian-plugin.xml нашу DAOFactory. Это необходимо чтобы избежать проблем с обращением к AO. Кому интересно: рассуждение на тему Active Objects injection.
<component key="dao-factory" class="com.edsd.jira.plugins.simpleplugin.DAO.DAOFactory">
</component>
Собственно, все необходимое для работы с базой данных мы описали и реализовали, осталось теперь добавить функционал в MyAction:
public class MyAction extends JiraWebActionSupport {
private Project project;
private StudentEntity[] students;
@Override
public String execute() throws Exception {
project = getSelectedProjectObject();
request.setAttribute("com.atlassian.jira.projectconfig.util.ServletRequestProjectConfigRequestCache:project", project);
students = DAOFactory.getInstance().getStudentDAO().getStudents();
return super.execute();
}
public String doAdd() throws Exception {
String name = request.getParameterValues("name")[0];
Student student = new StudentImpl(name);
DAOFactory.getInstance().getStudentDAO().addStudent(student);
ServletActionContext.getResponse().sendRedirect("/secure/action.jspa");
return NONE;
}
}
И в шаблоне velocity привести тег body к такому виду:
<body>
<form action="/secure/action!add.jspa">
<input type="text" name="name"/>
<input type="submit" value="OK"/>
</form>
<table>
<thead>
<th>id</th>
<th>name</th>
<th>created</th>
</thead>
<tbody>
#foreach($student in $students)
<tr>
<td>$student.getID()</td>
<td>$student.getName()</td>
<td>$student.getCreated()</td>
</tr>
#end
</tbody>
</table>
</body>
В общем, запускаем, тестируем, получаем:

На этом на первый раз все, в следующей статье расскажу:
- как создать таблицы, аналогичные таблицам во вкладках Компоненты и Версии,
- как создать выпадающий список, с таким же оформлением как стандартный в JIRA,
- что такое JIRA.Restfultable
- и как пользоваться JIRA REST API.
Весь код плагина на GitHub
Продолжение: придаём нашему плагину нормальный внешний вид.

Больше проектов:
Как за 5233 человеко-часа создать софт для микротомографа
SDK для внедрения поддержки электронных книг в формате FB2
Управление доступом к электронным документам. От DefView до Vivaldi
Интегрируем две системы видеонаблюдения: Axxon Next и SureView
Подробнее о разработке софта рентгеновского томографа
«Сфера»: как мониторить миллиарды киловатт-часов
Разработка простого плагина для JIRA для работы с базой данных
В помощь DevOps: сборщик прошивок для сетевых устройств на Debian за 1008 часов
Автообновление службы Windows через AWS для бедных