Статьи

JBoss Fuse — менее известный трюк

TL; DR

  1. выставлять Java статические вызовы как собственные команды оболочки Karaf
  2. переопределить заголовки OSGi во время развертывания
  3. переопределить заголовки OSGi после времени развертывания с фрагментами OSGi

Выставлять Java статические вызовы как собственные команды оболочки Karaf

Как часть моей работы в качестве инженера-программиста, которая должна сотрудничать с сотрудниками службы поддержки и клиентами , я очень часто испытываю необходимость извлечения дополнительной информации из системы, к которой у меня нет доступа. Обычные подходы, действительные во всех видах программного обеспечения, обычно извлекают журналы, вызывают интерактивные команды для получения определенных выходных данных или, в наиболее сложном случае, развертывают некоторый модуль PoC, который должен проверять определенное поведение.

JBoss Fuse, adn Karaf, платформа, на которой она основана, уже проделала большую работу по раскрытию всех этих данных.

У тебя есть:

  • обширные журналы и интеграция с Log4j
  • обширный список операций jmx (вы можете в конечном итоге вызывать через http с помощью jolokia )
  • большой список команд оболочки

Но иногда этого недостаточно. Если вы видели мой предыдущий пост о том, как использовать Byteman в JBoss Fuse , вы можете представить все остальные случаи:

  1. вам нужно напечатать значения , которые не зарегистрированы или не возвращены в коде
  2. вам может понадобиться замкнуть некоторую логику, чтобы попасть в конкретную ветвь выполнения вашего кода
  3. Вы хотите ввести строку кода , которого там вообще не было

Byteman по-прежнему очень хороший вариант, но у Karaf есть средство, которое мы можем использовать для запуска пользовательского кода .

Караф, позволяет писать код прямо в его оболочке; и позволяет записывать эти биты кода как макрос, который вы можете повторно вызывать. Этот макрос будет выглядеть как встроенная команда оболочки Karaf!

Давайте посмотрим на реальный пример, который я должен был реализовать:

убедитесь, что jvm, на котором запущен мой экземпляр JBoss Fuse, разрешает определенный DNS, как и ожидалось.

Стандартный JDK имеет метод, который вы можете вызвать для разрешения имени DNS: InetAddress.gettAllByName(String)

Поскольку эта команда достаточно проста, то есть не требует сложного или структурированного ввода, я подумал, что могу превратить ее в команду, которую легко использовать повторно:

1
2
3
# add all public static methods on a java class as commands  to the namespace "my_context":
# bundle 0 is because system libs are served by that bundle classloader
addcommand my_context (($.context bundle 0) loadClass java.net.InetAddress)

Эта забавная строка объясняется так:

  • addcommand — это функциональность оболочки karaf, которая принимает новые команды
  • my_context — это пространство имен / префикс, к my_context вы прикрепите команду. В моем случае «DNS» сделал бы хорошее пространство имен. ($.context bundle 0) вызывает код Java. В частности, мы $.context экземпляры $.context , которые являются встроенным экземпляром, предоставляемым оболочкой $.context , для предоставления инфраструктуры OSGi, тип которой org.apache.felix.framework.BundleContextImpl , и мы вызываем его метод, называемый передачей пакетов. это аргумент 0 представляющий идентификатор загрузчика классов OSGi, ответственного за загрузку классов JDK. Этот вызов возвращает экземпляр org.apache.felix.framework.Felix который мы можем использовать для загрузки определенного определения класса, который нам нужен, то есть java.net.InetAddress .

Как говорится во встроенном комментарии, вызов addcommand предоставляет все открытые статические методы для этого класса . Таким образом, теперь мы можем вызывать эти методы, и в частности тот, который может разрешать записи DNS:

1
2
3
4
5
6
7
JBossFuse:karaf@root> my_context:getAllByName "www.google.com"
www.google.com/74.125.232.146
www.google.com/74.125.232.145
www.google.com/74.125.232.148
www.google.com/74.125.232.144
www.google.com/74.125.232.147
www.google.com/2a00:1450:4002:804:0:0:0:1014

Эта функциональность описана на странице документации Karaf .

Переопределить заголовки OSGi во время развертывания

