Visual paradigm ― мощный инструмент, идеология использования которого выходит за рамки простого рисования диаграмм. Главное назначение инструментов данного класса (Visual Paradigm, Enterprise Architect и др.) ― описание модели информационной системы и дальнейшая работа с ней. Под работой подразумевается ее визуализация в виде диаграммы, экспорт документации, генерация исходных кодов, анализ, подсчет метрик и т. п.
Как правило, сценарии использования модели могут быть самые разные и разработчики систем все их предусмотреть не могут. При этом они предоставляют возможности по разработке собственных плагинов, которые могут преобразовать или модифицировать модель необходимым конкретно вам образом.
В статье я расскажу об основах создания плагинов для Visual Paradigm. В качестве примера возьмем формирование SQL-скрипта с комментариями к таблицам и колонкам на основе ER-диаграммы.
Плагин Visual Paradigm ― набор скомпилированных для JVM классов, реализующих интерфейсы Visual Paradigm OpenAPI. Плагин может быть написан на любом JVM языке: Java, Kotlin, Scala, Groovy и т. д. Единственное ограничение ― использование JDK версии 11 (для 16-x и 17-х версий Visual Paradigm).
Все интерфейсы, предоставленные Visual Paradigm для разработки плагинов, собраны в библиотеку OpenAPI. Она находится в папке «bundled» в каталоге установки Visual Paradigm. Каждый плагин должен иметь класс с реализацией интерфейса VPPlugin
и один или несколько ―с реализацией интерфейсов действий (action controller):
Для отладки загрузки плагина и, в первую очередь, корректности plugin.xml, нужно смотреть файл vp.log, который находится в каталоге настроек пользователя Visual Paradigm. В этот файл попадает весь System.out (в Kotlin для записи в System.out используется функция println).
Установка уже разработанных плагинов доступна через Help -> Install plugin. Есть три способа:
https://github.com/leonidv/vp-plugin-hot-reload/releases/tag/1.0 ) . Он позволяет избегать перезагрузки Visual Paradigm при разработке плагина в том случае, если поменялся только его исходный код. Когда меняется plugin.xml или состав подключаемых библиотек, перезагружать программу все равно придется. Hot Reload не зависит от контекста, поэтому после установки он будет доступен на вкладке Plugins (кнопка Reload plugins).
Кнопка установленного плагина Hot Reload
Шаги разработки плагина
Разработка плагина для Visual Paradigm состоит из следующих шагов:
Подготовка проекта плагина, включая:
Создание пустого Gradle (Maven) проекта.
Подключение к нему openapi.jar.
Настройка Gradle-задач для удобной установки разрабатываемого плагина в Visual Paradigm.
Разработка самого плагина (здесь порядок действий достаточно произвольный):
Реализация VPPlugin
.
Реализация действия (самой логики).
Описание манифеста ― plugin.xml.
В реальной практике подготовка проекта занимает малое время ― так или иначе все сводится к копированию уже имеющихся наработок.
Подготовка заготовки плагина
В данном примере будут использованы следующие инструменты:
Kotlin;
Gradle, в качестве языка описания конфигурации ― Kotlin;
IntelliJ Idea (возможностей бесплатной версии будет достаточно).
Если вы используете что-то другое, легко сможете адаптировать учебный проект под свои инструменты.
Шаг 1a. Создание пустого Gradle-проекта
Создать Gradle-проект можно любым способом. Я предпочитаю делать это с помощью IntelliJ Idea, указав следующие параметры:
Шаг 1b. Подключение openapi.jar
После создания проекта нужно сделать два шага:
Скопировать файл openapi.jar в папку проекта lib (ее нужно создать). При стандартной установке в Windows openapi.jar можно найти по пути: C:\Program Files\Visual Paradigm 16.3\bundled.
Добавить его в зависимости скрипта сборки:compileOnly(files("lib/openapi.jar"))
.
Шаг 1c. Gradle-задачи для деплоя плагина
Это минимально рабочий проект (по сути, достаточно указать одну зависимость). А для удобной разработки нам понадобится еще ряд вспомогательных задач (основные):
С учетом всех пожеланий итоговый файл сборки выглядит так:
import org.apache.tools.ant.taskdefs.Mkdir import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { kotlin("jvm") version "1.7.10" } group = "com.vygovskiy.vpplugin" version = "1.0-SNAPSHOT" val userHome = System.getProperty("user.home") val userName = System.getProperty("user.name") val vpPluginsDir = "${userHome}\\AppData\\Roaming\\VisualParadigm\\plugins" val vpPluginName = project.name val vpPluginDir = "$vpPluginsDir/$vpPluginName" repositories { mavenCentral() } dependencies { compileOnly(files("lib/openapi.jar")) testImplementation(kotlin("test")) } tasks.test { useJUnitPlatform() } tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" } val createPluginDir by tasks.register("vpCreatePluginDir") { group = "Visual Paradigm" description = "Create plugin's directory" doLast { mkdir(vpPluginDir) } } tasks.register("vpInstallPlugin") { group = "Visual Paradigm" description = "Copy all plugins file into Visual Paradigm plugins folder" dependsOn( copyClasses, copyDependenciesJars ) } val deletePluginDir by tasks.register<Delete>("vpDeletePluginDir") { group = "Visual Paradigm" description = "Delete plugin's folder" delete(vpPluginDir) } val copyClasses by tasks.register<Copy>("vpCopyClasses") { group = "Visual Paradigm" description = "Compile and copy classes into plugins folder. Use it for hot-reload " dependsOn("build") from( "$buildDir/classes/kotlin/main", "$buildDir/resources/main" ) into(vpPluginDir) } val copyDependenciesJars by tasks.register<Copy>("vpCopyDependenciesJars") { group = "Visual Paradigm" description = "Copy plugin's dependencies into plugin folder" dependsOn(copyPluginXml) from( getJarDependencies() .map {it.absoluteFile} .toTypedArray() ) into("$vpPluginDir/lib") } val copyPluginXml by tasks.register("vpCopyPluginXml") { group = "Visual Paradigm" description = "Prepare and copy plugin.xml into plugin folder" dependsOn(createPluginDir) doLast { val runtimeSection = getJarDependencies().joinToString( separator = "\n", prefix = "\n <runtime>\n", postfix = "\n </runtime>\n" ) {" <library path='lib/${it.name}' relativePath='true'/>"} val pluginXmlFile = File("$buildDir/resources/main/plugin.xml").readText() val content = pluginXmlFile .replace("<runtime/>", runtimeSection, false) .replace("#pluginId#", vpPluginName) .replace("#user#", userName) File("$vpPluginDir/plugin.xml").writeText(content) } } fun getJarDependencies(): FileCollection { return configurations.runtimeClasspath.get() .filter {it.name.endsWith("jar")} }
Список всех добавленных команд с описанием:
Список команд Gradle-скрипта, помогающих в разработке плагинов Visual Paradigm
Разработка логики экспорта комментариев
Теперь у нас все готово для удобной реализации изначально поставленной задачи ― выгрузки описания колонок и таблица из ER-диаграмм в виде SQL кода для Postgresql. Данное действие лучше добавить в контекстное меню ER-диаграммы, т. е. оно контекстно-зависимое. Поэтому:
Реализация VPPlugin
Реализация интерфейса VPPlugin обычно остается пустой. Интерфейс описывает два метода, вызываемых при загрузке (loaded) и выгрузке (unloaded) классов плагина. При желании можно выводить информацию в System.out
и читать ее из файла vp.log
(файл находится в каталоге уровнем выше каталога с плагинами).
Обратите внимание! VP создает инстанс класса для вызова loaded
и отдельный инстанс для unloaded
.
package ercomments import com.vp.plugin.VPPlugin import com.vp.plugin.VPPluginInfo class Plugin : VPPlugin { override fun loaded(vpPluginInfo: VPPluginInfo) { // Выводим сообщение в стандартный вывод. Вы можете увидеть сообщение // в файле vp.log, который лежит в каталоге настроек пользователя VP. // В этом же каталоге находится папка plugins. println("plugin [ercomments] is loaded ") } override fun unloaded() { println("[ercomments] is unloaded") } }
Реализация класса действий
Интерфейс VPContextActionController
Класс с действием должен реализовать интерфейс VPContextActionController
с двумя функциями:
performAction
― здесь надо поместить логику плагина;
update
― вызывается при отображении действия в меню (в реализации можно, например, заблокировать элемент меню).
Самый важный параметр обеих функций ― параметр с типом VPContext
. Из него можно получить диаграмму или ее элемент, на котором было вызвано действие.
Элементы модели и элементы диаграммы
Важно различать элемент диаграммы и элемент модели . Элемент диаграммы ― графический элемент с координатами, стилем и т. п. Он принадлежит конкретной диаграмме, и все изменения будут видны только на ней. Как правило, каждый элемент диаграммы отображает свойства одного элемента модели.
Элемент модели ― это часть модели проекта. Он содержит такие свойства, как имя, тип, описание (description), комментарии, видимость, операции и т. д. Полный список типов задан константами MODEL_TYPE_*
в интерфейсе IModelElementFactory
.
Важно! Один элемент модели может быть представлен разными элементами диаграммы. Если изменяется свойство элемента модели (например, имя) на одной диаграмме, оно изменится и на других.
В большинстве случаев нужно работать с элементами модели. Для получения списка элементов модели проще всего воспользоваться методом диаграммы toDiagramElementArray()
и вызывать у каждого элемента диаграммы функцию getModelElement()
. Для удобства, при вызове toDiagramElementArray()
можно передать список типов моделей.
Интерфейс пользователя
Visual Paradigm реализована на фреймворке Swing и позволяет в плагинах отображать любые окна. Информация о создании полноценного UI выходит за рамки этой статьи.
Для нашей задачи необходимо узнать у пользователя имя файла, в который будет выгружен SQL. Для решения этой задачи подойдет стандартный Java-класс JFileChooser
. Прочитать про него можно здесь: https://docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html
Исходный код реализации плагина
Реализация плагина заключается в следующих шагах:
Узнаем у пользователя имя файла, в который он хочет экспортировать SQL (функция chooseFile).
Получаем все элементы модели типа DB_TABLE.
Для каждой таблицы:
Исходный код:
package ercomments import com.vp.plugin.ApplicationManager import com.vp.plugin.ViewManager import com.vp.plugin.action.VPAction import com.vp.plugin.action.VPContext import com.vp.plugin.action.VPContextActionController import com.vp.plugin.model.IDBTable import com.vp.plugin.model.factory.IModelElementFactory import java.awt.Component import java.awt.event.ActionEvent import java.io.PrintWriter import javax.swing.JFileChooser class ExportERCommentsPluginAction : VPContextActionController { private val TABLE_COMMENT = """COMMENT ON TABLE #TABLE_NAME IS '#COMMENT';""" private val COLUMN_COMMENT = """COMMENT ON COLUMN #TABLE_NAME.#COLUMN_NAME IS '#COMMENT';""" private val viewManager : ViewManager get() = ApplicationManager.instance().viewManager private fun makeCommentValue(s: String?): String = if (s.isNullOrBlank()) { "NULL" } else { s.replace("'", "''") } private fun makeTableComment(name: String, comment: String): String = TABLE_COMMENT .replace("#TABLE_NAME", name) .replace("#COMMENT", makeCommentValue(comment)) private fun makeColumnComment(table: String, column: String, comment: String): String = COLUMN_COMMENT .replace("#TABLE_NAME", table) .replace("#COLUMN_NAME", column) .replace("#COMMENT", makeCommentValue(comment)) /** * Предлагаем пользователю выбрать файл и если он выбран - вызываем action. */ private fun chooseFile(action: (PrintWriter) -> Unit) { val fc = viewManager.createJFileChooser() if (fc.showSaveDialog(viewManager.rootFrame) == JFileChooser.APPROVE_OPTION) { val file = fc.selectedFile viewManager.showMessage("Export ER Comments into ${file.absoluteFile}") file.printWriter().use { action(it) it.flush() } viewManager.showMessage("export completed") } } override fun performAction(action: VPAction, context: VPContext, event: ActionEvent) { chooseFile {writer -> val diagram = context.diagram val tables = diagram // Получаем массив всех элементов диаграммы, которые относятся к таблицам .toDiagramElementArray(IModelElementFactory.MODEL_TYPE_DB_TABLE) // Достаем из элемента диаграммы элемент модели .map {it.modelElement as IDBTable} tables.forEach {table -> viewManager.showMessage(" export table ${table.name}") writer.println(makeTableComment(table.name, table.description)) table.toDBColumnArray().forEach {column -> writer.println(makeColumnComment(table.name, column.name, column.description)) } } } } override fun update(vpAction: VPAction, vpContext: VPContext) { } }
Манифест плагина
Теперь давайте добавим плагин в UI Visual Paradigm. Для этого нужно создать манифест плагина ― plugin.xml. Его нужно сохранить в каталоге проекта src/main/resources/
Нужный манифест выглядит таким образом:
<?xml version="1.0" encoding="UTF-8" ?> <!-- Общее описание плагина. Ключевое здесь - аттрибут class - имя класса плагина, который реализует интерфейс com.vp.plugin.VPPlugin --> <plugin id="ercomments" name="ER Comments exporter" description="Export only comments from ER Diagram" provider="Leonid Vygovskiy" class="ercomments.Plugin"> <!-- Место-заглушка для путей к используемым библиотекам. Заполняется динамически с помощью gradle --> <runtime/> <actionSets> <contextSensitiveActionSet id="ercomments.Export"> <!-- Определяем контекст действия --> <contextTypes all="false"> <include type="ERDiagram"/> </contextTypes> <!-- menuPath: Пункт контекстного меню, после которого будет выведен наш пункт. Для Open Specification... нужно указать OpenSpecification. --> <action style="normal" menuPath="Export" id="ercomments.Export" label="Export comments as DDL"> <actionController class="ercomments.ExportERCommentsPluginAction"/> </action> </contextSensitiveActionSet> </actionSets> </plugin>
Разберем его подробнее:
Объявление плагина корневым тэгом plugin. В его атрибутах задается метаинформация о плагине (id, имя и т. п.) и указывается класс реализации VPPlugin
.
Информация об используемых зависимостях ― секция runtime. В нашем примере она создается автоматически Gradle-скриптом.
В тэге actionSets
задаются действия (action) и правила их отображения на UI.
Самое интересное и значимое в plugin.xml ― это определение действия. В actionSets можно вложить два элемента:
Действие экспорта описания таблиц и колонок в SQL имеет смысл только для ER-диаграмм, т. е. оно контекстно-зависимое. Определим это с помощью блока.
Иногда бывает трудно понять, какой именно тип имеет нужная нам диаграмма или элемент модели. Документация здесь, откровенно, хромает. Чтобы определить тип документа, проще всего включить режим all=”true” (отображать в любом контекстном меню) и использовать следующий код в реализации действия:
override fun performAction(action: VPAction, context: VPContext, event: ActionEvent) { val viewManager = ApplicationManager.instance().viewManager viewManager.showMessage(context.contextType) viewManager.showMessage(context.diagram?.type ?: "") viewManager.showMessage(context.modelElement?.modelType ?: "") }
В атрибуте menuPath
указывается пункт меню, после которого будет добавлен наш пункт. В данном случае ― сразу после подменю Export. Чтобы добавить сразу после пункта меню Open Specification, надо указать “OpenSpecification”.
Пример оформления контекстно-независимого плагина можно посмотреть в реализации плагина Hot Reload: https://github.com/leonidv/vp-plugin-hot-reload/blob/master/src/main/resources/plugin.xml#L13
Деплой плагина
Последнее, что осталось сделать, ― установить плагин в Visual Paradigm:
Вызвать задачу Gradle-скрипта vpInstallPlugin
.
Запустить Visual Paradigm.
Проверить, что в контекстном меню ER-диаграмм появился пункт Export descriptions as DDL.
На рисунке показан пункт в меню Visual Paradigm и пример полученного SQL-файла.
Что делать, если что-то пошло не так
Логи и сообщения
Важно отметить, что в реальной жизни я делаю плагин в такой последовательности:
Делаю реализацию VPActionController
/ VPContextActionController
, которая в функции performAction
выводит строку в панель сообщений Visual Paradigm:ApplicationManager.instance().viewManager.showMessage(":)")
Добавляю действие в plugin.xml и убеждаюсь, что он появился в нужном месте.
Реализую логику работы плагина.
При появлении проблемы, полезно смотреть в vp.log Visual Paradigm. Он находится в той же папке, что и plugins с нашим плагином. В него попадают:
Для отладки логики и вывода промежуточных сообщений лучше использовать viewManager. showMessage.
Отладка в дебаггере
Если ваш плагин имеет очень сложную логику и требуется полноценная отладка, вы можете запускать Visual Paradigm под дебаггером IDE. Как это сделать, хорошо описано в этих статьях:
Исходные коды примера
Шаблон для создания своих плагинов: https://github.com/leonidv/vp-plugin-template
Исходные коды плагина из статьи: https://github.com/leonidv/vp-plugin-er-comments
Ссылки
Нельзя сказать, что материал создания плагинов для Visual Paradigm хорошо раскрыт в официальной документации. Тем не менее, для дальнейшего изучения будут полезны следующие ссылки:
Заключение
Писать плагины к Visual Paradigm просто, а за счет них можно экономить время при решении рутинных задач. При этом можно решать и достаточно комплексные и сложные задачи, необходимые именно вашему процессу архитектурного описания систем.
Сообщество пользователей Visual Paradigm
Visual Paradigm достаточно широкого используется в компаниях Российской Федерации в совершенно разных областях: стартапах, крупном бизнесе, госорганах. При этом как такового сообщества пользователей не сформировано. Я предлагаю всем заинтересованным присоединиться к группе в телеграмме https://t.me/visualparadigm_ru для обмена опытом. Группа только появилась и начинает развиваться. Присоединяйтесь, в группе будут появляться полезные материалы по работе с Visual Paradigm.
Добавить комментарий