Статьи

JBoss AS 7 объяснение загрузки классов

Это пример главы из книги « Развертывание и администрирование конфигурации JBoss AS 7» под редакцией Франческо Марчиони, который управляет специализированным порталом JBoss mastertheboss.com .

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

Это также называется изоляцией пространства имен (спецификации Java EE 5, раздел EE.8.4). Однако загрузка классов из разных пространств имен может вызвать некоторые проблемы, которые нелегко решить: например, что произойдет, если я добавлю в приложение более новую версию служебной библиотеки, когда приложение загрузит более старую версию этой же библиотеки сервер? Или также, как я могу одновременно использовать две разные версии одной и той же библиотеки утилит в одном и том же экземпляре сервера приложений? Стратегия загрузки классов JBoss AS с годами ощутимо изменилась: в основном, в версиях сервера приложений 4.x использовался UnifiedClassLoader , который был направлен на сокращение накладных расходов на связь между запущенными приложениями, поскольку данные класса могли передаваться по ссылке или простым копиям.

Одной из основных нерешенных проблем, не решаемых с помощью UnifiedClassLoader, является зависимость загрузки классов. Идея состоит в том, что если одно приложение (A) использует классы другого приложения (B), система должна знать, чтобы повторно развернуть A, когда B будет перераспределено, иначе это будет ссылка на устаревшие классы. На самом деле было две разные попытки попытаться заставить это работать без необходимости что-либо настраивать. Ни одна попытка действительно не сработала, и обе были отброшены. С момента появления JBoss AS 5.0 появился новый загрузчик классов на основе новой виртуальной файловой системы (VFS) . VFS была реализована для упрощения и унификации обработки файлов на сервере приложений. Новый загрузчик классов, называемый загрузчиком классов VFS, использует VFS для поиска файлов JAR и классов. Несмотря на то, что это представляет собой существенное изменение в том, как классы загружаются в JBoss AS 5.0, получающееся поведение во многом такое же, как и в предыдущих версиях JBoss AS.

Распространенным источником ошибок является включение классов API в развертывание, которые также предоставляются контейнером. Это может привести к созданию нескольких версий класса и неправильному развертыванию развертывания. Загрузка классов в AS7 знаменует радикальный отход от предыдущих попыток. Загрузка классов теперь основана на проекте модулей JBoss, и любое развернутое приложение фактически является модулем. Это подтверждение вызывает некоторые вопросы у внимательного читателя: какое имя модуля назначено для развернутого приложения? А также, как зависимости между модулями обрабатываются AS? Давайте ответим на каждый вопрос в отдельном разделе:

Знакомство с именами модулей

Знакомство с названием модуля не является академическим упражнением. Фактически, мы можем установить зависимости между модулями, поэтому во многих случаях необходимо знать, какое имя модуля назначено приложению. Поэтому приложениям, упакованным в архивы верхнего уровня (например, WAR, JAR и SAR), присваивается следующее имя модуля:

1
deployment.[archive name]

Например, веб-приложение с именем WebExample1.war будет развернуто как имя модуля:

1
deployment.WebExample1.war

С другой стороны, в приложениях, которые содержат вложенные модули (например, архив EAR), каждому отдельному архиву будет присвоено имя модуля с использованием этой классификации:

1
deployment.[ear archive name].[sub deployment archive name]

Таким образом, то же веб-приложение, если оно содержится в архиве EnterpriseApp.ear , будет развернуто с именем:

1
deployment.EnterpriseApp.ear.WebExample1.war

Нахождение уровня изоляции

В главе 2 «Настройка сервера приложений» мы намеренно развернули приложение, использующее log4j Api, добавив библиотеку log4j в папку WEB-INF / lib . Приложение было развернуто без помех, оставив вопрос: зачем нам добавлять библиотеки, которые уже включены в качестве модуля на сервер приложений? (В нашем случае в модулях \ org \ apache \ log4j \ main path).

Общее правило заключается в том, что в JBoss AS 7 каждый развернутый модуль приложения изолирован от других модулей; это означает, что по умолчанию он не имеет видимости для модулей AS, а модули AS не имеют видимости для приложения.

Однако использование модулей сервера приложений довольно просто и может быть сведено к простому предложению: добавьте зависимость к требуемому модулю, и AS его будет использовать. Сервер приложений автоматически добавляет зависимости, или они должны сигнализироваться пользователем:

  • Библиотеки базовых модулей (а именно классы Enterprise) квалифицируются как неявные зависимости, поэтому они автоматически добавляются в ваше приложение, когда средство развертывания обнаруживает их использование
  • Другие библиотеки модулей должны быть явно объявлены пользователем в файле MANIFEST приложения или в пользовательском файле развертывания JBoss с именем jboss-deploy-structure.xm l (подробнее об этом файле в разделе «Расширенные стратегии развертывания»).

