Java/JPackagePolicyTranslation

Материал из ALT Linux Wiki

JPackage JavaTM infrastructure design and packaging policy

Nicolas Mailhot
JPackage Project
Ville Skyttä
JPackage Project

$Id: jpackage-1.5-policy.xhtml,v 1.2 2005/09/17 07:06:26 david Exp $

Резюме

Этот документ содержит политику упаковки в соответствии с JPackage cross-distribution RPM Java? packaging project.

Почему?

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

Как следствие, приложения были проверены только с версиями компонентов, с которыми они были связаны. Вся Java-система страдает бесконечным дублированием одних и тех же модулей, объединение повторяющихся частей может быть кошмаром, так как они обязаны зависеть от тех же самых компонентов -- только с различными и изощренно несовместимыми версиями[1]. Любое обновление по безопасности или по совместимости должно быть выполнено для всех этих дублирующихся компонентов.

Эта проблема получена в результате текущей практики вложения расширений прямо в JVM. По прошествии некоторого времени, компонент, который мог нормально быть встроен в приложение, неожиданно начинает конфликтовать с частью JVM и приводить к коварным падениям и ошибкам.

Это не удивительно, что сложные Java системы имеют тенденцию костенеть очень быстро. И приводить к быстрому росту стоимости обслуживания, что в свою очередь приводит к падению интереса к поддержанию таких систем в рабочем состоянии. И в конечном итоге люди бросают этим заниматься.

Данная ситуация не совместима с типичной быстро развивающейся Linux-системой. Чтобы достигнуть своей цели, создания дружественных для пользователей и администраторов методов rpm упаковки Java приложений, JPackage Project должен развить свою системную инфраструктуру и строгие правила упаковки.

Глава 1. Общие правила

Будь модульным

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

Be vendor and implementation agnostic

Пользователь должен иметь возможность выбора между различными реализациями одного и того же компонента, в особенности когда их лицензионные условия не эквивалентны или когда их уровень законченности отличается. Необходимо учесть возможность параллельной установки таких компонентов, с возможностью самостояетльного выбора пользователем между ними или лучшего из них системой[2]

Be distribution agnostic

Пакеты должны работать на большинстве основных rpm-based дистрибутивах. Это означает следование стандартам, таких как Linux Standard Base, Filesystem Hierarchy Standard или freedesktop.org спецификациям

Прежде всего запрещено использование дистрибутивно-специфичных особенностей[3]

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

Глава 2. Назначение имен

Имя пакета

Пакеты должны быть названы общим именем их оригинального проекта в нижнем регистре. Когда пакет предоставляет расширение, которое было в какой-то момент заложено в Java стандарте, -ext (как external) суффикс должен быть добавлен, для различия между именем пакета и именем расширения. И JVM, которое включает данное расширение, и автономный пакет расширения, должны иметь оригинальное название как virtual Provides.

JVM должны носить имя -- java-standard_version-vendor. Оригинальное название с сайта не должно использоваться в связи с широкой практикой разброса в именовании от вендора к вендроу и от версии к версии.

Название Jar-файлов

  • Если пакет предоставляет только один jar, он должен иметь тоже название что и пакет, с добавлением версии продукта.
jaf-1.0.2.jar
  • Безверсионная символьная ссылка, смотрящая на оригинальный файл тоже должна быть предоставлена (провайдена)
jaf-1.0.2.jar
jaf.jar -> jaf-1.0.2.jar
  • Если название проекта и часто используемое jar-имя отличаются, символьная ссылка на принятое имя должна так же быть предоставлена (провайдиться).
jaf-1.0.2.jar
jaf.jar ->  jaf-1.0.2.jar
activation.jar -> jaf-1.0.2.jar
  • Если пакет состоит из нескольких jar'ов, их обычные названия также будут использоваться.
