Иногда спасти ситуацию могут unit тесты. Но для кода связанного с представлением, написание тестов является занятием малоприятным.
В очередной раз мучаясь с отладкой приложения, решил исследовать вопрос изменения кода приложения без его перезапуска (согласитесь, возможность разработки в стиле Ruby on Rails, когда изменение исходного кода сразу же находит отражение в запущенном приложении, прекрасна). Но кое-что есть и в Java. Среди продуктов общего назначения стоит отметить JRebel и HotSwap. JRebel продукт коммерческий, HotSwap открытый. Я решил поиграть со вторым.
HotSwap использует расширения JPDA, появившиеся еще в версии Java 1.4. Они позволяют во время отладочной сессии менять код классов. Требования на возможные изменения достаточно жесткие. Можно менять только тела методов (попытка перегрузить класс с новым методом или с измененной сигнатурой метода приведет к UnsupportedOperationException).
Итак ближе к делу. Исходная точка: клиент и сервер. Клиент вызывает методы сервера.
public class Server {
private String a() {
return "'a value'";
}
public String action() {
return a();
}
}
public class Client {
public static void main(String[] args)throws InterruptedException{
final Server server = new Server();
while (true) {
System.out.println(server.action());
Thread.sleep(1000);
}
}
}
Код собираем и запускаем ant скриптом:
<property name="classes" value="${basedir}/classes"/> <property name="src" value="${basedir}/src"/> <src path="${src}"/> <pathelement location="${classes}"/>
...
<property name="port" value="5555"/>
...
...
<pathelement location="${classes}"/>
<jvmarg value="-Xdebug"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${port}"/>
...
<property name="classes" value="${basedir}/classes"/> <property name="src" value="${basedir}/src"/> <property name="port" value="5555"/> <taskdef name="hotswap" classname="dak.ant.taskdefs.Hotswap"/> <format property="class.tstamp" pattern="MM/dd/yyyy kk:mm:ss"/> <src path="${src}"/> <date datetime="${class.tstamp}" pattern="MM/dd/yyyy kk:mm:ss" when="after" granularity="0"/> <src path="${src}"/> <pathelement location="${classes}"/> <jvmarg value="-Xdebug"/> <jvmarg value="-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=${port}"/>
private String a() {
return "'new a value'";
}
Ant hotswap и voila наша программа стала печатать 'new a value'. Но не стоит ожидать от HotSwap магии. Например, рассмотрим такой код сервера:
public class Server {
private String b;
public void init() {
b = "'b value'";
}
private String b() {
return b;
}
private String a() {
return "'a value'";
}
public String action() {
return a() + " " + b();
}
}
Мы запускаем приложение, меняем код инициализации:
public void init() {
b = "'new b value'";
}
Выполняем горячую замену и ничего не происходит... Переменная b уже проинициализирована в момент старта, и наше изменение кода инициализации никак не влияет на уже установленное значение. Нам самим надо заботится о переинициализации наших компонентов. Например, так:
public final class Client {
public static void main(String[] args)throws InterruptedException{
final Server server = new Server();
server.init();
final Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
final String command = scanner.nextLine();
if ("reinit".equals(command)) {
server.init();
}
System.out.println(server.action());
}
}
}
В этом случае, после изменения кода и горячей замены, посылаем сигнал reinit и получаем в выводе измененное значение.
В качестве заключения можно сказать, что HotSwap очень приятная библиотека (прежде всего простотой использования). Я использовал её в большом приложении со сложной системой сборки, и при этом внедрить HotSwap оказалось довольно легко. Но в силу ограниченных возможностей, использовать её можно только для отладки или совсем минимальной разработки.
Комментариев нет:
Отправить комментарий