Плагин будет представлять собой вкладку в административной части проекта, через которую и будем осуществлять работу с базой данных.
Плагин буду делать для джира 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
Продолжение: придаём нашему плагину нормальный внешний вид.
Плагин буду делать для джира 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
Продолжение: придаём нашему плагину нормальный внешний вид.
Комментарии (5)
MrSeventh
29.04.2015 13:58+2Спасибо за статью, буду очень рад подробному примеру с RESTfull table и выпадающими списками. Сам раскопал это наполовину, но возникли некоторые затруднения.
nmike
30.04.2015 00:14Для JIRA 4.4.4? А это будет работать на 6.3 или 6.4? И почему для такой не свежей версии? Просто свежие инсталляции (меньше пары лет) с 6ой ветки идут…
Firfi
Раньше занимался плагинами для Джиры, и тема для меня интересная в том плане, насколько процесс разработки изменился в лучшую сторону.
У них уже есть hot reload? Раньше надо было переустанавливать плагин через админку (а еще раньше — перезапускать весь инстанс).
Как решаете вопрос с использованием общего кода? Например, часто возникает необходимость в общих для нескольких плагинов утилитах, или подключении какой-нибудь библиотеки (например Joda-time), которую хотелось бы использовать в нескольких разных плагинах и при этом не включать ее в каждый из них.
И спасибо за статью.
MrSeventh
1) При запуске atlas-run из директории с плагином в JIRA появляются dev-тулы, в том числе hot-reload. Минус этого — нет тестовых данных. Поэтому я верен переустановке через админку.
2) Вроде был способ подключать OSGI и non-OSGI компоненты, сейчас не вспомню
3) Насчет библиотек — так все же за Вас делает maven