java.lang.RuntimeException: wXb6Vnl31u :: Ошибка для HTML= 001 003 004 005 006 007 008 009
010

Как настроить входящую миграцию через 011 PostgreSQL с нуля

012 013

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

016 017

Общая концепция миграции

018 019
    020
  1. Нужно в сторонней PostgreSQL БД создать таблицы нужного 021 формата.
  2. 022 023
  3. Потом в них будет сторонняя система заливать данные.
  4. 024 025
  5. После заливки она будет отмечать завершение заливки удалением 026 записи в специальной маркерной таблице.
  6. 027 028
  7. После этот будет запускаться миграция, по окончанию которой 029 данные будут залиты на платформу.
  8. 030
031 032

Что нужно знать, чтобы пользоваться этим 033 руководством

034 035
    036
  1. Где находится директория логирования
  2. 037 038
  3. Где находятся файлы конфигурации
  4. 039
040 041

Алгоритм настройки

042 043

Для настройки входящей миграции через PostgreSQL необходимо 044 выполнить следующие пункты:

045 046
    047
  1. Отметить поля в бизнес-объектах, которые нужно мигрировать 048 из-вне на платформе MyBPM;
  2. 049 050
  3. Подключиться к сторонней базе-данных типа PostgreSQL например с 051 помощью pgAdmin - далее будем это БД называть импортной БД;
  4. 052 053
  5. Получить DDL-таблиц из платформы MyBPM;
  6. 054 055
  7. Накатить полеченные DDL-ы в импортной БД;
  8. 056 057
  9. Настроить подключение платформы MyBPM к импортной БД;
  10. 058 059
  11. Активировать миграцию и провести холостой пуск;
  12. 060 061
  13. Загрузить данные и смотреть процесс миграции;
  14. 062
063 064

Пункт 1. Отметить поля в бизнес-объектах

065 066

Давайте в качестве примера создадим простейший набор 067 бизнес-объектов, в котором будут все возможные сочетания полей.

068 069

Бизнес-объекты создаются в режиме редактирования нажатием на 070 кнопку с плюсом и выбором пункта "Бизнес-объект" как показано на 071 картинке:

072 073

pic001.png

074 075

Давайте создадим бизнес-объект "Физическое лицо" как показано на 076 картинке:

077 078

pic001.png

079 080

И укажем ему код: NaturalPerson как показано на 081 картинке:

082 083

pic002.png

084 085

Теперь зададим код Surname для поля 086 Фамилия как показано на картинке:

087 088

pic003.png

089 090

pic004.png

091 092

Остальные поля также, в итоге должно получиться два 093 бизнес-объекта с такими полями:

094 095
096  Бизнес-объект "Физическое лицо" с кодом "NaturalPerson"
097  И полями:  Фамилия        с кодом Surname       тип "Текстовое поле"
098             Имя            с кодом Name          тип "Текстовое поле"
099             ИИН            с кодом IIN           тип "Текстовое поле"
100             Дата рождения  с кодом BirthDate     тип "Дата"
101  
102  Бизнес-объект "Юридическое лицо" с кодом "LegalPerson"
103  И полями:  Имя            с кодом Name          тип "Текстовое поле"
104             БИН            с кодом BIN           тип "Текстовое поле"
105             Дата создания  с кодом CreateDate    тип "Дата"
106  
107  
108  
109  
110 111

Теперь создадим составной бизнес-объект, как показано на 112 картинке:

113 114

pic006.png

115 116

Выберем для его формирования бизнес-объекты Физическое и 117 Юридическое лица, как показано на рисунке:

118 119

pic007.png

120 121

Сформируем поля этого составного объекта-как показано на 122 рисунке:

123 124

pic008.png

125 126

Так же необходимо задать коды.

127 128

Должен получиться такой составной бизнес-объект:

129 130
131  Состановй бизнес-объект: "ЦО Клиент" с кодом "Client"
132  С полями:   "Дата рожд./созд." с кодом "CrDate"  связанный с полями: "Дата рождения" и "Дата создания"
133              "ИИН/БИН"          с кодом "BIIN"    связанный с полями: "ИИН" и "БИН"
134              "Имя"              с кодом "Name"    связанный с полями: "Имя" и "Имя"
135  
136  
137  
138  
139 140

Создадим ещё бизнес-объект "Заявка на клиента" (стрелочкой 141 обозначен перенос мышкой):

142 143

009.png

144 145

И создадим последний бизнес объект "Заявка на физическое лицо" 146 (стрелочкой обозначен перенос мышкой):

147 148

010.png

149 150

Теперь давайте перечислим всю созданную структуру 151 бизнес-объектов:

