Pull to refresh

JRebel

Reading time8 min
Views38K
На Хабре несколько раз публиковались статьи, где JRebel либо просто упоминался, либо выкладывалась информация, что вышла новая версия. При этом, не всем читателям было понятно, о чём вообще речь, и как данное ПО работает.

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

Откуда ноги растут?



Изначальная проблема известна практически любому разработчику, который работает с Java: после каких-либо изменений в проекте, для того, чтобы увидеть результат, тратится довольно много времени на сборку и развёртывание в контейнере. На Хабре уже публиковались отличные статьи о том, как можно ускорить или автоматизировать процесс разработки, не стану повторяться. Но дело в том, что в упомянутых способах есть свои изъяны: далеко не все изменения возможно перегрузить в развёрнутом приложении штатными средствами; очень легко получить утечки памяти, которые приведут к надобности перезапуска контейнера. Технические детали хорошо расписаны в серии статей в нашем сайте — любопытных приглашаю почитать.

Куда уходит время?


Как выглядит цикл разработки web-приложения, в классическом виде:
1. Сделали изменения в коде (или в ресурсах)
2. Собрали JAR/WAR/EAR
3. Развернули полученный архив в контейнере
4. Открыли развёрнутое приложение, и, после некоторых манипуляций увидели результаты своего труда.

В зависимости от размера приложения, используемого контейнера, и некоторых других факторов, этапы 2, 3 и 4 могут занимать от нескольких секунд, до совершенно невменяемых цифр. Наша компания проводила опрос разработчиков относительно используемых технологий и времени которое затрачивается на развёртывание приложения. Как оказалось, в среднем на развёртывание тратится около 3 минут за раз, и около 10 минут в час. В плачевных случаях, где на развёртывание уходит более получаса, нет даже смысла спрашивать у человека, сколько раз в час он может повторить этот процесс. Ответ очевиден.

Когда перезапуск контейнера/приложения занимает считанные секунды, проблема, описанная выше, не ощущается так сильно. Однако, по мере роста и усложнения проекта, неудобства дадут о себе знать. Тут-то и можно задуматься: может быть, JRebel — это то, что вам нужно?

JRebel в помощь!



Итак, JRebel — это инструмент, призванный избавить от проблемы повторного развёртывания приложения во время разработки, т.е. сэкономит вам много времени.

Какие приемущества появляются при использовании JRebel:
1. Поскольку JRebel умеет загружать ресурсы прямо из рабочего пространства, то отпадает надобность собирать полный архив приложения (JAR/WAR/EAR). Остаётся только скомпилировать изменённый код, что занимает гораздо меньше времени, чем полная сборка архива.
2. Не происходит повторного развёртывания приложения — снова экономим время.
3. Не создаются новые загрузчики классов (classloaders), поэтому меньше риск получить утечки памяти при обновлениях. Соответственно, экономим время на вынужденных перезапусках самого контейнера.
4. Сохраняется состояние объектов и пользовательской сессии. Поэтому, в идеале, можно оставаясь на одной и той же странице, видеть результаты изменений — лишь жми F5.

Установка и настройка


Есть несколько вариантов установки. Т.к. большинство разработчиков всё-таки работают используя IDE, такие как Eclipse, NetBeans или IntelliJ, то и естественным способом установки является установка плагина для отдельно взятого IDE.

Пользователи Eclipse, для установки, могут воспользоваться сервисом Eclipse Marketplace. Пользователи NetBeans и IntelliJIDEA могут найти JRebel в соответствующем списке плагинов. Детальные инструкции можно найти здесь. После установки плагина вам дадут знать, что неплохо бы зарегистрироваться и получить лицензию



После регистрации JRebel готов к употреблению.

На самом деле, JRebel не привязан к конкретной среде разработки, т.к. работает он не в IDE, а там, где запущено приложение — т.е. привязан к JVM процессу при помощи -javaagent аргумента, примерно так:

java -javaagent:/opt/jrebel/jrebel.jar -cp. my.awesome.Application

Конфигурация


В большинстве случаев JRebel не требует дополнительной настройки, лишь конфигурационный файл — rebel.xml — который может быть сгенерирован автоматически при помощи IDE-плагина.



Суть в том, что агент (javaagent), коим и является JRebel, должен знать, где находятся скомпилированные классы, и статические файлы (html, css, итд). Это позволит загружать все требуемые ресурсы не из развёрнутого архива, а прямо из проекта, где программист и вносит свои изменения.

Если бы все проекты следовали бы “стандартной” структуре каталогов, и компилировались бы только штатными средствами IDE, то возможно, конфигурационный файл был бы и не нужен — мы могли бы получать всю информацию о проекте из IDE или работать просто по конвенции. Но поскольку у всех разработчиков своё представление о стандартах, то и JRebel нуждается в некоторых подсказках.

Сам конфигурационный файл имеет довольно простую структуру — нужно всего лишь задать classpath и место нахождения статических ресурсов:

<?xml version="1.0" encoding="UTF-8"?>
<application
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.zeroturnaround.com"
  xsi:schemaLocation="http://www.zeroturnaround.com http://www.zeroturnaround.com/alderaan/rebel-2_0.xsd">
 <classpath>
    <dir name="c:\myWorkspace\myWar\target\classes"/>
    <dir name="c:\myWorkspace\myWar\src\main\resources"/>
 </classpath>
  <web>
    <link target="/">
      <dir name="c:\myWorkspace\myWar\src\main\webapp"/>
   </link>
    <link target="/jsps/">
     <dir name="c:\myWorkspace\myWar\src\main\jsps"/>
   </link>
  </web>
</application>



Как видите, при помощи такого файла, возможно отобразить любую, самую нестандартную структуру проекта — на моём опыте, такие требования возникают довольно часто. Детальное описание опций для rebel.xml можно найти в документации.

Важно! Для того чтобы JRebel мог правильно зачитать конфигурацию, rebel.xml должен быть приложен к развёртываемому приложению: для web-приложений, rebel.xml должен оказаться в WEB-INF/classes. Для JAR файлов rebel.xml должен оказаться в корне архива.



В идеальном случае в архиве может находиться только web.xml и rebel.xml, а остальные ресурсы могут быть отображены через пути в rebel.xml.

В случае Maven проектов, есть возможность использовать плагин, который умеет генерировать конфигурационный файл, и сохраняет его в правильное место.

Запуск


Из IDE запуск достаточно тривиален. В случае с IntelliJIDEA достаточно воспользоваться новой кнопкой запуска, которая появилась после установки плагина:



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



В случае, если хочется запускать контейнер вне среды разработки, то достаточно добавить правильный путь к jrebel.jar в параметре -javaagent в скриптах для избранного контейнера. В руководстве по конфигурации приведён список контейнеров с примерами скриптов для запуска для разных JVM и ОС.

Возможности



Кроме отображения проекта в развёрнутом приложении, что является просто удобным средством автоматизации, JRebel предлагает ещё некоторую функциональность. Прежде всего — это основная функциональность — перегрузка изменений в коде. Допустим по ходу работы наткнулись на метод, который вот руки как чешутся порефакторить. Сказано-сделано, вызываем extract method, что приводит к добавлению нового метода в класс. Стандартное средство для перегрузки кода, HotSwap, с таким изменением справиться не может. JRebel, в свою очередь, класс перегрузит и напишет об этом в консоль. Перегрузка состоится в тот момент, когда изменённый класс будет использован, тем самым симулируется ленивое поведение которое присуще Java.

Далее, предположим мы используем аннотации для настроек в рамках какого-либо фреймворка. Например, значение @RequestMapping используется для определения пути по которому будет доступен контроллер. Поменяв значение аннотации, мы ожидаем обнаружить ресурс по новому пути в нашем приложении.
Отработает это следующим образом: после компиляции, значение аннотации будет доступно в новой версии класса. Аннотации — это не исполняемый код, а некоторые мета-данные, в зависимости от которых меняет своё поведение сам фреймворк. Теперь, когда новая версия класса будет подгружена, нужно дать знать фреймворку знать, что мета-данные поменялись и стоит обновить своё поведение. Для таких случаев в JRebel необходима специальная интеграция для каждого фреймворка или контейнера.