Если вы работаете с Karaf, вы работаете с OSGi, любите его или ненавидите . Типичным шагом в каждом рабочем процессе OSGi является игра (или борьба) с заголовками OSGi . Если вы полностью контролируете свой проект, это может быть более или менее легко, в зависимости от отношения между вашими подразделениями развертывания. Посмотрите пост Кристиана Поста, чтобы увидеть не совсем очевидный пример.

В этих условиях очень типичной является ситуация, когда вам приходится использовать пакет, свой или чей-то другой, и заголовки этого пакета неверны . То, что вы в конечном итоге делаете, очень часто заключается в том, чтобы повторно упаковать эти пакеты, чтобы вы могли изменить содержимое их MANIFEST , чтобы добавить необходимые заголовки OSGi.

В этом отношении у Карафа есть средство, называемое протоколом wrap . Возможно, вы уже знаете, что это быстрый способ развернуть банку без пакета на Карафе, но на самом деле это нечто большее .
На самом деле, как следует из названия, это завернуть. Но это может обернуть и не связки и связки! Это означает, что мы также можем использовать его для изменения метаданных уже упакованного пакета, который мы собираемся установить.

Давайте приведем пример, вновь взятый из реального жизненного опыта. Apache HttpClient не является полностью дружественным OSGi. Мы можем установить его на Karaf по протоколу wrap: и экспортировать все его пакеты .

01
02
03
04
05
06
07
08
09
10
11
12
JBossFuse:karaf@root> install -s 'mvn:org.apache.httpcomponents/httpclient/4.2.5'
Bundle ID: 257
JBossFuse:karaf@root> exports | grep -i 257
   257 No active exported packages. This command only works on started bundles, use osgi:headers instead
JBossFuse:karaf@root> install -s 'wrap:mvn:org.apache.httpcomponents/httpclient/\ 4.2.5$Export-Package=*; version=4.2.5'
Bundle ID: 259
JBossFuse:karaf@root> exports | grep -i 259
   259 org.apache.http.client.entity; version=4.2.5
   259 org.apache.http.conn.scheme; version=4.2.5
   259 org.apache.http.conn.params; version=4.2.5
   259 org.apache.http.cookie.params; version=4.2.5
...

И мы видим, что он работает и с простыми пакетами :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
JBossFuse:karaf@root> la -l | grep -i camel-core
[ 142] [Active     ] [            ] [       ] [   50] mvn:org.apache.camel/camel-core/2.12.0.redhat-610379
JBossFuse:karaf@root> install -s 'wrap:mvn:org.apache.camel/camel-core/2.12.0.redhat-610379\
$overwrite=merge&Bundle-SymbolicName=paolo-s-hack&Export-Package=*; version=1.0.1'
Bundle ID: 269
 
JBossFuse:karaf@root> headers 269
 
camel-core (269)
----------------
...
 
Bundle-Vendor = Red Hat, Inc.
Bundle-Activator = org.apache.camel.impl.osgi.Activator
Bundle-Name = camel-core
Bundle-DocURL = http://redhat.com
Bundle-Description = The Core Camel Java DSL based router
 
Bundle-SymbolicName = paolo-s-hack
 
Bundle-Version = 2.12.0.redhat-610379
Bundle-License = http://www.apache.org/licenses/LICENSE-2.0.txt
Bundle-ManifestVersion = 2
 
...
 
Export-Package =
    org.apache.camel.fabric;
        uses:="org.apache.camel.util,
            org.apache.camel.model,
            org.apache.camel,
            org.apache.camel.processor,
            org.apache.camel.api.management,
            org.apache.camel.support,
            org.apache.camel.spi";
        version=1.0.1,
 
...

Где вы можете увидеть Bundle-SymbolicName а версия экспортированных пакетов содержит установленные мной значения.

Опять же, функциональность описана в документах Karaf, и вы можете найти полезную ссылку на протокол обтекания .

Переопределить заголовки OSGi после времени развертывания с фрагментами OSGi

Последний трюк силен, но, вероятно, он требует удаления исходного пакета, если вы не хотите рисковать тем, что половина классов будет открыта одним загрузчиком классов, а остальные (те пакеты, которые вы могли добавить в переопределенный Export ) в другом один.

На самом деле есть лучший способ переопределить заголовки OSGi, и он приходит непосредственно от стандартной функциональности OSGi : фрагменты OSGi .

Если вы не знакомы с этой концепцией, определение, взятое непосредственно из OSGi wiki :

