Русский
Русский
English
Статистика
Реклама

Создаем plugin для IDEA для мониторинга транзакций в Spring

Disclaimer: я не являюсь сотрудником JetBrains (а жаль), поэтому код может являться не оптимальным и служит только для примера и исследовательских целей.

Введение

Часто во время работы со Spring непонятно, правильно ли работает аннотация @Transaction:

  • в правильном ли месте мы ее поставили

  • правильно ли объявился interceptor

  • и т.д.

Самым простым способом для меня было остановиться в debug в IDEA в необходимом методе и исследовать, что возвращает

TransactionSynchronizationManager.isActualTransactionActive();

Но "я же программист" и захотелось это дело хоть как-то автоматизировать, заодно поизучать возможности написания plugin для IDEA.

Хочется иметь семафор, который визуально показывает, активна ли транзакция или нет, а также иметь возможность сделать срез основных свойств для анализа в будущем.

Примерно вот что получилось:

Подробней про транзакции можно прочитать здесь.

С чего начать создания plugin?

Про настройку проекта для создание plugin можно прочитать здесь.

Повторюсь, что главными отправными точками будут:

Реализация

Исходный код получившегося решения размещен здесь.

Создание action

Прочитать про action можно здесь. Под action, в основном, понимается кнопка или элемент меню.

Сначала определим новый action. У action есть два метода update и actionPerformed.

  • update - вызывается idea несколько раз в секунду для того, чтобы установить корректное состояние (включен/не включен, виден/не виден)

  • actionPerformed - вызывается idea при выполнении действия (нажатия на кнопку).