ant-1.5.3.jar
ant.jar -> ant-1.5.3.jar
ant-optional-1.5.3.jar
ant-optional.jar -> ant-optional-1.5.3.jar

  • Если число jar'ов превышает два, или если они были предоставлены в виде монолитного jar'а, файлы должны быть помещены в поддиректорию с названием пакета (так же как и для одиночного jar'а).
javamail
javamail/imap-1.3.jar
javamail/imap.jar -> imap-1.3.jar
javamail/mailapi-1.3.jar
javamail/mailapi.jar -> mailapi-1.3.jar
javamail/pop3-1.3.jar
javamail/pop3.jar -> pop3-1.3.jar
javamail/smtp-1.3.jar
javamail/smtp.jar -> smtp-1.3.jar
  • Если проект предлагает выбор метода упаковки, между одним монолитным jar'ом или разбиением на несколько более мелких, упаковка с разбиением будет предпочтительнее.

Глава 3. Структура директорий

Основная структура директорий, предоставляемая пакетом jpackage-utils состоит из:

%{_javadir}

/usr/share/java

RPM макрос %{_javadir} задает главный jar-репозиторий. Исторически это была единственная директория, используемая 1.0 JPakage, прежде чем упаковочные ограничения привели к более сложной системе. Он обычно раскрывается в /usr/share/java.

Все jar-файлы и директории с jar-файлами, которые не зависят от конретной версии Java-стандарта или JNI должны быть установлены в %{_javadir}.

/usr/share/java-ext

От %{_javadir} мы получаем %{_javadir}-ext. Все jar-файлы и директории jar-файлов, которые зависят от конкретной версии Java-стандарта, но не от JNI, должны быть установлены в %{_javadir}-ext.

/usr/share/java-x.y.z

Директории %{_javadir}-x.y.z содержат символьные ссылки на файлы или директории для всех элементов %{_javadir}-ext, которые действительны для x.y.z версии Java-стандарта. С тех пор как реализация обычно действительна для ряда повторений Java-стандартов, файл или директория в %{_javadir}-ext будет содержать несколько символьных ссылок ссылающихся на нее. Так же следует отметить безверсионные символьные ссылки: для двух jar'ов названных foo13.jar и foo14.jar, foo.jar символьная ссылка будет указывать на foo13.jar в %{_javadir}-1.3.0 и %{_javadir}-1.3.1, и foo14.jar в %{_javadir}-1.4.0, %{_javadir}-1.4.1 и %{_javadir}-1.4.2.

К сожалению, Java-стандарт как известно меняется довольно сильно между младшими версиями, поэтому мы должны учитывать полную версию и различия между %{_javadir}-1.4.0 и %{_javadir}-1.4.1.

Версионные особенности Java-стандарт при использовании в репозитории

/usr/share/java-ext/jsse
/usr/share/java-ext/jsse/jcert-1.0.3.01.jar
/usr/share/java-ext/jsse/jcert.jar -> jcert-1.0.3.01.jar
/usr/share/java-ext/jsse/jnet-1.0.3.01.jar
/usr/share/java-ext/jsse/jnet.jar -> jnet-1.0.3.01.jar
/usr/share/java-ext/jsse/jsse-1.0.3.01.jar
/usr/share/java-ext/jsse/jsse.jar -> jsse-1.0.3.01.jar
/usr/share/java-1.3.0/jsse  -> /usr/share/java-ext/jsse

/usr/share/java-1.3.1/jsse -> /usr/share/java-ext/jsse

/usr/share/java-utils

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

%{_jnidir}: /usr/lib/java...

RPM макрос %{_jnidir} определяет главный JNI jar-репозиторий. Так же как и %{_javadir} разветвляется на ветки -ext и -x.y.z . Придерживается тех же правил что и %{_javadir}-ответвление, исключая, то что он содержит jar'ы, которые используют JNI.

%{_jnidir} обычно указывает на /usr/lib/java.


%{_jvmdir}

%{_libdir}/jvm: /usr/lib/jvm

RPM макрос %{_jvmdir} определяет корневую директорию, в которую устанавливаются различные JVM-системы. Обычно это /usr/lib/jvm.

%{_libdir}/jvm-exports: /usr/lib/jvm-exports

От %{_jvmdir} мы переходим к %{_jvmdir}-exports. Каждая поддиректория %{_jvmdir} должна иметь соответствующую в %{_jvmdir}-exports. Они используются для регистрации Java-расширений связанных с SDK или RE символьными ссылками, указывающими внутрь JMV структуры в %{_jvmdir}.

Символьные ссылки должны указывать на фактический JVM jar-файл, предоставляющий расширение (хотя это не жесткое требование для работы системы), должны присутствовать версионные и не версионные варианты и следовать общим правилам назначения имен.

%{_libdir}/jvm-private: /usr/lib/jvm-private

%{_jvmdir}-private директория содержит "внутренние" JVM-файлы, но по каким-то причинам расположенные не в стандартной JVM-директории. Скрипты не должны ссылаться на эти файлы. Внутри данной директории расположены версионные директории в соответствии с их назначением.

Как пример "внутренних" файлов можно рассмотреть файлы политики JCE (Java Cryptography Extension), те что идут вместе с различными 1.4.x JVM'ами ограничены в функциональности и производители поставляют неограниченные по функциональности файлы политик отдельно. Зачастую эти файлы -- часть какой-то версии Java-стандарта конкретного вендора (типа java-1.4.2-sun).

JCE файлы политик для Sun's J2SE 1.4.2:

/usr/lib/jvm-private/java-1.4.2-sun
/usr/lib/jvm-private/java-1.4.2-sun/jce
/usr/lib/jvm-private/java-1.4.2-sun/jce/vanilla
/usr/lib/jvm-private/java-1.4.2-sun/jce/vanilla/US_export_policy.jar
/usr/lib/jvm-private/java-1.4.2-sun/jce/vanilla/local_policy.jar
/usr/lib/jvm-private/java-1.4.2-sun/jce/unlimited
/usr/lib/jvm-private/java-1.4.2-sun/jce/unlimited/US_export_policy.jar
/usr/lib/jvm-private/java-1.4.2-sun/jce/unlimited/local_policy.jar

Различные версии jar'ов JCE-политик в дальнейшем управляются через систему альтернатив, использующую ссылку, которая указывает на соответствующий jar-файл в JVM jre/lib/security директории, с большим приоритетом на более функциональную версию чем та что принадлежит JVM.

%{_sysconfdir}/java: /etc/java

%{_sysconfdir}/java содержит основные файлы конфигурации, принадлежащие java-подсистеме, главным образом java.conf.

%{_javadocdir}

Это корень всех установленных javadoc документов. Его расположение и предполагаемое использование обсуждается в данный момент.

Директории зависимые от приложения

  • Собственные директории приложения должны располагаться в системе в соответствии с правилами Filesystem Hierarchy Standard
  • Если приложению необходимо свое файловое дерево, как в других операционных системах (а FHS требует, чтобы поддиректории были установленные в разные части системы), макрос %{_datadir}/appname должен быть использован как корневая-домашняя директория с символьными ссылками, указывающими на реальное расположение поддиректорий в системе. Конечно лучше изменить приложение, чтобы оно понимало правильное разделение файлов и отменяло пляску с символьными ссылками.

FHS и централизованная домашняя директория приложения:

/usr/share/tomcat4
/usr/share/tomcat4/bin
/usr/share/tomcat4/common -> /var/lib/tomcat4/common
/usr/share/tomcat4/conf -> /etc/tomcat4
/usr/share/tomcat4/logs -> /var/log/tomcat4
/usr/share/tomcat4/server -> /var/lib/tomcat4/server
/usr/share/tomcat4/shared -> /var/lib/tomcat4/shared
/usr/share/tomcat4/temp  -> /var/cache/tomcat4/temp
/usr/share/tomcat4/webapps  ->/var/lib/tomcat4/webapps
/usr/share/tomcat4/work -> /var/cache/tomcat4/work
  • Велосипеды лучше не создавать. Если директория всегда имеет только один единственный подкаталог, избавьтесь от него[5]
  • Если приложение использует classpath-компоненты, которые не являются jar-файлами, они должны быть установлены в собственную директорию приложения

zip, war и другие файлы архивов классов

Любой может столкнуться с другими типами архивов используемых в classpaths. Если прямой необходимости в использовании таких файлов нет, то следует конвертировать их в jar. Это особенно важно для zip-файлов, которые были одно время в широком использовании, но в последствии вышли из употребления.

  • Если приложение состоит из собственных jar-файлов, то это всегда будет только одного пользователя, и они могут быть установлены в собственные директории приложения.

Собственные jar-файлы

Это предполагает, что стандартные правила, не могут быть использованы для построения classpath-приложения, таким образом эта часть должна быть обработана самим приложением или мантейнером пакета[6]. Тем не менее, когда приложение может и будут читать собственный jar-репозиторий, мы действительно обеспечиваем средствами для управления частями репозитория, которые используются совместно с другими приложениями.

Глава 4. Скрипты и выбор classpath при запуске

Мы предполагаем что верные значения переменных окружения уже выставлены, по крайней мене $JAVA_HOME для выбора JVM в %{_jvmdir}, и в конечном счете $JAVACMD, $LD_ASSUME_KERNEL, $LANG, и $JAVA_COMPILER переменные.

FIXME: Исправить

Текущая реализация данной политики не столь хороша как хотелось бы и тащит с собой кучу наследственного мусора. Нормальная реализация будет использовать значения переменных из /.apprc, с откатом в /etc/app.conf, хранением пользовательских переменных окружения в /.java и /etc/java/java.conf. Если $JAVA_HOME после этого будет не определена, то по умолчанию будет использовано значение /usr/lib/jvm/java.

К сожалению, мы не делаем этого прямо сейчас.

Основные правила разрешения

Когда мы запрашиваем foo/bar-x.y, мы ищем foo/bar-x.y.jar jar-файл, затем foo/bar-x.y jar-директорию, в следующих местах:

  • %{_jvmdir}-exports/name Отображение JVM определено как $JAVA_HOME=%{_jvmdir}/name. Это JVM-специфичный репозиторий, в который вы вносим Java-расширения, принадлежащие ему.
  • %{_jnidir}-java_version Где java_version является JVM стандартным Java уровнем согласия как видно из
[bob@sys dir]$ $JAVACMD -version
  • %{_javadir}-java_version java-version specific non-JNI jar repository.
  • %{_jnidir} Основной JNI jar-репозиторий.
  • %{_javadir} Основной jar-репозиторий.

Если мы ничего не находим, поиск повторяется для foo/bar.jar, foo/bar, foo.jar, и наконец foo каталог. Заметим, поиск выполняется для пары jar/директория, т.е. поддиректория расположенная в более конкретном репозитории будет всегда иметь превосходство перед jar с таким же именем, но расположенном в менее конкретном репозитории.

find-jar

find-jar  {объект}

Команда find-jar проверяет расположение указанного объекта. Возвращает имя jar-файл или директории. Эта утилита предназначена исключительно для тестирования расположения объекта и не должна использоваться в скриптах. Данная команда является обязательной даже для определения classpath'а для единственного объекта, в связи с тем что поиск для единственного объекта может приводить к различным результатам, когда объект в итоге раскрывается в директорию.

[bob@sys dir]$ find-jar jndi
/usr/lib/jvm-exports/java-1.3.1-blackdown/jndi.jar

build-classpath

build-classpath  {объект...}

Команда build-classpath находит classpath, следуя основным правилам разрешения. Аргументами команды -- является список объектов. Если объект разрешается в директорию, все jar-файлы этой директории будут включены в classpath.

Результат выполнения build-classpath:

[bob@sys dir]$ export JAVA_HOME=/usr/lib/jvm/java-1.3.1-blackdown
[bob@sys dir]$ build-classpath jsse javamail/mailapi jaxp_parser_impl
/usr/share/java-1.3.1/jsse/jcert-1.0.3.01.jar:/usr/share/java-1.3.1/jsse/jnet-1.0.3.01.jar:/usr/share/java-1.3.1/jsse/jsse-1.0.3.01.jar:/usr/share/java/javamail/mailapi.jar:/usr/share/java/jaxp_parser_impl.jar
[bob@sys dir]$ export JAVA_HOME=/usr/lib/jvm/java-1.4.1-sun
[bob@sys dir]$ build-classpath jsse javamail/mailapi jaxp_parser_impl

/usr/lib/jvm-exports/java-1.4.1-sun/jsse.jar:/usr/share/java/javamail/mailapi.jar:/usr/share/java/jaxp_parser_impl.jar

Рекомендованная практика построения classpath, состоящей из достаточной и опциональной частей

Построение classpath с достаточной и опциональной частью:

CLASSPATH=$(build-classpath list_of_required_elements):$(build-classpath list_of_optional_elements 2> /dev/null)


build-jar-repository

build-jar-repository  [[-s] |  [--soft] |  [--symbolic] |  [-h] |  [--hard] |  [-c] |  [--copy]] [[-p] |  [--preserve-naming]] {директория} {объект...}

Команда build-jar-repository создает структуру директорий из симлинков на jar-файлы, по следующим правилам. Принимает в качестве аргументов имя директории и список объектов. После чего попытается создать набор символьных ссылок [foo][bar]xxx.jar в указанной директории для каждого запрошенного foo/bar объекта[7]. Особая структура имен символьных ссылок делает возможным определить были ли они созданным именно этой командой, и является необходимым условием для использования rebuild-jar-repository. Утилите build-jar-repository можно указать какого типа должны быть создаваемые объекты: символьной, жесткой ссылкой или копией файла. По умолчанию -- создается символьная ссылка, другой тип должен быть использован только для сильно поврежденного ПО.

Инкрементное использование

build-jar-repository' может быть свободно использован для одной и той же директории. Предыдущие симлинки не будут удалены. Удаление символьных ссылок в директории, производится только пользователем, утилита этого не делает.

Задание имен, для безопасного перемещения =

Если вы хотите распространить директория с jar копиями или ссылками на них, вы можете использовать ключ --preserve-naming. При его использовании, build-jar-repository будет создавать файлы с именами наиболее близкими к оригинальным. Заметим, что это приведет к будущим проблемам при обновления этого репозитория. Не используйте данный ключ, пока вы точно не будете уверены, что вы делаете.

--preserve-naming включает в себя --copy, пока не определено обратного.

Результат выполнения build-jar-repository
[bob@sys dir]$ export JAVA_HOME=/usr/lib/jvm/java-1.3.1-blackdown
[bob@sys dir]$ build-jar-repository lib jsse javamail/mailapi
[bob@sys dir]$ build-jar-repository lib jaxp_parser_impl
[bob@sys dir]$ tree lib
[nim@rousalka nim]$ tree lib
lib
|-- [javamail][mailapi].jar -> /usr/share/java/javamail/mailapi.jar
|-- [jaxp_parser_impl].jar -> /usr/share/java/jaxp_parser_impl.jar
|-- [jsse]jcert-1.0.3.01.jar -> /usr/share/java-1.3.1/jsse/jcert-1.0.3.01.jar
|-- [jsse]jcert.jar -> /usr/share/java-1.3.1/jsse/jcert.jar
|-- [jsse]jnet-1.0.3.01.jar -> /usr/share/java-1.3.1/jsse/jnet-1.0.3.01.jar
|-- [jsse]jnet.jar -> /usr/share/java-1.3.1/jsse/jnet.jar
|-- [jsse]jsse-1.0.3.01.jar -> /usr/share/java-1.3.1/jsse/jsse-1.0.3.01.jar
`-- [jsse]jsse.jar -> /usr/share/java-1.3.1/jsse/jsse.jar

rebuild-jar-repository

rebuild-jar-repository  [[-s] |  [--soft] |  [--symbolic] |  [-h] |  [--hard] |  [-c] |  [--copy]] {директория}

Команда rebuild-jar-repository обновляет jar-репозиторий, созданный build-jar-repository. Аргументом команды является имя директории, и предназначена для исправления символьных ссылок после изменения $JAVA_HOME, для совместимости с новым JVM. Опции для типа создаваемых объектов такие же что и у build-jar-repository.

Смешанное использование

Специфичные имена символьных ссылок, которые создаются build-jar-repository и rebuild-jar-repository позволяют смешивать автоматические ссылки, созданные в ручную и обычные файлы в директориях, обрабатываемых этими утилитами. Скрипты меняют только имена собственных ссылок

Результаты выполнения rebuild-jar-repository:

[bob@sys dir]$ export export JAVA_HOME=/usr/lib/jvm/java-1.4.1-sun
[bob@sys dir]$ rebuild-jar-repository lib
[bob@sys dir]$ tree lib
lib
|-- [javamail][mailapi].jar -> /usr/share/java/javamail/mailapi.jar
|-- [jaxp_parser_impl].jar -> /usr/share/java/jaxp_parser_impl.jar
`-- [jsse].jar -> /usr/lib/jvm-exports/java-1.4.1-sun/jsse.jar

Невозможность разрешения объекта

В отличие от build-jar-repository, rebuild-jar-repository будет создавать символьные ссылки. Это сделано для предотвращения потери объектов при сломанной java-подсистеме.[8] Созданная символьная ссылка указывает в "никуда" и всегда должна быть "поломанной".

Примечания

  1. Различные требования, различные ошибки
  2. Основанные на лицензионных условиях, известные завершенности, относительное отсутствие ошибок, рекламные проникновения и так далее
  3. Как Mandrake'овская gprintf функция.
  4. Для примера RPM 4 возможности, или Debian'ская система alternatives(8)...
  5. те не создавайте единственный подкаталог для библиотеки в %{_datadir}/appname, только потому что более сложные приложения делают это. Если он всегда будет один, используйте сразу %{_datadir}/appname
  6. Hardcoding classpath bits for example
  7. Единичный объект может быть разрешен в директорию с большим числом jar-файлов.
  8. Если устранить проблемы: установка нужных jar'ов или выбор более полной версии JVM, то rebuild-jar-repository отработает нормально, как-будто ничего и не случалось.