Створення модульних додатків на мові java

Створення модульних додатків на мові java

Загальновідомо, що в мові програмування Java відсутня підтримка модульності. Ця проблема була помічена давно, робилися неодноразові спроби виправити ситуацію. Мова в першу чергу йде про проект Jigsaw.

Jigsaw puzzle - складова картинка-загадка (головоломка, в якій потрібно скласти окремі шматочки, щоб вийшла картинка). Кожен шматочок (модуль) з'єднується з іншими (за допомогою інтерфейсів), для того щоб вийшла закінчена картинка (програмне забезпечення).

Так що ж таке модульність, які переваги дає модульна архітектура ПЗ і що робити до появи Java 9? Модульна архітектура програмного забезпечення - це підхід до розробки, при якому програма розбивається на незалежні компоненти, які можуть взаємодіяти між собою за допомогою чітко описаних контрактів. Ці компоненти - модулі - повинні володіти декількома ознаками. Перший - інкапсуляція (вони повинні приховувати свою реалізацію від зовнішнього оточення), другий - слабка зв'язність (модулі взаємодіють тільки за допомогою заздалегідь обумовлених контрактів), третій - динамічність (можливість заміщатися «на льоту», без необхідності зупинки всього програми).

Що робити, коли стоїть завдання розробити по-справжньому модульну архітектуру на мові Java? На допомогу приходить OSGi (Open Services Gateway Initiative) - специфікація динамічної модульної системи і сервісної платформи для Java-додатків, що розробляється OSGi Alliance. Специфікація описує таку модель розробки ПЗ, при якій компоненти володіють всіма трьома описаними вище ознаками. Основою концепції OSGi є 2 сутності: набори (Bundles) і сервіси (Services).

набори OSGi

При розробці ПО на Java, як правило, використовуються сторонні бібліотеки. У світі Java бібліотеки упаковуються в спеціальні файли з розширенням JAR (Java ARchive). Це звичайний ZIP-архів, в якому знаходяться Java-класи (файли з розширенням .class). При цьому використання бібліотек може бути пов'язане з певними складнощами.

Як тільки ви починаєте використовувати будь-яку бібліотеку в своєму додатку, вам, як правило, стають доступні всі класи в цій бібліотеці. Справа в тому, що розробники бібліотек не мають можливості заховати класи, які використовуються для реалізації їх внутрішньої логіки. Таким чином, якщо ви використовуєте код, яка не передбачався для застосування поза бібліотекою, ви можете зіткнутися з несумісністю при використанні нової версії бібліотеки або порушити її коректне функціонування.

Ще однією проблемою є так званий JAR Hell. Багато розробники зламали про нього чимало списів. Суть полягає в тому, що як тільки у вас в проекті починають використовуватися різні версії однієї і тієї ж бібліотеки (як правило, це великі проекти, які еволюціонують з часом), ви можете зіткнутися з ситуацією, коли один і той же клас має різні методи в різних версіях бібліотеки. Java же влаштована так, що буде використана перша версія бібліотеки, яку знайде завантажувач класів. Тим самим, звернувшись в коді до якого-небудь класу під час виконання програми, ви отримаєте помилку, що метод, до якого ви звертаєтеся, не існує. Пов'язано це з тим, що на етапі виконання Java нічого не знає про версії бібліотеки, яка повинна використовуватися в тому чи іншому випадку.

Варто відзначити, що розробники OSGi вирішили не змінювати структуру JAR-файлів для забезпечення модульності, а просто додали в них додаткову інформацію, яка використовується середовищем OSGi. Більш того, ця додаткова інформація ніяк не впливає на використання JAR-файлів в звичайних Java-додатках. Отже, щоб JAR-файл став OSGi-набором, в нього додаються дані, які визначають, які пакети даного набору доступні для використання поза ним (Export-Package) і які пакети інших наборів потрібні для роботи цього набору (Import-Package). При цьому можна задати як версію API, яку набір надає для інших наборів, так і версію або діапазон версій API, які набір вимагає для своєї роботи від них же. Всі класи набору, які не перебувають в його експортується секції, не доступні поза набору (Private). Таким способом OSGi-набір виконує вимогу слабкою зв'язності.

Зараз більшість Java-бібліотек вже є OSGi ready, тобто містять інформацію для можливості виконання в OSGi-контейнері. Також існує безліч інструментів і утиліт, які дозволяють створити із звичайних JAR-файлів модулі для OSGi.

Мал. 1. Імпорт / експорт пакетів в OSGi-наборах

Створення модульних додатків на мові java

Так як залежності між наборами і інтерфейси, що надаються цими наборами, мають чіткі версії, Виконавча OSGi дозволяє легко уникати ситуацій JAR Hell, які ми описали вище.

Таким чином, трохи розширивши опис звичайного JAR-файлу і додавши підтримку цього опису в середу виконання, OSGi-спільнота вирішила проблему створення модулів в Java. Середовище виконання OSGi дозволяє динамічно завантажувати і вивантажувати нові набори під час виконання. Більш того, OSGi відстежує залежності між наборами і динамічно дозволяє їх.

сервіси OSGi

Отже, маючи OSGi-набори, ми можемо розробляти модульні додатки, які взаємодіють за допомогою інтерфейсів (API). Виникає питання: де взяти клас, який реалізує необхідний інтерфейс? Додати такий клас в API набору - погане рішення, тому що в рамках модульної архітектури ми домовилися не використовувати внутрішні класи наборів поза цими наборів. Можна було б використовувати шаблон «фабрика» для реалізації інтерфейсу і додати його в API набору, але розробляти кожен раз новий клас для приховування реалізації інтерфейсу теж здається не найкращою ідеєю.