Неявные зависимости:

Указание зависимостей для Api, которые обычно используются приложением Enterprise, может быть утомительным. Итак, как мы и ожидали, они автоматически добавляются для вас сервером приложений. Некоторые из них добавляются, когда сервер приложений обнаруживает некоторые аннотации или файлы конфигурации, типичные для этого модуля. Например, добавление файла beans.xml вызывает автоматическую зависимость Weld .

Следующая схема отображает синтетическое представление модулей, которые неявно добавляются в ваше приложение:

Смысл этого изображения прост: если ваше приложение использует какой-либо из указанных модулей ядра, вам не нужно указывать какую-либо зависимость, поскольку сервер приложений может автоматически связать модуль. Явные зависимости Модули, которые не квалифицируются как неявные зависимости, должны быть объявлены пользователем. В нашем первоначальном примере библиотека log4j не упоминалась как неявная зависимость, поэтому нам пришлось упаковать JAR log4j вместе с нашим приложением. Однако мы можем дать указание развертывателю использовать библиотеку log4j, которая входит в комплект поставки сервера приложений. Самый простой и рекомендуемый подход для достижения этой цели — включение в META-INF / MANIFEST.MF объявления Зависимости: [модуль]. В нашем случае, чтобы сделать ваше приложение зависимым от log4j, просто включите в файл манифеста следующий код:

Зависимости: org.apache.log4j

Обратите внимание, что имя модуля не всегда совпадает с именем пакета библиотеки. Фактическое имя модуля указывается в файле module.xml атрибутом name элемента module.

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

Вы не ограничены одной зависимостью, так как можете добавить несколько зависимостей через запятую. Например, чтобы добавить зависимость для log4j и Apache Velocity Api, вы должны использовать следующее:

Зависимости: org.apache.log4j, org.apache.velocity

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

Приложения, помеченные как зависимые от модуля deploy.WebApp.war, также будут иметь доступ к своим зависимостям:

Параметр экспорта также можно использовать для экспорта зависимости во все вложенные развертывания, содержащиеся в ухе. Следовательно, если вы экспортируете зависимость из верхнего уровня ear (или с помощью jar в каталоге ear / lib), то зависимость будет доступна также для всех подразделов развертывания.

В META-INF / MANIFEST.MF вы также можете указать дополнительные команды, которые могут изменить поведение сервера развертывания. Например, необязательный атрибут может быть добавлен, чтобы указать, что развертывание не завершится ошибкой, если модуль не найден во время развертывания.

Наконец, когда указано ключевое слово services, средство развертывания будет пытаться загрузить службы, размещенные в META-INF / services архива.

Служба Api стала общедоступной в Java SE 6. Службу можно определить как набор программных интерфейсов и классов, обеспечивающих доступ к некоторым конкретным функциональным возможностям или функциям приложения. Интерфейс поставщика услуг (SPI) — это набор открытых интерфейсов и абстрактных классов, которые определяет служба.

Вы можете определить поставщика услуг, реализовав API поставщика услуг. Обычно вы создаете файл JAR для хранения вашего провайдера. Чтобы зарегистрировать своего провайдера, вы должны создать файл конфигурации провайдера в каталоге META-INF / services файла JAR. При добавлении атрибута services в ваш META-INF / MANIFEST.MF вы фактически сможете загрузить службы, содержащиеся в каталоге META-INF / services.

Одно отличное введение в API SPI доступно по адресу: http://java.sun.com/developer/technicalArticles/javase/extensible.

Настройка глобальных модулей

Эта опция немного напоминает старый подход AS для загрузки общих библиотек, где вы использовали их для размещения в папке JBOSS_HOME / common / lib. Если вы определите раздел с именем global-modules в вашем standalone.xml / domain.xml, то вы сделаете модуль доступным для других модулей AS. Например, вместо объявления зависимости от log4j вы можете использовать следующий раздел:

1
2
3
4
5
<subsystem xmlns="urn:jboss:domain:ee:1.0">
  <global-modules>
    <module name="org.apache.log4j" />
  </global-modules>
</subsystem>

Хотя такой подход обычно не рекомендуется, поскольку он возвращает нас к концепции монолитного сервера приложений, он все же может принести некоторые преимущества. Например, если вы переносите некоторые старые приложения, и вы не хотите или просто не можете указать зависимости в архиве.

Расширенные стратегии развертывания

То, что мы узнали до сих пор, может быть достаточно для настройки многих видов приложений. Если вы используете сложную конфигурацию архива, такую ​​как архив EAR с несколькими модулями и зависимостями, было бы полезно определить стратегию загрузки классов в одном файле.

