Привет! Большинство разработчиков не спешат обновляться до новых версий Java. Многие опасаются, что все сломается, что появятся скрытые баги, что это займет очень много времени. Сегодня мы поделимся опытом перевода IGA-системы Solar InRights с Java 8 на актуальную Java 16, расскажем, для чего мы это сделали и почему именно сейчас. Подробно опишем, какие проблемы могут возникнуть при обновлении и как их устранить, а главное – поделимся тем, что мы в итоге получили. Ранее в разработке мы использовали Java 8. Эта версия платформы и сейчас является достаточно стабильной и функциональной, в целом это понятная и удобная версия. Однако в экосистеме произошло несколько важных событий, которые подтолкнули нас к переходу: Была выпущена Java 9 со значительными архитектурными изменениями. Начиная с этой версии платформа Java является модульной, появляются разные выпуски JVM с различным набором фич. Эти изменения ломают обратную совместимость, и большинство программ, написанных на Java 8, не запускаются на Java 9+. С выпуском Java 11 компания Oracle представила новую модель лицензирования и подписки. Теперь нужно особенно внимательно следить за тем, какую версию и где мы используем, чтобы не нарушить лицензию. Релиз Java 16 включает множество новых возможностей в языке Java. Да, кто-то обязательно возразит, что аргумент слабый. Можно и дальше пользоваться Java 8, где все понятно, просто и удобно. Но с новыми синтаксическими конструкциями Java 16 писать код удобнее, а отслеживать ошибки проще. Наряду с уже перечисленными причинами это довольно приятный бонус. У нас появилось свободное время на развитие системы, поэтому мы решили перейти на актуальный выпуск Java, и чем раньше это произойдет, тем легче будет процесс перехода. Версии с 11 по 16 называются промежуточными, потому что у них короткий период поддержки – 6 месяцев. На сентябрь 2021 года запланирован LTS (Long Term Support) релиз Java 17. Так мы решили перейти на Java 16, чтобы затем быстро перейти на LTS Java 17. Система Solar inRights в собранном виде – это веб-приложение в виде war-файла, которое запускается на сервере Tomcat. Для сборки используем Maven. Код организован по функциональным модулям. Один модуль – это один maven-проект. Система состоит из нескольких слоев: Мы активно используем Spring для инверсии зависимостей, Hibernate для абстракции над логикой баз данных и JAXB для сериализации данных. Для написания тестов используем язык Groovy, а запускаем тесты с помощью TestNG. В результате получается модульный монолит, в classpath которого собраны ресурсы от всех зависимостей. На текущий момент новейшая стабильная версия JDK – это 16. Это preview release, значит некоторые языковые конструкции могут быть изменены или отсутствовать в следующей версии Java. Мы решили, что будем собирать систему с помощью Liberica JDK 16 с русскоязычной поддержкой, но нам подойдет и OpenJDK, и любая открытая сборка JDK без специальных особенностей. Просто взять и собрать систему на JDK 16 не получилось. Видим море ошибок, говорящих о том, что наш код обращается к закрытым частям платформы. Теперь нельзя обращаться к com.sun.*,  java.security.* и прочим внутренностям JDK. Изменяем работу с XML на использование открытых API. Было: Стало: При сборке Maven отказывается читать наши Mojo, потому что не знает, как читать байткод классов, скомпилированных в Java 16. Обновляем версии зависимостей maven-plugin-tools (на текущий момент это 3.6.1): У  Maven также будут проблемы с чтением конфигурации из javadoc. Исправляем формат javadoc в Mojo классах. Было: Стало: Обновляем версию плагина Maven для запуска TestNG: Теперь нужно обновить библиотеки проксирования и манипуляций с байткодом – все, что изменяет внутреннюю структуру классов. У нас получилось так: Поскольку Java 16 является preview версией, необходимо добавить параметр сборки --enable- preview. Для Maven можно создать файл .mvn/jvm.config со следующим содержанием: Это удобно, потому что опция записана в одном месте, и ее можно удалить, когда мы перейдем на LTS выпуск Java 17. Запуск тестов показал, что внутри нам требуется доступ к инкапсулированным модулям  платформы:  В несколько итераций получаем подходящий список параметров запуска: Последний шаг – обновить сервер приложений Tomcat. Сейчас новейшей версия – 10, и в ней разработчики требуют, чтобы мы переписали все использования javax.* на jakarta.* – однако нам подойдет Tomcat 9, его последние сборки тоже поддерживают Java 16. Наконец, система полностью собрана на Java 16 и может быть запущена на любом JDK. Мы  используем Liberica JDK и OpenJDK. При тестировании мы увидели небольшие различия в поведении системы по сравнению с Java 8: Форматирование XMLGregorianCalendar теперь использует формат, настроенный в системе по умолчанию, вместо единого заданного формата. Если мы хотим сохранить прежний формат, мы должны явно его указать. Теперь не допускается хранение в resources файлов, имеющих кириллические символы в названии. JVM теперь не завершит работу, пока все потоки программы не завершат выполнение. Это хороший повод разобраться с ThreadPool, потому что Tomcat не завершит работу до тех пор, пока они не будут закрыты. В целом обновление прошло быстро, гладко, без сюрпризов. Что мы получили результате: Больше не опасаемся лицензионных ограничений, связанных с Java 8. Чуть более безопасную и производительную платформу с закрытыми уязвимостями, исправленными проблемами быстродействия, новыми фичами. Новые синтаксические конструкции в языке Java, которые уже давно есть в Kotlin и Groovy (например, улучшенный instanceof, record-классы, текстовые блоки). Возможность обновить сторонние зависимости, фреймворки, инструменты. Java 16 является промежуточным обновлением платформы с коротким циклом поддержки. Мы ожидаем стабильного релиза Java 17 в сентябре, чтобы сразу перевести Solar InRights на LTS выпуск платформы. А это в свою очередь, вместе с обновлением фреймворков и других библиотек, повысит качество продукта в целом. Автор: Олег Гетманский, архитектор информационных систем "Ростелеком-Солар"