├── .github └── workflows │ └── main.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── pom.xml └── src ├── main ├── java │ └── ru │ │ └── taximaxim │ │ ├── pgsqlblocks │ │ ├── PgSqlBlocks.java │ │ ├── common │ │ │ ├── DBQueries.java │ │ │ ├── models │ │ │ │ ├── DBBlock.java │ │ │ │ ├── DBBlockDeserializer.java │ │ │ │ ├── DBBlocksJournal.java │ │ │ │ ├── DBBlocksJournalListener.java │ │ │ │ ├── DBBlocksJournalProcess.java │ │ │ │ ├── DBModel.java │ │ │ │ ├── DBProcess.java │ │ │ │ ├── DBProcessQuery.java │ │ │ │ ├── DBProcessQueryCaller.java │ │ │ │ └── DBProcessStatus.java │ │ │ └── ui │ │ │ │ ├── DBBlocksJournalViewDataSource.java │ │ │ │ ├── DBModelsView.java │ │ │ │ ├── DBModelsViewContentProvider.java │ │ │ │ ├── DBModelsViewLabelProvider.java │ │ │ │ ├── DBModelsViewListener.java │ │ │ │ ├── DBProcessInfoView.java │ │ │ │ ├── DBProcessInfoViewListener.java │ │ │ │ ├── DBProcessesViewDataSource.java │ │ │ │ └── TreeLabelProvider.java │ │ ├── dialogs │ │ │ ├── AddDatabaseDialog.java │ │ │ ├── DBProcessInfoDialog.java │ │ │ ├── EditDatabaseDialog.java │ │ │ ├── PasswordDialog.java │ │ │ └── SettingsDialog.java │ │ ├── l10n │ │ │ ├── PgSqlBlocks.java │ │ │ ├── PgSqlBlocks_en.java │ │ │ └── PgSqlBlocks_ru.java │ │ ├── modules │ │ │ ├── application │ │ │ │ ├── controller │ │ │ │ │ └── ApplicationController.java │ │ │ │ └── view │ │ │ │ │ ├── ApplicationView.java │ │ │ │ │ └── ApplicationViewListener.java │ │ │ ├── blocksjournal │ │ │ │ └── view │ │ │ │ │ ├── BlocksJournalFilesContentProvider.java │ │ │ │ │ ├── BlocksJournalFilesLabelProvider.java │ │ │ │ │ └── BlocksJournalView.java │ │ │ ├── db │ │ │ │ ├── controller │ │ │ │ │ ├── DBController.java │ │ │ │ │ ├── DBControllerListener.java │ │ │ │ │ └── UserInputPasswordProvider.java │ │ │ │ └── model │ │ │ │ │ └── DBStatus.java │ │ │ ├── logs │ │ │ │ └── view │ │ │ │ │ └── LogsView.java │ │ │ └── processes │ │ │ │ ├── controller │ │ │ │ └── ProcessesController.java │ │ │ │ └── view │ │ │ │ └── ProcessesView.java │ │ ├── ui │ │ │ ├── AboutDlg.java │ │ │ └── UIAppender.java │ │ ├── utils │ │ │ ├── ColumnLayout.java │ │ │ ├── Columns.java │ │ │ ├── DateUtils.java │ │ │ ├── ImageUtils.java │ │ │ ├── Images.java │ │ │ ├── PathBuilder.java │ │ │ ├── Settings.java │ │ │ ├── SettingsListener.java │ │ │ └── UserCancelException.java │ │ └── xmlstore │ │ │ ├── ColumnLayoutsXmlStore.java │ │ │ ├── DBBlocksXmlStore.java │ │ │ ├── DBModelsXmlStore.java │ │ │ └── XmlStore.java │ │ └── treeviewer │ │ ├── ExtendedTreeViewer.java │ │ ├── dialog │ │ └── ColumnConfigDialog.java │ │ ├── filter │ │ ├── ColumnType.java │ │ ├── FilterChangeHandler.java │ │ ├── FilterComposite.java │ │ └── FilterOperation.java │ │ ├── l10n │ │ ├── TreeViewer.java │ │ ├── TreeViewer_en.java │ │ └── TreeViewer_ru.java │ │ ├── models │ │ ├── DataSource.java │ │ └── IObject.java │ │ ├── tree │ │ └── ExtendedTreeViewerComponent.java │ │ └── utils │ │ ├── AggregatingListener.java │ │ ├── ImageUtils.java │ │ ├── Images.java │ │ └── TriFunction.java └── resources │ ├── images │ ├── autoupdate_16.png │ ├── back-16.png │ ├── block-16x16.gif │ ├── block-256x256.png │ ├── block-32x32.png │ ├── block-48x48.png │ ├── block-512x512.png │ ├── blocked_16.png │ ├── blocks_journal_folder_16.png │ ├── cancel_update_16.png │ ├── clean_16.png │ ├── db_add_16.png │ ├── db_connect_16.png │ ├── db_del_16.png │ ├── db_disconnect_16.png │ ├── db_e_16.png │ ├── db_edit_16.png │ ├── db_f_16.png │ ├── db_group_16.png │ ├── db_ob_16.png │ ├── db_t_16.png │ ├── document_open_16.png │ ├── filter.png │ ├── folder_16.png │ ├── locked_16.png │ ├── locker_16.png │ ├── locker_8.png │ ├── log_hide_16.png │ ├── log_show_16.png │ ├── nb_16.png │ ├── on_update_16.png │ ├── save_16.png │ ├── settings.png │ ├── table_16.png │ ├── unblock-16x16.gif │ ├── unblock-256x256.png │ ├── unblock-32x32.png │ ├── unblock-48x48.png │ ├── unblock-512x512.png │ ├── update_16.png │ └── update_8.png │ ├── log4j2.xml │ ├── query.sql │ ├── query_10.sql │ ├── query_with_idle.sql │ ├── query_with_idle_10.sql │ └── version.sql └── test ├── java └── ru │ └── taximaxim │ └── pgsqlblocks │ ├── common │ └── models │ │ ├── DBBlocksJournalTest.java │ │ └── DBModelTest.java │ ├── modules │ └── db │ │ └── controller │ │ └── DBControllerTest.java │ └── utils │ └── DateUtilsTest.java └── resources ├── application.conf ├── create_index.sql ├── create_rule.sql ├── drop_rule.sql ├── select_1000.sql ├── select_sleep.sql └── testing_dump.sql /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | 8 | compile: 9 | runs-on: ubuntu-latest 10 | name: Running Java 8 compile 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Set up JDK 1.8 14 | uses: actions/setup-java@v4 15 | with: 16 | distribution: 'temurin' 17 | java-version: '8' 18 | - name: Compile code 19 | run: mvn compile 20 | 21 | test: 22 | runs-on: ubuntu-latest 23 | name: Running tests 24 | needs: compile 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up JDK 1.8 28 | uses: actions/setup-java@v4 29 | with: 30 | distribution: 'temurin' 31 | java-version: '8' 32 | - name: Run unit tests 33 | run: mvn test -B verify 34 | 35 | build: 36 | runs-on: ubuntu-latest 37 | name: mvn build 38 | needs: compile 39 | steps: 40 | - uses: actions/checkout@v4 41 | - name: Set up JDK 1.8 42 | uses: actions/setup-java@v4 43 | with: 44 | distribution: 'temurin' 45 | java-version: '8' 46 | - name: build 47 | run: mvn clean package -DskipTests 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /servers.xml 3 | /BlocksHistory/ 4 | /.idea/ 5 | *.iml 6 | /.classpath 7 | /.project 8 | /.settings/ 9 | github.token 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # pgSqlBlocks 2 | 3 | java-приложение для работы с блокировками в СУБД PostgreSQL 4 | 5 | ### Версии 6 | 7 | 1.9.0 8 | 9 | * Добавлен gitHub Actions 10 | * Обновлены библиотеки 11 | * Обновлена лицензия 12 | * Обновлены тесты 13 | * Добавлена возможность группировки баз данных в настройках соединения 14 | * Добавлена настройка для выбора расположения журналов блокировок 15 | * Добавлена кнопка открытия директории в журнале сохраненных блокировок 16 | 17 | 1.8.0 18 | 19 | * Добавлена настройка для ограничения количества блокировок в списке и в файлах журналов 20 | * Обновлены версии библиотек 21 | 22 | 1.7.6 23 | 24 | * Обновлена уязвимая библиотека log4j2 25 | 26 | 1.7.5 27 | 28 | * Версия PostgreSQL в настройках БД заменена на опцию чтения типа процесса 29 | * Исправлена утечка памяти при большом количестве блокировок 30 | * Исправлен фильтр для заблокированных процессов 31 | * Обновлена библиотека PgPass 32 | 33 | 1.7.4 34 | 35 | * Обновлены версии SWT-библиотек 36 | * Обновлена версия Log4j2 37 | * Обновлен драйвер JDBC 38 | 39 | 1.7.3 40 | 41 | * Исправлены ошибки при обновлении списка процессов БД 42 | 43 | 1.7.2 44 | 45 | * Исправлена ошибка при подсчете количества процессов 46 | 47 | 1.7.1 48 | 49 | * Добавлена возможность сортировки списка баз данных по имени или количеству процессов 50 | * Удалены сборки для 32-битных систем 51 | * Log4j заменен на Log4j2 52 | * Обновлены версии SWT-библиотек 53 | * Обновлен драйвер JDBC 54 | 55 | 1.7.0 56 | 57 | * Добавлено сохранение расположения и видимости столбцов между перезапусками 58 | * Добавлена сортировка по нескольким столбцам 59 | * Добавлена очистка списка процессов и блокировок при отключении от БД 60 | * Исправлена ошибка сохранения блокировок в Windows 61 | * Исправлена ошибка в отображении дерева сохраненных блокировок 62 | * Убрана автоматическая прокрутка при обновлении процессов и блокировок 63 | * Изменена иконка ожидающего процесса 64 | * Обновлен драйвер JDBC 65 | 66 | 1.6.0 67 | 68 | * Добавлены версии PostgreSQL для подключений (10.0, 9.6 и т.д.) 69 | * Если версия не задана, используется версия по-умолчанию 10.0 70 | * При старте приложения предлагает всем подключениям получить версию с сервера 71 | * В список процессов добавлена колонка backend_type (10.0+, https://www.postgresql.org/docs/10/static/monitoring-stats.html#PG-STAT-ACTIVITY-VIEW) 72 | * В список процессов добавлена колонка с продолжительностью запроса 73 | * В список БД добавлена колонка с количеством процессов 74 | * Появляется окно ввода пароля при подключении если пароль не задан в настройках и в pgpass 75 | * Диалог с подробной информацией о процессе открывается при нажатии ввода в таблице процессов 76 | * Улучшения в пользовательском интерфейсе 77 | * Панель с информацией о процессе сделана изменяемого размера 78 | * Улучшена видимость и выраженность элементов интерфейса 79 | 80 | 1.5.0 81 | 82 | * Выбор нескольких процессов для остановки/уничтожения 83 | * Исправлена сортировка колонки STATE при подключении к PostgreSQL 10 84 | * Исправлено состояние кнопок отмены/уничтожения процессов, добавлены тултипы 85 | * Исправлено автоподключение при добавлении новой БД 86 | * Обновлен драйвер JDBC 87 | 88 | 1.4.0 89 | 90 | * Возникающие блокировки автоматически пишутся в журнал и в файл 91 | * Добавлен вид для просмотра сохраненных журналов блокировок (кнопка на тулбаре) 92 | * Дерево процессов теперь реагирует на двойной клик 93 | * Переработан UI, сделаны отдельные фильтры процессов для каждой бд 94 | * Фильтр процессов по-умолчанию имеет условие "содержит" для всех полей фильтра 95 | * SQL запросы в таблице обрезаются до переноса строки для предотвращения изменения высоты строк 96 | 97 | 1.3.8 98 | 99 | * Сделано автоматическое сохранение блокировок в журнал (пока не пишется на диск) 100 | 101 | 1.3.7 102 | 103 | * Исправлено отображение циклических случаев блокировки 104 | * Сделана локализация приложения. Доступные языки: русский, английский 105 | * Время в лог консоли форматируется в соответствии с локалью 106 | * Исходный код приложения выложен в открытый доступ под лицензией Apache License 2.0 107 | 108 | 1.3.6 109 | 110 | * Добавлена кнопка отображения/скрытия окна с логами 111 | * Исправлена ситуация с прекращением обновления при возникновении тройных блокировок 112 | 113 | 1.3.5 114 | 115 | * При попытке отменить/терминировать процесс появляется окно подтверждения (отключить можно в настройках) 116 | * Подтверждение выхода из программы сделано отключаемым (отключить можно в настройках) 117 | * Сделана сборка для macOS (Cocoa) 118 | * Диалог с настройками был реорганизован 119 | * Исправлен диалог фильтра (не все контролы влезали) 120 | 121 | 1.3.4 122 | 123 | * Колонки стали перемещаемыми и скрываемыми (в настройках), названия переведены на русский, тултип на колонке показывает исходное название 124 | * Улучшен механизм обновления списка процессов (фикс автоподключения по закрытию настроек) 125 | * Добавлено меню приложения, диалог "О программе" 126 | 127 | 1.3.3 128 | 129 | * При появлении блокировок в БД изменяется ее иконка - добавлен декоратор "красный замок" 130 | * В настройки добавлена галочка "Скрывать собственные запросы" для скрытия процесса самого pgSqlBlocks из списка процессов 131 | * Добавлено всплывающее уведомление в трее при появлении блокировок в подключенных БД: 132 | * Работает на Windows, на Ubuntu сообщение появляется поверх приложения 133 | * По-умолчанию отключено, изменить можно в настройках 134 | * Улучшена панель с логами, теперь доступны следующие возможности: 135 | * Автопрокрутка включена по-умолчанию 136 | * Прокрутка колесом вверх отключает автопрокрутку 137 | * Прокрутка колесом вниз включает автопрокрутку 138 | * Нажатие клавиши "Ввод" добавляет перенос строки в конец текста 139 | * Исправлено высокое потребление памяти при частом обновлении БД 140 | * Исправлена выгрузка и загрузка блокировок в файл 141 | * Улучшено информирование об обновлении БД иконкой 142 | 143 | 1.3.2 144 | 145 | - Добавлена иконка приложения в системном трее, изменяющаяся в зависимости от наличия заблокированных процессов 146 | 147 | 1.3.1 148 | 149 | - Idle-процессы показываются по-умолчанию, изменить поведение можно в настройках 150 | - Улучшено чтение pgpass файла 151 | - Исправлено обновление списка при сортировке 152 | - Исправлен цвет текста SQL-запроса выбранного процесса 153 | - Исправлена ошибка при попытке отмены своего же процесса 154 | 155 | 1.3.0 156 | 157 | - Добавлена возможность отменить текущее и автообновление серверов 158 | - Обновление серверов не блокирует UI 159 | - Сервер в состоянии обновления теперь имеет свою иконку 160 | - Добавлено контекстное меню в списке серверов 161 | - Добавлена иконка приложения 162 | - Обновлена версия JDBC-драйвера (поддержка юникода в сообщениях об ошибках) 163 | - Исправление ошибок 164 | 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### pgSqlBlocks 2 | 3 | pgSqlBlocks - это standalone приложение, написанное на языке программирования Java, которое позволяет легко ориентироваться среди процессов и получать информацию о блокировках и ожидающих запросов в СУБД PostgreSQL. Отображается информация о состоянии подключения к БД, а также информация о процессах в БД. 4 | 5 | Требуется Java JRE версии 1.8 и выше для вашей платформы. 6 | 7 | ### Сборка, запуск тестов, запуск приложения 8 | 9 | ##### Для сборки без запуска тестов 10 | Выполните команду с использованием флага -DskipTests, к примеру: ```mvn package -P Linux-64 -DskipTests``` 11 | 12 | ##### Для запуска тестов требуется: 13 | 1. Наличие [docker](https://docs.docker.com/engine/install/). 14 | 2. Выполните команду: ```mvn test```. 15 | 16 | ##### Запуск приложения 17 | 18 | Запуск jar-файла через консоль командой ```java -jar pgSqlBlocks-1.3.6-Linux-64.jar``` 19 | 20 | * Для пользователей MacOS необходим дополнительный параметр ```-XstartOnFirstThread```. 21 | 22 | * Для пользователей Gtk3, если возникают сложности с отображаемыми всплывающими сообщениями, рекомендуется запускать приложение с ключом *SWT_GTK3=0*. 23 | 24 | ### Запросы 25 | 26 | Для получения всех процессов сервера, включая или исключая idle(бездействующие), используется [скрипт](src/main/resources/query_with_idle.sql) или [скрипт](src/main/resources/query.sql) соответственно. 27 | 28 | Для версии PostgreSQL 10 и выше, для получения всех процессов сервера, включая или исключая idle(бездействующие), используется [скрипт](src/main/resources/query_with_idle_10.sql) или [скрипт](src/main/resources/query_10.sql) соответственно. 29 | 30 | Уничтожается процесс командой: _select pg_terminate_backend(?);_ 31 | 32 | Послать сигнал для отмены процесса: _select pg_cancel_backend(?);_ 33 | 34 | ### UI 35 | Все, что связано с UI необходимо писать в пакете ru.taximaxim.pgsqlblocks.ui 36 | 37 | ### Homepage 38 | 39 | https://pgcodekeeper.org/pgsqlblocks.html 40 | 41 | ### License 42 | 43 | This application is licensed under the Apache License, Version 2.0. See [LICENCE](LICENSE) for details. 44 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/PgSqlBlocks.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks; 17 | 18 | import ru.taximaxim.pgsqlblocks.modules.application.controller.ApplicationController; 19 | 20 | public class PgSqlBlocks { 21 | public static final String APP_NAME = "pgSqlBlocks " + PgSqlBlocks.class.getPackage().getImplementationVersion(); 22 | private static PgSqlBlocks instance; 23 | 24 | private final ApplicationController applicationController; 25 | 26 | private PgSqlBlocks() { 27 | applicationController = new ApplicationController(); 28 | } 29 | 30 | public static void main(String[] args) { 31 | System.setProperty("java.awt.headless", "true"); 32 | PgSqlBlocks.getInstance().launch(); 33 | } 34 | 35 | public static PgSqlBlocks getInstance() { 36 | if (instance == null) 37 | instance = new PgSqlBlocks(); 38 | return instance; 39 | } 40 | 41 | public ApplicationController getApplicationController() { 42 | return applicationController; 43 | } 44 | 45 | private void launch() { 46 | applicationController.launch(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/DBQueries.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common; 17 | 18 | import java.io.BufferedReader; 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.io.InputStreamReader; 22 | import java.nio.charset.StandardCharsets; 23 | 24 | import org.apache.logging.log4j.LogManager; 25 | import org.apache.logging.log4j.Logger; 26 | 27 | public final class DBQueries { 28 | private static final Logger LOG = LogManager.getLogger(DBQueries.class); 29 | 30 | public static final String PG_BACKEND_PID_QUERY = "select pg_backend_pid();"; 31 | 32 | private static String versionQuery; 33 | private static String processesQuery; 34 | private static String processesQueryForTen; 35 | private static String processesQueryWithIdle; 36 | private static String processesQueryWithIdleForTen; 37 | 38 | private static final String PROCESSES_QUERY_FILE_NAME = "query.sql"; 39 | private static final String PROCESSES_QUERY_10_FILE_NAME = "query_10.sql"; 40 | private static final String PROCESSES_QUERY_WITH_IDLE_FILE_NAME = "query_with_idle.sql"; 41 | private static final String PROCESSES_QUERY_WITH_IDLE_10_FILE_NAME = "query_with_idle_10.sql"; 42 | private static final String PG_SERVER_VERSION_QUERY_FILE_NAME = "version.sql"; 43 | 44 | public static final String PG_TERMINATE_BACKEND_QUERY = "select pg_terminate_backend(?);"; 45 | 46 | public static final String PG_CANCEL_BACKEND_QUERY = "select pg_cancel_backend(?);"; 47 | 48 | public static synchronized String getProcessesQuery() { 49 | if (processesQuery == null) { 50 | processesQuery = loadQuery(PROCESSES_QUERY_FILE_NAME); 51 | } 52 | return processesQuery; 53 | } 54 | 55 | public static synchronized String getProcessesQueryForTen() { 56 | if (processesQueryForTen == null) { 57 | processesQueryForTen = loadQuery(PROCESSES_QUERY_10_FILE_NAME); 58 | } 59 | return processesQueryForTen; 60 | } 61 | 62 | public static synchronized String getProcessesQueryWithIdle() { 63 | if (processesQueryWithIdle == null) { 64 | processesQueryWithIdle = loadQuery(PROCESSES_QUERY_WITH_IDLE_FILE_NAME); 65 | } 66 | return processesQueryWithIdle; 67 | } 68 | 69 | public static synchronized String getProcessesQueryWithIdleForTen() { 70 | if (processesQueryWithIdleForTen == null) { 71 | processesQueryWithIdleForTen = loadQuery(PROCESSES_QUERY_WITH_IDLE_10_FILE_NAME); 72 | } 73 | return processesQueryWithIdleForTen; 74 | } 75 | 76 | public static synchronized String getVersionQuery(){ 77 | if (versionQuery == null) { 78 | versionQuery = loadQuery(PG_SERVER_VERSION_QUERY_FILE_NAME); 79 | } 80 | return versionQuery; 81 | } 82 | 83 | private static String loadQuery(String queryFile) { 84 | try (InputStream input = ClassLoader.getSystemResourceAsStream(queryFile); 85 | BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) { 86 | StringBuilder out = new StringBuilder(); 87 | String line; 88 | while ((line = reader.readLine()) != null) { 89 | out.append(line); 90 | } 91 | return out.toString(); 92 | } catch (IOException e) { 93 | LOG.error("Ошибка чтения файла " + queryFile, e); 94 | return null; 95 | } 96 | } 97 | 98 | private DBQueries() { 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBBlock.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.Objects; 19 | 20 | public class DBBlock { 21 | 22 | private final int blockingPid; 23 | private final String relation; 24 | private final String locktype; 25 | private final boolean granted; 26 | 27 | public DBBlock(int blockingPid, String relation, String locktype, boolean granted) { 28 | this.blockingPid = blockingPid; 29 | this.relation = relation == null ? "" : relation; 30 | this.locktype = locktype == null ? "" : locktype; 31 | this.granted = granted; 32 | } 33 | 34 | public int getBlockingPid() { 35 | return blockingPid; 36 | } 37 | 38 | public String getRelation() { 39 | return relation; 40 | } 41 | 42 | public String getLocktype() { 43 | return locktype; 44 | } 45 | 46 | public boolean isGranted() { 47 | return granted; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) { 53 | return true; 54 | } 55 | if (!(o instanceof DBBlock)) { 56 | return false; 57 | } 58 | 59 | DBBlock dbBlock = (DBBlock) o; 60 | 61 | return blockingPid == dbBlock.blockingPid 62 | && relation.equals(dbBlock.relation) 63 | && locktype.equals(dbBlock.locktype) 64 | && granted == dbBlock.granted; 65 | } 66 | 67 | @Override 68 | public int hashCode() { 69 | return Objects.hash(blockingPid, relation, locktype, granted); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBBlockDeserializer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | 21 | public class DBBlockDeserializer { 22 | 23 | private static final String BLOCKED_BY = "blockedBy"; 24 | private static final String RELATION = "relation"; 25 | private static final String LOCK_TYPE = "locktype"; 26 | private static final String GRANTED = "granted"; 27 | private static final String GRANTED_FLAG = "t"; 28 | 29 | public DBBlock deserialize(ResultSet resultSet) throws SQLException { 30 | int blockedBy = resultSet.getInt(BLOCKED_BY); 31 | String lockType = resultSet.getString(LOCK_TYPE); 32 | String relation = resultSet.getString(RELATION); 33 | boolean granted = GRANTED_FLAG.equals(resultSet.getString(GRANTED)); 34 | return new DBBlock(blockedBy, relation, lockType, granted); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBBlocksJournal.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.stream.Collectors; 21 | 22 | public class DBBlocksJournal{ 23 | 24 | private final List listeners = new ArrayList<>(); 25 | 26 | private final List processes = new ArrayList<>(); 27 | 28 | public List getProcesses() { 29 | return processes; 30 | } 31 | 32 | public DBBlocksJournal() { 33 | } 34 | 35 | public void add(List processes) { 36 | if (processes.isEmpty()) { 37 | closeProcesses(); 38 | return; 39 | } 40 | if (this.processes.isEmpty()) { 41 | this.processes.addAll(processes.stream().map(DBBlocksJournalProcess::new).collect(Collectors.toList())); 42 | } else { 43 | List newProcesses = processes.stream() 44 | .map(DBBlocksJournalProcess::new) 45 | .collect(Collectors.toList()); 46 | List needCloseProcesses = this.processes.stream() 47 | .filter(DBBlocksJournalProcess::isOpened) 48 | .filter(p -> !newProcesses.contains(p)) 49 | .collect(Collectors.toList()); 50 | if (!needCloseProcesses.isEmpty()) { 51 | needCloseProcesses.forEach(DBBlocksJournalProcess::close); 52 | listeners.forEach(listener -> listener.dbBlocksJournalDidCloseProcesses(needCloseProcesses)); 53 | } 54 | for (DBBlocksJournalProcess process : newProcesses) { 55 | if (this.processes.contains(process)) { 56 | DBBlocksJournalProcess prevJournalProcess = this.processes.get(this.processes.lastIndexOf(process)); 57 | if (prevJournalProcess.isClosed()) { 58 | this.processes.add(process); 59 | } 60 | } else { 61 | this.processes.add(process); 62 | } 63 | } 64 | } 65 | listeners.forEach(DBBlocksJournalListener::dbBlocksJournalDidAddProcesses); 66 | } 67 | 68 | public void setJournalProcesses(List processes) { 69 | clear(); 70 | this.processes.addAll(processes); 71 | listeners.forEach(DBBlocksJournalListener::dbBlocksJournalDidAddProcesses); 72 | } 73 | 74 | private void closeProcesses() { 75 | List openedProcesses = this.processes.stream() 76 | .filter(DBBlocksJournalProcess::isOpened) 77 | .collect(Collectors.toList()); 78 | if (!openedProcesses.isEmpty()) { 79 | openedProcesses.forEach(DBBlocksJournalProcess::close); 80 | listeners.forEach(listener -> listener.dbBlocksJournalDidCloseProcesses(openedProcesses)); 81 | listeners.forEach(DBBlocksJournalListener::dbBlocksJournalDidCloseAllProcesses); 82 | } 83 | } 84 | 85 | public void clear() { 86 | processes.clear(); 87 | } 88 | 89 | public boolean isEmpty() { 90 | return processes.isEmpty(); 91 | } 92 | 93 | public void addListener(DBBlocksJournalListener listener) { 94 | listeners.add(listener); 95 | } 96 | 97 | public void removeListener(DBBlocksJournalListener listener) { 98 | listeners.remove(listener); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBBlocksJournalListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.List; 19 | 20 | public interface DBBlocksJournalListener { 21 | 22 | void dbBlocksJournalDidAddProcesses(); 23 | 24 | void dbBlocksJournalDidCloseAllProcesses(); 25 | 26 | void dbBlocksJournalDidCloseProcesses(List processes); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBBlocksJournalProcess.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.Collections; 19 | import java.util.Date; 20 | import java.util.List; 21 | import java.util.Objects; 22 | import java.util.Set; 23 | import java.util.stream.Collectors; 24 | 25 | import ru.taximaxim.treeviewer.models.IObject; 26 | 27 | public class DBBlocksJournalProcess implements IObject { 28 | 29 | private final DBProcess process; 30 | 31 | private Date createDate; 32 | private Date closeDate; 33 | 34 | public DBBlocksJournalProcess(DBProcess process) { 35 | this.createDate = new Date(); 36 | this.process = process; 37 | } 38 | 39 | public DBBlocksJournalProcess(Date createDate, Date closeDate, DBProcess process) { 40 | this.createDate = createDate; 41 | this.closeDate = closeDate; 42 | this.process = process; 43 | } 44 | 45 | public DBProcess getProcess() { 46 | return process; 47 | } 48 | 49 | public boolean isClosed() { 50 | return closeDate != null; 51 | } 52 | 53 | public boolean isOpened() { 54 | return closeDate == null; 55 | } 56 | 57 | public void close() { 58 | if (!isClosed()) { 59 | closeDate = new Date(); 60 | } 61 | } 62 | 63 | public Date getCreateDate() { 64 | return createDate; 65 | } 66 | 67 | public Date getCloseDate() { 68 | return closeDate; 69 | } 70 | 71 | @Override 72 | public boolean equals(Object o) { 73 | if (this == o) { 74 | return true; 75 | } 76 | if (!(o instanceof DBBlocksJournalProcess)) { 77 | return false; 78 | } 79 | 80 | DBBlocksJournalProcess other = (DBBlocksJournalProcess) o; 81 | 82 | Date xactStart = process.getQuery().getXactStart(); 83 | Date otherXactStart = other.getProcess().getQuery().getXactStart(); 84 | 85 | boolean isSameXactStart = Objects.equals(xactStart, otherXactStart); 86 | boolean isSamePid = process.getPid() == other.getProcess().getPid(); 87 | 88 | return isSameXactStart && isSamePid && childrenEquals(process.getChildren(), other.getProcess().getChildren()); 89 | } 90 | 91 | private boolean childrenEquals(List processes, List other) { 92 | if (processes.size() != other.size()) { 93 | return false; 94 | } 95 | Set processChildrenPids = processes.stream().map(DBProcess::getPid).collect(Collectors.toSet()); 96 | Set otherProcessChildrenPids = other.stream().map(DBProcess::getPid).collect(Collectors.toSet()); 97 | if (!processChildrenPids.equals(otherProcessChildrenPids)) { 98 | return false; 99 | } 100 | for (DBProcess process : processes) { 101 | DBProcess sameProcess = other.stream().filter(p -> p.getPid() == process.getPid()).collect(Collectors.toList()).get(0); 102 | Date xactStart = process.getQuery().getXactStart(); 103 | Date otherXactStart = sameProcess.getQuery().getXactStart(); 104 | if (!(xactStart != null ? xactStart.equals(otherXactStart) : otherXactStart == null)) { 105 | return false; 106 | } 107 | if (!childrenEquals(process.getChildren(), sameProcess.getChildren())) { 108 | return false; 109 | } 110 | } 111 | return true; 112 | } 113 | 114 | @Override 115 | public int hashCode() { 116 | Date xactStart = process.getQuery().getXactStart(); 117 | int result = 31 * process.getPid(); 118 | result = 31 * result + (xactStart != null ? xactStart.hashCode() : 0); 119 | result = childrenHashCode(result, process.getChildren()); 120 | return result; 121 | } 122 | 123 | private int childrenHashCode(int hashCode, List children) { 124 | int result = hashCode; 125 | for (DBProcess process : children) { 126 | result = 31 * result + process.getPid(); 127 | result = childrenHashCode(result, process.getChildren()); 128 | } 129 | return result; 130 | } 131 | 132 | @Override 133 | public List getChildren() { 134 | return Collections.emptyList(); 135 | } 136 | 137 | @Override 138 | public boolean hasChildren() { 139 | return false; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBModel.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.Objects; 19 | 20 | public class DBModel { 21 | 22 | private final String name; 23 | private final String host; 24 | private final String port; 25 | private final String databaseName; 26 | private final String dbGroup; 27 | private final String user; 28 | private final String password; 29 | private final boolean readBackendType; 30 | private final boolean enabled; 31 | 32 | public DBModel(String name, String host, String port, String databaseName, String dbGroup, 33 | String user, String password, boolean readBackendType, boolean enabled) { 34 | this.name = name; 35 | this.host = host; 36 | this.port = port; 37 | this.databaseName = databaseName; 38 | this.user = user; 39 | this.password = password; 40 | this.readBackendType = readBackendType; 41 | this.enabled = enabled; 42 | this.dbGroup = dbGroup; 43 | } 44 | 45 | public String getName() { 46 | return name; 47 | } 48 | 49 | public String getHost() { 50 | return host; 51 | } 52 | 53 | public String getPort() { 54 | return port; 55 | } 56 | 57 | public boolean isReadBackendType() { 58 | return readBackendType; 59 | } 60 | 61 | public String getDatabaseName() { 62 | return databaseName; 63 | } 64 | 65 | public String getDbGroup() { 66 | return dbGroup; 67 | } 68 | 69 | public String getUser() { 70 | return user; 71 | } 72 | 73 | public String getPassword() { 74 | return password; 75 | } 76 | 77 | public boolean hasPassword() { 78 | return !password.isEmpty(); 79 | } 80 | 81 | public boolean isEnabled() { 82 | return enabled; 83 | } 84 | 85 | public DBModel copy() { 86 | return new DBModel(this.name, this.host, this.port, this.databaseName, this.dbGroup, 87 | this.user, this.password, this.readBackendType, this.enabled); 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return "DBModel{" + 93 | "name='" + name + '\'' + 94 | ", host='" + host + '\'' + 95 | ", port='" + port + '\'' + 96 | ", databaseName='" + databaseName + '\'' + 97 | ", dbGroup='" + dbGroup + '\'' + 98 | ", user='" + user + '\'' + 99 | ", password='" + password + '\'' + 100 | ", readBackendType='" + readBackendType + '\'' + 101 | ", enabled=" + enabled + 102 | '}'; 103 | } 104 | 105 | 106 | @Override 107 | public boolean equals(Object obj) { 108 | if (this == obj) { 109 | return true; 110 | } 111 | 112 | if (!(obj instanceof DBModel)) { 113 | return false; 114 | } 115 | 116 | DBModel other = (DBModel) obj; 117 | return Objects.equals(databaseName, other.databaseName) 118 | && Objects.equals(dbGroup, other.dbGroup) 119 | && enabled == other.enabled 120 | && Objects.equals(host, other.host) 121 | && Objects.equals(name, other.name) 122 | && Objects.equals(password, other.password) 123 | && Objects.equals(port, other.port) 124 | && readBackendType == other.readBackendType 125 | && Objects.equals(user, other.user); 126 | } 127 | 128 | @Override 129 | public int hashCode() { 130 | return Objects.hash(databaseName, dbGroup, enabled, host, name, password, port, readBackendType, user); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBProcess.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Date; 20 | import java.util.HashSet; 21 | import java.util.List; 22 | import java.util.Set; 23 | import java.util.stream.Collectors; 24 | 25 | import ru.taximaxim.treeviewer.models.IObject; 26 | 27 | public class DBProcess implements IObject { 28 | 29 | private List parents = new ArrayList<>(); 30 | private List children = new ArrayList<>(); 31 | 32 | private final Set blocks = new HashSet<>(); 33 | 34 | private final int pid; 35 | private final String state; 36 | private final String backendType; 37 | private final Date stateChange; //изменено 38 | private final DBProcessQuery query; 39 | private final DBProcessQueryCaller queryCaller; 40 | 41 | private DBProcessStatus status = DBProcessStatus.WORKING; 42 | 43 | public DBProcess(int pid, String backendType, DBProcessQueryCaller queryCaller, String state, Date stateChange, DBProcessQuery query) { 44 | this.pid = pid; 45 | this.backendType = backendType; 46 | this.queryCaller = queryCaller; 47 | this.state = state; 48 | this.stateChange = stateChange; 49 | this.query = query; 50 | } 51 | 52 | public void addBlock(DBBlock block) { 53 | blocks.add(block); 54 | } 55 | 56 | public List getParents() { 57 | return parents; 58 | } 59 | 60 | public void addChild(DBProcess process) { 61 | children.add(process); 62 | } 63 | 64 | public void addParent(DBProcess parentProcess) { 65 | this.parents.add(parentProcess); 66 | } 67 | 68 | public boolean hasParent() { 69 | return !parents.isEmpty(); 70 | } 71 | 72 | public DBProcessStatus getStatus() { 73 | return status; 74 | } 75 | 76 | public void setStatus(DBProcessStatus status) { 77 | this.status = status; 78 | } 79 | 80 | public Set getBlocks() { 81 | return blocks; 82 | } 83 | 84 | public String getBlocksPidsString() { 85 | return blocks.stream() 86 | .map(b -> String.valueOf(b.getBlockingPid())) 87 | .collect(Collectors.joining(",")); 88 | } 89 | 90 | public String getBlocksLocktypesString() { 91 | return blocks.stream() 92 | .map(DBBlock::getLocktype) 93 | .distinct() 94 | .collect(Collectors.joining(",")); 95 | } 96 | 97 | public String getBlocksRelationsString() { 98 | return blocks.stream() 99 | .map(DBBlock::getRelation) 100 | .filter(r -> r != null && !r.isEmpty()) 101 | .distinct() 102 | .collect(Collectors.joining(",")); 103 | } 104 | 105 | public int getPid() { 106 | return pid; 107 | } 108 | 109 | public String getBackendType() { 110 | return backendType; 111 | } 112 | 113 | public String getState() { 114 | return state; 115 | } 116 | 117 | public Date getStateChange() { 118 | return stateChange; 119 | } 120 | 121 | public DBProcessQuery getQuery() { 122 | return query; 123 | } 124 | 125 | public DBProcessQueryCaller getQueryCaller() { 126 | return queryCaller; 127 | } 128 | 129 | @Override 130 | public List getChildren() { 131 | return children; 132 | } 133 | 134 | @Override 135 | public boolean hasChildren() { 136 | return !children.isEmpty(); 137 | } 138 | 139 | @Override 140 | public boolean equals(Object o) { 141 | if (this == o) return true; 142 | if (!(o instanceof DBProcess)) return false; 143 | 144 | return pid == ((DBProcess) o).pid; 145 | } 146 | 147 | @Override 148 | public int hashCode() { 149 | return pid; 150 | } 151 | 152 | @Override 153 | public String toString() { 154 | return "DBProcess{" + 155 | "parents=" + parents + 156 | ", children=" + children.size() + 157 | ", blocks=" + blocks + 158 | ", pid=" + pid + 159 | ", state='" + state + '\'' + 160 | ", stateChange=" + stateChange + 161 | ", query=" + query + 162 | ", queryCaller=" + queryCaller + 163 | ", status=" + status + 164 | '}'; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBProcessQuery.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.Date; 19 | import java.util.Objects; 20 | 21 | /** 22 | * Когда DBProcessQuery создается внутри десериализации resultSet, невозможно 23 | * создать Duration. В связи с этим передается таймстамп, на основании которого вычисляется 24 | * длительность процесса. 25 | *
26 | * Длительность процесса вычисляется на момент получения результат запроса. 27 | */ 28 | public class DBProcessQuery { 29 | 30 | private final String queryString; 31 | private final String queryFirstLine; 32 | private final boolean slowQuery; 33 | private final Date backendStart; //время подключения к серверу 34 | private final Date queryStart; //старт запроса 35 | private final Date xactStart; //старт транзакции 36 | private final String duration; //длительность запроса 37 | 38 | public DBProcessQuery(String queryString, boolean slowQuery, Date backendStart, 39 | Date queryStart, Date xactStart, String duration) { 40 | this.queryString = queryString == null ? "" : queryString; 41 | int indexOfNewLine = this.queryString.indexOf('\n'); 42 | String substring = this.queryString.substring(0, indexOfNewLine >= 0 ? indexOfNewLine : this.queryString.length()); 43 | this.queryFirstLine = indexOfNewLine >= 0 ? substring + " ..." : substring; 44 | 45 | this.slowQuery = slowQuery; 46 | this.backendStart = backendStart; 47 | this.queryStart = queryStart; 48 | this.xactStart = xactStart; 49 | this.duration = duration; 50 | } 51 | 52 | public String getQueryString() { 53 | return queryString; 54 | } 55 | 56 | public String getQueryFirstLine() { 57 | return queryFirstLine; 58 | } 59 | 60 | public boolean isSlowQuery() { 61 | return slowQuery; 62 | } 63 | 64 | public Date getBackendStart() { 65 | return backendStart; 66 | } 67 | 68 | public Date getQueryStart() { 69 | return queryStart; 70 | } 71 | 72 | public Date getXactStart() { 73 | return xactStart; 74 | } 75 | 76 | public String getDuration() { 77 | return duration; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "DBProcessQuery{" + 83 | "queryString='" + queryString + '\'' + 84 | ", slowQuery=" + slowQuery + 85 | ", backendStart='" + backendStart + '\'' + 86 | ", queryStart='" + queryStart + '\'' + 87 | ", xactStart='" + xactStart + '\'' + 88 | '}'; 89 | } 90 | 91 | @Override 92 | public boolean equals(Object o) { 93 | if (this == o) { 94 | return true; 95 | } 96 | if (!(o instanceof DBProcessQuery)) { 97 | return false; 98 | } 99 | 100 | DBProcessQuery that = (DBProcessQuery) o; 101 | return Objects.equals(queryString, that.queryString) 102 | && slowQuery == that.slowQuery 103 | && Objects.equals(backendStart, that.backendStart) 104 | && Objects.equals(queryStart, that.queryStart) 105 | && Objects.equals(xactStart, that.xactStart); 106 | } 107 | 108 | @Override 109 | public int hashCode() { 110 | return Objects.hash(queryString, slowQuery, backendStart, queryStart, xactStart); 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBProcessQueryCaller.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import java.util.Objects; 19 | 20 | public class DBProcessQueryCaller { 21 | 22 | private final String applicationName; 23 | private final String databaseName; 24 | private final String userName; 25 | private final String client; 26 | 27 | public DBProcessQueryCaller(String applicationName, String databaseName, String userName, String client) { 28 | this.applicationName = applicationName == null ? "" : applicationName; 29 | this.databaseName = databaseName == null ? "" : databaseName; 30 | this.userName = userName == null ? "" : userName; 31 | this.client = client == null ? "" : client; 32 | } 33 | 34 | public String getApplicationName() { 35 | return applicationName; 36 | } 37 | 38 | public String getDatabaseName() { 39 | return databaseName; 40 | } 41 | 42 | public String getUserName() { 43 | return userName; 44 | } 45 | 46 | public String getClient() { 47 | return client; 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return "DBProcessQueryCaller{" + 53 | "applicationName='" + applicationName + '\'' + 54 | ", databaseName='" + databaseName + '\'' + 55 | ", userName='" + userName + '\'' + 56 | ", client='" + client + '\'' + 57 | '}'; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object o) { 62 | if (this == o) { 63 | return true; 64 | } 65 | if (!(o instanceof DBProcessQueryCaller)) { 66 | return false; 67 | } 68 | 69 | DBProcessQueryCaller that = (DBProcessQueryCaller) o; 70 | return Objects.equals(applicationName, that.applicationName) 71 | && Objects.equals(databaseName, that.databaseName) 72 | && Objects.equals(userName, that.userName) 73 | && Objects.equals(client, that.client); 74 | } 75 | 76 | @Override 77 | public int hashCode() { 78 | return Objects.hash(applicationName, databaseName, userName, client); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/models/DBProcessStatus.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import ru.taximaxim.pgsqlblocks.utils.Images; 19 | 20 | public enum DBProcessStatus { 21 | WORKING("Working"), 22 | BLOCKING("Blocking"), 23 | BLOCKED("Blocked"); 24 | 25 | private final String descr; 26 | 27 | DBProcessStatus(String descr) { 28 | this.descr = descr; 29 | } 30 | 31 | public String getDescr() { 32 | return descr; 33 | } 34 | 35 | public static DBProcessStatus getInstanceForDescr(String descr) { 36 | if (descr == null || descr.isEmpty()) { 37 | return WORKING; 38 | } 39 | 40 | switch (descr) { 41 | case "Working": 42 | return WORKING; 43 | case "Blocking": 44 | return BLOCKING; 45 | case "Blocked": 46 | return BLOCKED; 47 | default: 48 | return WORKING; 49 | } 50 | } 51 | 52 | /** 53 | * Получение иконки в зависимости от состояния 54 | */ 55 | public Images getStatusImage() { 56 | switch(this) { 57 | case BLOCKING: 58 | return Images.PROC_BLOCKING; 59 | case BLOCKED: 60 | return Images.PROC_BLOCKED; 61 | default: 62 | return Images.PROC_WORKING; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBBlocksJournalViewDataSource.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.ResourceBundle; 21 | 22 | import org.eclipse.swt.graphics.Image; 23 | 24 | import ru.taximaxim.pgsqlblocks.common.models.DBBlocksJournalProcess; 25 | import ru.taximaxim.pgsqlblocks.common.models.DBProcess; 26 | import ru.taximaxim.pgsqlblocks.utils.Columns; 27 | import ru.taximaxim.pgsqlblocks.utils.DateUtils; 28 | 29 | // FIXME seems wrong to inherit from DBProcessesViewDataSource which is DBProcess-related 30 | public class DBBlocksJournalViewDataSource extends DBProcessesViewDataSource { 31 | 32 | private final boolean isModeProcLimit; 33 | private int limitBlocks; 34 | 35 | public void setLimitBlocks(int limitBlocks) { 36 | this.limitBlocks = limitBlocks; 37 | } 38 | 39 | public DBBlocksJournalViewDataSource(ResourceBundle resourceBundle, boolean isModeProcLimit) { 40 | super(resourceBundle); 41 | this.isModeProcLimit = isModeProcLimit; 42 | } 43 | 44 | @Override 45 | public List getColumns() { 46 | return Arrays.asList(Columns.values()); 47 | } 48 | 49 | @Override 50 | public Image getColumnImage(Object element, int columnIndex) { 51 | if (element instanceof DBBlocksJournalProcess) { 52 | DBBlocksJournalProcess process = (DBBlocksJournalProcess) element; 53 | return super.getColumnImage(process.getProcess(), columnIndex); 54 | } 55 | 56 | return super.getColumnImage(element, columnIndex); 57 | } 58 | 59 | @Override 60 | public String getRowText(Object element, Columns column) { 61 | if (element instanceof DBBlocksJournalProcess) { 62 | DBBlocksJournalProcess process = (DBBlocksJournalProcess) element; 63 | switch (column) { 64 | case BLOCK_CREATE_DATE: 65 | return DateUtils.dateToString(process.getCreateDate()); 66 | case BLOCK_END_DATE: 67 | return DateUtils.dateToString(process.getCloseDate()); 68 | default: 69 | return super.getRowText(process.getProcess(), column); 70 | } 71 | } 72 | 73 | return super.getRowText(element, column); 74 | } 75 | 76 | @Override 77 | public Object[] getElements(Object inputElement) { 78 | List input = (List) inputElement; 79 | if (isModeProcLimit && limitBlocks != 0 && input.size() > limitBlocks) { 80 | return input.subList(input.size() - limitBlocks, input.size()).toArray(); 81 | } 82 | 83 | return input.toArray(); 84 | } 85 | 86 | @Override 87 | public Object[] getChildren(Object parentElement) { 88 | if (parentElement instanceof DBBlocksJournalProcess) { 89 | return ((DBBlocksJournalProcess) parentElement).getProcess().getChildren().toArray(); 90 | } 91 | 92 | return ((DBProcess) parentElement).getChildren().toArray(); 93 | } 94 | 95 | @Override 96 | public boolean hasChildren(Object element) { 97 | if (element instanceof DBProcess) { 98 | return ((DBProcess) element).hasChildren(); 99 | } 100 | 101 | return element instanceof DBBlocksJournalProcess; 102 | } 103 | 104 | @Override 105 | public int compare(Object e1, Object e2, Columns column) { 106 | if (e1 instanceof DBBlocksJournalProcess) { 107 | DBBlocksJournalProcess process1 = (DBBlocksJournalProcess) e1; 108 | DBBlocksJournalProcess process2 = (DBBlocksJournalProcess) e2; 109 | switch (column) { 110 | case BLOCK_CREATE_DATE: 111 | return DateUtils.compareDates(process1.getCreateDate(), process2.getCreateDate()); 112 | case BLOCK_END_DATE: 113 | return DateUtils.compareDates(process1.getCloseDate(), process2.getCloseDate()); 114 | default: 115 | return super.compare(process1.getProcess(), process2.getProcess(), column); 116 | } 117 | } 118 | 119 | return super.compare(e1, e2, column); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBModelsViewContentProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import java.util.ArrayList; 19 | import java.util.LinkedHashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.ResourceBundle; 23 | 24 | import org.eclipse.jface.viewers.ITreeContentProvider; 25 | 26 | import ru.taximaxim.pgsqlblocks.modules.db.controller.DBController; 27 | 28 | public class DBModelsViewContentProvider implements ITreeContentProvider { 29 | 30 | private final String DEFAULT_DB_GROUP = "default_db_group"; 31 | 32 | private final ResourceBundle bundle; 33 | private final Map> map = new LinkedHashMap<>(); 34 | 35 | public DBModelsViewContentProvider(ResourceBundle bundle) { 36 | this.bundle = bundle; 37 | } 38 | 39 | @Override 40 | public Object[] getElements(Object inputElement) { 41 | map.clear(); 42 | if (inputElement instanceof List) { 43 | for (Object o : (List) inputElement) { 44 | DBController el = (DBController) o; 45 | map.computeIfAbsent(getDbGroup(el), e -> new ArrayList<>()).add(el); 46 | } 47 | if (hasntGroup()) { 48 | return map.get(bundle.getString(DEFAULT_DB_GROUP)).toArray(); 49 | } 50 | } 51 | return map.keySet().toArray(); 52 | } 53 | 54 | @Override 55 | public Object[] getChildren(Object parentElement) { 56 | if (parentElement instanceof String) { 57 | return map.get(parentElement).toArray(); 58 | } 59 | return null; 60 | } 61 | 62 | @Override 63 | public Object getParent(Object element) { 64 | if (hasntGroup()) { 65 | return null; 66 | } 67 | if (element instanceof DBController) { 68 | return getDbGroup((DBController) element); 69 | } 70 | return null; 71 | } 72 | 73 | @Override 74 | public boolean hasChildren(Object element) { 75 | return element instanceof String; 76 | } 77 | 78 | private boolean hasntGroup() { 79 | return map.size() == 1 && map.containsKey(bundle.getString(DEFAULT_DB_GROUP)); 80 | } 81 | 82 | private String getDbGroup(DBController controller) { 83 | String dbGroup = controller.getModelDbGroup(); 84 | return dbGroup.isEmpty() ? bundle.getString(DEFAULT_DB_GROUP) : dbGroup; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBModelsViewLabelProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import java.util.ResourceBundle; 19 | 20 | import org.eclipse.swt.graphics.Image; 21 | 22 | import ru.taximaxim.pgsqlblocks.modules.db.controller.DBController; 23 | import ru.taximaxim.pgsqlblocks.utils.ImageUtils; 24 | import ru.taximaxim.pgsqlblocks.utils.Images; 25 | 26 | public class DBModelsViewLabelProvider extends TreeLabelProvider { 27 | 28 | private final ResourceBundle bundle; 29 | 30 | public DBModelsViewLabelProvider(ResourceBundle bundle) { 31 | this.bundle = bundle; 32 | } 33 | 34 | @Override 35 | public Image getColumnImage(Object element, int columnIndex) { 36 | if (columnIndex == 0) { 37 | if (element instanceof DBController) { 38 | DBController controller = (DBController) element; 39 | return getImage(controller); 40 | } 41 | return ImageUtils.getImage(Images.GROUP_DATABASE); 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public String getColumnText(Object element, int columnIndex) { 48 | if (element instanceof String) { 49 | switch (columnIndex) { 50 | case 0: 51 | return element.toString(); 52 | case 1: 53 | return ""; 54 | default: 55 | return bundle.getString("undefined"); 56 | } 57 | } 58 | 59 | DBController controller = (DBController) element; 60 | switch (columnIndex) { 61 | case 0: 62 | return controller.getModelName(); 63 | case 1: 64 | return String.valueOf(controller.getProcessesCount()); 65 | default: 66 | return bundle.getString("undefined"); 67 | } 68 | } 69 | 70 | private Image getImage(DBController controller) { 71 | if (controller.isBlocked()) { 72 | return ImageUtils.getDecoratedBlockedImage(controller.getStatus().getStatusImage()); 73 | } 74 | 75 | return ImageUtils.getImage(controller.getStatus().getStatusImage()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBModelsViewListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import org.eclipse.jface.action.IMenuManager; 19 | 20 | import ru.taximaxim.pgsqlblocks.modules.db.controller.DBController; 21 | 22 | public interface DBModelsViewListener { 23 | 24 | void dbModelsViewDidSelectController(DBController controller); 25 | 26 | void dbModelsViewDidSelectGroup(); 27 | 28 | void dbModelsViewDidCallActionToController(DBController controller); 29 | 30 | void dbModelsViewDidShowMenu(IMenuManager menuManager); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBProcessInfoView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.ResourceBundle; 21 | 22 | import org.eclipse.swt.SWT; 23 | import org.eclipse.swt.layout.GridData; 24 | import org.eclipse.swt.layout.GridLayout; 25 | import org.eclipse.swt.widgets.Button; 26 | import org.eclipse.swt.widgets.Composite; 27 | import org.eclipse.swt.widgets.Text; 28 | 29 | import ru.taximaxim.pgsqlblocks.common.models.DBProcess; 30 | 31 | public class DBProcessInfoView extends Composite { 32 | 33 | private final ResourceBundle resourceBundle; 34 | 35 | private final List listeners = new ArrayList<>(); 36 | 37 | private Composite buttonBar; 38 | private Text processInfoText; 39 | private GridData layoutData; 40 | 41 | public DBProcessInfoView(ResourceBundle resourceBundle, Composite parent, int style) { 42 | super(parent, style); 43 | this.resourceBundle = resourceBundle; 44 | GridLayout layout = new GridLayout(); 45 | layout.marginWidth = 0; 46 | layout.marginHeight = 0; 47 | layoutData = new GridData(SWT.FILL, SWT.BOTTOM, true, false); 48 | setLayout(layout); 49 | setLayoutData(layoutData); 50 | createContent(); 51 | } 52 | 53 | private void createContent() { 54 | buttonBar = new Composite(this, SWT.NULL); 55 | GridLayout layout = new GridLayout(2, false); 56 | GridData btnData = new GridData(SWT.FILL, SWT.TOP, true, false); 57 | buttonBar.setLayout(layout); 58 | buttonBar.setLayoutData(btnData); 59 | 60 | Button cancelProcessButton = new Button(buttonBar, SWT.PUSH); 61 | cancelProcessButton.setText(resourceBundle.getString("cancel_process")); 62 | cancelProcessButton.setToolTipText("pg_cancel_backend"); 63 | cancelProcessButton.addListener(SWT.Selection, event -> { 64 | listeners.forEach(DBProcessInfoViewListener::dbProcessInfoViewCancelProcessButtonClicked); 65 | }); 66 | 67 | Button terminateProcessButton = new Button(buttonBar, SWT.PUSH); 68 | terminateProcessButton.setText(resourceBundle.getString("kill_process")); 69 | terminateProcessButton.setToolTipText("pg_terminate_backend"); 70 | terminateProcessButton.addListener(SWT.Selection, event -> { 71 | listeners.forEach(DBProcessInfoViewListener::dbProcessInfoViewTerminateProcessButtonClicked); 72 | }); 73 | 74 | processInfoText = new Text(this, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL); 75 | GridData textLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 76 | textLayoutData.heightHint = 200; 77 | processInfoText.setLayoutData(textLayoutData); 78 | } 79 | 80 | public void hideToolBar() { 81 | this.buttonBar.setVisible(false); 82 | GridData layoutData = (GridData) this.buttonBar.getLayoutData(); 83 | layoutData.exclude = true; 84 | this.layout(); 85 | } 86 | 87 | public void show(DBProcess process) { 88 | StringBuilder stringBuilder = new StringBuilder(); 89 | stringBuilder.append(String.format("%s = %d\n", 90 | resourceBundle.getString("pid"), process.getPid())); 91 | stringBuilder.append(String.format("%s = %s\n", 92 | resourceBundle.getString("user_name"), process.getQueryCaller().getUserName())); 93 | stringBuilder.append(String.format("%s = %s\n", 94 | resourceBundle.getString("db_name"), process.getQueryCaller().getDatabaseName())); 95 | stringBuilder.append(String.format("\n%s:\n%s\n", 96 | resourceBundle.getString("query"), process.getQuery().getQueryString())); 97 | 98 | processInfoText.setText(stringBuilder.toString()); 99 | this.setVisible(true); 100 | GridData layoutData = this.layoutData; 101 | layoutData.exclude = false; 102 | this.getParent().layout(); 103 | } 104 | 105 | public void hide() { 106 | processInfoText.setText(""); 107 | this.setVisible(false); 108 | GridData layoutData = this.layoutData; 109 | layoutData.exclude = true; 110 | this.getParent().layout(); 111 | } 112 | 113 | public void addListener(DBProcessInfoViewListener listener) { 114 | listeners.add(listener); 115 | } 116 | 117 | public void removeListener(DBProcessInfoViewListener listener) { 118 | listeners.remove(listener); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/DBProcessInfoViewListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | public interface DBProcessInfoViewListener { 19 | 20 | void dbProcessInfoViewTerminateProcessButtonClicked(); 21 | 22 | void dbProcessInfoViewCancelProcessButtonClicked(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/common/ui/TreeLabelProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.ui; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import org.eclipse.jface.viewers.ILabelProviderListener; 22 | import org.eclipse.jface.viewers.ITableLabelProvider; 23 | 24 | public abstract class TreeLabelProvider implements ITableLabelProvider { 25 | // The listeners 26 | private List listeners; 27 | 28 | /** 29 | * Constructs a FileTreeLabelProvider 30 | */ 31 | protected TreeLabelProvider() { 32 | listeners = new ArrayList<>(); 33 | } 34 | 35 | @Override 36 | public void addListener(ILabelProviderListener listener) { 37 | listeners.add(listener); 38 | } 39 | 40 | @Override 41 | public void dispose() { 42 | // TODO Auto-generated method stub 43 | } 44 | 45 | @Override 46 | public boolean isLabelProperty(Object element, String property) { 47 | return false; 48 | } 49 | 50 | @Override 51 | public void removeListener(ILabelProviderListener listener) { 52 | // TODO Auto-generated method stub 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/dialogs/EditDatabaseDialog.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.dialogs; 17 | 18 | import java.util.List; 19 | import java.util.ResourceBundle; 20 | import java.util.Set; 21 | 22 | import org.eclipse.swt.widgets.Composite; 23 | import org.eclipse.swt.widgets.Control; 24 | import org.eclipse.swt.widgets.Shell; 25 | 26 | import ru.taximaxim.pgsqlblocks.common.models.DBModel; 27 | 28 | public class EditDatabaseDialog extends AddDatabaseDialog { 29 | 30 | private final DBModel editedModel; 31 | 32 | public EditDatabaseDialog(ResourceBundle resourceBundle, Shell shell, 33 | List reservedConnectionNames, Set dbGroupNames, DBModel editedModel) { 34 | super(resourceBundle, dbGroupNames, shell, reservedConnectionNames); 35 | reservedConnectionNames.remove(editedModel.getName()); 36 | this.editedModel = editedModel; 37 | } 38 | 39 | @Override 40 | protected void configureShell(Shell newShell) { 41 | super.configureShell(newShell); 42 | newShell.setText(resourceBundle.getString("edit_connection")); 43 | } 44 | 45 | public DBModel getEditedModel() { 46 | return editedModel; 47 | } 48 | 49 | @Override 50 | protected Control createDialogArea(Composite parent) { 51 | Control dialogArea = super.createDialogArea(parent); 52 | nameText.setText(editedModel.getName()); 53 | hostText.setText(editedModel.getHost()); 54 | portText.setText(editedModel.getPort()); 55 | databaseNameText.setText(editedModel.getDatabaseName()); 56 | cmdDbGroup.getCombo().setText(editedModel.getDbGroup()); 57 | userText.setText(editedModel.getUser()); 58 | passwordText.setText(editedModel.getPassword()); 59 | readBackendTypeButton.setSelection(editedModel.isReadBackendType()); 60 | enabledButton.setSelection(editedModel.isEnabled()); 61 | return dialogArea; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/dialogs/PasswordDialog.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.dialogs; 17 | 18 | import java.text.MessageFormat; 19 | import java.util.ResourceBundle; 20 | 21 | import org.eclipse.jface.dialogs.Dialog; 22 | import org.eclipse.swt.SWT; 23 | import org.eclipse.swt.layout.GridData; 24 | import org.eclipse.swt.layout.GridLayout; 25 | import org.eclipse.swt.widgets.Composite; 26 | import org.eclipse.swt.widgets.Control; 27 | import org.eclipse.swt.widgets.Label; 28 | import org.eclipse.swt.widgets.Shell; 29 | import org.eclipse.swt.widgets.Text; 30 | 31 | import ru.taximaxim.pgsqlblocks.common.models.DBModel; 32 | 33 | /** 34 | * Dialog for password for database 35 | */ 36 | public class PasswordDialog extends Dialog { 37 | private ResourceBundle resourceBundle; 38 | private String password; 39 | private Text passwordText; 40 | private DBModel model; 41 | 42 | public PasswordDialog(ResourceBundle resourceBundle, Shell shell, DBModel model) { 43 | super(shell); 44 | this.resourceBundle = resourceBundle; 45 | this.model = model; 46 | } 47 | 48 | @Override 49 | protected void configureShell(Shell newShell) { 50 | super.configureShell(newShell); 51 | newShell.setText(resourceBundle.getString("attention")); 52 | } 53 | 54 | @Override 55 | protected Control createDialogArea(Composite parent) { 56 | Composite container = (Composite) super.createDialogArea(parent); 57 | 58 | GridLayout layout = new GridLayout(1, false); 59 | layout.marginRight = 5; 60 | layout.marginLeft = 10; 61 | layout.marginTop = 10; 62 | container.setLayout(layout); 63 | container.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false)); 64 | 65 | String builder = MessageFormat.format("{0} {1}\n {2}:{3}/{4}?user={5}", resourceBundle.getString("type_password_for"), 66 | model.getName(), model.getHost(), model.getPort(), model.getDatabaseName(), model.getUser()); 67 | Label answerLabel = new Label(container, SWT.HORIZONTAL); 68 | answerLabel.setText(builder); 69 | passwordText = new Text(container, SWT.BORDER); 70 | passwordText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); 71 | passwordText.setEchoChar('•'); 72 | return container; 73 | } 74 | 75 | @Override 76 | protected void okPressed() { 77 | password = passwordText.getText(); 78 | super.okPressed(); 79 | } 80 | 81 | public String getPassword() { 82 | return password; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/l10n/PgSqlBlocks.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.l10n; 17 | 18 | public class PgSqlBlocks extends PgSqlBlocks_ru { 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/application/controller/ApplicationController.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.application.controller; 17 | 18 | import org.eclipse.swt.SWT; 19 | 20 | import ru.taximaxim.pgsqlblocks.modules.application.view.ApplicationView; 21 | import ru.taximaxim.pgsqlblocks.modules.application.view.ApplicationViewListener; 22 | import ru.taximaxim.pgsqlblocks.modules.logs.view.LogsView; 23 | import ru.taximaxim.pgsqlblocks.modules.processes.controller.ProcessesController; 24 | import ru.taximaxim.pgsqlblocks.modules.processes.view.ProcessesView; 25 | import ru.taximaxim.pgsqlblocks.utils.Settings; 26 | 27 | public class ApplicationController implements ApplicationViewListener { 28 | 29 | private final ApplicationView applicationView; 30 | 31 | private final Settings settings = Settings.getInstance(); 32 | 33 | private ProcessesController processesController; 34 | 35 | public ApplicationController() { 36 | applicationView = new ApplicationView(settings); 37 | applicationView.setListener(this); 38 | } 39 | 40 | public void launch() { 41 | applicationView.show(); 42 | } 43 | 44 | @Override 45 | public void applicationViewDidLoad() { 46 | new LogsView(applicationView.getBottomPanelComposite(), SWT.NONE); 47 | processesController = new ProcessesController(settings); 48 | ProcessesView processesView = new ProcessesView(applicationView.getTopPanelComposite(), SWT.NONE); 49 | processesController.setView(processesView); 50 | processesController.load(); 51 | } 52 | 53 | public ApplicationView getApplicationView() { 54 | return applicationView; 55 | } 56 | 57 | @Override 58 | public void applicationViewWillDisappear() { 59 | processesController.close(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/application/view/ApplicationViewListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.application.view; 17 | 18 | public interface ApplicationViewListener { 19 | void applicationViewDidLoad(); 20 | void applicationViewWillDisappear(); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/blocksjournal/view/BlocksJournalFilesContentProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.blocksjournal.view; 17 | 18 | import java.util.List; 19 | 20 | import org.eclipse.jface.viewers.IStructuredContentProvider; 21 | import org.eclipse.jface.viewers.Viewer; 22 | 23 | public class BlocksJournalFilesContentProvider implements IStructuredContentProvider { 24 | @Override 25 | public Object[] getElements(Object inputElement) { 26 | return ((List) inputElement).toArray(); 27 | } 28 | 29 | @Override 30 | public void dispose() { 31 | 32 | } 33 | 34 | @Override 35 | public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/blocksjournal/view/BlocksJournalFilesLabelProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.blocksjournal.view; 17 | 18 | import java.io.File; 19 | 20 | import org.eclipse.swt.graphics.Image; 21 | 22 | import ru.taximaxim.pgsqlblocks.common.ui.TreeLabelProvider; 23 | 24 | public class BlocksJournalFilesLabelProvider extends TreeLabelProvider { 25 | 26 | @Override 27 | public Image getColumnImage(Object element, int columnIndex) { 28 | return null; 29 | } 30 | 31 | @Override 32 | public String getColumnText(Object element, int columnIndex) { 33 | File file = (File)element; 34 | return file.getName(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/db/controller/DBControllerListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.db.controller; 17 | 18 | import java.sql.SQLException; 19 | 20 | import ru.taximaxim.pgsqlblocks.modules.db.model.DBStatus; 21 | 22 | public interface DBControllerListener { 23 | 24 | void dbControllerStatusChanged(DBController controller, DBStatus newStatus); 25 | 26 | void dbControllerDidConnect(DBController controller); 27 | 28 | void dbControllerWillConnect(DBController controller); 29 | 30 | void dbControllerConnectionFailed(DBController controller, SQLException exception); 31 | 32 | void dbControllerDisconnectFailed(DBController controller, boolean forcedByUser, SQLException exception); 33 | 34 | void dbControllerDidDisconnect(DBController controller, boolean forcedByUser); 35 | 36 | void dbControllerWillUpdateProcesses(DBController controller); 37 | 38 | void dbControllerProcessesUpdated(DBController controller); 39 | 40 | void dbControllerBlockedChanged(DBController controller); 41 | 42 | void dbControllerBlocksJournalChanged(DBController controller); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/db/controller/UserInputPasswordProvider.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.db.controller; 17 | 18 | import ru.taximaxim.pgsqlblocks.utils.UserCancelException; 19 | 20 | public interface UserInputPasswordProvider { 21 | 22 | String getPasswordFromUser(DBController controller) throws UserCancelException; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/db/model/DBStatus.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.db.model; 17 | 18 | import ru.taximaxim.pgsqlblocks.utils.Images; 19 | 20 | public enum DBStatus { 21 | DISABLED, 22 | CONNECTED, 23 | CONNECTION_ERROR, 24 | UPDATE; 25 | 26 | /** 27 | * Получение иконки в зависимости от состояния 28 | */ 29 | public Images getStatusImage() { 30 | switch(this) { 31 | case DISABLED: 32 | return Images.CONN_DISABLED; 33 | case CONNECTED: 34 | return Images.CONN_CONNECTED; 35 | case CONNECTION_ERROR: 36 | return Images.CONN_ERROR; 37 | case UPDATE: 38 | return Images.CONN_UPDATE; 39 | default: 40 | throw new IllegalStateException("Unsupported status: " + this); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/logs/view/LogsView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.logs.view; 17 | 18 | import org.eclipse.swt.SWT; 19 | import org.eclipse.swt.custom.StyledText; 20 | import org.eclipse.swt.custom.StyledTextContent; 21 | import org.eclipse.swt.events.ModifyEvent; 22 | import org.eclipse.swt.events.ModifyListener; 23 | import org.eclipse.swt.layout.GridData; 24 | import org.eclipse.swt.layout.GridLayout; 25 | import org.eclipse.swt.widgets.Composite; 26 | import org.eclipse.swt.widgets.Display; 27 | import org.eclipse.swt.widgets.Listener; 28 | 29 | import ru.taximaxim.pgsqlblocks.ui.UIAppender; 30 | 31 | public class LogsView extends Composite { 32 | 33 | private static final int TEXT_LIMIT = 20000; 34 | private StyledText text; 35 | private StyledTextContent styledTextContent; 36 | private boolean autoScrollEnabled = true; 37 | 38 | private final ModifyListener modifyListener = (ModifyEvent e) -> { 39 | if (text.getText().length() > TEXT_LIMIT) { 40 | styledTextContent.replaceTextRange(0, text.getText().length() - TEXT_LIMIT - 1, ""); 41 | styledTextContent.replaceTextRange(0, styledTextContent.getLine(0).length(), ""); 42 | } 43 | if (autoScrollEnabled) { 44 | text.setTopIndex(text.getLineCount() - 1); 45 | } 46 | }; 47 | 48 | private final Listener logListener = e -> { 49 | Display display = getDisplay(); 50 | if (display != null && !display.isDisposed()) { 51 | display.asyncExec(() -> { 52 | if (text != null && !text.isDisposed()) { 53 | text.append(e.data.toString()); 54 | } 55 | }); 56 | } 57 | }; 58 | 59 | public LogsView(Composite parent, int style) { 60 | super(parent, style); 61 | GridLayout layout = new GridLayout(); 62 | layout.marginTop = 0; 63 | GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 64 | setLayout(layout); 65 | setLayoutData(layoutData); 66 | createContent(); 67 | UIAppender.addListener(logListener); 68 | } 69 | 70 | private void createContent() { 71 | GridLayout layout = new GridLayout(); 72 | layout.marginTop = 0; 73 | text = new StyledText(this, SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); 74 | text.setLayout(layout); 75 | text.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 76 | text.setMargins(3, 3, 3, 3); 77 | text.layout(true); 78 | text.addModifyListener(modifyListener); 79 | 80 | layout(true, true); 81 | 82 | // add empty string on ENTER pressed 83 | text.addTraverseListener(e -> { 84 | if (e.detail == SWT.TRAVERSE_RETURN && !text.isDisposed()) { 85 | text.append("\n"); 86 | text.setTopIndex(text.getLineCount() - 1); 87 | text.setCaretOffset(text.getCharCount() - 1); 88 | } 89 | }); 90 | 91 | // wheel up and down 92 | text.addMouseWheelListener(e -> autoScrollEnabled = e.count <= 0); 93 | styledTextContent = text.getContent(); 94 | } 95 | 96 | @Override 97 | public void dispose() { 98 | UIAppender.removeListener(logListener); 99 | super.dispose(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/modules/processes/view/ProcessesView.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.modules.processes.view; 17 | 18 | import org.eclipse.swt.SWT; 19 | import org.eclipse.swt.custom.SashForm; 20 | import org.eclipse.swt.layout.GridData; 21 | import org.eclipse.swt.layout.GridLayout; 22 | import org.eclipse.swt.widgets.Composite; 23 | import org.eclipse.swt.widgets.ToolBar; 24 | 25 | public class ProcessesView extends Composite { 26 | 27 | private Composite leftPanelComposite; 28 | private Composite rightPanelComposite; 29 | 30 | private ToolBar toolBar; 31 | 32 | public ProcessesView(Composite parentComposite, int style) { 33 | super(parentComposite, style); 34 | GridLayout layout = new GridLayout(); 35 | layout.marginHeight = 0; 36 | layout.marginWidth = 0; 37 | GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 38 | setLayout(layout); 39 | setLayoutData(layoutData); 40 | createContent(); 41 | } 42 | 43 | public ToolBar getToolBar() { 44 | return toolBar; 45 | } 46 | 47 | private void createContent() { 48 | toolBar = new ToolBar(this, SWT.HORIZONTAL); 49 | GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 50 | GridLayout layout = new GridLayout(); 51 | layout.marginWidth = 0; 52 | layout.marginHeight = 0; 53 | SashForm sashForm = new SashForm(this, SWT.HORIZONTAL); 54 | sashForm.SASH_WIDTH = 2; 55 | sashForm.setLayoutData(layoutData); 56 | sashForm.setLayout(layout); 57 | 58 | createLeftPanel(sashForm); 59 | createRightPanel(sashForm); 60 | 61 | sashForm.setSashWidth(2); 62 | sashForm.setWeights(new int[] {15, 85}); 63 | } 64 | 65 | private void createLeftPanel(SashForm sashForm) { 66 | GridLayout layout = new GridLayout(); 67 | layout.marginHeight = 0; 68 | layout.marginWidth = 0; 69 | layout.marginLeft = 5; 70 | GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 71 | leftPanelComposite = new Composite(sashForm, SWT.NONE); 72 | leftPanelComposite.setLayout(layout); 73 | leftPanelComposite.setLayoutData(layoutData); 74 | } 75 | 76 | private void createRightPanel(SashForm sashForm) { 77 | GridLayout layout = new GridLayout(); 78 | layout.marginLeft = 0; 79 | layout.marginHeight = 0; 80 | GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); 81 | rightPanelComposite = new Composite(sashForm, SWT.NONE); 82 | rightPanelComposite.setLayout(layout); 83 | rightPanelComposite.setLayoutData(layoutData); 84 | } 85 | 86 | public Composite getLeftPanelComposite() { 87 | return leftPanelComposite; 88 | } 89 | 90 | public Composite getRightPanelComposite() { 91 | return rightPanelComposite; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/ui/AboutDlg.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.ui; 17 | 18 | import org.eclipse.jface.layout.PixelConverter; 19 | import org.eclipse.swt.SWT; 20 | import org.eclipse.swt.events.SelectionAdapter; 21 | import org.eclipse.swt.events.SelectionEvent; 22 | import org.eclipse.swt.graphics.Image; 23 | import org.eclipse.swt.layout.GridData; 24 | import org.eclipse.swt.layout.GridLayout; 25 | import org.eclipse.swt.program.Program; 26 | import org.eclipse.swt.widgets.Button; 27 | import org.eclipse.swt.widgets.Dialog; 28 | import org.eclipse.swt.widgets.Display; 29 | import org.eclipse.swt.widgets.Label; 30 | import org.eclipse.swt.widgets.Link; 31 | import org.eclipse.swt.widgets.Shell; 32 | 33 | public class AboutDlg extends Dialog { 34 | 35 | private static final String DIALOGTITLE = "О программе"; 36 | private static final String DISTRIB_LINK = "http://pgcodekeeper.org/pgsqlblocks/"; 37 | private static final String HOMEPAGE = "http://pgcodekeeper.org/pgsqlblocks.html"; 38 | private static final String TELEGRAM_LINK = "https://telegram.me/joinchat/Bxn1Zwh02WM96O-55GAryA"; 39 | private static final int OK_BUTTON_HEIGHT_IN_CHARS = 15; 40 | 41 | public AboutDlg(Shell parentShell) { 42 | super(parentShell, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL); 43 | } 44 | 45 | public void open() { 46 | Shell shell = new Shell(getParent(), getStyle()); 47 | shell.setText(DIALOGTITLE); 48 | createContent(shell); 49 | shell.pack(); 50 | shell.open(); 51 | Display display = getParent().getDisplay(); 52 | while (!shell.isDisposed()) { 53 | if (!display.readAndDispatch()) { 54 | display.sleep(); 55 | } 56 | } 57 | } 58 | 59 | private void createContent(Shell container){ 60 | 61 | GridLayout layout = new GridLayout(1, false); 62 | container.setLayout(layout); 63 | 64 | Image logo = new Image(null, 65 | getClass().getClassLoader().getResourceAsStream("images/block-48x48.png")); 66 | Label logoLabel = new Label(container, SWT.HORIZONTAL); 67 | logoLabel.setImage(logo); 68 | 69 | //FIXME localization??? 70 | Label infoLabel = new Label(container, SWT.HORIZONTAL); 71 | infoLabel.setText("pgSqlBlocks - это приложение, \n" 72 | + "которое позволяет легко ориентироваться среди процессов \n" 73 | + "и получать информацию о блокировках и ожидающих запросов.\n"); 74 | 75 | Link distbLink = new Link(container, SWT.HORIZONTAL); 76 | distbLink.setText("Последнюю версию можно скачать по ссылке: \n" 77 | + "" + DISTRIB_LINK + ""); 78 | distbLink.addListener(SWT.Selection, event -> Program.launch(DISTRIB_LINK)); 79 | 80 | Link helpPageLink = new Link(container, SWT.HORIZONTAL); 81 | helpPageLink.setText("Страница продукта: \n" 82 | + "" + HOMEPAGE + ""); 83 | helpPageLink.addListener(SWT.Selection, event -> Program.launch(HOMEPAGE)); 84 | 85 | Link linkFAQ = new Link(container, SWT.HORIZONTAL); 86 | linkFAQ.setText("Свои вопросы можете задать на канале в Телеграмм: \n" 87 | + "" + TELEGRAM_LINK + ""); 88 | linkFAQ.addListener(SWT.Selection, event -> Program.launch(TELEGRAM_LINK)); 89 | 90 | Label copyrightLabel = new Label(container, SWT.HORIZONTAL); 91 | copyrightLabel.setText("© \"Technology\" LLC"); 92 | 93 | Button ok = new Button(container, SWT.PUSH); 94 | ok.setText("OK"); 95 | GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END); 96 | PixelConverter pc = new PixelConverter(getParent()); 97 | data.widthHint = pc.convertWidthInCharsToPixels(OK_BUTTON_HEIGHT_IN_CHARS); 98 | ok.setLayoutData(data); 99 | ok.addSelectionListener(new SelectionAdapter() { 100 | @Override 101 | public void widgetSelected(SelectionEvent event) { 102 | container.close(); 103 | } 104 | }); 105 | container.setDefaultButton(ok); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/ui/UIAppender.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.ui; 17 | 18 | import java.nio.charset.StandardCharsets; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import org.apache.logging.log4j.core.Appender; 23 | import org.apache.logging.log4j.core.Core; 24 | import org.apache.logging.log4j.core.Filter; 25 | import org.apache.logging.log4j.core.Layout; 26 | import org.apache.logging.log4j.core.LogEvent; 27 | import org.apache.logging.log4j.core.appender.AbstractAppender; 28 | import org.apache.logging.log4j.core.config.Property; 29 | import org.apache.logging.log4j.core.config.plugins.Plugin; 30 | import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 31 | import org.apache.logging.log4j.core.config.plugins.PluginElement; 32 | import org.apache.logging.log4j.core.config.plugins.PluginFactory; 33 | import org.apache.logging.log4j.core.layout.PatternLayout; 34 | import org.eclipse.swt.widgets.Event; 35 | import org.eclipse.swt.widgets.Listener; 36 | 37 | @Plugin(name = UIAppender.PLUGIN_NAME, category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true) 38 | public final class UIAppender extends AbstractAppender { 39 | 40 | public static final String PLUGIN_NAME = "TextComposite"; 41 | 42 | private static final List LISTENERS = new ArrayList<>(); 43 | 44 | public static void addListener(Listener e) { 45 | LISTENERS.add(e); 46 | } 47 | 48 | public static void removeListener(Listener e) { 49 | LISTENERS.remove(e); 50 | } 51 | 52 | private UIAppender(String name, Layout layout, Filter filter, boolean ignoreExceptions) { 53 | super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY); 54 | } 55 | 56 | @Override 57 | public void append(LogEvent event) { 58 | String text = new String(getLayout().toByteArray(event), StandardCharsets.UTF_8); 59 | Event ev = new Event(); 60 | ev.data = text; 61 | for (Listener listener : LISTENERS) { 62 | listener.handleEvent(ev); 63 | } 64 | } 65 | 66 | @PluginFactory 67 | public static UIAppender createAppender( 68 | @PluginAttribute("name") String name, 69 | @PluginAttribute("ignoreExceptions") boolean ignoreExceptions, 70 | @PluginElement("Layout") Layout layout, 71 | @PluginElement("Filters") Filter filter) { 72 | if (name == null) { 73 | LOGGER.error("No name provided for UIAppender"); 74 | return null; 75 | } 76 | 77 | if (layout == null) { 78 | layout = PatternLayout.createDefaultLayout(); 79 | } 80 | 81 | return new UIAppender(name, layout, filter, ignoreExceptions); 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/ColumnLayout.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | public class ColumnLayout { 19 | 20 | private final int order; 21 | private final Columns column; 22 | private final Integer width; 23 | 24 | public ColumnLayout(int order, Columns column, Integer width) { 25 | this.order = order; 26 | this.column = column; 27 | this.width = width; 28 | } 29 | 30 | public int getOrder() { 31 | return order; 32 | } 33 | 34 | public Columns getColumn() { 35 | return column; 36 | } 37 | 38 | public Integer getWidth() { 39 | return width; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/Columns.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | public enum Columns { 19 | 20 | PID("pid"), 21 | BACKEND_TYPE("backend_type"), 22 | BLOCK_CREATE_DATE("block_start_date"), 23 | BLOCK_END_DATE("block_end_date"), 24 | BLOCKED_COUNT("num_of_blocked_processes"), 25 | APPLICATION_NAME("application"), 26 | DATABASE_NAME("db_name"), 27 | USER_NAME("user_name"), 28 | CLIENT("client"), 29 | BACKEND_START("backend_start"), 30 | QUERY_START("query_start"), 31 | XACT_START("xact_start"), 32 | DURATION("duration"), 33 | STATE("state"), 34 | STATE_CHANGE("state_change"), 35 | BLOCKED("blocked_by"), 36 | LOCK_TYPE("lock_type"), 37 | RELATION("relation"), 38 | SLOW_QUERY("slow_query"), 39 | QUERY("query"); 40 | 41 | private final String columnName; 42 | 43 | Columns(String columnName) { 44 | this.columnName = columnName; 45 | } 46 | 47 | public String getColumnName() { 48 | return columnName; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/DateUtils.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | import java.time.Duration; 19 | import java.time.Instant; 20 | import java.time.LocalDateTime; 21 | import java.time.ZoneId; 22 | import java.time.format.DateTimeFormatter; 23 | import java.util.Date; 24 | 25 | public final class DateUtils { 26 | 27 | private static final DateTimeFormatter FILE_DATE = 28 | DateTimeFormatter.ofPattern("yyyy-MM-dd HH''mm''ss"); 29 | 30 | private static final DateTimeFormatter DATE_WITH_TZ = 31 | DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"); 32 | 33 | private static final DateTimeFormatter DATE_WITHOUT_TZ = 34 | DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); 35 | 36 | public static Date dateFromString(String dateString) { 37 | if (dateString == null || dateString.isEmpty()) { 38 | return null; 39 | } 40 | 41 | return Date.from(Instant.from(DATE_WITH_TZ.parse(dateString))); 42 | } 43 | 44 | public static String dateToString(Date date) { 45 | if (date == null) { 46 | return ""; 47 | } 48 | 49 | return DATE_WITHOUT_TZ.format(date.toInstant()); 50 | } 51 | 52 | public static String dateToStringWithTz(Date date) { 53 | if (date == null) { 54 | return ""; 55 | } 56 | return DATE_WITH_TZ.format(date.toInstant().atZone(ZoneId.systemDefault())); 57 | } 58 | 59 | public static String dateToString(LocalDateTime date) { 60 | if (date == null) { 61 | return ""; 62 | } 63 | return FILE_DATE.format(date); 64 | } 65 | 66 | public static int compareDates(Date d1, Date d2) { 67 | if (d1 == null) { 68 | return d2 == null ? 0 : -1; 69 | } else { 70 | return d2 == null ? 1 : d1.compareTo(d2); 71 | } 72 | } 73 | 74 | public static String durationToString(Duration duration) { 75 | if (duration == null) { 76 | return ""; 77 | } else { 78 | long seconds = duration.getSeconds(); 79 | long absSeconds = Math.abs(seconds); 80 | String positive = String.format("%02d:%02d:%02d", absSeconds / 3600, (absSeconds % 3600) / 60, absSeconds % 60); 81 | return seconds < 0 ? "-" + positive : positive; 82 | } 83 | } 84 | 85 | private DateUtils() {} 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/ImageUtils.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | import java.util.EnumMap; 19 | import java.util.Map; 20 | 21 | import org.eclipse.jface.resource.ImageDescriptor; 22 | import org.eclipse.jface.viewers.DecorationOverlayIcon; 23 | import org.eclipse.jface.viewers.IDecoration; 24 | import org.eclipse.swt.graphics.Image; 25 | 26 | public final class ImageUtils { 27 | 28 | private static final Map IMAGES_MAP = new EnumMap<>(Images.class); 29 | private static final Map IMAGES_DECORATED_MAP = new EnumMap<>(Images.class); 30 | 31 | private static final int BLOCKED_ICON_QUADRANT = IDecoration.TOP_RIGHT; 32 | 33 | private static final ImageDescriptor BLOCKED_DESCRIPTOR = ImageDescriptor 34 | .createFromURL(ImageUtils.class.getClassLoader() 35 | .getResource(Images.DECORATOR_BLOCKED.getImageAddr())); 36 | 37 | public static Image getImage(Images type) { 38 | return IMAGES_MAP.computeIfAbsent(type, 39 | k -> new Image(null, ImageUtils.class.getClassLoader().getResourceAsStream(type.getImageAddr()))); 40 | } 41 | 42 | public static Image getDecoratedBlockedImage(Images images) { 43 | return IMAGES_DECORATED_MAP.computeIfAbsent(images, 44 | e -> decorateImage(getImage(e), BLOCKED_DESCRIPTOR, BLOCKED_ICON_QUADRANT)); 45 | } 46 | 47 | private static Image decorateImage(Image image, ImageDescriptor imageDescriptor, int iconQuadrant) { 48 | return new DecorationOverlayIcon(image, imageDescriptor, iconQuadrant).createImage(); 49 | } 50 | 51 | private ImageUtils() {} 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/Images.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | public enum Images { 19 | 20 | ADD_DATABASE("images/db_add_16.png"), 21 | DELETE_DATABASE("images/db_del_16.png"), 22 | EDIT_DATABASE("images/db_edit_16.png"), 23 | CONNECT_DATABASE("images/db_connect_16.png"), 24 | DISCONNECT_DATABASE("images/db_disconnect_16.png"), 25 | GROUP_DATABASE("images/db_group_16.png"), 26 | UPDATE("images/update_16.png"), 27 | AUTOUPDATE("images/autoupdate_16.png"), 28 | VIEW_ONLY_BLOCKED("images/db_ob_16.png"), 29 | EXPORT_BLOCKS("images/save_16.png"), 30 | IMPORT_BLOCKS("images/document_open_16.png"), 31 | SETTINGS("images/settings.png"), 32 | FILTER("images/filter.png"), 33 | CANCEL_UPDATE("images/cancel_update_16.png"), 34 | BLOCKED("images/block-16x16.gif"), 35 | UNBLOCKED("images/unblock-16x16.gif"), 36 | DECORATOR_BLOCKED("images/locker_8.png"), 37 | DECORATOR_UPDATE("images/update_8.png"), 38 | CONN_DISABLED("images/db_f_16.png"), 39 | CONN_CONNECTED("images/db_t_16.png"), 40 | CONN_ERROR("images/db_e_16.png"), 41 | CONN_UPDATE("images/on_update_16.png"), 42 | PROC_WORKING("images/nb_16.png"), 43 | PROC_BLOCKING("images/locker_16.png"), 44 | PROC_BLOCKED("images/blocked_16.png"), 45 | SHOW_LOG_PANEL("images/log_show_16.png"), 46 | HIDE_LOG_PANEL("images/log_hide_16.png"), 47 | BLOCKS_JOURNAL_FOLDER("images/blocks_journal_folder_16.png"), 48 | FOLDER("images/folder_16.png"), 49 | TABLE("images/table_16.png"), 50 | BACK("images/back-16.png"); 51 | 52 | private String location; 53 | 54 | private Images(String location) { 55 | this.location = location; 56 | } 57 | 58 | public String getImageAddr() { 59 | return location; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/PathBuilder.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | import java.io.IOException; 19 | import java.nio.file.Files; 20 | import java.nio.file.LinkOption; 21 | import java.nio.file.Path; 22 | import java.nio.file.Paths; 23 | 24 | import org.apache.logging.log4j.LogManager; 25 | import org.apache.logging.log4j.Logger; 26 | 27 | public final class PathBuilder { 28 | 29 | private static final Logger LOG = LogManager.getLogger(PathBuilder.class); 30 | 31 | private static PathBuilder instance; 32 | 33 | private Path path; 34 | 35 | private PathBuilder() { 36 | try { 37 | String os = System.getProperty("os.name").toLowerCase(); 38 | if (os.contains("win")) { 39 | path = Paths.get(System.getProperty("user.home"), "pgSqlBlocks"); 40 | Files.createDirectories(path); 41 | Files.setAttribute(path, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS); 42 | } else { 43 | path = Paths.get(System.getProperty("user.home"), ".pgSqlBlocks"); 44 | Files.createDirectories(path); 45 | } 46 | } catch (IOException e) { 47 | LOG.error(String.format("Ошибка создания директории %s: %s", path, e.getMessage())); 48 | } 49 | } 50 | 51 | public static PathBuilder getInstance() { 52 | if(instance == null) { 53 | instance = new PathBuilder(); 54 | } 55 | return instance; 56 | } 57 | 58 | public Path getBlocksJournalsDir() { 59 | Path blocksJournalsDir = path.resolve(Settings.getInstance().getBlocksJournalPath()); 60 | if (Files.notExists(path)) { 61 | try { 62 | Files.createDirectory(blocksJournalsDir); 63 | } catch (IOException e) { 64 | LOG.error(String.format("Ошибка создания директории %s: %s", blocksJournalsDir, e.getMessage())); 65 | } 66 | } 67 | return blocksJournalsDir; 68 | } 69 | 70 | public Path getServersPath() { 71 | return path.resolve("servers.xml"); 72 | } 73 | 74 | public Path getDefaultBlocksJournalPath() { 75 | return path.resolve("blocksJournals"); 76 | } 77 | 78 | public Path getColumnsPath() { 79 | return path.resolve("columns"); 80 | } 81 | 82 | // TODO разные конфиги log4j.propertires для разных ОС 83 | Path getPropertiesPath() { 84 | Path propPath = path.resolve("pgsqlblocks.properties"); 85 | if (!propPath.toFile().exists()) { 86 | try { 87 | Files.createFile(propPath); 88 | } catch (IOException e) { 89 | LOG.error(String.format("Ошибка создания файла %s: %s", propPath, e.getMessage())); 90 | } 91 | } 92 | return propPath; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/SettingsListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | public interface SettingsListener { 19 | 20 | void settingsUpdatePeriodChanged(int updatePeriod); 21 | void settingsShowIdleChanged(boolean isShowIdle); 22 | void settingsShowBackendPidChanged(boolean isShowBackendPid); 23 | void settingsAutoUpdateChanged(boolean isAutoUpdate); 24 | void settingsLimitBlocksChanged(int limitBlocks); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/utils/UserCancelException.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | /** 19 | * Handling cancel button action in password dialog 20 | */ 21 | public class UserCancelException extends Exception{ 22 | 23 | private static final long serialVersionUID = -464421662555050760L; 24 | 25 | public UserCancelException() { 26 | super(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/xmlstore/ColumnLayoutsXmlStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.xmlstore; 17 | 18 | import java.nio.file.Path; 19 | import java.util.List; 20 | 21 | import org.w3c.dom.Document; 22 | import org.w3c.dom.Element; 23 | import org.w3c.dom.Node; 24 | 25 | import ru.taximaxim.pgsqlblocks.utils.ColumnLayout; 26 | import ru.taximaxim.pgsqlblocks.utils.Columns; 27 | import ru.taximaxim.pgsqlblocks.utils.PathBuilder; 28 | 29 | public class ColumnLayoutsXmlStore extends XmlStore { 30 | 31 | private static final String ROOT_TAG = "columns"; 32 | 33 | private static final String COLUMN = "column"; 34 | private static final String COLUMN_ORDER = "column_order"; 35 | private static final String COLUMN_NAME = "column_name"; 36 | private static final String COLUMN_SIZE = "column_size"; 37 | 38 | private final String fileName; 39 | 40 | public ColumnLayoutsXmlStore(String fileName) { 41 | super(ROOT_TAG); 42 | this.fileName = fileName; 43 | } 44 | 45 | @Override 46 | protected Path getXmlFile() { 47 | return PathBuilder.getInstance().getColumnsPath().resolve(fileName); 48 | } 49 | 50 | @Override 51 | public List readObjects() { 52 | List layouts = super.readObjects(); 53 | layouts.sort((o1, o2) -> Integer.compare(o1.getOrder(), o2.getOrder())); 54 | return layouts; 55 | } 56 | 57 | @Override 58 | protected ColumnLayout parseElement(Node node) { 59 | Element el = (Element) node; 60 | int order = Integer.parseInt(el.getElementsByTagName(COLUMN_ORDER).item(0).getTextContent()); 61 | String name = el.getElementsByTagName(COLUMN_NAME).item(0).getTextContent(); 62 | String value = el.getElementsByTagName(COLUMN_SIZE).item(0).getTextContent(); 63 | Integer width = value.isEmpty() ? null : Integer.valueOf(value); 64 | 65 | return new ColumnLayout(order, Columns.valueOf(name), width); 66 | } 67 | 68 | @Override 69 | protected void appendChildren(Document xml, Element root, List list) { 70 | for (ColumnLayout layout : list) { 71 | Element parent = xml.createElement(COLUMN); 72 | root.appendChild(parent); 73 | createSubElement(xml, parent, COLUMN_ORDER, Integer.toString(layout.getOrder())); 74 | createSubElement(xml, parent, COLUMN_NAME, layout.getColumn().name()); 75 | Integer i = layout.getWidth(); 76 | String width = i == null ? "" : String.valueOf(i); 77 | createSubElement(xml, parent, COLUMN_SIZE, width); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/xmlstore/DBModelsXmlStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.xmlstore; 17 | 18 | import java.nio.file.Path; 19 | import java.util.List; 20 | 21 | import org.w3c.dom.Document; 22 | import org.w3c.dom.Element; 23 | import org.w3c.dom.Node; 24 | 25 | import ru.taximaxim.pgsqlblocks.common.models.DBModel; 26 | import ru.taximaxim.pgsqlblocks.utils.PathBuilder; 27 | 28 | public class DBModelsXmlStore extends XmlStore { 29 | 30 | private static final String ROOT_TAG = "servers"; 31 | 32 | private static final String ROOT_ELEMENT_TAG_NAME = "server"; 33 | private static final String ELEMENT_NAME_TAG_NAME = "name"; 34 | private static final String ELEMENT_HOST_TAG_NAME = "host"; 35 | private static final String ELEMENT_PORT_TAG_NAME = "port"; 36 | private static final String ELEMENT_DATABASE_NAME_TAG_NAME = "dbname"; 37 | private static final String ELEMENT_DB_GROUP_NAME = "dbgroup"; 38 | private static final String ELEMENT_USER_TAG_NAME = "user"; 39 | private static final String ELEMENT_PASSWORD_TAG_NAME = "passwd"; 40 | private static final String ELEMENT_READ_BACKEND_TAG_NAME = "readbt"; 41 | private static final String ELEMENT_ENABLED_TAG_NAME = "enabled"; 42 | 43 | public DBModelsXmlStore() { 44 | super(ROOT_TAG); 45 | } 46 | 47 | @Override 48 | protected Path getXmlFile() { 49 | return PathBuilder.getInstance().getServersPath(); 50 | } 51 | 52 | @Override 53 | protected DBModel parseElement(Node node) { 54 | Element element = (Element) node; 55 | Node nameNode = element.getElementsByTagName(ELEMENT_NAME_TAG_NAME).item(0); 56 | Node hostNode = element.getElementsByTagName(ELEMENT_HOST_TAG_NAME).item(0); 57 | Node portNode = element.getElementsByTagName(ELEMENT_PORT_TAG_NAME).item(0); 58 | Node databaseNameNode = element.getElementsByTagName(ELEMENT_DATABASE_NAME_TAG_NAME).item(0); 59 | Node dbGroupNode = element.getElementsByTagName(ELEMENT_DB_GROUP_NAME).item(0); 60 | Node userNode = element.getElementsByTagName(ELEMENT_USER_TAG_NAME).item(0); 61 | Node passwordNode = element.getElementsByTagName(ELEMENT_PASSWORD_TAG_NAME).item(0); 62 | Node readBackendNode = element.getElementsByTagName(ELEMENT_READ_BACKEND_TAG_NAME).item(0); 63 | Node enabledNode = element.getElementsByTagName(ELEMENT_ENABLED_TAG_NAME).item(0); 64 | 65 | String name = getTextContentFromNode(nameNode); 66 | String host = getTextContentFromNode(hostNode); 67 | String port = getTextContentFromNode(portNode); 68 | String databaseName = getTextContentFromNode(databaseNameNode); 69 | String dbGroup = getTextContentFromNode(dbGroupNode); 70 | String user = getTextContentFromNode(userNode); 71 | String password = getTextContentFromNode(passwordNode); 72 | boolean readBackend = readBackendNode != null && Boolean.parseBoolean(getTextContentFromNode(readBackendNode)); 73 | boolean enabled = enabledNode != null && Boolean.parseBoolean(getTextContentFromNode(enabledNode)); 74 | 75 | return new DBModel(name, host, port, databaseName, dbGroup, user, password, readBackend, enabled); 76 | } 77 | 78 | @Override 79 | protected void appendChildren(Document xml, Element root, List list) { 80 | for (DBModel model : list) { 81 | Element rootElement = xml.createElement(ROOT_ELEMENT_TAG_NAME); 82 | root.appendChild(rootElement); 83 | 84 | createSubElement(xml, rootElement, ELEMENT_NAME_TAG_NAME, model.getName()); 85 | createSubElement(xml, rootElement, ELEMENT_HOST_TAG_NAME, model.getHost()); 86 | createSubElement(xml, rootElement, ELEMENT_PORT_TAG_NAME, model.getPort()); 87 | createSubElement(xml, rootElement, ELEMENT_DATABASE_NAME_TAG_NAME, model.getDatabaseName()); 88 | createSubElement(xml, rootElement, ELEMENT_DB_GROUP_NAME, model.getDbGroup()); 89 | createSubElement(xml, rootElement, ELEMENT_USER_TAG_NAME, model.getUser()); 90 | createSubElement(xml, rootElement, ELEMENT_PASSWORD_TAG_NAME, model.getPassword()); 91 | createSubElement(xml, rootElement, ELEMENT_READ_BACKEND_TAG_NAME, String.valueOf(model.isReadBackendType())); 92 | createSubElement(xml, rootElement, ELEMENT_ENABLED_TAG_NAME, String.valueOf(model.isEnabled())); 93 | } 94 | } 95 | 96 | private String getTextContentFromNode(Node node) { 97 | if (node != null) { 98 | return node.getTextContent(); 99 | } 100 | return ""; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/pgsqlblocks/xmlstore/XmlStore.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.xmlstore; 17 | 18 | import java.io.IOException; 19 | import java.io.Reader; 20 | import java.io.Writer; 21 | import java.nio.charset.StandardCharsets; 22 | import java.nio.file.Files; 23 | import java.nio.file.NoSuchFileException; 24 | import java.nio.file.Path; 25 | import java.util.ArrayList; 26 | import java.util.List; 27 | 28 | import javax.xml.parsers.DocumentBuilderFactory; 29 | import javax.xml.parsers.ParserConfigurationException; 30 | import javax.xml.transform.OutputKeys; 31 | import javax.xml.transform.Transformer; 32 | import javax.xml.transform.TransformerException; 33 | import javax.xml.transform.TransformerFactory; 34 | import javax.xml.transform.dom.DOMSource; 35 | import javax.xml.transform.stream.StreamResult; 36 | 37 | import org.apache.logging.log4j.LogManager; 38 | import org.apache.logging.log4j.Logger; 39 | import org.w3c.dom.Document; 40 | import org.w3c.dom.Element; 41 | import org.w3c.dom.Node; 42 | import org.w3c.dom.NodeList; 43 | import org.xml.sax.InputSource; 44 | import org.xml.sax.SAXException; 45 | 46 | public abstract class XmlStore { 47 | 48 | private static final Logger LOG = LogManager.getLogger(XmlStore.class); 49 | private static final String KEY_VERSION = "version"; 50 | private static final String VALUE_VERSION = "1.0"; 51 | 52 | protected final String rootTag; 53 | 54 | protected XmlStore(String rootTag) { 55 | this.rootTag = rootTag; 56 | } 57 | 58 | protected Element createSubElement(Document xml, Element parent, String name, String value) { 59 | Element newElement = xml.createElement(name); 60 | newElement.setTextContent(value); 61 | parent.appendChild(newElement); 62 | return newElement; 63 | } 64 | 65 | protected abstract Path getXmlFile(); 66 | 67 | public List readObjects() { 68 | try (Reader xmlReader = Files.newBufferedReader(getXmlFile(), StandardCharsets.UTF_8)) { 69 | return getObjects(readXml(xmlReader)); 70 | } catch (NoSuchFileException ex) { 71 | return new ArrayList<>(); 72 | } catch (IOException | SAXException ex) { 73 | LOG.error(ex); 74 | return new ArrayList<>(); 75 | } 76 | } 77 | 78 | protected List getObjects(Document xml) { 79 | List objects = new ArrayList<>(); 80 | Element root = (Element) xml.getElementsByTagName(rootTag).item(0); 81 | NodeList nList = root.getChildNodes(); 82 | for (int i = 0; i < nList.getLength(); i++) { 83 | Node node = nList.item(i); 84 | if (node.getNodeType() == Node.ELEMENT_NODE) { 85 | objects.add(parseElement(node)); 86 | } 87 | } 88 | return objects; 89 | } 90 | 91 | protected abstract T parseElement(Node node); 92 | 93 | public void writeObjects(List list) { 94 | try { 95 | Path path = getXmlFile(); 96 | Files.createDirectories(path.getParent()); 97 | try (Writer xmlWriter = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) { 98 | Document xml = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); 99 | Element root = xml.createElement(rootTag); 100 | root.setAttribute(KEY_VERSION, VALUE_VERSION); 101 | xml.appendChild(root); 102 | appendChildren(xml, root, list); 103 | serializeXml(xml, xmlWriter); 104 | } 105 | 106 | } catch (IOException | ParserConfigurationException | TransformerException ex) { 107 | LOG.error(ex); 108 | } 109 | } 110 | 111 | protected abstract void appendChildren(Document xml, Element root, List list); 112 | 113 | /** 114 | * Reads (well-formed) list XML and checks it for basic validity: 115 | * root node must be <rootTagName> 116 | */ 117 | private Document readXml(Reader reader) throws IOException, SAXException { 118 | try { 119 | Document xml = DocumentBuilderFactory.newInstance().newDocumentBuilder() 120 | .parse(new InputSource(reader)); 121 | xml.normalize(); 122 | 123 | if (!xml.getDocumentElement().getNodeName().equals(rootTag)) { 124 | throw new IOException("XML root element name is not as requested"); 125 | } 126 | 127 | return xml; 128 | } catch (ParserConfigurationException ex) { 129 | throw new IOException(ex); 130 | } 131 | } 132 | 133 | private void serializeXml(Document xml, Writer writer) throws TransformerException { 134 | Transformer tf = TransformerFactory.newInstance().newTransformer(); 135 | tf.setOutputProperty(OutputKeys.INDENT, "yes"); 136 | tf.setOutputProperty(OutputKeys.METHOD, "xml"); 137 | tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); 138 | tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); 139 | tf.transform(new DOMSource(xml), new StreamResult(writer)); 140 | } 141 | } -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/ExtendedTreeViewer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer; 17 | 18 | import java.util.Locale; 19 | import java.util.ResourceBundle; 20 | 21 | import org.eclipse.swt.SWT; 22 | import org.eclipse.swt.layout.GridData; 23 | import org.eclipse.swt.layout.GridLayout; 24 | import org.eclipse.swt.widgets.Composite; 25 | import org.eclipse.swt.widgets.ToolBar; 26 | import org.eclipse.swt.widgets.ToolItem; 27 | 28 | import ru.taximaxim.pgsqlblocks.modules.db.controller.DBController; 29 | import ru.taximaxim.pgsqlblocks.xmlstore.ColumnLayoutsXmlStore; 30 | import ru.taximaxim.treeviewer.dialog.ColumnConfigDialog; 31 | import ru.taximaxim.treeviewer.filter.FilterChangeHandler; 32 | import ru.taximaxim.treeviewer.filter.FilterComposite; 33 | import ru.taximaxim.treeviewer.l10n.TreeViewer; 34 | import ru.taximaxim.treeviewer.models.DataSource; 35 | import ru.taximaxim.treeviewer.models.IObject; 36 | import ru.taximaxim.treeviewer.tree.ExtendedTreeViewerComponent; 37 | import ru.taximaxim.treeviewer.utils.ImageUtils; 38 | import ru.taximaxim.treeviewer.utils.Images; 39 | 40 | /** 41 | * Tree viewer that implements common logic. Comes with toolbar where filters and column selection are located. 42 | *
43 | * Supports filtering, sorting, l10n, hiding columns. 44 | */ 45 | public class ExtendedTreeViewer extends Composite { 46 | 47 | private ResourceBundle resourceBundle; 48 | private ExtendedTreeViewerComponent tree; 49 | private FilterComposite filterComposite; 50 | private final FilterChangeHandler filterChangeHandler; 51 | private ToolItem filterToolItem; 52 | private Runnable updateToolItemAction; 53 | private final boolean isBlockJournalTab; 54 | private DBController controller; 55 | 56 | public ExtendedTreeViewer(Composite parent, int style, Object userData, 57 | DataSource dataSource, Locale locale, 58 | ColumnLayoutsXmlStore columnLayoutsStore, boolean isBlockJournalTab) { 59 | super(parent, style); 60 | this.isBlockJournalTab = isBlockJournalTab; 61 | initResourceBundle(locale); 62 | GridLayout mainLayout = new GridLayout(); 63 | GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); 64 | setLayout(mainLayout); 65 | setLayoutData(data); 66 | filterChangeHandler = new FilterChangeHandler(dataSource); 67 | createContent(dataSource); 68 | tree.setData(dataSource, columnLayoutsStore); 69 | getTreeViewer().setInput(userData); 70 | } 71 | 72 | public ExtendedTreeViewer(Composite parent, int style, Object userData, 73 | DataSource dataSource, Locale locale, 74 | ColumnLayoutsXmlStore columnLayoutsStore) { 75 | this(parent, style, userData, dataSource, locale, columnLayoutsStore, false); 76 | } 77 | 78 | public void setController(DBController controller) { 79 | this.controller = controller; 80 | } 81 | 82 | private void initResourceBundle(Locale locale) { 83 | resourceBundle = ResourceBundle.getBundle(TreeViewer.class.getName(), 84 | locale == null ? new Locale("ru") : locale); 85 | } 86 | 87 | public ExtendedTreeViewerComponent getTreeViewer() { 88 | return tree; 89 | } 90 | 91 | private void createContent(DataSource dataSource) { 92 | createToolItems(); 93 | filterComposite = new FilterComposite(this, SWT.TOP | SWT.BORDER, 94 | resourceBundle, dataSource, filterChangeHandler); 95 | tree = new ExtendedTreeViewerComponent<>(this, 96 | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.FULL_SELECTION | SWT.MULTI); 97 | filterChangeHandler.setTree(tree); 98 | filterComposite.hide(); 99 | } 100 | 101 | // If not called then null 102 | public void setUpdateButtonAction(Runnable runnable) { 103 | this.updateToolItemAction = runnable; 104 | } 105 | 106 | private void createToolItems() { 107 | ToolBar toolBar = new ToolBar(this, SWT.HORIZONTAL); 108 | 109 | ToolItem updateToolItem = new ToolItem(toolBar, SWT.PUSH); 110 | updateToolItem.setImage(ImageUtils.getImage(Images.UPDATE)); 111 | updateToolItem.setToolTipText(Images.UPDATE.getDescription(resourceBundle)); 112 | updateToolItem.addListener(SWT.Selection, event -> { 113 | if (updateToolItemAction != null) { 114 | updateToolItemAction.run(); 115 | } 116 | tree.refreshWithoutSelection(); 117 | }); 118 | if (isBlockJournalTab) { 119 | ToolItem clearButton = new ToolItem(toolBar, SWT.PUSH); 120 | clearButton.setImage(ImageUtils.getImage(Images.CLEAN)); 121 | clearButton.setToolTipText(Images.CLEAN.getDescription(resourceBundle)); 122 | clearButton.addListener(SWT.Selection, event -> clean()); 123 | } 124 | 125 | filterToolItem = new ToolItem(toolBar, SWT.CHECK); 126 | filterToolItem.setSelection(false); 127 | filterToolItem.setImage(ImageUtils.getImage(Images.FILTER)); 128 | filterToolItem.addListener(SWT.Selection, event -> openFilter()); 129 | filterToolItem.setToolTipText(Images.FILTER.getDescription(resourceBundle)); 130 | 131 | ToolItem configColumnToolItem = new ToolItem(toolBar, SWT.PUSH); 132 | configColumnToolItem.setImage(ImageUtils.getImage(Images.TABLE)); 133 | configColumnToolItem.setToolTipText(Images.TABLE.getDescription(resourceBundle)); 134 | configColumnToolItem.addListener(SWT.Selection, event -> openConfigColumnDialog()); 135 | } 136 | 137 | private void openFilter() { 138 | if (filterComposite.isVisible()) { 139 | filterComposite.hide(); 140 | filterToolItem.setSelection(false); 141 | } else { 142 | filterComposite.show(); 143 | filterToolItem.setSelection(true); 144 | } 145 | } 146 | 147 | private void openConfigColumnDialog() { 148 | new ColumnConfigDialog(resourceBundle, tree, this.getShell()).open(); 149 | } 150 | 151 | public void clean() { 152 | if (controller != null) { 153 | controller.getBlocksJournal().getProcesses().clear(); 154 | } 155 | tree.getTree().removeAll(); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/dialog/ColumnConfigDialog.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.dialog; 17 | 18 | import java.util.ResourceBundle; 19 | import java.util.Set; 20 | 21 | import org.eclipse.jface.dialogs.Dialog; 22 | import org.eclipse.jface.viewers.ArrayContentProvider; 23 | import org.eclipse.jface.viewers.CheckboxTableViewer; 24 | import org.eclipse.jface.viewers.LabelProvider; 25 | import org.eclipse.swt.SWT; 26 | import org.eclipse.swt.graphics.Point; 27 | import org.eclipse.swt.layout.GridData; 28 | import org.eclipse.swt.layout.GridLayout; 29 | import org.eclipse.swt.widgets.Composite; 30 | import org.eclipse.swt.widgets.Control; 31 | import org.eclipse.swt.widgets.Shell; 32 | 33 | import ru.taximaxim.pgsqlblocks.utils.Columns; 34 | import ru.taximaxim.treeviewer.models.DataSource; 35 | import ru.taximaxim.treeviewer.tree.ExtendedTreeViewerComponent; 36 | 37 | public class ColumnConfigDialog extends Dialog { 38 | 39 | private final ExtendedTreeViewerComponent tree; 40 | private final ResourceBundle bundle; 41 | private final Set visibleColumn; 42 | 43 | private CheckboxTableViewer viewer; 44 | 45 | public ColumnConfigDialog(ResourceBundle resourceBundle, 46 | ExtendedTreeViewerComponent tree, Shell parent) { 47 | super(parent); 48 | this.tree = tree; 49 | this.bundle = resourceBundle; 50 | this.visibleColumn = tree.getVisibleColumns(); 51 | } 52 | 53 | @Override 54 | protected void configureShell(Shell newShell) { 55 | super.configureShell(newShell); 56 | newShell.setText(bundle.getString("columns")); 57 | } 58 | 59 | @Override 60 | protected Control createDialogArea(Composite parent) { 61 | Composite container = new Composite(parent, SWT.NONE); 62 | container.setLayout(new GridLayout()); 63 | container.setLayoutData(new GridData(GridData.FILL_BOTH)); 64 | 65 | DataSource dataSource = tree.getDataSource(); 66 | viewer = CheckboxTableViewer.newCheckList(container, SWT.BORDER); 67 | viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 68 | viewer.setContentProvider(ArrayContentProvider.getInstance()); 69 | viewer.setLabelProvider(new LabelProvider() { 70 | 71 | @Override 72 | public String getText(Object element) { 73 | Columns column = (Columns) element; 74 | return dataSource.getLocalizeString(column.getColumnName()); 75 | } 76 | }); 77 | 78 | viewer.setInput(dataSource.getColumns()); 79 | viewer.setCheckedElements(visibleColumn.toArray()); 80 | 81 | return container; 82 | } 83 | 84 | @Override 85 | protected Point getInitialSize() { 86 | Point size = super.getInitialSize(); 87 | return new Point(size.x, size.y + 25); 88 | } 89 | 90 | @Override 91 | protected void okPressed() { 92 | visibleColumn.clear(); 93 | 94 | for (Object obj : viewer.getCheckedElements()) { 95 | visibleColumn.add((Columns) obj); 96 | } 97 | 98 | tree.showColumns(); 99 | super.okPressed(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/filter/ColumnType.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.filter; 17 | 18 | enum ColumnType { 19 | INTEGER, 20 | STRING, 21 | DATE; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/filter/FilterChangeHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.filter; 17 | 18 | import java.util.ArrayList; 19 | import java.util.EnumMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | import org.eclipse.jface.viewers.Viewer; 24 | import org.eclipse.jface.viewers.ViewerFilter; 25 | 26 | import ru.taximaxim.pgsqlblocks.utils.Columns; 27 | import ru.taximaxim.treeviewer.models.DataSource; 28 | import ru.taximaxim.treeviewer.models.IObject; 29 | import ru.taximaxim.treeviewer.tree.ExtendedTreeViewerComponent; 30 | 31 | public class FilterChangeHandler { 32 | 33 | private final DataSource dataSource; 34 | private ExtendedTreeViewerComponent tree; 35 | private final Map columnFilters = new EnumMap<>(Columns.class); 36 | private ViewerFilter allTextFilter; 37 | 38 | public FilterChangeHandler(DataSource dataSource) { 39 | this.dataSource = dataSource; 40 | } 41 | 42 | public void setTree(ExtendedTreeViewerComponent tree) { 43 | this.tree = tree; 44 | } 45 | 46 | void filterAllColumns(String searchText) { 47 | if (searchText.isEmpty()) { 48 | allTextFilter = null; 49 | } else { 50 | allTextFilter = new ViewerFilter() { 51 | @Override 52 | public boolean select(Viewer viewer, Object parentElement, Object element) { 53 | if (parentElement instanceof IObject) { 54 | // Show all children (not first-level elements) because all children 55 | // should be shown if parent is shown. If parent is not matched then 56 | // his children will not be shown anyway. 57 | return true; 58 | } else { 59 | return dataSource.getColumnsToFilter().stream() 60 | .anyMatch(column -> matches(column, (IObject)element, searchText, 61 | FilterOperation.CONTAINS)); 62 | } 63 | } 64 | }; 65 | } 66 | updateFilters(); 67 | } 68 | 69 | void filter(String searchText, FilterOperation value, Columns column) { 70 | if (searchText.isEmpty() || value == FilterOperation.NONE) { 71 | columnFilters.remove(column); 72 | } else { 73 | ViewerFilter columnFilter = new ViewerFilter() { 74 | @Override 75 | public boolean select(Viewer viewer, Object parentElement, Object element) { 76 | if (parentElement instanceof IObject) { 77 | // Show all children (not first-level elements) because all children 78 | // should be shown if parent is shown. If parent is not matched then 79 | // his children will not be shown anyway. 80 | return true; 81 | } else { 82 | return matches(column, (IObject) element, searchText, value); 83 | } 84 | } 85 | }; 86 | columnFilters.put(column, columnFilter); 87 | } 88 | updateFilters(); 89 | } 90 | 91 | void deactivateFilters() { 92 | allTextFilter = null; 93 | columnFilters.clear(); 94 | updateFilters(); 95 | } 96 | 97 | private boolean matches(Columns column, IObject element, String searchText, 98 | FilterOperation value) { 99 | if (element.hasChildren()) { 100 | for (IObject child : element.getChildren()) { 101 | if (matches(column, child, searchText, value)) { 102 | return true; 103 | } 104 | } 105 | } 106 | String textFromObject = dataSource.getRowText(element, column); 107 | return value.matchesForType(textFromObject, searchText, getColumnType(column)); 108 | } 109 | 110 | private ColumnType getColumnType(Columns column) { 111 | switch (column) { 112 | case PID: return ColumnType.INTEGER; 113 | case BLOCK_CREATE_DATE: 114 | case BLOCK_END_DATE: 115 | case BACKEND_START: 116 | case QUERY_START: 117 | case XACT_START: 118 | case DURATION: 119 | case STATE_CHANGE: return ColumnType.DATE; 120 | default: return ColumnType.STRING; 121 | } 122 | } 123 | 124 | private void updateFilters() { 125 | List filters = new ArrayList<>(columnFilters.values()); 126 | if (allTextFilter != null) { 127 | filters.add(allTextFilter); 128 | } 129 | tree.setFilters(filters.toArray(new ViewerFilter[0])); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/filter/FilterComposite.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.filter; 17 | 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.List; 21 | import java.util.ResourceBundle; 22 | import java.util.Set; 23 | 24 | import org.eclipse.swt.SWT; 25 | import org.eclipse.swt.layout.GridData; 26 | import org.eclipse.swt.layout.GridLayout; 27 | import org.eclipse.swt.widgets.Combo; 28 | import org.eclipse.swt.widgets.Composite; 29 | import org.eclipse.swt.widgets.Label; 30 | import org.eclipse.swt.widgets.Text; 31 | 32 | import ru.taximaxim.pgsqlblocks.utils.Columns; 33 | import ru.taximaxim.treeviewer.models.DataSource; 34 | 35 | /** 36 | * GUI of Filters 37 | */ 38 | public class FilterComposite extends Composite { 39 | 40 | private final Set filterList; 41 | private final ResourceBundle innerResourceBundle; 42 | private final DataSource dataSource; 43 | private final int numberOfColumns; 44 | private final FilterChangeHandler filterChangeHandler; 45 | private final List filterTextList = new ArrayList<>(); 46 | 47 | public FilterComposite(Composite parent, int style, ResourceBundle innerResourceBundle, 48 | DataSource dataSource, FilterChangeHandler filterChangeHandler) { 49 | super(parent, style); 50 | this.innerResourceBundle = innerResourceBundle; 51 | this.dataSource = dataSource; 52 | this.filterList = dataSource.getColumnsToFilter(); 53 | this.filterChangeHandler = filterChangeHandler; 54 | 55 | // label, combo, text 56 | numberOfColumns = findColumnNumber() * 3; 57 | GridLayout glayout = new GridLayout(); 58 | glayout.numColumns = numberOfColumns; 59 | setLayout(glayout); 60 | 61 | GridData layoutData = new GridData(SWT.FILL, SWT.TOP, true, false); 62 | setLayoutData(layoutData); 63 | 64 | createContent(); 65 | } 66 | 67 | private void createContent() { 68 | createAllTextFilter(); 69 | filterList.forEach(this::createFilterView); 70 | } 71 | 72 | private void createAllTextFilter() { 73 | Label label = new Label(this, SWT.NONE); 74 | GridData data = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); 75 | label.setLayoutData(data); 76 | label.setText(innerResourceBundle.getString("filter")); 77 | label.setToolTipText(innerResourceBundle.getString("all-filter-tooltip")); 78 | 79 | Text filterText = new Text(this, SWT.FILL | SWT.BORDER); 80 | int horizontalSpan = Math.max(numberOfColumns - 1, 1); 81 | GridData textLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false, horizontalSpan, 1); 82 | filterText.setLayoutData(textLayoutData); 83 | filterText.setToolTipText(innerResourceBundle.getString("all-filter-tooltip")); 84 | filterTextList.add(filterText); 85 | filterText.addModifyListener(e -> filterChangeHandler.filterAllColumns(filterText.getText())); 86 | } 87 | 88 | private void createFilterView(Columns column) { 89 | GridData comboLayoutData = new GridData(SWT.CENTER, SWT.CENTER, false,false); 90 | comboLayoutData.widthHint = 60; 91 | GridData textLayoutData = new GridData(SWT.FILL, SWT.FILL, true, false); 92 | textLayoutData.widthHint = 150; 93 | 94 | Label label = new Label(this, SWT.NONE); 95 | label.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); 96 | label.setText(dataSource.getLocalizeString(column.getColumnName())); 97 | 98 | Combo combo = new Combo(this, SWT.DROP_DOWN | SWT.READ_ONLY); 99 | combo.setLayoutData(comboLayoutData); 100 | List filterValues = Arrays.asList(FilterOperation.values()); 101 | filterValues.forEach( f -> combo.add(f.toString())); 102 | combo.select(FilterOperation.CONTAINS.ordinal()); 103 | 104 | Text filterText = new Text(this, SWT.FILL | SWT.BORDER); 105 | filterText.setLayoutData(textLayoutData); 106 | filterTextList.add(filterText); 107 | 108 | combo.addModifyListener(e -> filterChangeHandler.filter( 109 | filterText.getText(), FilterOperation.find(combo.getText()), column)); 110 | filterText.addModifyListener(e -> filterChangeHandler.filter( 111 | filterText.getText(), FilterOperation.find(combo.getText()), column)); 112 | } 113 | 114 | /** 115 | * Method returns the number of columns in filter 116 | */ 117 | private int findColumnNumber() { 118 | int i = filterList.size(); 119 | 120 | if (i == 4) { 121 | return 2; 122 | } 123 | 124 | return Math.min(i, 3); 125 | } 126 | 127 | public void show() { 128 | this.setVisible(true); 129 | GridData layoutData = (GridData) this.getLayoutData(); 130 | layoutData.exclude = false; 131 | this.getParent().layout(); 132 | } 133 | 134 | public void hide() { 135 | this.setVisible(false); 136 | GridData layoutData = (GridData) this.getLayoutData(); 137 | layoutData.exclude = true; 138 | filterChangeHandler.deactivateFilters(); 139 | filterTextList.forEach(t -> t.setText("")); 140 | this.getParent().layout(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/filter/FilterOperation.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.filter; 17 | 18 | import java.util.Optional; 19 | import java.util.function.BiFunction; 20 | import java.util.function.Function; 21 | 22 | import ru.taximaxim.treeviewer.utils.TriFunction; 23 | 24 | /** 25 | * Enum for types of column filters 26 | */ 27 | enum FilterOperation { 28 | 29 | NONE("", (o, s, t) -> true), 30 | EQUALS("=", FilterOperation::equalsByType), 31 | NOT_EQUALS("!=", (o, s, t) -> !EQUALS.matchesForType(o, s, t)), 32 | CONTAINS("~", (o, s, t) -> o.toLowerCase().contains(s.toLowerCase())), 33 | GREATER(">", FilterOperation::greater), 34 | GREATER_OR_EQUAL(">=", (o, s, t) -> EQUALS.matchesForType(o, s, t) || GREATER.matchesForType(o, s, t)), 35 | LESS("<", FilterOperation::less), 36 | LESS_OR_EQUAL("<=", (o, s, t) -> EQUALS.matchesForType(o, s, t) || LESS.matchesForType(o, s, t)); 37 | 38 | private final String conditionText; 39 | private final TriFunction matcherFunction; 40 | 41 | FilterOperation(String conditionText, TriFunction matcherFunction) { 42 | this.conditionText = conditionText; 43 | this.matcherFunction = matcherFunction; 44 | } 45 | 46 | boolean matchesForType(String objectValue, String searchValue, ColumnType columnType) { 47 | return matcherFunction.apply(objectValue, searchValue, columnType); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return conditionText; 53 | } 54 | 55 | public String getConditionText() { 56 | return conditionText; 57 | } 58 | 59 | public static FilterOperation find(String text) { 60 | FilterOperation[] list = FilterOperation.values(); 61 | for (FilterOperation filterOperation : list) { 62 | if (filterOperation.getConditionText().equals(text)) { 63 | return filterOperation; 64 | } 65 | } 66 | return FilterOperation.NONE; 67 | } 68 | 69 | private static boolean equalsByType(String objectValue, String searchValue, ColumnType columnType) { 70 | switch (columnType) { 71 | case INTEGER: 72 | return matches(objectValue, searchValue, FilterOperation::getInteger, Integer::equals); 73 | case DATE: 74 | case STRING: 75 | return objectValue.equals(searchValue); 76 | } 77 | return false; 78 | } 79 | 80 | private static boolean greater(String objectValue, String searchValue, ColumnType columnType) { 81 | switch (columnType) { 82 | case INTEGER: 83 | return matches(objectValue, searchValue, FilterOperation::getInteger, (i1, i2) -> i1 > i2); 84 | case DATE: 85 | case STRING: 86 | return objectValue.compareTo(searchValue) > 0; 87 | } 88 | return false; 89 | } 90 | 91 | private static boolean less(String objectValue, String searchValue, ColumnType columnType) { 92 | switch (columnType) { 93 | case INTEGER: 94 | return matches(objectValue, searchValue, FilterOperation::getInteger, (i1, i2) -> i1 < i2); 95 | case DATE: 96 | case STRING: 97 | return objectValue.compareTo(searchValue) < 0; 98 | } 99 | return false; 100 | } 101 | 102 | private static boolean matches(String objectValue, String searchValue, 103 | Function> converter, BiFunction matcher) { 104 | Optional objectOpt = converter.apply(objectValue); 105 | Optional searchOpt = converter.apply(searchValue); 106 | return objectOpt.isPresent() && searchOpt.isPresent() && matcher.apply(objectOpt.get(), searchOpt.get()); 107 | } 108 | 109 | private static Optional getInteger(String value) { 110 | try { 111 | return Optional.of(Integer.parseInt(value)); 112 | } catch (NumberFormatException e) { 113 | return Optional.empty(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/l10n/TreeViewer.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.l10n; 17 | 18 | public class TreeViewer extends TreeViewer_ru { 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/l10n/TreeViewer_en.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.l10n; 17 | 18 | import java.util.ListResourceBundle; 19 | 20 | public class TreeViewer_en extends ListResourceBundle { 21 | @Override 22 | protected Object[][] getContents() { 23 | return new Object[][]{ 24 | {"update", "Update"}, 25 | {"clean", "Clean"}, 26 | {"filter", "Filter"}, 27 | {"all-filter-tooltip", "Filter by filters below using \"contains (~)\""}, 28 | {"columns", "Columns"}, 29 | {"default_action", "Empty"}, 30 | {"choose_dir", "Choose directory"}, 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/l10n/TreeViewer_ru.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.l10n; 17 | 18 | import java.util.ListResourceBundle; 19 | 20 | public class TreeViewer_ru extends ListResourceBundle { 21 | @Override 22 | protected Object[][] getContents() { 23 | return new Object[][]{ 24 | {"update", "Обновить"}, 25 | {"clean", "Очистить"}, 26 | {"filter", "Фильтр"}, 27 | {"all-filter-tooltip", "Фильтровать по всем фильтрам снизу используя условие \"содержит (~)\""}, 28 | {"columns", "Колонки"}, 29 | {"default_action", "Пусто"}, 30 | {"choose_dir", "Выберите директорию"}, 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/models/DataSource.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.models; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.ResourceBundle; 21 | import java.util.Set; 22 | 23 | import org.eclipse.jface.viewers.ILabelProviderListener; 24 | import org.eclipse.jface.viewers.ITableLabelProvider; 25 | import org.eclipse.jface.viewers.ITreeContentProvider; 26 | 27 | import ru.taximaxim.pgsqlblocks.utils.Columns; 28 | 29 | /** 30 | * Класс, выполняющий работу с формированием таблиц и данных.
31 | * От него необходимо будет унаследоваться и переопределить методы для получения текста колонок

32 | * getElements должен превращать список данных для treeViewerа;
33 | * getChildren должен возвращать список дочерних объектов того же типа;
34 | * getParent должен возвращать родительский объект, если нужно, чаще null;
35 | * hasChildren должен возвращать true если у объекта есть дочерние объекты;
36 | * **********************************************************************
37 | * getColumns возвращает список объектов, где объект колонки имплементит IColumn;
38 | * getColumnImage получить для строки объекта изображение для определенной колонки
39 | * getColumnText получить значение для определенной ячейки. сделать метод getRowText(element, getColumns().get(columnIndex))
40 | * для возможно проходить не по индексу колонки, а по самой колонке
41 | * localizeString позволяет получить строку из resourceBundle 42 | */ 43 | public abstract class DataSource implements ITableLabelProvider, ITreeContentProvider { 44 | 45 | protected List listeners = new ArrayList<>(); 46 | 47 | public abstract List getColumns(); 48 | 49 | public abstract Set getColumnsToFilter(); 50 | 51 | public abstract ResourceBundle getResourceBundle(); 52 | 53 | public abstract String getRowText(Object element, Columns column); 54 | 55 | public abstract int compare(Object e1, Object e2, Columns column); 56 | 57 | public String getLocalizeString(String name) { 58 | if (getResourceBundle() == null || !getResourceBundle().containsKey(name)) { 59 | return name; 60 | } 61 | 62 | return getResourceBundle().getString(name); 63 | } 64 | 65 | @Override 66 | public String getColumnText(Object element, int columnIndex) { 67 | return getRowText(element, getColumns().get(columnIndex)); 68 | } 69 | 70 | @Override 71 | public void dispose() { 72 | 73 | } 74 | 75 | @Override 76 | public boolean isLabelProperty(Object element, String property) { 77 | return false; 78 | } 79 | 80 | @Override 81 | public void addListener(ILabelProviderListener listener) { 82 | listeners.add(listener); 83 | } 84 | 85 | @Override 86 | public void removeListener(ILabelProviderListener listener) { 87 | listeners.remove(listener); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/models/IObject.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.models; 17 | 18 | import java.util.List; 19 | 20 | /** 21 | * Methods which need to implement in model class 22 | */ 23 | public interface IObject { 24 | 25 | List getChildren(); 26 | 27 | boolean hasChildren(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/utils/AggregatingListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.utils; 17 | 18 | import org.eclipse.swt.internal.SWTEventListener; 19 | import org.eclipse.swt.widgets.Event; 20 | import org.eclipse.swt.widgets.TypedListener; 21 | 22 | /** 23 | * Aggregates multiple consecutive events into one with defined time threshold. 24 | *

25 | * Note: it doesn't make sense to use this listener on events that don't come 26 | * in series in quick succession. 27 | */ 28 | public class AggregatingListener extends TypedListener { 29 | 30 | private static final int DEFAULT_AGGREGATION_THRESHOLD_MS = 500; 31 | 32 | private int aggregationThresholdMs = DEFAULT_AGGREGATION_THRESHOLD_MS; 33 | private DelayedEvent lastEvent; 34 | 35 | public void setAggregationThresholdMs(int aggregationThresholdMs) { 36 | this.aggregationThresholdMs = aggregationThresholdMs; 37 | } 38 | 39 | public AggregatingListener(SWTEventListener listener) { 40 | super(listener); 41 | } 42 | 43 | @Override 44 | public void handleEvent(Event e) { 45 | e.display.timerExec(aggregationThresholdMs, new DelayedEvent(e)); 46 | } 47 | 48 | private class DelayedEvent implements Runnable { 49 | 50 | private final Event e; 51 | 52 | public DelayedEvent(Event e) { 53 | this.e = e; 54 | lastEvent = this; 55 | } 56 | 57 | @Override 58 | public void run() { 59 | if (this == lastEvent) { 60 | AggregatingListener.super.handleEvent(e); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/utils/ImageUtils.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.utils; 17 | 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.concurrent.ConcurrentMap; 20 | 21 | import org.eclipse.swt.graphics.Image; 22 | 23 | public final class ImageUtils { 24 | 25 | private static final ConcurrentMap imagesMap = new ConcurrentHashMap<>(); 26 | 27 | public static Image getImage(Images type) { 28 | return imagesMap.computeIfAbsent(type.toString(), 29 | k -> new Image(null, ImageUtils.class.getClassLoader().getResourceAsStream(type.getImageAddr()))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/utils/Images.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.utils; 17 | 18 | import java.util.ResourceBundle; 19 | 20 | public enum Images { 21 | 22 | UPDATE("images/update_16.png", "update"), 23 | CLEAN("images/clean_16.png", "clean"), 24 | FILTER("images/filter.png", "filter"), 25 | TABLE("images/table_16.png", "columns"); 26 | 27 | private String location; 28 | private String description; 29 | 30 | private Images(String location, String description) { 31 | this.location = location; 32 | this.description = description; 33 | } 34 | 35 | public String getImageAddr() { 36 | return location; 37 | } 38 | 39 | public String getDescription(ResourceBundle resources) { 40 | return resources.getString(description); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ru/taximaxim/treeviewer/utils/TriFunction.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.treeviewer.utils; 17 | 18 | import java.util.Objects; 19 | import java.util.function.Function; 20 | 21 | @FunctionalInterface 22 | public interface TriFunction { 23 | 24 | R apply(A a, B b, C c); 25 | 26 | default TriFunction andThen(Function after) { 27 | Objects.requireNonNull(after); 28 | return (A a, B b, C c) -> after.apply(apply(a, b, c)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/images/autoupdate_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/autoupdate_16.png -------------------------------------------------------------------------------- /src/main/resources/images/back-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/back-16.png -------------------------------------------------------------------------------- /src/main/resources/images/block-16x16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/block-16x16.gif -------------------------------------------------------------------------------- /src/main/resources/images/block-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/block-256x256.png -------------------------------------------------------------------------------- /src/main/resources/images/block-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/block-32x32.png -------------------------------------------------------------------------------- /src/main/resources/images/block-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/block-48x48.png -------------------------------------------------------------------------------- /src/main/resources/images/block-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/block-512x512.png -------------------------------------------------------------------------------- /src/main/resources/images/blocked_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/blocked_16.png -------------------------------------------------------------------------------- /src/main/resources/images/blocks_journal_folder_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/blocks_journal_folder_16.png -------------------------------------------------------------------------------- /src/main/resources/images/cancel_update_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/cancel_update_16.png -------------------------------------------------------------------------------- /src/main/resources/images/clean_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/clean_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_add_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_add_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_connect_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_connect_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_del_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_del_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_disconnect_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_disconnect_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_e_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_e_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_edit_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_edit_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_f_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_f_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_group_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_group_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_ob_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_ob_16.png -------------------------------------------------------------------------------- /src/main/resources/images/db_t_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/db_t_16.png -------------------------------------------------------------------------------- /src/main/resources/images/document_open_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/document_open_16.png -------------------------------------------------------------------------------- /src/main/resources/images/filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/filter.png -------------------------------------------------------------------------------- /src/main/resources/images/folder_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/folder_16.png -------------------------------------------------------------------------------- /src/main/resources/images/locked_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/locked_16.png -------------------------------------------------------------------------------- /src/main/resources/images/locker_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/locker_16.png -------------------------------------------------------------------------------- /src/main/resources/images/locker_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/locker_8.png -------------------------------------------------------------------------------- /src/main/resources/images/log_hide_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/log_hide_16.png -------------------------------------------------------------------------------- /src/main/resources/images/log_show_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/log_show_16.png -------------------------------------------------------------------------------- /src/main/resources/images/nb_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/nb_16.png -------------------------------------------------------------------------------- /src/main/resources/images/on_update_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/on_update_16.png -------------------------------------------------------------------------------- /src/main/resources/images/save_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/save_16.png -------------------------------------------------------------------------------- /src/main/resources/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/settings.png -------------------------------------------------------------------------------- /src/main/resources/images/table_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/table_16.png -------------------------------------------------------------------------------- /src/main/resources/images/unblock-16x16.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/unblock-16x16.gif -------------------------------------------------------------------------------- /src/main/resources/images/unblock-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/unblock-256x256.png -------------------------------------------------------------------------------- /src/main/resources/images/unblock-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/unblock-32x32.png -------------------------------------------------------------------------------- /src/main/resources/images/unblock-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/unblock-48x48.png -------------------------------------------------------------------------------- /src/main/resources/images/unblock-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/unblock-512x512.png -------------------------------------------------------------------------------- /src/main/resources/images/update_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/update_16.png -------------------------------------------------------------------------------- /src/main/resources/images/update_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/technology16/pgsqlblocks/a252e9017d7bca95119d7ee7fff189de74dd8394/src/main/resources/images/update_8.png -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/main/resources/query.sql: -------------------------------------------------------------------------------- 1 | WITH blocks AS ( 2 | SELECT 3 | blocking_locks.pid as pid, 4 | blocked_locks.pid as blocked_pid, 5 | blocking_locks.locktype as locktype, 6 | blocking_locks.relation::regclass as relation, 7 | blocking_locks.granted as granted 8 | FROM 9 | pg_catalog.pg_locks blocked_locks 10 | JOIN 11 | pg_catalog.pg_locks blocking_locks 12 | ON blocking_locks.locktype = blocked_locks.locktype 13 | AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE 14 | AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation 15 | AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page 16 | AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple 17 | AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid 18 | AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid 19 | AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid 20 | AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid 21 | AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid 22 | AND blocking_locks.pid != blocked_locks.pid 23 | WHERE NOT blocked_locks.granted 24 | ) 25 | SELECT 26 | procs.pid AS pid, 27 | application_name, 28 | datname, 29 | usename, 30 | CASE WHEN client_port=-1 THEN 'local pipe' 31 | WHEN length(client_hostname)>0 THEN client_hostname||':'||client_port 32 | ELSE textin(inet_out(client_addr))||':'||client_port 33 | END AS client, 34 | date_trunc('second', backend_start) AS backend_start, 35 | CASE WHEN state='active' THEN date_trunc('second', query_start)::text 36 | ELSE '' 37 | END AS query_start, 38 | date_trunc('second', xact_start) AS xact_start, 39 | state, 40 | date_trunc('second', state_change) AS state_change, 41 | blocks.pid AS blockedby, 42 | /* deprecated 43 | null::text AS blocking_locks,*/ 44 | blocks.locktype AS locktype, 45 | blocks.relation AS relation, 46 | blocks.granted AS granted, 47 | query AS query, 48 | CASE WHEN query_start IS NULL OR state<>'active' THEN false 49 | ELSE query_start < now() - '10 seconds'::interval 50 | END AS slowquery 51 | FROM 52 | pg_stat_activity procs 53 | LEFT JOIN blocks 54 | ON blocks.blocked_pid = procs.pid 55 | WHERE 56 | procs.state != 'idle' 57 | OR procs.state IS NULL 58 | ORDER BY 59 | pid 60 | -------------------------------------------------------------------------------- /src/main/resources/query_10.sql: -------------------------------------------------------------------------------- 1 | WITH blocks AS ( 2 | SELECT 3 | blocking_locks.pid as pid, 4 | blocked_locks.pid as blocked_pid, 5 | blocking_locks.locktype as locktype, 6 | blocking_locks.relation::regclass as relation, 7 | blocking_locks.granted as granted 8 | FROM 9 | pg_catalog.pg_locks blocked_locks 10 | JOIN 11 | pg_catalog.pg_locks blocking_locks 12 | ON blocking_locks.locktype = blocked_locks.locktype 13 | AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE 14 | AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation 15 | AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page 16 | AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple 17 | AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid 18 | AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid 19 | AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid 20 | AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid 21 | AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid 22 | AND blocking_locks.pid != blocked_locks.pid 23 | WHERE NOT blocked_locks.granted 24 | ) 25 | SELECT 26 | procs.pid AS pid, 27 | application_name, 28 | datname, 29 | usename, 30 | backend_type, 31 | CASE WHEN client_port=-1 THEN 'local pipe' 32 | WHEN length(client_hostname)>0 THEN client_hostname||':'||client_port 33 | ELSE textin(inet_out(client_addr))||':'||client_port 34 | END AS client, 35 | date_trunc('second', backend_start) AS backend_start, 36 | CASE WHEN state='active' THEN date_trunc('second', query_start)::text 37 | ELSE '' 38 | END AS query_start, 39 | date_trunc('second', xact_start) AS xact_start, 40 | state, 41 | date_trunc('second', state_change) AS state_change, 42 | blocks.pid AS blockedby, 43 | /* deprecated 44 | null::text AS blocking_locks,*/ 45 | blocks.locktype AS locktype, 46 | blocks.relation AS relation, 47 | blocks.granted AS granted, 48 | query AS query, 49 | CASE WHEN query_start IS NULL OR state<>'active' THEN false 50 | ELSE query_start < now() - '10 seconds'::interval 51 | END AS slowquery 52 | FROM 53 | pg_stat_activity procs 54 | LEFT JOIN blocks 55 | ON blocks.blocked_pid = procs.pid 56 | WHERE 57 | procs.state != 'idle' 58 | OR procs.state IS NULL 59 | ORDER BY 60 | pid 61 | -------------------------------------------------------------------------------- /src/main/resources/query_with_idle.sql: -------------------------------------------------------------------------------- 1 | WITH blocks AS ( 2 | SELECT 3 | blocking_locks.pid as pid, 4 | blocked_locks.pid as blocked_pid, 5 | blocking_locks.locktype as locktype, 6 | blocking_locks.relation::regclass as relation, 7 | blocking_locks.granted as granted 8 | FROM 9 | pg_catalog.pg_locks blocked_locks 10 | JOIN 11 | pg_catalog.pg_locks blocking_locks 12 | ON blocking_locks.locktype = blocked_locks.locktype 13 | AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE 14 | AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation 15 | AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page 16 | AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple 17 | AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid 18 | AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid 19 | AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid 20 | AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid 21 | AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid 22 | AND blocking_locks.pid != blocked_locks.pid 23 | WHERE NOT blocked_locks.granted 24 | ) 25 | SELECT 26 | procs.pid AS pid, 27 | application_name, 28 | datname, 29 | usename, 30 | CASE WHEN client_port=-1 THEN 'local pipe' 31 | WHEN length(client_hostname)>0 THEN client_hostname||':'||client_port 32 | ELSE textin(inet_out(client_addr))||':'||client_port 33 | END AS client, 34 | date_trunc('second', backend_start) AS backend_start, 35 | CASE WHEN state='active' THEN date_trunc('second', query_start)::text 36 | ELSE '' 37 | END AS query_start, 38 | date_trunc('second', xact_start) AS xact_start, 39 | state, 40 | date_trunc('second', state_change) AS state_change, 41 | blocks.pid AS blockedby, 42 | /* deprecated 43 | null::text AS blocking_locks,*/ 44 | blocks.locktype AS locktype, 45 | blocks.relation AS relation, 46 | blocks.granted AS granted, 47 | query AS query, 48 | CASE WHEN query_start IS NULL OR state<>'active' THEN false 49 | ELSE query_start < now() - '10 seconds'::interval 50 | END AS slowquery 51 | FROM 52 | pg_stat_activity procs 53 | LEFT JOIN blocks 54 | ON blocks.blocked_pid = procs.pid 55 | ORDER BY 56 | pid 57 | -------------------------------------------------------------------------------- /src/main/resources/query_with_idle_10.sql: -------------------------------------------------------------------------------- 1 | WITH blocks AS ( 2 | SELECT 3 | blocking_locks.pid as pid, 4 | blocked_locks.pid as blocked_pid, 5 | blocking_locks.locktype as locktype, 6 | blocking_locks.relation::regclass as relation, 7 | blocking_locks.granted as granted 8 | FROM 9 | pg_catalog.pg_locks blocked_locks 10 | JOIN 11 | pg_catalog.pg_locks blocking_locks 12 | ON blocking_locks.locktype = blocked_locks.locktype 13 | AND blocking_locks.DATABASE IS NOT DISTINCT FROM blocked_locks.DATABASE 14 | AND blocking_locks.relation IS NOT DISTINCT FROM blocked_locks.relation 15 | AND blocking_locks.page IS NOT DISTINCT FROM blocked_locks.page 16 | AND blocking_locks.tuple IS NOT DISTINCT FROM blocked_locks.tuple 17 | AND blocking_locks.virtualxid IS NOT DISTINCT FROM blocked_locks.virtualxid 18 | AND blocking_locks.transactionid IS NOT DISTINCT FROM blocked_locks.transactionid 19 | AND blocking_locks.classid IS NOT DISTINCT FROM blocked_locks.classid 20 | AND blocking_locks.objid IS NOT DISTINCT FROM blocked_locks.objid 21 | AND blocking_locks.objsubid IS NOT DISTINCT FROM blocked_locks.objsubid 22 | AND blocking_locks.pid != blocked_locks.pid 23 | WHERE NOT blocked_locks.granted 24 | ) 25 | SELECT 26 | procs.pid AS pid, 27 | application_name, 28 | datname, 29 | usename, 30 | backend_type, 31 | CASE WHEN client_port=-1 THEN 'local pipe' 32 | WHEN length(client_hostname)>0 THEN client_hostname||':'||client_port 33 | ELSE textin(inet_out(client_addr))||':'||client_port 34 | END AS client, 35 | date_trunc('second', backend_start) AS backend_start, 36 | CASE WHEN state='active' THEN date_trunc('second', query_start)::text 37 | ELSE '' 38 | END AS query_start, 39 | date_trunc('second', xact_start) AS xact_start, 40 | state, 41 | date_trunc('second', state_change) AS state_change, 42 | blocks.pid AS blockedby, 43 | /* deprecated 44 | null::text AS blocking_locks,*/ 45 | blocks.locktype AS locktype, 46 | blocks.relation AS relation, 47 | blocks.granted AS granted, 48 | query AS query, 49 | CASE WHEN query_start IS NULL OR state<>'active' THEN false 50 | ELSE query_start < now() - '10 seconds'::interval 51 | END AS slowquery 52 | FROM 53 | pg_stat_activity procs 54 | LEFT JOIN blocks 55 | ON blocks.blocked_pid = procs.pid 56 | ORDER BY 57 | pid 58 | -------------------------------------------------------------------------------- /src/main/resources/version.sql: -------------------------------------------------------------------------------- 1 | SELECT CAST (pg_catalog.current_setting('server_version_num') AS INT); 2 | -------------------------------------------------------------------------------- /src/test/java/ru/taximaxim/pgsqlblocks/common/models/DBBlocksJournalTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Collections; 24 | import java.util.Date; 25 | import java.util.List; 26 | 27 | import org.junit.Test; 28 | 29 | public class DBBlocksJournalTest { 30 | 31 | private final static String INCORRECT_NUMB_OF_PROC = "Incorrect number of processes in DBBlocksJournal"; 32 | 33 | static class Listener implements DBBlocksJournalListener { 34 | 35 | private boolean isDidCloseProcesses; 36 | private boolean isDidCloseAllProcesses; 37 | private boolean isDidAddProcesses; 38 | 39 | @Override 40 | public void dbBlocksJournalDidAddProcesses() { 41 | isDidAddProcesses = true; 42 | } 43 | 44 | @Override 45 | public void dbBlocksJournalDidCloseAllProcesses() { 46 | isDidCloseAllProcesses = true; 47 | } 48 | 49 | @Override 50 | public void dbBlocksJournalDidCloseProcesses(List processes) { 51 | isDidCloseProcesses = true; 52 | } 53 | 54 | public boolean verifyDidCloseProcesses() { 55 | if (isDidCloseProcesses) { 56 | isDidCloseProcesses = false; 57 | return true; 58 | } 59 | return false; 60 | } 61 | 62 | public boolean verifyDidCloseAllProcesses() { 63 | if (isDidCloseAllProcesses) { 64 | isDidCloseAllProcesses = false; 65 | return true; 66 | } 67 | return false; 68 | } 69 | 70 | public boolean verifyDidAddProcesses() { 71 | if (isDidAddProcesses) { 72 | isDidAddProcesses = false; 73 | return true; 74 | } 75 | return false; 76 | } 77 | } 78 | 79 | @Test 80 | public void addProcessesTest() { 81 | DBBlocksJournal journal = new DBBlocksJournal(); 82 | Listener listener = new Listener(); 83 | journal.addListener(listener); 84 | 85 | List empty = Collections.emptyList(); 86 | journal.add(empty); 87 | assertTrue("DBBlocksJournal is not empty", journal.isEmpty()); 88 | 89 | DBProcessQuery procQuery = createDBProcessQuery(); 90 | DBProcess proc1 = createDBProcess(0, procQuery); 91 | DBProcess proc2 = createDBProcess(1, procQuery); 92 | proc1.addChild(proc2); 93 | List procList = new ArrayList<>(); 94 | procList.add(proc1); 95 | procList.add(proc2); 96 | journal.add(procList); 97 | assertFalse("DBBlocksJournal is empty", journal.isEmpty()); 98 | assertEquals(INCORRECT_NUMB_OF_PROC, 2, journal.getProcesses().size()); 99 | 100 | List procList2 = new ArrayList<>(); 101 | DBProcess proc3 = createDBProcess(3, procQuery); 102 | procList2.add(proc3); 103 | journal.add(procList2); 104 | assertEquals(INCORRECT_NUMB_OF_PROC, 3, journal.getProcesses().size()); 105 | assertTrue("Error in method add() variable needCloseProcesses is empty", listener.verifyDidCloseProcesses()); 106 | 107 | journal.add(procList2); 108 | assertEquals(INCORRECT_NUMB_OF_PROC, 3, journal.getProcesses().size()); 109 | 110 | journal.getProcesses().get(2).close(); 111 | journal.add(procList2); 112 | assertTrue("Wrong status of processes must be closed.", journal.getProcesses().get(2).isClosed()); 113 | assertEquals(INCORRECT_NUMB_OF_PROC, 4, journal.getProcesses().size()); 114 | 115 | journal.add(empty); 116 | assertTrue( 117 | "Error while processing method closeProcesses didn't enter into method dbBlocksJournalDidCloseProcesses", 118 | listener.verifyDidCloseProcesses()); 119 | assertTrue( 120 | "Error while processing method closeProcesses didn't enter into method dbBlocksJournalDidCloseAllProcesses", 121 | listener.verifyDidCloseAllProcesses()); 122 | } 123 | 124 | @Test 125 | public void setJournalProcessesTest() { 126 | DBBlocksJournal journal = new DBBlocksJournal(); 127 | Listener listener = new Listener(); 128 | journal.addListener(listener); 129 | 130 | List processes = new ArrayList<>(); 131 | DBProcessQuery procQuery = createDBProcessQuery(); 132 | DBBlocksJournalProcess journalProc = new DBBlocksJournalProcess(createDBProcess(0, procQuery)); 133 | processes.add(journalProc); 134 | 135 | journal.setJournalProcesses(processes); 136 | assertTrue( 137 | "Error while processing method setJournalProcesses didn't enter into method dbBlocksJournalDidAddProcesses", 138 | listener.verifyDidAddProcesses()); 139 | } 140 | 141 | private DBProcessQuery createDBProcessQuery() { 142 | return new DBProcessQuery("query", false, new Date(), new Date(), new Date(), "15678"); 143 | } 144 | 145 | private DBProcess createDBProcess(int pid, DBProcessQuery processQuery) { 146 | DBProcessQueryCaller c = new DBProcessQueryCaller("appName", "test", "user", "client"); 147 | return new DBProcess(pid, "type", c, "state", new Date(), processQuery); 148 | } 149 | } -------------------------------------------------------------------------------- /src/test/java/ru/taximaxim/pgsqlblocks/common/models/DBModelTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.common.models; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import org.junit.Test; 21 | 22 | public class DBModelTest { 23 | 24 | @Test 25 | public void copyTest() { 26 | DBModel model1 = new DBModel("test", "", "host", "port", "dbName", "user", "password", true, false); 27 | DBModel model2 = model1.copy(); 28 | 29 | assertEquals(model1, model2); 30 | } 31 | } -------------------------------------------------------------------------------- /src/test/java/ru/taximaxim/pgsqlblocks/utils/DateUtilsTest.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017-2024 TAXTELECOM, LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | *******************************************************************************/ 16 | package ru.taximaxim.pgsqlblocks.utils; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | 20 | import java.time.Duration; 21 | import java.util.Collections; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | import org.junit.Test; 26 | 27 | public class DateUtilsTest { 28 | 29 | private static final int HOUR = 3600; 30 | private static final Map durationToExpectedRepresentation = 31 | Collections.unmodifiableMap(new HashMap() {{ 32 | put(null, ""); 33 | put(Duration.ZERO, "00:00:00"); 34 | put(Duration.ofSeconds(0), "00:00:00"); 35 | put(Duration.ofSeconds(6), "00:00:06"); 36 | put(Duration.ofSeconds(60 + 1), "00:01:01"); 37 | put(Duration.ofSeconds(60 + 13), "00:01:13"); 38 | put(Duration.ofSeconds(300), "00:05:00"); 39 | put(Duration.ofSeconds(300 + 5), "00:05:05"); 40 | put(Duration.ofSeconds(HOUR), "01:00:00"); 41 | put(Duration.ofSeconds(HOUR + 9), "01:00:09"); 42 | put(Duration.ofSeconds(10 * HOUR), "10:00:00"); 43 | put(Duration.ofSeconds(10 * HOUR + 305), "10:05:05"); 44 | put(Duration.ofSeconds(30 * HOUR + 305), "30:05:05"); 45 | put(Duration.ofSeconds(100 * HOUR + 305), "100:05:05"); 46 | }}); 47 | 48 | @Test 49 | public void durationToStringTest() { 50 | durationToExpectedRepresentation.forEach((d, r) -> assertEquals(r, DateUtils.durationToString(d))); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/resources/application.conf: -------------------------------------------------------------------------------- 1 | pgsqlblocks-test-configs { 2 | remote-host = "localhost" // test DB host 3 | remote-db = "postgres" // test DB name 4 | remote-port = "5432" 5 | remote-username = "pgsqlblocks_test" // test user login 6 | remote-password = "pgsqlblocks_test_user_password" // test user password 7 | 8 | select-pg-backend-pid = "select pg_backend_pid();" 9 | pg-terminate-backend = "select pg_terminate_backend(?);" 10 | terminated-succesed = "pg_terminate_backend" 11 | pg-backend = "pg_backend_pid" 12 | testing-dump-sql = "testing_dump.sql" 13 | create-rule-sql = "create_rule.sql" 14 | drop-rule-sql = "drop_rule.sql" 15 | select-1000-sql = "select_1000.sql" 16 | select-sleep-sql = "select_sleep.sql" 17 | create-index-sql = "create_index.sql" 18 | 19 | delay-ms = 250.0 // waiting time between request's 20 | } -------------------------------------------------------------------------------- /src/test/resources/create_index.sql: -------------------------------------------------------------------------------- 1 | CREATE INDEX CONCURRENTLY ix_pgsqlblocks_testing_name 2 | ON public.pgsqlblocks_testing 3 | USING btree 4 | (name COLLATE pg_catalog."default"); 5 | -------------------------------------------------------------------------------- /src/test/resources/create_rule.sql: -------------------------------------------------------------------------------- 1 | CREATE OR REPLACE RULE rule_pgsqlblocks_testing AS 2 | ON INSERT TO public.pgsqlblocks_testing 3 | WHERE new.count >= 0 DO INSTEAD INSERT INTO public.pgsqlblocks_testing (name, count, description) 4 | VALUES (new.name, new.count, new.description); -------------------------------------------------------------------------------- /src/test/resources/drop_rule.sql: -------------------------------------------------------------------------------- 1 | DROP RULE rule_pgsqlblocks_testing ON public.pgsqlblocks_testing; SELECT pg_sleep(28800); -------------------------------------------------------------------------------- /src/test/resources/select_1000.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM public.pgsqlblocks_testing LIMIT 1000; SElECT pg_sleep(600); -------------------------------------------------------------------------------- /src/test/resources/select_sleep.sql: -------------------------------------------------------------------------------- 1 | BEGIN; 2 | SELECT pg_sleep(300); 3 | COMMIT; 4 | -------------------------------------------------------------------------------- /src/test/resources/testing_dump.sql: -------------------------------------------------------------------------------- 1 | SET statement_timeout = 0; 2 | SET lock_timeout = 0; 3 | SET client_encoding = 'UTF8'; 4 | SET standard_conforming_strings = on; 5 | SET check_function_bodies = false; 6 | SET client_min_messages = warning; 7 | SET row_security = off; 8 | 9 | DROP TABLE IF EXISTS pgsqlblocks_testing; 10 | CREATE TABLE pgsqlblocks_testing 11 | ( 12 | id serial NOT NULL, 13 | name character varying(25), 14 | count integer NOT NULL DEFAULT 0, 15 | description character varying(60), 16 | CONSTRAINT pkey_id PRIMARY KEY (id) 17 | ) 18 | WITH ( 19 | OIDS=FALSE 20 | ); 21 | ALTER TABLE pgsqlblocks_testing 22 | OWNER TO pgsqlblocks_test; 23 | 24 | 25 | INSERT INTO pgsqlblocks_testing(name, description) VALUES('val1', 'value 1'), ('val2' , 'value 2'), ('val3', 'value 3'); --------------------------------------------------------------------------------