Так же как и в случае с аннотациями, специальная интеграция требуется в случае если фреймворк опирается на внешнюю конфигурацию. Так например в случае со Spring Framework конфигурации могут задаваться как через аннотации так и через XML файлы.
К примеру, добавили новый компонент (bean) в XML конфигурации, и при помощи @Autowired аннотации хотим передать новоиспечённый компонент в контроллер.

Ограничения


Как и у всех подходов описанных на Хабре и у JRebel есть некоторые технические ограничения.

На момент написания этой статьи не поддерживается изменения иерархии классов, т.е. если в программе один класс уже описан как “A extends B” то изменить его на “A extends C” нельзя. Тоже самое относится и к изменению списка интерфейсов — нельзя добавить или удалить интерфейсы из объявления класса.

Существуют ещё некоторые моменты, которые стоит учитывать.

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

Изменив значение статического поля мы ожидаем увидеть это новое значение в новой версии класса. Значение это, на самом деле, будет присвоено в статическом блоке, который JRebel не перезапустил. Соответственно, нового значения мы не увидим. В данный момент JRebel перезапустит статический блок только в случае добавления нового статического поля.

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

Второе следствие, которое выходит из того, что JRebel не перезапускает конструкторы, это то, что при добавлении нового поля в класс будет присвоено значение “по умолчанию” для данного типа. Т.е. если добавить поле, тип которого не будет примитивом, то присвоенное значение будет null, что в общем случае может повлечь за собой NullPointerException, в случае если это поле будет разыменовано для существующего объекта.

JRebel SDK



Как было описано выше, для поддержки конфигураций фреймворков в JRebel требуется специальная интеграция. Мы потратили уже довольно много сил и времени для реализации всевозможных интегараций, и список поддерживаемых фреймворков довольно внушителен. При работе с некоторыми фреймворками, случается, что интеграция и не требуется и JRebel работает с ними довольно неплохо “из коробки”. За примерами ходить далеко не надо: не имея специальной интеграции для Vaadin, JRebel работает с ним довольно хорошо, и наши финские коллеги очень довольны тем, что они могут использовать JRebel с собственным фреймворком.
Таких примеров, к сожалению, не так много. Большая часть фреймворков всё таки опирается на внешнюю конфигурацию, для которой требуется дополнительная интеграция. Мы бы рады реализовать поддержку всех и вся, но успеть за всеми неизвестными фреймворками мы не можем, да и во многих фирмах создаются свои «велосипеды», кода которых мы никогда не увидим. Для таких случаев в JRebel есть возможность писать свои интеграции. У нас на сайте есть небольшое руководство о том, как можно реализовать поддержку JRebel для своих нужд.

Ресурсы



Для тех, кто заинтересовался и хочет узнать больше о продукте, и как его использовать, могу привести ссылки на некоторые ресурсы.
Создатель JavaPassion ведёт страничку, где собирает и обновляет материалы по использованию JRebel с разными контейнерами, фреймворками и IDE.
Довольно часто мы проводим тематические вебинары, на которые можно бесплатно зарегистрироваться и участвовать — в прямом эфире задать вопросы на интересующую тему.
На Vimeo существует канал JRebel, где можно найти записи вебинаров, а также и всевозможные демо-записи.

Итого



Надеюсь, после данной статьи, непосвящённым читателям стало понятнее, что такое JRebel и как его можно использовать. Если возникнут вопросы, буду рад ответить на них в комментариях.
Для тех, кто уже пользуется, и находит какие то изъяны в поведении данного ПО, не стесняйтесь написать нам на форум или обратиться в службу техподдержки, будем очень признательны.
Спасибо за внимание!
Tags:
Hubs:
+23
Comments23

Articles