Фрагмент пакета или просто фрагмент — это пакет, содержимое которого доступно для другого пакета (хоста фрагмента). Важно отметить, что фрагменты разделяют загрузчик классов своего родительского пакета.

Эта страница также дает дополнительную подсказку о том, что я опишу:

Иногда фрагменты используются для «исправления» существующих пакетов.

Мы можем использовать эту стратегию для:

  • вставьте .jars в путь к классам нашего целевого комплекта
  • изменить заголовки нашего целевого пакета

Я использовал первый случай, чтобы исправить плохо сконфигурированный пакет, который искал дескриптор конфигурации xml, который он не включал, и который я предоставил для развертывания легкого Фрагмента, который содержал только это.

Но вариант использования, который я хочу показать вам здесь, является улучшением способа развертывания Byteman на JBoss Fuse / Karaf .

Если вы помните мой предыдущий пост , поскольку классы Byteman должны были быть доступны из любого другого развернутого пакета и, возможно, требовался доступ ко всем доступным классам, нам пришлось добавить пакеты org.osgi.framework.bootdelegation свойство org.osgi.framework.bootdelegation , которое инструктирует OSGi Framework выставить перечисленные пакеты через пакет виртуальной системы (id = 0) .

Вы можете проверить, что в данный момент обслуживается с headers 0 , я не буду включать здесь вывод, так как это длинный список классов расширения и каркаса jdk.

Если вы добавите ваши пакеты, org.jboss.byteman.rule,org.jboss.byteman.rule.exception в моем случае, даже эти пакеты будут перечислены в выходных данных этой команды.

Проблема с этим решением состоит в том, что это свойство времени загрузки . Если вы хотите использовать Byteman для манипулирования байт-кодом уже запущенного экземпляра, вы должны перезапустить его после того, как вы отредактировали эти свойства.

Здесь могут помочь фрагменты OSGi и избежать предварительной настройки во время загрузки.

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

1
2
3
4
5
6
<Export-Package>
    org.jboss.byteman.rule,org.jboss.byteman.rule.exception
</Export-Package>
<Fragment-Host>
    system.bundle; extension:=framework
</Fragment-Host>

Это отрывок конфигурации плагина maven-bundle-plugin, здесь вы pom.xml полный рабочий проект Maven , несмотря на то, что проект на самом деле всего 30 строк pom.xml :

1
JBossFuse:karaf@root> install -s mvn:test/byteman-fragment/1.0-SNAPSHOT

Получив эту конфигурацию, вы готовы использовать Byteman, например, для вставки строки в конструктор по умолчанию java.lang.String .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# find your Fuse process id
PROCESS_ID=$(ps aux | grep karaf | grep -v grep | cut -d ' ' -f2)
 
# navigate to the folder where you have extracted Byteman
cd /data/software/redhat/utils/byteman/byteman-download-2.2.0.1/
 
# export Byteman env variable:
export BYTEMAN_HOME=$(pwd)
cd bin/
 
# attach Byteman to Fabric8 process, no output expected unless you enable those verbose flags
sh bminstall.sh -b -Dorg.jboss.byteman.transform.all $PROCESS_ID
# add these flags if you have any kind of problem and what to see what's going on: -Dorg.jboss.byteman.debug -Dorg.jboss.byteman.verbose
 
# install our Byteman custom rule, we are passing it directly inline with some bash trick
sh bmsubmit.sh /dev/stdin <<OPTS
 
# smoke test rule that uses also a custom output file
RULE DNS StringSmokeTest
CLASS java.lang.String
METHOD <init>()
AT ENTRY
IF TRUE
DO traceln(" works: " );
traceOpen("PAOLO", "/tmp/byteman.txt");
traceln("PAOLO", " works in files too " );
traceClose("PAOLO");
ENDRULE
 
OPTS

Теперь, чтобы убедиться, что Byteman работает, мы можем просто вызвать конструктор java.lang.String в оболочке Karaf:

1
2
JBossFuse:karaf@root> new java.lang.String
 works:

И согласно нашему правилу, вы также увидите содержимое в /tmp/byteman.txt

Вдохновение для этого третьего трюка пришло как с OSGi Wiki, так и с этой интересной страницы от Spring, ребята .

Ссылка: JBoss Fuse — менее известный трюк от нашего партнера JCG Паоло Антинори в блоге Someday Never Comes .