152 153
154  Бизнес-объект "Физическое лицо" с кодом "NaturalPerson"
155  И полями:  Фамилия        с кодом Surname       тип "Текстовое поле"
156             Имя            с кодом Name          тип "Текстовое поле"
157             ИИН            с кодом IIN           тип "Текстовое поле"
158             Дата рождения  с кодом BirthDate     тип "Дата"
159  
160  
161  
162 163

 

164 165
166  Бизнес-объект "Юридическое лицо" с кодом "LegalPerson"
167  И полями:  Имя            с кодом Name          тип "Текстовое поле"
168             БИН            с кодом BIN           тип "Текстовое поле"
169             Дата создания  с кодом CreateDate    тип "Дата"
170  
171  
172  
173 174

 

175 176
177  Составной бизнес-объект: "ЦО Клиент" с кодом "Client"
178  С полями:   "Дата рожд./созд." с кодом "CrDate"  связанный с полями: "Дата рождения" и "Дата создания"
179              "ИИН/БИН"          с кодом "BIIN"    связанный с полями: "ИИН" и "БИН"
180              "Имя"              с кодом "Name"    связанный с полями: "Имя" и "Имя"
181  
182  
183  
184 185

 

186 187
188  Бизнес-объект "Заявка на клиента" с кодом "ClientClaim"
189  И полями:  Название       с кодом Name          тип "Текстовое поле"
190             Описание       с кодом Descr         тип "Текстовый блок"
191             ЦО Клиент      с кодом CoClient      ссылается на ЦО "ЦО Клиент"
192  
193  
194  
195 196

 

197 198
199  Бизнес-объект "Заявка на физическое лицо" с кодом "NaturalClaim"
200  И полями:  Наименование     с кодом Name          тип "Текстовое поле"
201             Описание         с кодом Descr         тип "Текстовый блок"
202             Физическое лицо  с кодом Natural       ссылается на БО "Физическое лицо"
203  
204  
205  
206  
207 208

Теперь все поля этих бизнес объектов необходимо отметить, что 209 они участвуют в in-migration как показано на рисунке:

210 211

011.png

212 213

Это нужно сделать для всех полей простых бизнес-объектов. Для 214 составных бизнес-объектов это делать не нужно, так как в составном 215 бизнес-объекте данны нет - они распределены по составляющим этого 216 БО.

217 218

Не забывайте сохранять изменённые данные.

219 220

Пункт 2. Подключение к сторонней БД 221 PostgreSQL

222 223

Описание этого пункта не входит в обязанность данного 224 руководства. Вам необходимо заранее ознакомиться с подобными 225 руководствами.

226 227

Единственное отметим, что для подключения необходимо знать 228 следующие параметры:

229 230
    231
  1. Хост или IP-адрес сервера базы данных, на котором размещена БД. 232 Например: 192.168.32.31 - это IP-адрес, или db01.mybpm.local
  2. 233 234
  3. Порт подключения - это число например 5432
  4. 235 236
  5. Имя базы данных - например mybpm_out
  6. 237 238
  7. Имя схемы в базе данных - например in_tables
  8. 239 240
  9. Имя пользователя - например mybpm
  10. 241 242
  11. Пароль пользователя - например 6dfxHTESrO
  12. 243
244 245

Эти данные необходимо получить от администратора баз данных. 246 Дальше их ввести в pgAdmin-е при создании подключения, и, если 247 подключение будет успешно, то они правильные.

248 249

Дальше по тексту мы будет пользоваться следующими 250 значениями:

251 252
253  IP-адрес сервера базы данных = 192.168.11.23
254  Порт подключения             = 10018
255  Имя базы данных              = migration
256  Имя схемы в базе данных      = in_tables
257  Имя пользователя             = mybpm_migration
258  Пароль пользователя          = 6dfxHTESrO
259  
260  
261  
262  
263 264

Пункт 3. Получить DDL-таблиц из платформы 265 MyBPM

266 267

В начале в конфиге нужно прописать схему базы данных. Для этого 268 откройте конфиг

269 270
271  /mybpm/configs/InMigrationPostgresConfig.txt
272  
273  
274  
275  
276 277

И в нём укажите схему базы данных, в которой будут помещены 278 таблицы для миграции:

279 280

012.png

281 282

Теперь можно получить DDL-для создания входящих таблиц. Для 283 этого нужно выполнить Rest-запрос:

284 285
286  GET http://localhost:1313/web/migration/generate-in-ddl?companyCode=greetgo
287  
288  
289  
290  
291 292

Здесь вместо greetgo вам необходимо указать код 293 того аккаунта, в котором вы создали бизнес-объекты.