Конфигурационный файл jboss-deploy-structure.xml может сделать это именно так. Преимущество использования этого файла много:

  • Вы можете определить зависимости всех модулей приложения в одном файле
  • Вы можете загружать классы модулей, используя более детальный подход, включая / исключая все или часть модулей
  • Вы можете определить политику изоляции загрузки классов для ваших приложений, упакованных в архив Enterprise.

Давайте посмотрим на некоторых практических примерах, что может сделать для вас jboss-deploy-structure.xml.

Настройка зависимости одного модуля

Мы уже узнали, как активировать зависимость log4j, используя атрибут Dependencies в файле MANIFEST архива. Тот же эффект может быть достигнут с помощью файла jboss-deploy-structure.xml. Давайте вспомним структуру архива, которая в основном состоит из веб-приложения с именем WebApp.war.

Как видите, файл jboss-deploy-structure.xml необходимо поместить в папку META-INF EAR.

Вот его содержание:

1
2
3
4
5
6
7
<jboss-deployment-structure>
  <sub-deployment name="WebApp.war">
    <dependencies>
      <module name="org.apache.log4j" />
    </dependencies>
  </sub-deployment>
</jboss-deployment-structure>

Файл jboss-deploy-structure не предназначен для исключительного использования EAR; на самом деле вы можете развернуть его также в приложении WebApp, поместив его в папку WEB-INF архива. Однако он применим только в качестве архива верхнего уровня. Таким образом, если файл jboss-deploy-structure.xml помещен в папку WEB-INF WAR, а WAR упакован в архив EAR, то файл jboss-deploy-structure.xml игнорируется.

Соответствующей частью этого файла является элемент развертывания, который ссылается на веб-приложение, в том числе внутри него, на элемент зависимостей. Ожидаемый результат заключается в том, что сервер приложений будет вызывать зависимость от log4j Api, которая поэтому будет видна нашему веб-приложению.

Исключая автоматические зависимости сервера

Ранее в этой главе мы показали, как сервер приложений может автоматически запускать некоторые зависимости при выполнении некоторых условий. Например, если вы развертываете приложение JSF (содержащее файл face-config.xml), то реализация JSF 2.1 Api добавляется автоматически.

Это может быть не всегда желаемым вариантом, например, потому что вы хотите предоставить другую реализацию выпуска для этого модуля. Вы можете легко добиться этого, используя раздел исключения в jboss-deploy-structure.xml, как показано здесь:

01
02
03
04
05
06
07
08
09
10
11
12
<jboss-deployment-structure>
  <deployment>
    <exclusions>
      <module name="javax.faces.api" />
      <module name="com.sun.jsf-impl" />
    </exclusions>
    <dependencies>
      <module name="javax.faces.api" slot="1.2"/>
      <module name="com.sun.jsf-impl" slot="1.2"/>
    </dependencies>
  </deployment>
</jboss-deployment-structure>

Обратите внимание, что в разделе зависимостей мы добавили нашу альтернативную реализацию JSF 1.2, которая будет использоваться вашим приложением. На самом деле эта реализация JSF поставляется с дистрибутивом сервера приложений вместе с путем к модулю javax.faces.api в папке, указанной атрибутом slot. В нашем случае это соответствует папке JBOSS_HOME / modules / javax /faces / api / 1.2.

Изоляция суб развертываний

Предположим, у вас есть приложение, которое состоит из веб-приложения, модуля EJB и файла JAR, содержащего служебные классы. Все вложенные развертывания размещаются в корне архива, чтобы они могли видеть друг друга. Однако это может быть удобно, если предположить, что ваше веб-приложение содержит несколько реализаций одного и того же EJB-компонента. Это абсолютно возможно, поскольку спецификация Java EE 6 позволяет вашему веб-приложению включать классы EJB в папку WEB-INF / classes или WEB-INF / lib.

Как загрузчик классов решает этот конфликт? Загрузчик классов сервера приложений имеет список приоритетов при загрузке классов, которые используются для предотвращения любых конфликтов между загруженными классами.

  • Максимальный приоритет автоматически присваивается модулям контейнером, включая API Java EE. Библиотеки, которые содержатся в папке модулей, включены в эту категорию.
  • Затем библиотеки, указанные пользователем в файле MANIFEST.MF упакованного архива в виде зависимостей (или в файле jboss-deploy-structure.xml).
  • Затем библиотеки, упакованные в самом приложении, такие как классы, содержащиеся в WEB-INF / lib или WEB-INF / classes.
  • Наконец, библиотеки, которые упакованы в одном архиве EAR (в папке lib EAR).

