Написание прослушивателей событий Jira с библиотекой событий atlassian

Применимость

Jira 7.1.0 и более поздние.

Уровень опыта

Intermediate. Вы должны были пройти хотя бы один учебник для начинающих, прежде чем работать с этим учебником. См. Список учебников для разработчиков.

Временная оценка

Для завершения этого урока вам потребуется примерно полчаса.

Обзор учебника

Jira поддерживал простой API-интерфейс слушателя в течение длительного времени. Однако у этого API есть несколько проблем, и его установка и конфигурация сложны. Кроме того, начиная с Jira 4.0 невозможно обменяться кодами между слушателями и приложениями.

К счастью, вы можете использовать библиотеку atlassian-event, чтобы реализовать ту же функциональность и избежать этих проблем.

В этом учебном пособии вы создадите приложение, которое будет прослушивать события, сгенерированные при создании или разрешении задачи. Когда события произойдут, приложение зарегистрирует событие.

Ваше завершенное приложение будет состоять из следующих компонентов:

  • Java-классы, инкапсулирующие логику приложения.
  • Дескриптор приложения (т. е. Файл XML), чтобы включить модуль плагина в приложение Atlassian.

Когда вы закончите, все эти компоненты будут упакованы в один JAR-файл.

Об этих инструкциях

Вы можете использовать любую поддерживаемую комбинацию операционной системы и IDE для создания этого приложения. Эти инструкции были написаны с использованием IntelliJ IDEA 2017.3 на Ubuntu Linux. Если вы используете другую операционную систему или комбинацию IDE, вы должны использовать эквивалентные операции для своей конкретной среды.

Этот учебник был последний раз проверен с помощью Jira 7.7.1.

Прежде чем вы начнете

Чтобы завершить этот учебник, вам необходимо знать следующее:

  1. Основы разработки Java: классы, интерфейсы, методы, использование компилятора и т. д.
  2. Как создать проект плагина Atlassian с помощью Atlassian Plugin SDK.
  3. Основы использования и управления

Источник приложения

Мы рекомендуем вам проработать этот учебник. Если вы хотите пропустить или проверить свою работу, когда закончите, вы можете найти исходный код приложения на Atlassian Bitbucket.

 

Чтобы клонировать репозиторий, выполните следующую команду:


git clone https://bitbucket.org/atlassian_tutorial/jira-event-listener

Кроме того, вы можете загрузить исходный код в виде ZIP-архива.

Шаг 1. Создайте проект приложения

На этом этапе вы будете использовать команду atlas для создания кода-заглушки для вашего приложения. Команды atlas  являются частью SDK Atlassian Plugin и автоматизируют большую часть разработки приложений для вас.

  1. Настройте SDK Atlassian Plugin и создайте проект, если вы еще этого не сделали.
  2. Откройте терминал и перейдите в каталог, в котором вы хотите сохранить код приложения.
  3. Чтобы создать скелет приложения, выполните следующую команду:

atlas-create-jira-plugin

  1. Чтобы определить ваше приложение, введите следующую информацию.

group-id

com.example.tutorial.plugins

artifact-id

new-listener-plugin

version

1.0-SNAPSHOT

package

com.example.tutorial.plugins

  1. Подтвердите свои записи при появлении запроса.
  2. Удалите тестовые каталоги.

Настройка тестирования для вашего приложения не входит в этот учебник. Чтобы удалить сгенерированный тестовый скелет, выполните следующие команды:


rm -rf src/test/resources
rm -rf src/test/java

  1. Удалите ненужные файлы классов Java.