OSGi вирішує завдання пошуку реалізації інтерфейсу за допомогою реєстру сервісів. Набір може зареєструвати реалізацію з описує її інтерфейсом в реєстрі сервісів. Набір, який використовує інтерфейс з іншого набору, може виконати пошук в реєстрі необхідної реалізації потрібного йому інтерфейсу. Як правило, набори реєструють сервіси при запуску в OSGi-контейнері. Більш того, в реєстрі сервісів можуть бути зареєстровані одні і ті ж інтерфейси з різними реалізаціями і додатковими ідентифікаційними даними. При цьому набір, який шукає в реєстрі необхідний йому сервіс, може вибрати найбільш підходящий (наприклад, використовуючи фільтрацію).

Мікросервісная архітектура у віртуальній машині Java

  • Легкість (при розробці мікросервіса виконується реалізація всього лише однієї конкретної частини функціональності. При цьому немає потреби турбуватися про інших частинах програми).
  • Простота заміни (якщо ви вирішите, що в поточній реалізації сервіс не справляється зі своїм завданням, ви легко можете переписати його і замінити на нову версію без будь-якого впливу на інші частини програми. Більш того, без зупинки поточного ПО).
  • Повторне використання (тому що мікросервіс виконує одну маленьку задачу, він може багаторазово використовуватися там, де це необхідно).

Як видно з опису вище, розробники модульних додатків із застосуванням OSGi давно користуються всіма перевагами мікросервісной архітектури, але тільки в рамках віртуальної машини Java. Кожен набір з OSGi, який публікує сервіс в реєстр, є мікросервісом всередині Java Virtual Machine, JVM (в світі OSGi такі мікросервіси називаються μServices).

Red Hat JBoss Fuse

Ми в Центрі програмних рішень використовуємо всі переваги OSGi при розробці ПЗ для телеком-операторів, страхових, процесингових компаній. Для цього ми застосовуємо продукт Red Hat JBoss Fuse, а конкретно його конфігурацію Fuse Fabric. Платформа JBoss Fuse надає гнучку OSGi-середовище для виконання модульного додатка.

Сьогодні до ПО пред'являються такі вимоги, як безперервність роботи, можливість легкого горизонтального масштабування, простота розгортання і наявність засобів управління кластером для кластерного ПО. Fuse Fabric вирішує ці завдання. Технологія дозволяє розгорнути кілька примірників Red Hat JBoss Fuse і об'єднати їх в кластер, а також надає централізований інструмент управління вийшла середовищем.

В рамках Fuse Fabric існують такі абстракції:

  • Фічі (features) - сукупність OSGi-наборів, які реалізують будь-якої функціонал.
  • Профілі (profiles) - сукупність фич, які повинні виконуватися в рамках одного контейнера, і конфігураційні налаштування для наборів, які входять в фичу.
  • Контейнери (containers) - окремі JVM-процеси, які виконуються на конкретному вузлі кластера Fuse Fabric під керуванням контейнера Red Hat JBoss Fuse.

Мал. 3. Ієрархія сутностей в Fuse Fabric

Створення модульних додатків на мові java

Таким чином, будь-яке ПЗ, яке будується на технології Fuse Fabric, складається з OSGi-наборів, які групуються в фичи і розгортаються в рамках будь-якого окремого JVM-процесу на одному або декількох вузлах кластера відповідно до заздалегідь заданим профілем.

Середа Fuse Fabric містить безліч інструментів, які дозволяють легко управляти отриманим кластером. Ми можемо створювати профілі, на основі профілів створювати контейнери, створювати / видаляти / запускати / зупиняти контейнери на будь-якому хості, який входить в кластер, підключати нові вузли кластера і т.д. І всі ці дії ми можемо виконувати online, тобто без переривання функціонування інших вузлів кластера. Середовище підтримує можливість зберігання кількох версій профілів, фич, OSGi-наборів.

В отриманій середовищі OSGi-набори можуть взаємодіяти не тільки в рамках одного контейнера, але і викликати один одного в рамках різних контейнерів (навіть в рамках різних хостів). Цю можливість забезпечує технологія Distributed OSGi. Додатково - при мінімальних затратах на розробку - кожен сервіс, який надає OSGi-набір, можна перетворити в REST / web-сервіс, який можна викликати з зовнішніх систем.

Таким чином, використовуючи Fuse Fabric, ми можемо створювати мікросервісную архітектуру, яка підтримує простоту настройки і розгортання, ізольоване виконання сервісів в рамках своїх JVM-процесів (зі своїми специфічними налаштуваннями, наприклад, різними настройками збирача сміття), при цьому сервіси взаємодіють між собою по мережі з використанням легкого протоколу.

Оскільки продукт Red Hat JBoss Fuse заснований на Open Source стеку технологій Apache ServiceMix, в нашому розпорядженні є такі потужні технології, як Apache ActiveMQ (реалізація специфікації JMS), Apache Camel (реалізація шаблонів проектування корпоративних додатків), Apache CXF (бібліотека для розробки REST / web СЕРВІС), Blueprint (надає можливості впровадження залежностей), Spring і т.д. Оскільки зазначені технології бесшовно інтегруються між собою в Red Hat JBoss Fuse, це значно знижує час розробки необхідного функціоналу.

Схожі статті