294 295

А вместо http://localhost:1313 нужно указать адрес 296 установленной платформы MyBPM.

297 298

Вот пример как это сделано в Postman-е:

299 300

013.png

301 302

На выходе этого запроса получиться список команд create 303 table ..., в которых будут созданы все необходимые таблицы, 304 через которые будет происходить миграция.

305 306

Пункт 4. Накатить полеченные DDL-ы в импортной 307 БД

308 309

Эти DDL-и нужно применить для импортной базы данных, чтобы в ней 310 все эти таблицы появились. Вот пример как это сделано в 311 DataGrip:

312 313

014.png

314 315

Сгенерировались следующие таблицы:

316 317
318  in_naturalperson - данные простых полей NaturalPerson
319  in_legalperson   - данные простых полей LegalPerson
320  in_naturalclaim  - данные простых полей NaturalClaim
321  in_clientclaim   - данные простых полей ClientClaim
322  in_naturalclaim_natural - связка NaturalClaim через поле NaturalClaim.Natural на бизнес-объект NaturalClaim
323  in_clientclaim_coclient_neturalperson - связка ClientClaim через поле ClientClaim.CoClient на бизнес-объект NaturalPerson
324  in_clientclaim_coclient_legalperson   - связка ClientClaim через поле ClientClaim.CoClient на бизнес-объект LegalPerson
325  in_filestorage - файлы для полей с файлами
326  process_tracking - индикация начала запуска
327  
328  
329  
330  
331 332

Пункт 5. Настроить подключение платформы MyBPM к 333 импортной БД;

334 335

Настройка подключения к импортной БД осуществляется в 336 конфиге:

337 338
339  /mybpm/configs/InMigrationPostgresConfig.txt
340  
341  
342  
343  
344 345

Там ранее уже прописали параметр schemaName. Теперь 346 нужно остальные прописать.

347 348
349  host=192.168.11.23
350  port=10018
351  dbName=migration
352  username=mybpm_migration
353  password=6dfxHTESrO
354  schemaName=in_tables
355  hasInMigration=true
356  
357  
358  
359  
360 361

В данном случае пароль и имя пользователя прописаны в открытом 362 виде. Если это недопустимо, то можно определить переменные 363 окружения, например такие:

364 365
366  IN_MIGRATION_DATABASE=migration
367  IN_MIGRATION_USERNAME=mybpm_migration
368  IN_MIGRATION_PASSWORD=6dfxHTESrO
369  
370  
371  
372  
373 374

И теперь прописать в конфиге 375 /mybpm/configs/InMigrationPostgresConfig.txt ссылки на 376 эти переменные окружения:

377 378
379  host=192.168.11.23
380  port=10018
381  dbName:ENV=IN_MIGRATION_DATABASE
382  username:ENV=IN_MIGRATION_USERNAME
383  password:ENV=IN_MIGRATION_PASSWORD
384  schemaName=in_tables
385  hasInMigration=true
386  
387  
388  
389  
390 391

В результате у конфигурационном файле нет отрытых паролей.

392 393

Пункт 6. Активировать миграцию и провести холостой 394 пуск

395 396

Теперь давайте активируем миграцию и проведём холостой пуск, 397 т.е. пуск на пустые таблицы, чтобы убедиться, что всё работает.

398 399

Информация о миграции кидается в категорию 400 in_pg_migration.

401 402

Вначале настроим информационный журнал миграции. Для этого 403 необходимо зайти в конфиг настройки журналирования:

404 405
406  /mybpm/logging/structure.txt
407  
408  
409  
410  
411 412

В этом файле найти запись (примечание, в редакторе 413 ZooNavigator-а есть горячая кнопа Ctrl+H для поиска)

414 415
416  category tracer_in_pg_migration
417    level TRACE
418    assign_to tracer_in_pg_migration
419  
420  
421  
422  
423 424

Эта категория отфильтровывает логи до уровня TRACE и отсылает их 425 к приёмнику tracer_in_pg_migration. Посмотрим на этот 426 приёмник. В этом же файле нужно найти запись (в zoonavigator-е для 427 поиска можно использовать горячую клавишу Ctrl+H)

428 429
430  destination tracer_in_pg_migration to_big_file traces/in_pg_migration
431    layout trace
432    level TRACE
433  
434  
435  
436  
437 438

Как видим этот приёмник кладёт логи в файл 439 migration/in_pg_migration.log. Найдём этот файл и 440 будем за ним наблюдать.

441 442
443

Миграция активируется в двух местах: активация факта миграции, 444 расписание запуска миграции, таблица 445 in_table.process_tracking