rm -rf src/main/java/com/example/tutorial/plugins/*

  1. Импортируйте проект в свою избранную среду IDE.

Шаг 2. Измените метаданные POM и добавьте зависимость

POM (то есть файл определения объектной модели проекта) объявляет зависимости вашего приложения, настройки и метаданные (информация о вашем приложении). Измените POM следующим образом:

 

  1. Перейдите в каталог new-listener-plugin, созданный SDK.
  2. Откройте файл pom.xml.
  3. Добавьте название вашей компании или организации и URL вашего веб-сайта в элемент organization.

<organization>
    <name>Example Company</name>
    <url>http://www.example.com/</url>
</organization>

  1. Обновите элемент description:

<description> Этот плагин реализует простой прослушиватель событий задач для JIRA с использованием atlassian-события. </ description>

  1. Добавьте следующую зависимость dependency в качестве дочернего элемента зависимостей dependencies:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.6.RELEASE</version>
    <scope>provided</scope>
</dependency>

Версия org.springframework должна соответствовать версии, используемой UPM (то есть Universal Plugin Manager).

 

  1. Для этого приложения мы будем использовать собственный файл log4j.properties. Добавьте следующий элемент в качестве дочернего элемента project.build.plugins.plugin.configuration:

<log4jProperties>src/aps/log4j.properties</log4jProperties>

Полный элемент plugin должен выглядеть так:


<plugin>
    <groupId>com.atlassian.maven.plugins</groupId>
    <artifactId>maven-jira-plugin</artifactId>
    <version>${amps.version}</version>
    <extensions>true</extensions>
    <configuration>
        <productVersion>${jira.version}</productVersion>
        <productDataVersion>${jira.version}</productDataVersion>
        <log4jProperties>src/aps/log4j.properties</log4jProperties>
        <enableQuickReload>true</enableQuickReload>
        <enableFastdev>false</enableFastdev>
        <instructions>
            <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key>
            <Export-Package>
                com.example.tutorial.plugins.api,
            </Export-Package>
            <Import-Package>
                org.springframework.osgi.*;resolution:="optional",
                org.eclipse.gemini.blueprint.*;resolution:="optional",
                *
            </Import-Package>
            <Spring-Context>*</Spring-Context>
        </instructions>
    </configuration>
</plugin>

  1. Сохраните файл.

 

Шаг 3. Напишите класс Java

В этом учебнике не требуются обновления дескриптора приложения. Шаги для создания прослушивателя событий следующие:

  1. Внедрите реализацию EventPublisher в свой класс.
  2. Добавьте аннотацию @EventListener к любому методу, который должен получать события.
  3. Обращайтесь с событиями, когда они входят.

Чтобы продемонстрировать это, мы создадим слушателя, который будет регистрировать уведомление, когда задача будет создана, разрешена или закрыта.

Вы можете использовать один и тот же подход для отправки уведомлений по электронной почте или в IRC (то есть в Internet Relay Chat).

На следующих шагах вы создадите файл класса и построите его.

  1. Перейдите в src / main / java / com / example / tutorial / plugins / и создайте файл с именем IssueCreatedResolvedListener.java.
  2. Добавьте в файл следующий код:

package com.example.tutorial.plugins;
 
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.issue.Issue;
import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
 
@Component
public class IssueCreatedResolvedListener {
     private static final Logger log = LoggerFactory.getLogger(IssueCreatedResolvedListener.class);
 }

До сих пор мы объявили несколько операторов импорта и создали экземпляр регистратора, который мы будем использовать для записи событий.

 

  1. Добавьте конструктор, который вводит экземпляр EventPublisher в приложение и регистрирует нашего слушателя с помощью EventPublisher:

@Autowired
public IssueCreatedResolvedListener(@JiraImport EventPublisher eventPublisher) {
    eventPublisher.register(this);    // Demonstration only -- don't do this in real code!
}

Мы добавили аннотацию @JiraImport для EventPublisher, поэтому Atlassian Spring Scanner импортирует ее для нас. Объект EventPublisher обрабатывает публикацию событий и регистрацию прослушивателей событий.

 

  1. Чтобы обработать событие, добавьте следующий метод:

@EventListener
public void onIssueEvent(IssueEvent issueEvent) {
   Long eventTypeId = issueEvent.getEventTypeId();
   Issue issue = issueEvent.getIssue();
 
   if (eventTypeId.equals(EventType.ISSUE_CREATED_ID)) {
      log.info("Issue {} has been created at {}.", issue.getKey(), issue.getCreated());
   } else if (eventTypeId.equals(EventType.ISSUE_RESOLVED_ID)) {
      log.info("Issue {} has been resolved at {}.", issue.getKey(), issue.getResolutionDate());
   } else if (eventTypeId.equals(EventType.ISSUE_CLOSED_ID)) {
      log.info("Issue {} has been closed at {}.", issue.getKey(), issue.getUpdated());
   }
}

Обратите внимание, что метод аннотируется с помощью EventListener. Вы можете применить аннотацию EventListener к любому общедоступному методу. Метод должен принимать параметр, соответствующий событию, которое он должен обрабатывать, IssueEvent в этом случае.

Jira предоставляет несколько событий, описание которых приведено на странице о событиях, характерных для Jira-событий специфичных для Atlassian.

  1. Перейдите в src / aps и создайте файл с именем log4j.properties.

Это пользовательский ресурс, который мы добавили в конфигурацию приложения в POM. Нам нужен пользовательский файл поскольку реализация Jira log4j ничего не знает о нашем регистраторе приложений. И поскольку наше имя пакета com.example.tutorial.plugins, мы не наследуем ни один из иерархических регистраторов com.atlassian.

  1. Добавьте в файл следующий код:

#####################################################
# УРОВНИ ОБРАБОТКИ
#####################################################
 
# Чтобы включить более подробное ведение журнала - измените «WARN» на «DEBUG
 
log4j.rootLogger=WARN, console, filelog
 
######################################################
# LOG FILE LOCATIONS
######################################################
 
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d %t %p %X{jira.username} %X{jira.request.id} %X{jira.request.assession.id} %X{jira.request.ipaddr} %X{jira.request.url} [%c{4}] %m%n
 
 
log4j.appender.nowarnconsole=org.apache.log4j.ConsoleAppender
log4j.appender.nowarnconsole.Threshold=DEBUG
log4j.appender.nowarnconsole.layout=org.apache.log4j.PatternLayout
log4j.appender.nowarnconsole.layout.ConversionPattern=%d %t %p %X{jira.username} %X{jira.request.id} %X{jira.request.assession.id} %X{jira.request.ipaddr} %X{jira.request.url} [%c{4}] %m%n
 
 
log4j.appender.filelog=com.atlassian.jira.logging.JiraHomeAppender
log4j.appender.filelog.File=atlassian-jira.log
log4j.appender.filelog.MaxFileSize=20480KB
log4j.appender.filelog.MaxBackupIndex=5
log4j.appender.filelog.layout=com.atlassian.logging.log4j.FilteredPatternLayout
log4j.appender.filelog.layout.ConversionPattern=%d %t %p %X{jira.username} %X{jira.request.id} %X{jira.request.assession.id} %X{jira.request.ipaddr} %X{jira.request.url} [%c{4}] %m%n
log4j.appender.filelog.layout.MinimumLines=6
log4j.appender.filelog.layout.FilteringApplied=true
log4j.appender.filelog.layout.ShowEludedSummary=true
#
# These are the Java stack frames that will be filtered out when an exception is logged.  This is to help reduce the
# noise to information ratio in the JIRA logs.  Its a long comma seperated list with .properties line continuation
# provided by the / character.
#
# It will always how the 'MinimumLines' value above regardless of these settings.  Also DEBUG level stack traces will always
# be shown in their entirety.
#
# You can quickly revert to full stack traces by setting 'FilteringApplied'=false above or by removing the lines below
# and restarting JIRA.
#
log4j.appender.filelog.layout.FilteredFrames=\
  sun.reflect, \
  \
  org.apache.catalina, \
  org.apache.coyote, \
  org.apache.tomcat.util.net, \
  org.apache.catalina.core.ApplicationFilterChain, \
  \
  webwork.interceptor, \
  webwork.dispatcher, \
  webwork.action.ActionSupport, \
  com.opensymphony.sitemesh, \
  \
  com.sun.jersey.server.impl, \
  com.sun.jersey.spi.container.servlet, \
  \
  com.atlassian.jira.web.dispatcher, \
  com.atlassian.jira.web.filters, \
  com.atlassian.jira.web.filters.steps, \
  com.atlassian.jira.startup.JiraStartupChecklistFilter, \
  com.atlassian.jira.security.xsrf.XsrfTokenAdditionRequestFilter, \
  \
  com.atlassian.seraph.filter, \
  com.atlassian.security.auth.trustedapps.filter, \
  com.atlassian.plugin.servlet.filter, \
  com.atlassian.plugins.rest.common, \
  com.atlassian.core.filters, \
  com.atlassian.util.profiling.filters, \
  com.atlassian.johnson.filters, \
  \
  com.atlassian.gzipfilter.GzipFilter, \
  com.atlassian.applinks.core.rest.context.ContextFilter, \
  com.atlassian.plugins.rest.module.servlet.RestServletUtilsUpdaterFilter, \
  com.atlassian.oauth.serviceprovider.internal.servlet.OAuthFilter, \
  \
  org.tuckey.web.filters.urlrewrite.UrlRewriteFilter, \
  com.sysbliss.jira.plugins.workflow.servlet.JWDSendRedirectFilter, \
 
 
 
log4j.appender.soapaccesslog=com.atlassian.jira.logging.JiraHomeAppender
log4j.appender.soapaccesslog.File=atlassian-jira-soap-access.log
log4j.appender.soapaccesslog.MaxFileSize=20480KB
log4j.appender.soapaccesslog.MaxBackupIndex=5
log4j.appender.soapaccesslog.layout=org.apache.log4j.PatternLayout
log4j.appender.soapaccesslog.layout.ConversionPattern=%m%n
 
log4j.appender.soapdumplog=com.atlassian.jira.logging.JiraHomeAppender
log4j.appender.soapdumplog.File=atlassian-jira-soap-dump.log
log4j.appender.soapdumplog.MaxFileSize=20480KB
log4j.appender.soapdumplog.MaxBackupIndex=5
log4j.appender.soapdumplog.layout=org.apache.log4j.PatternLayout
log4j.appender.soapdumplog.layout.ConversionPattern=%m%n
 
log4j.appender.httpaccesslog=com.atlassian.jira.logging.JiraHomeAppender
log4j.appender.httpaccesslog.File=atlassian-jira-http-access.log
log4j.appender.httpaccesslog.MaxFileSize=20480KB
log4j.appender.httpaccesslog.MaxBackupIndex=5
log4j.appender.httpaccesslog.layout=org.apache.log4j.PatternLayout
log4j.appender.httpaccesslog.layout.ConversionPattern=%m%n
 
log4j.appender.httpdumplog=com.atlassian.jira.logging.JiraHomeAppender
log4j.appender.httpdumplog.File=atlassian-jira-http-dump.log
log4j.appender.httpdumplog.MaxFileSize=20480KB
log4j.appender.httpdumplog.MaxBackupIndex=5
log4j.appender.httpdumplog.layout=org.apache.log4j.PatternLayout
log4j.appender.httpdumplog.layout.ConversionPattern=%m%n
 
log4j.appender.sql

Ключевыми строками являются последние две. Это предпочтительный подход.

 

  1. Сохраните измененные файлы.

Шаг 4. Тестирование приложения

Теперь вы готовы проверить приложение.

  1. Настройте SDK для запуска Jira Software.
  2. Откройте терминал и перейдите в корневой каталог проекта, где находится файл pom.xml.
  3. Запустите следующую команду SDK:

atlas-run

Это создает код приложения, запускает экземпляр Jira и устанавливает ваше приложение. Это может занять несколько минут.

  1. Перейдите на домашнюю страницу Jira в браузере (URL-адрес указан в выводе терминала).
  2. Создайте новый проект на основе типа проекта разработки программного обеспечения. Это дает нам рабочий процесс, необходимый для тестирования каждого типа событий, то есть, который включает в себя переходы для решения и закрытия задач.
  3. Следя за Терминалом, где вы запустили Jira, создайте проблему, а затем разрешите ее. Вы должны увидеть событие, зарегистрированное на выходе терминала, например:

[INFO] [talledLocalContainer] 2013-07-24 12:52:42,347 http-2990-3 INFO admin 772x1164x1 slv00c 127.0.1.1 /secure/QuickCreateIssue.jspa [example.tutorial.plugins.IssueCreatedResolvedListener] Issue TST-1 has been created at 2013-07-24 12:52:42.105.
[INFO] [talledLocalContainer] 2013-07-24 17:11:42,562 http-2990-8 INFO admin 1031x1858x1 slv00c 127.0.1.1 /secure/CommentAssignIssue.jspa [example.tutorial.plugins.IssueCreatedResolvedListener] Issue TST-1 has been resolved at 2013-07-24 17:11:42.512

Если вы пропустили сообщение журнала на выходе терминала, вы можете проверить файл журнала в следующем каталоге: target / jira / home / log / atlassian-jira.log.

 

Оно работает! Тем не менее, приложение еще не готово к прайм-тайму. Нам нужно сделать еще немного, чтобы убедиться, что наш слушатель событий играет хорошо с учитывая обычным жизненным циклом  приложения в производственном развертывании, как описано ниже. Вы можете поддерживать запуск Jira , когда вы продолжаете работать.

Шаг 5. Запомните жизненный цикл приложения

Более подробную информацию о жизненном цикле приложения можно найти в нашем руководстве по жизненному циклу приложения Jira.

Код до сих пор делает два предположения о жизненном цикле приложения:

  1. Конструктор будет когда-либо вызываться один раз.
  2. Приложение отключится, когда сама система выключится.

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

Как @Component, IssueCreatedResolvedListener станет компонентом Spring, поэтому мы можем применить интерфейсы Spring InitializingBean и DisposableBean. Spring гарантирует, что компонент - в этом случае, наш класс слушателей - будет иметь возможность объединить свои действия, прежде чем он будет введен в активную службу, или уйдет из него.

  1. Чтобы скопировать наш пример прослушивателя событий с жизненным циклом приложения, замените содержимое IssueCreatedResolvedListener.java следующим:

package com.example.tutorial.plugins;
 
import com.atlassian.event.api.EventListener;
import com.atlassian.event.api.EventPublisher;
import com.atlassian.jira.event.issue.IssueEvent;
import com.atlassian.jira.event.type.EventType;
import com.atlassian.jira.issue.Issue;
import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class IssueCreatedResolvedListener implements InitializingBean, DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(IssueCreatedResolvedListener.class);
 
    @JiraImport
    private final EventPublisher eventPublisher;
 
    @Autowired
    public IssueCreatedResolvedListener(EventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
 
    /**
     *  Вызывается, когда плагин включен.
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("Enabling plugin");
        eventPublisher.register(this);
    }
 
    /**
     * Вызывается, когда плагин отключен или удален.
     * @throws Exception
     */
    @Override
    public void destroy() throws Exception {
        log.info("Disabling plugin");
        eventPublisher.unregister(this);
    }
 
    @EventListener
    public void onIssueEvent(IssueEvent issueEvent) {
        Long eventTypeId = issueEvent.getEventTypeId();
        Issue issue = issueEvent.getIssue();
 
        if (eventTypeId.equals(EventType.ISSUE_CREATED_ID)) {
            log.info("Issue {} has been created at {}.", issue.getKey(), issue.getCreated());
        } else if (eventTypeId.equals(EventType.ISSUE_RESOLVED_ID)) {
            log.info("Issue {} has been resolved at {}.", issue.getKey(), issue.getResolutionDate());
        } else if (eventTypeId.equals(EventType.ISSUE_CLOSED_ID)) {
            log.info("Issue {} has been closed at {}.", issue.getKey(), issue.getUpdated());
        }
    }
 
}

