Разработка простого плагина для JIRA для работы с базой данных: Часть 1

от автора

Плагин будет представлять собой вкладку в административной части проекта, через которую и будем осуществлять работу с базой данных.

Плагин буду делать для джира 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

ссылка на оригинал статьи http://habrahabr.ru/post/256789/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *