Темы



Оставьте пути классов в зеркале заднего вида

Представляем компонуемые инструменты командной строки для современной разработки на Java, являющиеся родными для системы модулей и удобными для агентов

Автор: Дэнни Томас (Danny Thomas), команда экосистемы JVM

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

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

Мы рады представить превью-версию ja и семейства компонуемых инструментов, которые опираются на возможности системы модулей Java, чтобы обеспечить современный опыт разработки на Java через командную строку. Мы делаем дескриптор модуля полным описанием проекта: версии зависимостей располагаются прямо рядом с директивами requires, а метаданные модуля задаются с помощью тегов документации:

/**
* @mainClass com.example.application.Main
*/
module com.example.application {
requires com.example.framework; // @1.2.3
}

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

Компонуемые инструменты

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

ja предоставляет только эргономику командной строки и оркестрацию инструментов, в то время как каждая функция подкреплена отдельным автономным инструментом. Вам не обязательно внедрять ja, чтобы получить преимущества этих инструментов — вы можете комбинировать их любым удобным для вас способом:

  • jig выполняет разрешение версий модулей, компиляцию и сборку, выводя стандартные аргументы системы модулей для использования с другими инструментами. Он также служит мостом к репозиториям Maven и из них, предоставляя автономный прокси модулей и команды публикации.
  • jfmt форматирует исходный код в соответствии с конвенциями кода для языка программирования Java, адаптированными для современного языка Java. Позволяет избежать очень распространенных проблем с пробелами, отступами, порядком импорта и квалифицированными ссылками на классы, которые появляются в коде, написанном агентами.
  • jist обеспечивает поиск символов с учетом исходного кода, предоставляя интерфейс в стиле grep для понимания файлов классов и связанных с ними исходных кодов. Предоставляет агентам программирования доступ к символам и исходным кодам без индексации, LSP или MCP, при этом взаимодействуя с другими инструментами сборки через контракт файлов аргументов.
  • jdocserver обслуживает локально просматриваемую документацию по API.

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

Это также модель обнаружения и выполнения инструментов для ja. В нем мы используем OptionChecker и необязательные пользовательские метаданные, чтобы определить, какие параметры системы модулей поддерживаются, что позволяет разрешать аргументы от имени инструмента. Это обеспечивает плавный переход от модулей пути к источникам к стандартным инструментам JDK, таким как jdeps, jlink и jshell.

Maven как основа

Согласно недавнему опросу 1000 самых популярных артефактов в Maven Central, только 232 имели явные определения модулей, а еще 248 объявляли имена автоматических модулей. Остальные 520 не выразили никакого мнения относительно имен модулей Java. Система модулей также не делает различий между пространством имен и именем модуля, поэтому инструменты, ориентированные в первую очередь на модули, требуют решения проблемы именования модулей и их размещения в существующих репозиториях.

К счастью, Maven Central уже предоставляет опубликованным артефактам проверенное пространство имен. Издатели подтверждают контроль над идентификаторами групп с обратным порядком доменов (reverse domain group ID), что отражает давнюю позицию Sonatype в отношении пространств имен в публичных репозиториях.

Мы используем эти соглашения для создания канонических координат модуля Maven, объединяя проверяемое пространство имен DNS с полным именем модуля, например pkg: maven/com.netflix/com.netflix.tools.ja. Для существующих модулей авторы могут опубликовать единый файл relocation pom в канонических координатах, чтобы обеспечить обнаружение исходных координат.

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

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

Целостность по умолчанию

ALL-UNNAMED, к сожалению, стал обычным делом в параметрах доступа Java из-за интенсивного использования путей классов (class path). Это скрывает источник технического долга, который накапливают приложения, разрешая такой доступ, и становится все более важным по мере того, как Java движется к целостности по умолчанию (Integrity by Default). Например, инициатива Подготовка к тому, чтобы сделать final действительно final требует от приложений явной авторизации модулей, которым разрешено изменять поля final.

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

/**
* @enableFinalFieldMutation com.example.framework
*/
module com.example.framework {
}

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

/**
* @mainClass com.example.application.Main
* @enableFinalFieldMutation com.example.framework
*/
module com.example.application {
requires com.example.framework; // @1.2.3
}

Интерфейс командной строки для ja позволяет добавлять зависимость и авторизацию одновременно:

ja require com.example.framework@1.2.3 \
--enable-final-field-mutation com.example.framework

Без этой авторизации разрешение зависимостей завершается с ошибкой из-за неудовлетворенного требования к доступу. Нативный доступ следует той же модели через @enableNativeAccess, также поддерживаются квалифицированный экспорт и открытие (qualified exports and opens).

Целостность модулей обеспечивается постоянными хэшами разрешенных бинарных зависимостей в файле module-info.hash, при последующем разрешении происходит проверка этих хэшей, и изменившийся артефакт отклоняется.

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

Сделайте модули своими основными компонентами

Мы считаем, что каждый проект на Java должен быть модульным, независимо от используемого инструмента сборки. Если вы являетесь автором библиотеки, создающей автоматические модули, мы рекомендуем вам избегать разделенных пакетов (split packages) и создавать явные модули.

Вы можете начать работу с нашими инструментами уже сегодня с помощью нашего руководства по установке.

Статья Leave the Class Path in the Rearview Mirror была изначально опубликована в блоге Netflix TechBlog на платформе Medium, авторы продолжают делиться историями в этой публикации.

© Netflix Tech Blog