446 447

Сам факт миграции активируется в конфигурационном файле:

448 449
450  /mybpm/configs/InMigrationPostgresConfig.txt
451  
452  
453  
454  
455 456

В параметре:

457 458
459  hasInMigration=true
460  
461  
462  
463  
464 465

Ранее мы уже его поставили в состояние true, т.е. 466 миграция активирована. Проверьте это на всякий случай.

467 468
469

Чтобы миграция запускалась необходимо, чтобы таблица 470 in_table.process_tracking была пустой. Так как мы её 471 только что создали, то она должна быть пустой. Проверьте это. Если 472 там есть запись, то удалите её.

473 474

Запись в этой таблице показывает, что миграция уже отработала 475 или ещё работает, и повторную миграцию запускать не нужно.

476 477
478

Теперь нужно настроить расписание запуска миграции. Оно 479 находиться в конфигурационном файле:

480 481
482  /mybpm/scheduler/core/InMigrationScheduler.scheduler-config.txt
483  
484  
485  
486  
487 488

В параметре:

489 490
491  tryToStartMigration = repeat every 1 minute
492  
493  
494  
495  
496 497

Давайте сделаем чтобы инициация запуска миграции происходила 498 каждую минуту. Не беспокойтесь, параллельно вторая миграция не 499 запуститься, так как это не даст таблица 500 in_table.process_tracking. При запуске миграции в ней 501 появляется запись, которая блокирует последующий запуск миграции 502 параллельно.

503 504

Дальше смотрим ранее указанный лог. Там в течении минуты должны 505 появиться записи. Если там появилась ошибка

506 507
508  java.lang.RuntimeException: nYz1en475t :: config.hasInMigration() = true
509  
510  
511  
512  
513 514

То нужно перезапустить сервер. Изменения в конфиге не 515 применились.

516 517

При старте сервера может вылететь ошибка:

518 519
520  Caused by: org.postgresql.util.PSQLException: FATAL: password authentication failed for user "mybpm_migration"
521  
522  
523  
524  
525 526

Это обозначает что система не смогла подключиться к базе данных. 527 Проверьте правильность параметров в конфиге:

528 529
530  /mybpm/configs/InMigrationPostgresConfig.txt
531  
532  
533  
534  
535 536

И запустите сервер mybpm-api заново.

537 538

Если сервер запустился, значит доступ к БД настроен верно.

539 540

Вот нормальный лог миграции:

541 542
543  2024-12-26T08:59:33.261 INFO  Q inMigration g5mP3u7hF4 :: cleanErrorsTable start
544  2024-12-26T08:59:33.264 INFO  Q inMigration mX9xgIdrQt :: clean days = 10, limit = 10000
545  2024-12-26T08:59:33.268 INFO  Q inMigration vhhSIlN1W5 :: started saving data to kafka
546  2024-12-26T08:59:33.269 INFO  Q inMigration 1RHJ7aF2vu :: errors cleaner deleted 0rows in 6 ms.
547  2024-12-26T08:59:33.285 INFO  Q inMigration w7qP9r57 :: start migration to Kafka by companyId=6f27ebce5e49a79d69e522a8
548  2024-12-26T08:59:33.290 INFO  Q inMigration 27TtdVdLGj :: periodic_update_time updated
549  2024-12-26T08:59:33.298 INFO  Q inMigration uENuePJ3M1 :: start to vacuum system tables
550  2024-12-26T08:59:33.347 INFO  Q inMigration uENuePJ3M1 :: finish to vacuum system tables
551  2024-12-26T08:59:33.440 INFO  Q inMigration 7cUnbAdw29 :: in migration worker returned boStructure as null for Аккаунт
552  2024-12-26T08:59:33.445 INFO  Q inMigration 7cUnbAdw29 :: in migration worker returned boStructure as null for Пользователи
553  2024-12-26T08:59:33.448 INFO  Q inMigration 7cUnbAdw29 :: in migration worker returned boStructure as null for Департамент
554  2024-12-26T08:59:33.450 INFO  Q inMigration 7cUnbAdw29 :: in migration worker returned boStructure as null for Рабочая группа
555  2024-12-26T08:59:33.465 INFO  Q inMigration uAhZ1PFqMe :: copyInTableData start
556  2024-12-26T08:59:33.469 INFO  Q inMigration Gf4MsN7oi5 :: copyInTableData for table = in_NaturalPerson start
557  2024-12-26T08:59:33.479 INFO  Q inMigration 6ri5uiI970 :: copy table with name copy_2024_12_26_in_NaturalPerson is already exists, will be skipped
558  2024-12-26T08:59:33.479 INFO  Q inMigration 9o0WErV28h :: copyInTableData for table = in_NaturalPerson finish
559  2024-12-26T08:59:33.479 INFO  Q inMigration XvCndCKLGg :: extra table is empty, so no copy table will be created for extra
560  2024-12-26T08:59:33.480 INFO  Q inMigration AI0KjWef :: start setting status ON_WORK to in_tables.in_NaturalPerson occupied_id=612308124825686351
561  2024-12-26T08:59:33.489 INFO  Q inMigration CHWarE4y :: finish setting status ON_WORK to in_tables.in_NaturalPerson occupied_id=612308124825686351
562  2024-12-26T08:59:33.496 INFO  Q inMigration uAhZ1PFqMe :: copyInTableData start
563  2024-12-26T08:59:33.497 INFO  Q inMigration Gf4MsN7oi5 :: copyInTableData for table = in_LegalPerson start
564  2024-12-26T08:59:33.497 INFO  Q inMigration 6ri5uiI970 :: copy table with name copy_2024_12_26_in_LegalPerson is already exists, will be skipped
565  2024-12-26T08:59:33.497 INFO  Q inMigration 9o0WErV28h :: copyInTableData for table = in_LegalPerson finish
566  2024-12-26T08:59:33.497 INFO  Q inMigration XvCndCKLGg :: extra table is empty, so no copy table will be created for extra
567  2024-12-26T08:59:33.497 INFO  Q inMigration AI0KjWef :: start setting status ON_WORK to in_tables.in_LegalPerson occupied_id=612308124825686351
568  2024-12-26T08:59:33.502 INFO  Q inMigration CHWarE4y :: finish setting status ON_WORK to in_tables.in_LegalPerson occupied_id=612308124825686351
569  2024-12-26T08:59:33.505 INFO  Q inMigration 7cUnbAdw29 :: in migration worker returned boStructure as null for ЦО Клиент
570  2024-12-26T08:59:33.511 INFO  Q inMigration uAhZ1PFqMe :: copyInTableData start
571  
572  
573  
574  
575 576