Таким образом, в этом примере библиотеки EJB, расположенные в папке WEB-INF, будут «скрывать» реализации развертывания верхнего уровня EJB.jar . Независимо от того, является ли это желаемым действием из контейнера, вы все равно можете переопределить его:

1
2
3
4
5
6
7
8
<jboss-deployment-structure>
  <ear-subdeployments-isolated>false</ear-subdeployments-isolated>
  <sub-deployment name="WebApp.war">
    <dependencies>
      <module name="deployment.App.ear.EJB.jar" />  
    </dependencies>
  </sub-deployment>
</jboss-deployment-structure>

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

Обратите внимание на элемент, изолированный от ушей, размещенный в верхней части файла. Установив уровень изоляции EAR, вы сможете указать, видны ли модули суб-развертываний друг другу.

Значение по умолчанию для этого атрибута — false , что означает, что модули дополнительного развертывания смогут видеть друг друга. Если вы устанавливаете для изоляции значение true, каждый модуль будет затем загружен другим загрузчиком классов, поэтому в нашем примере веб-приложение не сможет найти классы, содержащиеся в библиотеке EJB.jar и Utility.jar .

Если вы хотите сохранить развёртывание изолированным, но разрешить некоторые из них, у вас есть несколько вариантов:

  • Переместите библиотеку в папку EAR / lib, чтобы она была выбрана в качестве отдельного модуля.
  • Укажите зависимость с помощью Dependencies или Class-Path в файле MANIFEST.MF вызывающего приложения.

На следующем снимке экрана вы можете увидеть, как правильно настроить EAR, поместив общие библиотеки в папку lib и добавив зависимость к классам EJB:

И это соответствующая конфигурация, требуемая в jboss-deploy-structure.xm l:

1
2
3
4
5
6
7
8
<jboss-deployment-structure>
  <ear-subdeployments-isolated>true</ear-subdeployments-isolated>     
  <sub-deployment name="WebApp.war">
    <dependencies>
      <module name="deployment.App.ear.EJB.jar" />
    </dependencies>
  </sub-deployment>
</jboss-deployment-structure>

Доступна упаковка библиотек в разделяемую библиотеку, поскольку Java EE 5 обычно используется для хранения файлов JAR, которые используются всеми модулями EAR.

Имя по умолчанию для разделяемой библиотеки — lib, однако вы можете переопределить ее в любое время, используя элемент library-directory в файле META-INF / application.xml . Например, предположим, что вы хотите использовать папку common для хранения общей библиотеки, вы можете добавить ее в application.xml :

1
<library-directory>common</library-directory>

В качестве дополнительного примечания мы рекомендуем вам не размещать аннотации, объявляющие компоненты (например, EJB3), в общей папке, так как это может иметь непреднамеренные и нежелательные последствия для процесса развертывания. По этой причине мы настоятельно рекомендуем размещать только служебные классы в папке общей библиотеки.

Использование объявления Class-Path для решения зависимостей:

До сих пор мы решали зависимости между модулями, используя способ JBoss, который мы, очевидно, предлагаем в качестве первого выбора. Тем не менее, мы также должны учитывать переносимый способ Java ссылаться на одну или несколько библиотек, включенных в файл EAR.

Это можно сделать с помощью атрибута Class-Path в файле MANIFEST.MF модуля, который должен ссылаться на другую библиотеку, которая в противном случае не могла бы быть видна приложению (вспомним предыдущий пример модуля развертывания с набором изоляции). к истине ).

Например, предположим, что вам нужно сослаться на приложение Utility.jar из вашего веб-приложения, а затем просто добавить в свой META-INF / MANIFEST.MF следующее:

  • Манифест-Версия: 1.0
  • Путь к классу: Utility.jar

На самом деле вы можете включить в Class-Path несколько библиотек, разделяя их запятыми, почти так же, как вы делали это с атрибутом Dependencies JBoss. В отличие от атрибута Dependencies, атрибут Class-Path указывает на фактическое имя файла JAR (а не имя модуля) для ссылки на зависимые библиотеки.

Выбор между подходом Class-Path и подходом JBoss ‘Dependencies зависит от того, как структурировано ваше приложение: с помощью JBoss’ Dependencies вы получаете более богатый набор опций, в частности, возможность экспорта зависимостей в другие развертывания, как мы показали ранее. Еще один момент в пользу подхода JBoss ‘Dependencies — это возможность ссылаться на модули, которые на самом деле не упакованы в приложении; Например, мы увидели, как добавить зависимость к log4j Api, которые являются частью дистрибутива сервера.

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

Это пример главы из книги « Развертывание и администрирование конфигурации JBoss AS 7» под редакцией Франческо Марчиони, который управляет специализированным порталом JBoss mastertheboss.com .