среда, 20 июля 2011 г.

Подключение ICEPush в Vaadin

Я не специалист в ICEPush и Vaadin. Но вот недавно возникла необходимость наговнять веб-приложение, которое будет динамически отображать изменение состояния сервера. Server push. Для веб-приложения я выбрал Vaadin ибо не понимаю html и css. Возник вопрос как реализовать Server push. Поиск сказал, что в Vaadin есть add-on icepush. Вроде бы все просто.

Я не буду писать как использовать IcePush - это все есть в примере.

Но возникли трудности. При запуске приложение выводило следующую белиберду:

Widgetset does not contain implementation for org.vaadin.artur.icepush.ICEPush. Check its @ClientWidget mapping, widgetsets GWT module description file and re-compile your widgetset. In case you have downloaded a vaadin add-on package, you might want to refer to add-on instructions. Unrendered UIDL:

org.vaadin.artur.icepush.ICEPush(NO CLIENT IMPLEMENTATION FOUND)


Оказалось, что в icepush используется клиентский код (код на GWT, работающий на стороне браузера) и для работы нуна создать свой widgetset и откомпилировать его. И почему то как это делается не описано в инструкции к icepush (правда описано в инструкции к vaadin)

Тут я и хочу описать шаги, необходимые для выполнения вышеописанного.

1) создаем файл с widgetset нашего приложения. Создается он в директории где лежат java исходники приложения, примерно в том же пакете, где лежит само приложение.

<module>
<!-- WS Compiler: manually edited -->

<!-- Inherit DefaultWidgetSet -->
<!--<inherits name="com.vaadin.terminal.gwt.DefaultWidgetSet">-->

<inherits name="org.vaadin.artur.icepush.IcepushaddonWidgetset">
<!--<inherits name="org.icepush.gwt.ICEpush">-->

<!-- adding support for icepush -->
<!--<script src="icepush.js"></script>-->

</module>



В изначальном руководстве строчки <inherits name="com.vaadin.terminal.gwt.DefaultWidgetSet" />, <inherits name="org.icepush.gwt.ICEpush" /> присутствовали, но поскольку этим модули наследуются в org.vaadin.artur.icepush.IcepushaddonWidgetset, то их можно не указывать.

Строчка <script src="icepush.js"> тоже присутствовала, но при этом выводилась сообщение, что этот файл (icepush.js) не был найден в директории widgetset-а, приходилось копировать его туда руками. Я ее удалил - без нее все работает.

2) Надо откомпилировать widgetset. Если вы используете maven, то надо добавить зависимости в pom.xml, и запустить соответствующий таргет. Я этого не делал и поэтому писать не буду - опишу если понадобится попробовать.
Я компилировал widgetset через ant.

Берем дистрибутив vaadin, достаем оттуда файл WebContent/docs/example-source/build-widgetset.xml, копируем этот файл себе в проект. Изменяем таргеты, которые отвечают за установку разных property (например путь к файлу widgetset). Этот скрипт занимается еще и компиляцией проекта. Поскольку мой проект имеет немного более сложную систему зависимостей, а копировать все в WEB-INF/lib мне неохота, то я тупо выкинул таргет compile-server-side из ant скрипта build-widgetset.xml и указал переменную server-side-destination на уже откомпилированные классы.

Запускаем ант.
ant -f build-widgetset.xml


При этом у меня возникли проблемы. Таска java из таргета compile-widgetset ругалась на то, что кончилась память и предлагала установить параметр -Xmx128M. На самом деле кончалась память в Perm Gen:
    [java]    Compiling 6 permutations
[java] Compiling permutation 0...
[java] Compiling permutation 1...
[java] [ERROR] OutOfMemoryError: Increase heap size or lower gwt.jjs.maxThreads
[java] java.lang.OutOfMemoryError: PermGen space
[java] at sun.misc.Unsafe.defineClass(Native Method)


и необходимо было установить -XX:MaxPermSize=128M. Вот как стало выглядеть
 <java classname="com.google.gwt.dev.Compiler"
failonerror="yes" fork="yes" >
<arg value="-war" />
<arg value="${client-side-destination}" />
<arg value="${widgetset}" />
<jvmarg value="-Xms256M"/>
<jvmarg value="-Xmx256M"/>
<jvmarg value="-XX:PermSize=128M"/>
<jvmarg value="-XX:MaxPermSize=128M"/>
<jvmarg value="-Xss1024k"/>
<jvmarg value="-Djava.awt.headless=true"/>
<classpath>
<path refid="compile.classpath"/>
</classpath>
</java>


Я ставлю сразу ms и mx и PermSize и MaxPermSize потому что так быстрее, а память есть.

В результате компиляции widgetset появилась директория с кучей файлов в директории ${client-side-destination}. Название директории совпадает с названием нашего widgetset. В веб приложении это должно лежать в /VAADIN/widgetsets.

3) Добавляем параметр в сервлет. Теперь наш web.xml выглядит так
<servlet>
<servlet-name>ICEPush for Portlets</servlet-name>
<servlet-class>org.vaadin.artur.icepush.ICEPushServlet</servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>com.jnetx.app.colt.ui.ColtUi</param-value>
</init-param>
<init-param>
<description>ColtUi widgetset</description>
<param-name>widgetset</param-name>
<param-value>com.jnetx.app.colt.ui.ColtUiWidgetSet</param-value>
</init-param>
<!--<param-value>org.vaadin.artur.icepush.IcepushaddonWidgetset</param-value>-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ICEPush for Portlets</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>


После этого работают обновления, инициированные сервером.

PS: Не забудьте добавить pusher в mainWindow.