Пишем и отлаживаем компонент для GWT и Vaadin

    image


    Фреймворки Google Web Toolkit и Vaadin достаточно хорошо себя показывают, когда вы просто используете их. Если вдруг вам понадобилось расширить их функциональность, то вам придётся серьёзно попотеть. В этой статье я хочу рассказать, как написать простой компонент на GWT, добавить к нему серверную часть Vaadin и использовать в своём приложении. Я не буду создавать некий совсем пустой GWT/Vaadin проект и с самого начала настраивать компиляцию GWT, вместо этого возьму приложение на базе CUBA.Platform, в котором и реализую сам компонент, а затем попробую в деле. Так будет видно, насколько хорошо всё впишется в настоящее приложение. Отдельное внимание я бы хотел уделить отладке компонента, поскольку она нетривиальна и всегда вызывает трудности у разработчиков.

    Хочу предупредить, что не всё описанное в статье относится к GWT и Vaadin, часть шагов и приёмов применимы только в контексте использования CUBA.Platform, за счёт этого сильно упрощена настройка окружения и некоторые рутинные действия.

    Подготовка окружения


    Мы будем использовать для опытов пустой проект, созданный в CUBA Studio. CUBA — наша платформа для разработки бизнес приложений на Java, которая позволяет быстро создавать модель данных и интерфейс приложения, определять логику работы с данными и управлять правами пользователей. В основе UI платформы активно используется веб-фреймворк Vaadin, что позволяет нам реализовывать множество интересных задумок.

    Для начала создадим модуль GWT, который будет компилироваться в JavaScript. Выполним действие 'Create web toolkit module' в Studio. Это простая вспомогательная операция, которую нет смысла выполнять вручную. Studio сгенерирует описатель GWT модуля AppWidgetSet.gwt.xml, директорию для модуля и пустой пакет, а также добавит в описание сборки build.gradle необходимые задачи.

    Следующим шагом запустим действие 'Create or update IDE project files', чтобы сгенерировать файлы проекта IntelliJ IDEA, и отправимся писать код компонента в IDE.

    Для программирования самого компонента нам не потребуется каких-то особенных возможностей IDE кроме подсветки Java кода, поэтому совсем необязательно использовать IntelliJ Idea, с таким же успехом мы можем использовать Eclipse или Netbeans. Благодаря Google Web Toolkit мы можем использовать знакомые Java инструменты, а это большое преимущество при разработке масштабных проектов.

    Пишем компонент


    image

    Сам компонент будет довольно простым — поле рейтинга в виде 5 звёзд. Это поле ввода, в котором пользователь выбирает рейтинг при помощи мыши. У него есть состояние на сервере и отображение должно меняться при его изменении.

    image

    Вот так выглядит наш новый модуль web-toolkit в окне проекта Idea. В корневом пакете расположен описатель GWT модуля.
    AppWidgetSet.gwt.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.0/distro-source/core/src/gwt-module.dtd">
    <module>
        <inherits name="com.haulmont.cuba.web.toolkit.ui.WidgetSet" />    
    </module>

    Он наследует базовый модуль CUBA.Platform и является отправной точкой всей клиентской части нашего приложения (той, что исполняется в браузере). По умолчанию реализации GWT компонентов должны располагаться в подпакете 'client'. Создадим пакет client и в нём подпакет ratingfield.

    Первая деталь нашего компонента — виджет GWT. Класс RatingFieldWidget, который мы разместим в модуле web-toolkit:
    RatingFieldWidget.java
    package com.haulmont.ratingsample.web.toolkit.ui.client.ratingfield;
    
    import com.google.gwt.dom.client.DivElement;
    import com.google.gwt.dom.client.SpanElement;
    import com.google.gwt.dom.client.Style.Display;
    import com.google.gwt.user.client.DOM;
    import com.google.gwt.user.client.Event;
    import com.google.gwt.user.client.ui.FocusWidget;
    
    import java.util.ArrayList;
    import java.util.List;
    
    // класс GWT виджета
    public class RatingFieldWidget extends FocusWidget {
    
        private static final String CLASSNAME = "ratingfield";
    
         // API для реакции на клики
        public interface StarClickListener {
                void starClicked(int value);
        }
    
        protected List<SpanElement> stars = new ArrayList<SpanElement>(5);
        protected StarClickListener listener;
        protected int value = 0;
    
        public RatingFieldWidget() {
            DivElement container = DOM.createDiv().cast();
            container.getStyle().setDisplay(Display.INLINE_BLOCK);
            for (int i = 0; i < 5; i++) {
                SpanElement star = DOM.createSpan().cast();
    
                // Добавляем элемент звезды в контейнер
                DOM.insertChild(container, star, i);
                // Подписываемся на событие ONCLICK
                DOM.sinkEvents(star, Event.ONCLICK);
    
                stars.add(star);
            }
            setElement(container);
    
            setStylePrimaryName(CLASSNAME);
        }
    
        // главный метод обработки событий в виджетах GWT
        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);
    
            switch (event.getTypeInt()) {
                // Реагируем на события ONCLICK
                case Event.ONCLICK:
                    SpanElement element = event.getEventTarget().cast();
                    // только если клик по звезде
                    int index = stars.indexOf(element);
                    if (index >= 0) {
                        int value = index + 1;
                        // устанавливаем внутреннее значение
                        setValue(value);
    
                        // уведомляем интересующихся
                        if (listener != null) {
                            listener.starClicked(value);
                        }
                    }
                    break;
                }
        }
    
        // Понадобиться если на сервере зададут другой primaryStyleName
         // это часто случается при наследовании классов компонентов
        @Override
        public void setStylePrimaryName(String style) {
            super.setStylePrimaryName(style);
    
            for (SpanElement star : stars) {
                star.setClassName(style + "-star");
            }
    
            updateStarsStyle(this.value);
    }
    
    // Позволим изменять состояние стороннему коду
    public void setValue(int value) {
            this.value = value;
    
            updateStarsStyle(value);
        }
    
        // обновляем визуальное представление
        private void updateStarsStyle(int value) {
                for (SpanElement star : stars) {
                    star.removeClassName(getStylePrimaryName() + "-star-selected");
                }
    
                for (int i = 0; i < value; i++) {
                    stars.get(i).addClassName(getStylePrimaryName() + "-star-selected");
                }
        }
    }

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

    Стоит отметить, что во всём коде виджета нет ни строки JavaScript, что довольно хорошо для больших и сложных компонентов. Но не забывайте, что этот Java код будет скомпилирован в JavaScript и вам могут быть недоступны многие части стандартной библиотеки Java, например рефлексия и ввод-вывод (полную информацию о совместимости смотри тут: www.gwtproject.org/doc/latest/RefJreEmulation.html).

    Определяем внешний вид


    Как вы могли заметить, в коде виджета нет упоминаний внешнего вида, кроме назначения имён стилей ключевым элементам. Этот приём постоянно используется при разработке компонентов и позволяет определять внешний вид в CSS на основе комбинаций стилей.

    Для того, чтобы определить внешний вид нашего компонента, сперва, создадим файлы стилей. Для этого можем воспользоваться действием 'Create theme extension' для темы 'halo'. Эта тема использует вместо иконок глифы шрифта FontAwesome, чем мы и воспользуемся. Studio создаст пустые файлы SCSS для наших экспериментов в каталоге themes модуля web.

    Стили каждого компонента принято выделять в отдельный файл componentname.scss в каталоге components/componentname в формате примеси SCSS:
    ratingfield.scss
    @mixin ratingfield($primary-stylename: ratingfield) {
      .#{$primary-stylename}-star {
        font-family: FontAwesome;
        font-size: $v-font-size--h2;
        padding-right: round($v-unit-size/4);
        cursor: pointer;
    
        &:after {
              content: '\f006'; // 'fa-star-o'
        }
      }
    
      .#{$primary-stylename}-star-selected {
        &:after {
              content: '\f005'; // 'fa-star'
        }
      }
    
      .#{$primary-stylename} .#{$primary-stylename}-star:last-child {
        padding-right: 0;
      }
    
      .#{$primary-stylename}.v-disabled .#{$primary-stylename}-star {
        cursor: default;
      }
    }

    Затем такой файл подключается в главном файле темы
    halo-ext.scss
    @import "../halo/halo";
    
    @import "components/ratingfield/ratingfield";
    
    /* Define your theme modifications inside next mixin */
    @mixin halo-ext {
      @include halo;
    
      @include ratingfield;
    }

    Пара слов об SCSS. Это формат описания CSS стилей, позволяющий использовать переменные, примеси и вычисляемые значения. Он активно используется во многих веб-фреймворках, в Vaadin 7 — это базовый формат тем приложения. В приложении CUBA.Platform мы можем просто использовать этот формат, поскольку Studio берёт на себя грязную работу по организации сборки тем SCSS.

    Формат описания в виде примеси поможет нам, если у компонента появятся наследники с другим primary-stylename. Мы просто включим стили предка при помощи SCSS include.

    Для наших звёзд мы используем два глифа FontAwesome — 'fa-star' и 'fa-star-o'. Сам CSS довольно прост и включает лишь символы звёзд в двух состояниях и курсор мыши для них.

    Для переключения темы в приложении нужно выбрать halo на странице Project Properties в CUBA.Studio.

    Добавляем серверную часть


    До этого момента мы могли использовать написанный нами виджет в каком-нибудь GWT приложении, поскольку он никак не зависел от сервера. Теперь давайте поговорим про фреймворк Vaadin и его сервер-ориентированную модель. Она имеет пару особенностей:

    — всё состояние компонентов и полей ввода хранится на сервере и может быть восстановлено даже после полного обновления страницы или обрыва связи с сервером
    — весь полезный код приложения, за исключением клиентской части, исполняется на сервере

    То есть компоненты Vaadin скрывают от разработчика то, как они работают в браузере и беспечный Java разработчик HTML/CSS никогда и не видит (ну или почти никогда не видит, а то вдруг ещё полезет писать компоненты).

    Создадим пакет com.haulmont.ratingsample.web.toolkit.ui в модуле web. В нём мы разместим код нашего компонента RatingField. Унаследуем класс от класса Vaadin AbstractField, определяющего базовую логику работы полей ввода.

    Ключевые серверные составляющие компонента Vaadin:

    1) Класс компонента RatingField определяет API для серверного кода, различные get/set методы для работы, слушатели событий и подключение источников данных. Прикладные разработчики всегда используют в своём коде методы этого класса.
    RatingField.java
    package com.haulmont.ratingsample.web.toolkit.ui;
    
    import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldServerRpc;
    import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldState;
    import com.vaadin.ui.AbstractField;
    
    // Наше поле будет иметь тип значения Integer
    public class RatingField extends AbstractField<Integer> {
    
        public RatingField() {
                // регистрируем особую реализацию интерфейса, которая будет вызвана при запросе с клиента
            registerRpc(new RatingFieldServerRpc() {
                @Override
                public void starClicked(int value) {
                    setValue(value, true);
                }
            });
        }
    
        // тип значения поля
        @Override
        public Class<? extends Integer> getType() {
                return Integer.class;
        }
    
        // определяем свой класс для состояния
        @Override
        protected RatingFieldState getState() {
                return (RatingFieldState) super.getState();
        }
    
        @Override
        protected RatingFieldState getState(boolean markAsDirty) {
                return (RatingFieldState) super.getState(markAsDirty);
        }
    
        // при вызове setValue из прикладного кода нужно обновить состояние
        @Override
        protected void setInternalValue(Integer newValue) {
            super.setInternalValue(newValue);
                if (newValue == null) {
                    newValue = 0;
                }
                getState().value = newValue;
        }
    }

    2) Класс состояния RatingFieldState отвечает за то, какие данные будут пересылаться между клиентом и сервером. В нём определяются публичные поля, которые будут автоматически сериализованы на сервере и десериализованы на клиенте.
    RatingFieldState.java
    package com.haulmont.ratingsample.web.toolkit.ui.client;
    
    import com.vaadin.shared.AbstractFieldState;
    
    public class RatingFieldState extends AbstractFieldState {
        {   // изменим главное имя стиля компонента
                primaryStyleName = "ratingfield";
        }
        // объявим поле для нашего значения
        public int value = 0;
    }

    3) Интерфейс RatingFieldServerRpc — определяет API сервера для клиентской части, его методы могут вызываться с клиента при помощи механизма удалённого вызова процедур встроенного в Vaadin. Этот интерфейс мы реализуем в самом компоненте, в данном случае просто вызываем метод setValue нашего поля.
    RatingFieldServerRpc.java
    package com.haulmont.ratingsample.web.toolkit.ui.client;
    
    import com.vaadin.shared.communication.ServerRpc;
    
    public interface RatingFieldServerRpc extends ServerRpc {
        // метод будет вызываться в клиентском коде
        void starClicked(int value);
    }

    Важный момент — классы состояния и rpc должны быть расположены в подпакете 'client', так цепкие лапы компилятора GWT без проблем доберутся до них, чтобы создать их JavaScript представление для клиентского кода. Помимо этого, классы не должны использовать код, который не может быть скомпилирован GWT.

    Вот теперь настал момент связать наш клиентский код с серверной частью. Эту роль в Vaadin выполняют классы-коннекторы. Они размещаются рядом с классами виджетов. Класс коннектора аннотируется Connect(ComponentName.class), так и задаётся соответствие клиентской части серверной:
    RatingFieldConnector.java
    package com.haulmont.ratingsample.web.toolkit.ui.client.ratingfield;
    
    import com.haulmont.ratingsample.web.toolkit.ui.RatingField;
    import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldServerRpc;
    import com.haulmont.ratingsample.web.toolkit.ui.client.RatingFieldState;
    import com.vaadin.client.communication.StateChangeEvent;
    import com.vaadin.client.ui.AbstractFieldConnector;
    import com.vaadin.shared.ui.Connect;
    
    // Связываем наш коннектор с серверной реализацией RatingField
    // наследуем коннектор для AbstractField
    @Connect(RatingField.class)
    public class RatingFieldConnector extends AbstractFieldConnector {
    
        // мы будем использовать виджет RatingFieldWidget
        @Override
        public RatingFieldWidget getWidget() {
                RatingFieldWidget widget = (RatingFieldWidget) super.getWidget();
    
            if (widget.listener == null) {
    widget.listener = new RatingFieldWidget.StarClickListener() {
                    @Override
                    public void starClicked(int value) {
                    getRpcProxy(RatingFieldServerRpc.class).starClicked(value);
                    }
                };
            }
            return widget;
        }
    
        // наш тип состояния - RatingFieldState
        @Override
        public RatingFieldState getState() {
                return (RatingFieldState) super.getState();
        }
    
        // реагируем на изменение состояния на сервере
        @Override
        public void onStateChanged(StateChangeEvent stateChangeEvent) {
            super.onStateChanged(stateChangeEvent);
    
          // если значение на сервере изменилось, обновляем виджет
            if (stateChangeEvent.hasPropertyChanged("value")) {
                getWidget().setValue(getState().value);
            }
        }
    }

    Пробный запуск


    Чтобы всё это попробовать в деле, выполним несколько подготовительных шагов:

    1) Создадим БД для приложения из меню Studio: Run — Create database
    2) Создадим экран для размещения компонента в модуле web:

    image

    3) Добавим экран в меню приложения: Main menu — Edit

    image

    4) Теперь перейдём к редактированию нашего экрана в IDE.
    Нам понадобится контейнер для нашего компонента, давайте объявим его в XML экрана:
    rating-screen.xml
    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <window xmlns="http://schemas.haulmont.com/cuba/5.3/window.xsd"
            caption="msg://caption"
            class="com.haulmont.ratingsample.web.RatingScreen"
            messagesPack="com.haulmont.ratingsample.web">
    
        <layout expand="container">
            <vbox id="container">
            <!-- вот сюда мы добавим наш компонент Vaadin -->
            </vbox>
        </layout>
    </window>

    Откроем класс контроллера экрана RatingScreen.java и добавим код размещения нашего компонента на экране:
    RatingScreen.java
    package com.haulmont.ratingsample.web;
    
    import com.haulmont.ratingsample.web.toolkit.ui.RatingField;
    import com.haulmont.cuba.gui.components.AbstractWindow;
    import com.haulmont.cuba.gui.components.BoxLayout;
    import com.haulmont.cuba.web.gui.components.WebComponentsHelper;
    
    import javax.inject.Inject;
    import java.util.Map;
    
    public class RatingScreen extends AbstractWindow {
        @Inject
        private BoxLayout container;
    
        @Override
        public void init(Map<String, Object> params) {
            super.init(params);
            // используем API CUBA чтобы добраться до Vaadin реализации контейнера:
            com.vaadin.ui.Layout containerLayout = WebComponentsHelper.unwrap(container);
    
            // используем наш компонент как в обычном Vaadin приложении:
            RatingField field = new RatingField();
            field.setCaption("Rate this!");
            containerLayout.addComponent(field);
        }
    }

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

    Запускаем приложение из Studio: Start application server, переходим на http://localhost:8080/app смотреть результат:

    image

    Радуемся полнофункциональному компоненту, который мы теперь можем использовать из нашего Java кода на сервере. Весь код пригоден для использования в любом Vaadin приложении.

    Полный код приложения можно найти тут: github.com/Haulmont/ratingsample.git

    Отладка в браузере


    Мы будем рассматривать только отладку кода виджетов, поскольку отлаживать код Java компонентов на сервере довольно просто.

    Отладка любого GWT кода совсем нетривиальна и требует аккуратности. Для отладки воспользуемся режимом SuperDevMode. Необходимо, чтобы ваш проект собирался с GWT 2.5.1 или старше. Этот режим предполагает использование сопоставления Java кода с JavaScript кодом в браузере (source-maps, см developer.chrome.com/devtools/docs/javascript-debugging#source-maps). То есть вы будете видеть и отлаживать Java код в браузере, но с некоторыми ограничениями.

    Схема работы такая:
    1. Вы запускаете сервер com.google.gwt.dev.codeserver.CodeServer отдающий на сторону браузера соответствие JS кода и Java кода, а также собирающий ваш виджетсет при обновлении страницы
    2. Открываете приложение с параметрами ?debug&superdevmode
    3. Настраиваете Developer Tools, F12, в нижнем правом углу есть кнопка открытия настроек. Отметить опцию Enable source maps
    4. Обновите страницу, откройте в Developer Tools вкладку Sources. Там должны быть показаны все Java классы GWT виджетов. В отладчике Chrome можно ставить точки останова, смотреть переменные и выполнять выражения.
    5. При изменении кода виджета в проекте достаточно обновить страницу, виджетсет будет пересобран и подхвачен браузером. Это позволяет на лету видеть изменения кода виджета, что заметно ускоряет разработку.

    Пробуем всё запустить в нашем проекте:
    1) Для запуска этого режима нам необходимо добавить runtime зависимость servletApi для модуля web-toolkit в файле build.gradle:
    build.gradle
    ...
    configure(webToolkitModule) {
        dependencies {
            ...
            runtime(servletApi)
        }
    ...

    2) Выполним в Studio действие 'Create or update IDE project files', чтобы Idea увидела новую зависимость
    3) Создаём новую конфигурацию запуска в Idea с типом Application и следующими параметрами:

    Main class: com.google.gwt.dev.codeserver.CodeServer
    VM options: -Xmx512M
    Use classpath of module: app-web-toolkit
    Program arguments: -workDir C:\Users\yuriy\work\ratingsample\build\tomcat\webapps\app\VAADIN\widgetsets -src C:\Users\yuriy\work\ratingsample\modules\web\src -src C:\Users\yuriy\work\ratingsample\modules\web-toolkit\src com.haulmont.ratingsample.web.toolkit.ui.AppWidgetSet

    Пути к каталогам build\tomcat\webapps\app\VAADIN\widgetsets, modules\web\src и modules\web-toolkit\src необходимо заменить на свои.

    image

    4) Выполняем в Studio: Run-Start application server
    5) Запускаем ранее созданную конфигурацию GWT в Idea
    6) Переходим по адресу http://localhost:8080/app?debug&superdevmode
    7) Открываем DevTools в Chrome и видим свой Java код:

    image

    Плюс этого способа в том, что он не требует особой поддержки от IDE, быстро работает и позволяет отлаживать код прямо в браузере. К минусам стоит отнести то, что вам недоступно выполнение Java кода во время отладки, а также точки останова с условиями на Java, да и непривычно это как-то. Есть ещё жирный минус — старые браузеры совсем не умеют source-maps, что затрудняет нормальную отладку.

    Вместо заключения


    GWT — очень сильный и развитый веб-фреймворк, в последние годы он активно используется большим числом разработчиков по всему миру. Google не забывает про своё детище и активно применяет его, совсем недавно они выпустили Gmail Inbox (http://gmailblog.blogspot.ru/2014/11/going-under-hood-of-inbox.html), который интенсивно использует GWT для веб интерфейса.

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

    Мы активно используем Vaadin вот уже 5+ лет и уверены в нём. Всем советую рассматривать его, в качестве основного фреймворка для построения веб-приложений, особенно бизнес направленности.

    Спасибо за внимание!
    • +10
    • 15,6k
    • 9

    Haulmont

    268,00

    Разработка корпоративного ПО

    Поделиться публикацией
    Комментарии 9
      +1
      Был на прошлой Java конференции на выступлении Вашей компании — интересно рассказывали о единой платформе — и Web (с GWT и Vaadin) и Desktop (с Swing). :)

      А теперь по статье — а Вы GWT какой версии используете? До 2.7 SuperDevMode не удобный — нужно перекомпиливать весь проект при малейшем изменении, а это долго. В 2.7 появилась инкрементальные компиляция, которая кардинально меняет дело. Но до 2.7, на мой взгляд удобнее пользоваться DevMode. Или Вам не часто нужно менять GWT Client код с перекомпиляцией всего?

      И IDEA у Вас какой версии? В IDEA 14 (реклама!) отличная поддержка GWT — в GWT плагине используется github.com/branflake2267/superdevmode-launcher-legacy (насколько я понял) который позволяет запускать code server вместе с web сервером (как обычный DevMode) плюс не нужно явно перекомпилировать код с помощью закладок в браузере. Далее, IDEA поддерживает source map и, если запустить, браузер с IDEA плагином (я работал в Chrome) то можно отлаживать Java код в IDE (как в DevMode). Свои ограничения там конечно же есть (по сравнению в DevMode и Java), но, на мой взгляд, работает лучше чем в браузере.

      Собственно, напишу, что узнал:
      Альтернативный запуск SuperDevMode
      По мимо использования стандартного DevMode можно использовать реализацию DevMode с поддержкой SuperDevMode без запуска отдельно code server и servlet container’а. Скачать её можно здесь:
      github.com/branflake2267/superdevmode-launcher-legacy
      Благодаря этой реализации так же ненужно явно вызывать компиляцию GWT через закладки в браузере – компиляция будет происходить автоматически (вроде как, перепроверить – проверено в IDEA, но возможно это преимущество GWT плагина IDEA).

      Ремарка по поводу GWT 2.7
      В GWT 2.7 добавили инкрементальную компиляцию, которая включена по умолчанию и позволяет не перекомпилировать весь проект. Т.ч. в идеале «горячий» старт в SuperDevMode будет таким-же или даже меньше чем в DevMode. Нужно только прописать –noprecompile и указать –workDir и –launcherDir (чтобы использовались конкретные папки, которые не сотрутся после перезапуска сервера) чтобы повторно не компилировать код при перезапуске сервера.
      «Холодный» старт остаётся таким же долгим – происходит полная GWT компиляция.

      Замечания о IDEA 14
      В IDEA 14 доработали поддержку SuperDevMode – запуск SuperDevMode происходит также, как и просто DevMode, без необходимости запускать отдельно code server и servlet container и не нужно использовать закладки браузера для явного запуска компиляции – компиляция будет происходить при обновлении страницы.
      Для того что бы компиляция не происходила каждый раз при перезапуске приложения, нужно в настройках запускаемой конфигурации явно указать папки workDir и war в поле Dev Mode parameters. Папка workDir – любая существующая папка, папка war – папка с web приложением, в которую будут компилироваться модули GWT. Пример:
      -workDir D:\project\osbb_gradle\osbb_web\code_server_work_dir -war D:\project\osbb_gradle\osbb_web\src\main\webapp
      Также нужно добавить памяти VM (VM options):
      -Xmx1024m

      Явно указывать папки workDir и war нужно потому что по умолчанию IDEA при каждом запуске копирует содержимое webapp папки в новую временную папку и запускает приложение уже там. Из-за чего скомпилированные в прошлый раз модули не используются и компиляция происходит заново. Но если указать явно папки, то при каждом запуске будут использоваться те же самые папки с уже скомпилированными модулями.

      Что бы не компилировать модули в существующую папку приложения, можно при запуске приложения копировать её в другое место и использовать уже там, но при повторном запуске не очищать её. Т.о. существующая папка с приложением останется нетронута.
        0
        Мы используем 2.7 (он включён в Vaadin 7.3), перекомпиляция быстрая. Мы вообще не очень часто правим GWT код, поскольку у нас стабильный набор серверных компонентов.

        К сожалению GWT плагин для Idea требует GWT SDK, а Vaadin использует свою сборку GWT (форк с фиксами). При попытке использования плагина в ClassPath попадают версии исходников из GWT SDK, а некоторым классам Vaadin требуются классы из их GWT сборки.
          0
          Плюс в Vaadin используется один большой permutation, вместо множества под каждый браузер (<collapse-all-properties/>).
            0
            На счёт своей сборки GWT не понял. А что будет, если в IDEA указать GWT SDK из Vaadin?
              0
              GWT для Vaadin нет в виде отдельного SDK. Все классы GWT перепакованы в jar файлы Vaadin. А IDEA хочет видеть жарники GWT. Вероятно есть какой-то вариант решения это проблемы, но видимо не простой.
                0
                Понятно. А Вы в support IDEA писали? А то странно — в IDEA есть поддержка Vaadin, но при этом с ним не работает GWT плагин.
                  0
                  Не писали, хотя стоит им написать, а то у них GWT плагин и Vaadin плагин живут отдельной жизнью.
          0
          Написание таких сложных компонентов еще никогда не было таким простым!
            0
            Ну вы уж сделайте поправку на то, что это с одной стороны демонстрационный пример, а с другой — готовый для использования на сервере компонент, с поддержкой биндинга данных и полноценным Java API.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.