public class PopupDialogAction extends AnAction {  @Override  public void update(AnActionEvent e) {    // Using the event, evaluate the context, and enable or disable the action.  }  @Override  public void actionPerformed(@NotNull AnActionEvent e) {    // Using the event, implement an action. For example, create and show a dialog.  }}

Для того, чтобы зарегистрировать новый action, требуется прописать в plugin.xml:

<actions>  <action id="TransactionStatusAction"          class="com.github.pyltsin.sniffer.debugger.TransactionStatusAction"          icon="SnifferIcons.RUNNING"          text="Current Transaction Status">    <add-to-group group-id="XDebugger.ToolWindow.TopToolbar" anchor="last"/>  </action></actions>

После этого должна была появиться новая кнопка на панели инструментов в debug-окне

Вычисление значений в debug

IDEA позволяет нам вычислять выражения при debug и взаимодействовать с памятью

И не только

Информация взята из twitter Тагира Валеева

В Evaluate даже встроен свой мини-интерпретатор Java, который позволяет выполнять прикольные вещи, например, такие:

Картинка из поста Тагира Валеева (http://personeltest.ru/aways/twitter.com/tagir_valeev/status/1360512527218728962)Картинка из поста Тагира Валеева (http://personeltest.ru/aways/twitter.com/tagir_valeev/status/1360512527218728962)

Поэтому, используя API IDEA, мы легко сможем узнать, когда транзакция активна, выполнив:

const val TRANSACTION_ACTIVE: String =    "org.springframework.transaction.support.TransactionSynchronizationManager"+  ".actualTransactionActive.get()==Boolean.TRUE"

Для начала сделаем так, чтобы наше действие было недоступно, если мы не находимся в режиме debug. Для этого получим XDebugSession и сравним с null

override fun update(e: AnActionEvent) {  val presentation = e.presentation  val currentSession: XDebugSession? = getCurrentSession(e)  if (currentSession == null) {    setDisabled(presentation)    return  }}private fun getCurrentSession(e: AnActionEvent): XDebugSession? {  val project = e.project  return if (project == null) null else   XDebuggerManager.getInstance(project).currentSession}

Многие вещи в idea реализованы через статические методы и паттерн singleton (хотя это уже почти считается антипаттерном - это очень удобно, что мы можем получить требуемые значения из любого места через статические методы, например, XDebuggerManager.getInstance)

Евгений Борисов не одобряет singleton как pattern, когда есть SpringЕвгений Борисов не одобряет singleton как pattern, когда есть Spring

Теперь мы хотим получить значения из контекста Spring из текущей сессии Java. Для этого можно воспользоваться следующим методом:

public abstract class XDebuggerEvaluator {  public abstract void evaluate(@NotNull String expression,                                 @NotNull XEvaluationCallback callback,                                 @Nullable XSourcePosition expressionPosition);}

Например, так

val currentSourcePosition: XSourcePosition? = currentSession.currentStackFrame?.sourcePositioncurrentSession.debugProcess.evaluator?.evaluate(  TRANSACTION_ACTIVE, object : XDebuggerEvaluator.XEvaluationCallback {    override fun errorOccurred(errorMessage: String) {      TODO("Not yet implemented")    }    override fun evaluated(result: XValue) {      TODO("Not yet implemented")    }  },  currentSourcePosition)

В XValue теперь хранится вычисленное значение. Чтобы посмотреть его, можно выполнить:

(result as JavaValue).descriptor.value

Он возвращает объект класса - com.sun.jdi.Value

Часть JavaDoc для com.sun.jdi.Value

The mirror for a value in the target VM. This interface is the root of a value hierarchy encompassing primitive values and object values.

Мы научились вычислять значения в debug с использованием API IDEA.

Рабочая панель (Tool Window)

Теперь попробуем их вывести в рабочую панель (Tool Window). Как всегда начинаем с документации и примера.

Объявляем в plugin.xml новое окно

<toolWindow id="TransactionView" secondary="true" icon="AllIcons.General.Modified" anchor="right"factoryClass="com.github.pyltsin.sniffer.ui.MyToolWindowFactory"/>
public class MyToolWindowFactory implements ToolWindowFactory {  public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) {    MyToolWindow myToolWindow = new MyToolWindow(toolWindow, project);    ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();    Content content = contentFactory.createContent(myToolWindow.getContent(), "", false);    final ContentManager contentManager = toolWindow.getContentManager();    contentManager.addContent(content);  }}

Само окно можно нарисовать во встроенном редакторе

IDEA сама сгенерирует для нас заготовку класса:

А дальше, вспоминая старый добрый Swing, описываем логику и добавляем необходимые Listener.

Получившееся окно инструментов (Tool Windows)Получившееся окно инструментов (Tool Windows)

Способы передачи данных

Вернемся к нашему action. При нажатии на кнопку вызывается метод actionPerformed.

Как из этого метода достучаться до нашего окна?

Самый простой способ - снова воспользоваться статическим методом:

val toolWindow: ToolWindow? =ToolWindowManager.getInstance(project).getToolWindow("TransactionView")

И передать туда требуемые значения.

IDEA предоставляет еще один способ - Message Bus (детальное описание лучше смотреть в документации). Один из вариантов использования следующий:

Объявить интерфейс:

public interface ChangeActionNotifier {    Topic<ChangeActionNotifier> CHANGE_ACTION_TOPIC = Topic.create("custom name", ChangeActionNotifier.class)    void beforeAction(Context context);    void afterAction(Context context);}

В месте, где принимаем сообщения:

bus.connect().subscribe(ActionTopics.CHANGE_ACTION_TOPIC, new ChangeActionNotifier() {        @Override        public void beforeAction(Context context) {            // Process 'before action' event.        }        @Override        public void afterAction(Context context) {            // Process 'after action' event.        }});

В месте, где отправляем сообщения:

ChangeActionNotifier publisher = myBus.syncPublisher(  ActionTopics.CHANGE_ACTION_TOPIC);publisher.beforeAction(context);

В любом случае необходимо быть аккуратным с многопоточностью и не выполнять долгие операции на UI Thread (подробности).

Осталось собрать все вместе и протестировать.

Исходный код получившегося решения размещен здесь.

Краткий "как бы" вывод

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

Ссылки

Источник: habr.com
К списку статей
Опубликовано: 20.04.2021 10:21:38
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Java

Kotlin

Idea

Plugin

Intellij idea plugin

Intellij idea

Transactions

Spring

Spring-boot

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru