Недавно я заново открыл функциональность JBoss Fuse, о которой я забыл, и подумал, что другие люди могут воспользоваться этим напоминанием .
Этот пост будет посвящен JBoss Fuse и Fabric8, но может заинтересовать и всех тех разработчиков, которые ищут минимально инвазивные способы добавления некоторой степени динамической поддержки в свои файлы статической конфигурации .
Идея динамической конфигурации в OSGi и в Fabric8
Фреймворк OSGi чаще всего запоминается за поведение при загрузке классов. Но отчасти это также определяет другие концепции и функциональные возможности, которые должна реализовывать инфраструктура. Одним из них является ConfigAdmin .
ConfigAdmin — это служба для определения внешнего набора файлов свойств, которые логически связаны с вашими единицами развертывания.
Жизненный цикл этих внешних файлов свойств связан с жизненным циклом пакета OSGi: если вы измените внешний файл свойств, ваш пакет будет уведомлен . В зависимости от того, как вы закодировали свой пакет, вы можете решить отреагировать на уведомление, и программно или с помощью различных вспомогательных сред, таких как Blueprint, вы можете вызвать код, который использует новую конфигурацию.
Этот механизм удобен и эффективен, и все разработчики, использующие OSGi, знакомы с ним.
Fabric8 основывается на идее ConfigAdmin и расширяет ее .
С его возможностями обеспечения, Fabric8 определяет концепцию профиля, которая включает в себя единицы развертывания и конфигурацию. Он добавляет некоторый уровень функциональности поверх простого OSGi и позволяет управлять любым типом модуля развертывания, не только пакетами OSGi, а также любым типом конфигурации или статического файла.
Если вы посмотрите официальную документацию, вы найдете список «расширений», которые предлагает слой Fabric8, и узнаете, что они разделены в основном на 2 группы: обработчики URL-адресов и преобразователи свойств .
Я предлагаю всем, кто интересуется этой технологией, копаться в документации; но чтобы предложить краткое резюме и короткий пример, представьте, что ваши профили Fabric имеют возможность разрешать некоторые значения во время выполнения с использованием определенных заполнителей. ех.
1
2
3
4
5
|
# sample url handler usage, ResourceName is a filename relative to the namespace of the containing Profile: profile:ResourceName # sample property handler, the value is read at deploy time, from the Apache Zookeeper distributed registry that is published when you run JBoss Fuse ${zk: /fabric/registry/containers/config/ContainerName/Property } |
Из коробки доступно несколько обработчиков, охватывающих то, что разработчики считают наиболее распространенными вариантами использования: Zookeeper, Profiles, Blueprint, Spring, System Properties, Managed Ports и т. Д.
Также вы можете подумать о расширении механизма, определяющего ваше собственное расширение: например, вы можете захотеть реагировать на показатели производительности, которые вы храните в какой-либо системе, вы можете написать расширение с его синтаксическим соглашением, которое вводит значения, полученные из вашей системы.
Предел всей этой мощи: статические файлы конфигурации
Возможности, которые я представил выше, являются захватывающими и мощными, но у них есть неявное ограничение : они доступны только для файлов .properties или для файлов, о которых знает Fabric .
Это означает, что эти функциональные возможности доступны, если вам нужно управлять профилями Fabric, свойствами OSGi или другими конкретными технологиями, которые взаимодействуют с ними, такими как Camel, но они не включены ни для чего, кроме Fabric-Unaware .
Представьте, что у вас есть собственный код, который читает файл конфигурации .xml
. И представьте, что ваш код не ссылается на какой-либо объект или службу Fabric.
Ваш код будет обрабатывать этот .xml
файл как есть. Не будет никакой волшебной замены токенов или путей, потому что, несмотря на то, что вы работаете внутри Fabric, вы НЕ используете напрямую поддерживаемую технологию и НЕ уведомляете Fabric о том, что вам могут потребоваться ее услуги.
Для решения этой проблемы у вас есть 3 варианта :
- Вы пишете расширение для Fabric для обработки и распознавания ваших статических ресурсов и делегируете динамическую замену коду инфраструктуры.
- Вы изменяете код, содержащийся в вашем модуле развертывания , и вместо непосредственного использования статических ресурсов вы просите службы Fabric интерполировать их для вас.
- * Вы используете обработчик
mvel:
url (и не трогайте любой другой код!)
Что такое MVEL?
MVEL на самом деле является языком программирования : https://en.wikipedia.org/wiki/MVEL . В частности, это также язык сценариев, который вы можете запускать напрямую из исходного кода, пропуская этап компиляции.
Он на самом деле имеет несколько специфических характеристик, которые могут сделать его интересным для встраивания в другое приложение и использования для определения нового поведения во время выполнения. По всем этим причинам, например, это также один из поддерживаемых языков для проекта JBoss Drools, который работает с бизнес-правилами, которые вы, возможно, захотите определить или изменить во время выполнения.
Почему это может быть полезно для нас? Главным образом по 2 причинам:
- это хорошо работает как шаблонный язык
- У Fabric8 уже есть обработчик mvel
mvel:
url, который неявно действует как обработчик ресурсов!
Язык шаблонов
Языки шаблонов — это семейство языков (часто это доменные языки), где вы можете чередовать статическую часть текста, которая читается как есть, и динамические инструкции, которые будут обрабатываться во время синтаксического анализа . Я, вероятно, более сложным образом говорю ту же идею, которую я уже представил выше: в вашем тексте могут быть токены, которые будут переведены в соответствии с определенным соглашением.
Это похоже на возможности, предоставляемые обработчиками, которые мы представили выше. С важным отличием: хотя это были контекстно- зависимые обработчики, MVEL — это технология общего назначения. Поэтому не ожидайте, что он будет знать что-либо о профилях Zookeeper или Fabric, но ожидайте, что он сможет поддерживать общие концепции языка программирования, такие как циклы, вызов кода, отражение и так далее.
Ткань это поддерживает!
Ссылку на поддержку в Fabric можно найти здесь: http://fabric8.io/gitbook/urlHandlers.html
Но позвольте мне добавить фрагмент исходного кода, который реализует эту функциональность, так как это та часть, где вы могли бы найти этот подход интересным даже вне контекста JBoss Fuse: https://github.com/fabric8io/fabric8/blob/1 .x / ткань / ткань-ядро / SRC / главная / Java / IO / fabric8 / сервис / MvelUrlHandler.java # L115-L126
01
02
03
04
05
06
07
08
09
10
11
12
|
public InputStream getInputStream() throws IOException { assertValid(); String path = url.getPath(); URL url = new URL(path); CompiledTemplate compiledTemplate = TemplateCompiler.compileTemplate(url.openStream()); Map<String, Object> data = new HashMap<String, Object>(); Profile overlayProfile = fabricService.get().getCurrentContainer().getOverlayProfile(); data.put(“profile”, Profiles.getEffectiveProfile(fabricService.get(), overlayProfile)); data.put(“runtime”, runtimeProperties.get()); String content = TemplateRuntime.execute(compiledTemplate, data).toString(); return new ByteArrayInputStream(content.getBytes()); } |
Что тут происходит?
Во-первых, поскольку это не показано во фрагменте, помните, что это обработчик URL. Это означает, что поведение get запускается для файлов, на которые ссылаются через определенный URI. В данном случае это mvel:
Например, допустимый путь может быть mvel:jetty.xml
.
Другая интересная и относительно простая вещь, которую стоит заметить, это взаимодействие с интерпретатором MVEL. Как и большинство шаблонных технологий, даже самые простые, которые вы можете реализовать самостоятельно, вы обычно имеете:
- движок / компилятор, вот это
TemplateCompiler
- переменная, которая содержит ваш шаблон, здесь это
url
- переменная, которая представляет ваш контекст, то есть набор переменных, которые вы хотите представить движку, здесь
data
Сложите их все вместе, попросив движок выполнить свою работу, здесь с TemplateRuntime.execute(...)
и в результате вы получите статическую строку. Уже не шаблонные инструкции, но вся логика, которую определял ваш шаблон, была применена и в конечном итоге дополнена некоторыми дополнительными входными значениями, взятыми из контекста.
Пример
Я надеюсь, что мое объяснение было достаточно простым, но, вероятно, пример — лучший способ выразить концепцию.
Давайте используем jetty.xml
, содержащийся в JBoss Fuse default.profile
, который является статическим ресурсом, который JBoss Fuse не обрабатывает как какой-либо специальный файл , поэтому он не предлагает никаких функций замены для него.
Я покажу здесь оба аспекта интеграции MVEL: чтение некоторого значения из контекстных переменных и покажу, как можно использовать программную логику (просто сумму 2 целых здесь):
1
|
< Property name = "jetty.port" default = "@{ Integer.valueOf( profile.configurations['org.ops4j.pax.web']['org.osgi.service.http.port'] ) + 10 }" /> |
Мы изменяем значение по умолчанию для порта Jetty, беря его начальное значение из контекстной переменной «profile», которая является объектом с поддержкой Fabric и имеет доступ к остальной части конфигурации:
profile.configurations['org.ops4j.pax.web']['org.osgi.service.http.port']
мы явно приводим его из String к Integer:
Integer.valueOf( ... )
и мы добавляем статическое значение 10
к возвращаемому значению:
.. + 10
Давайте сохраним файл, остановим наш экземпляр fuse
. Перезапустите его и заново создайте тестовую Fabric:
01
02
03
04
05
06
07
08
09
10
|
# in Fuse CLI shell shutdown -f # in bash shell rm -rf data instances bin /fuse # in Fuse CLI shell fabric:create --wait- for -provisioning |
Просто подожди и проверь логи и … О-о-о. Ошибка! Что творится?
Это ошибка:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
2015-10-05 12:00:10,005 | ERROR | pool-7-thread-1 | Activator | 102 - org.ops4j.pax.web.pax-web-runtime - 3.2.5 | Unable to start pax web server: Exception while starting Jetty java.lang.RuntimeException: Exception while starting Jetty at org.ops4j.pax.web.service.jetty.internal.JettyServerImpl.start(JettyServerImpl.java:143)[103:org.ops4j.pax.web.pax-web-jetty:3.2.5] … Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)[:1.7.0_76] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)[:1.7.0_76] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)[:1.7.0_76] at java.lang.reflect.Constructor.newInstance(Constructor.java:526)[:1.7.0_76] at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration. set (XmlConfiguration.java:572)[96:org.eclipse.jetty.aggregate.jetty-all-server:8.1.17.v20150415] at org.eclipse.jetty.xml.XmlConfiguration$JettyXmlConfiguration.configure(XmlConfiguration.java:396)[96:org.eclipse.jetty.aggregate.jetty-all-server:8.1.17.v20150415] … Caused by: java.lang.NumberFormatException: For input string: “@{profile.configurations[’org.ops4j.pax.web'][‘org.osgi.service.http.port’] + 1}” at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)[:1.7.0_76] at java.lang.Integer.parseInt(Integer.java:492)[:1.7.0_76] at java.lang.Integer.<init>(Integer.java:677)[:1.7.0_76] … 29 more |
Если вы заметили, в сообщении об ошибке говорится, что наш шаблонный фрагмент не может быть преобразован в число .
Почему наш шаблонный фрагмент отображается в первую очередь? Движок шаблонов должен был выполнить свою часть работы и вернуть нам статическую строку без каких-либо ссылок на директивы шаблонов!
Я специально продемонстрировал вам эту ошибку, чтобы настаивать на концепции, которую я описал выше, но это может оказаться неосуществленным в первом случае.
Поддержка MVEL в Fabric, реализована в виде обработчика URL.
До сих пор мы только что изменили содержимое статического файла ресурсов, но мы не дали никаких указаний на Fabric, которые мы хотели бы обработать в этом файле как шаблон mvel.
Как это сделать?
Это просто вопрос использования правильного URI для ссылки на тот же файл.
Итак, измените файл default.profile/org.ops4j.pax.web.properties
который является местом в профиле Fabric по умолчанию, где вы определяете, какой статический файл содержит конфигурацию Jetty:
1
2
|
# change it from org.ops4j.pax.web.config.url=profile:jetty.xml to org.ops4j.pax.web.config.url=mvel:profile:jetty.xml |
Теперь снова остановите экземпляр, удалите файлы конфигурации Fabric, заново создайте Fabric и обратите внимание, как правильно работает ваш экземпляр Jetty.
Мы можем проверить это следующим образом:
1
2
|
JBossFuse:karaf@root> config:list | grep org.osgi.service.http.port org.osgi.service.http.port = 8181 |
Хотя из вашего браузера вы можете убедиться, что Hawtio, веб-консоль JBoss Fuse, развернутая на верхней части Jetty, доступна для порта 8191
: http: // localhost: 8191 / hawtio
Ссылка: | JBoss Fuse — Превратите свою статическую конфигурацию в динамические шаблоны с MVEL от нашего партнера JCG Паоло Антинори в блоге Someday Never Comes . |