При запуске миграции, в таблице:

577 578
579  in_table.process_tracking
580  
581  
582  
583  
584 585

Появиться одна запись, которая будет блокировать дальнейшие 586 запуски миграции.

587 588

Пункт 7. Загрузить данные и смотреть процесс 589 миграции

590 591

Теперь можно заполнять эти таблицы данными.

592 593

После заполнения этих таблиц нужно удалить запись в таблице 594 in_table.process_tracking и миграция запуститься в 595 течении минуты.

596 597

При миграции будет создана таблица:

598 599
600  in_tables.err
601  
602  
603  
604  
605 606

В которой будут помещаться ошибки при миграции. Так же будет 607 создана таблица:

608 609
610  in_table.task
611  
612  
613  
614  
615 616

В которой будут помещаться задания для миграции. Миграция 617 разбивается на задания, и помещаются в эту таблицу. При запуске 618 система исполняет эти задания. Там вначале помещаются задания для 619 миграции простых полей. А потом задания для миграции связей между 620 бизнес-объектами.

621
622 623 at kz.greetgo.md_reader.util.MdUtil.xmlTextToDoc(MdUtil.java:80) at kz.greetgo.md_reader.core.MdConverter.prepareHtmlFileFrom(MdConverter.java:136) at kz.greetgo.md_reader.core.MdConverter.convert(MdConverter.java:208) at kz.greetgo.md_reader.controller.RenderController.downloadToc(RenderController.java:360) at kz.greetgo.md_reader.controller.RenderController.request(RenderController.java:108) at jdk.internal.reflect.GeneratedMethodAccessor7.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1081) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:974) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at kz.greetgo.md_reader.interceptors.TextReplaceFilter.doFilter(TextReplaceFilter.java:36) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:166) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:341) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:894) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1741) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.io.IOException: Server returned HTTP response code: 429 for URL: http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:2000) at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1589) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.java:677) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1397) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.java:1333) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.startPE(XMLDTDScannerImpl.java:732) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.skipSeparator(XMLDTDScannerImpl.java:2101) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDecls(XMLDTDScannerImpl.java:2064) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.scanDTDExternalSubset(XMLDTDScannerImpl.java:299) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(XMLDocumentScannerImpl.java:1165) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.next(XMLDocumentScannerImpl.java:1040) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:943) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605) at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:542) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:889) at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:825) at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) at java.xml/com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.java:247) at java.xml/com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:342) at java.xml/javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:122) at kz.greetgo.md_reader.util.MdUtil.xmlTextToDoc(MdUtil.java:71) ... 48 more