Здесь мы переместили ссылку EventPublisher на переменную-член и связали регистрацию с методами afterPropertiesSet () и destroy (), которые поступают из InitializingBean и DisposableBean соответственно. Это гарантирует правильное поведение, даже если наше приложение отключено, а затем снова включено, возможно, администратором, использующим универсальный менеджер приложений.

  1. Установите приложение, используя команду atlas-package, которая запускает QuickReload.

Следующие шаги

Хотя написание слушателя, как объяснено здесь, имеет преимущество работы внутри полноценного приложения, оно не поддерживает функцию свойств унаследованных (до 4.0) Jira слушателей. В этих слушателях администраторы могли устанавливать свойства для настройки слушателя. Например, слушатель, который пересылал события задач в IRC, мог иметь свойства для IRC-сервера, имени пользователя и пароля для использования. Слушатели событий в Atlassian автоматически не интегрируются с экраном конфигурации слушателя Jira.

Чтобы ваш слушатель был настроен администратором Jira, выполните свою собственную страницу администрирования, которая может настроить не только вашего слушателя, но и любую другую информацию, которая может потребоваться вашим пользователям. Инструкции о том, как это сделать, см. В разделе «Написание экрана конфигурации администратора».

 

По материалам Atlassian JIRA  Server Developer Writing Jira event listeners with the atlassian-event library