VaadinPlugin ru

Автор текста, он же автор всей функциональности NetBeans плагина: Денис Анисимов

Contents

Функциональное описание плагина для Vaadin

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

Конечно же в тесте будут затронуты некоторые бызовые аспекты самого фреймворка, но за подробной информацией обратитесь пожалуйста к секции Vaadin, где приведены ссылки.

Vaadin

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

Сайт : vaadin.com

Ссылка для скачивания : текущая версия

Плагин для NetBeans: текущая версия, здесь доступна версия с описанной функциональностью, она пока не выпущена официально.

Книга: Book of Vaadin

Руководство: tutorial

Демо: примеры

Базовые возможности

Для того чтобы создать проект, использующий Vaadin не нужно ничего скачивать и устанавливать кроме NetBeans и плагина для поддержки Vaadin. Для того, чтобы установить плагин скачайте nbm файл, откройте Tools->Plugins , вкладку Downloaded, используя кнопку "Add Plugins...", выберите скаченные файл и нажмите кнопку "Install".

После того как плагин будет установлен в визарде "New Project" появится категория "Vaadin". Выберите эту категорию и шаблон проекта "Vaadin Web Application Project".

Итоговый проект будет основан на инструменте сборки Maven и на второй панели визарда можно будет задать maven аттрибуты проекта. Но, если вы незнакомы с Maven, то можно оставить все значения по умолчанию кроме, возможно, имени проекта. Использование Maven абсолютно прозрачно для пользователя, так что нет нужды беспокоиться о том, что именно используется для сборки. Все действия по созданию war файла и развёртывнию его на сервере выполняются через доступный UI среды разработки.

После завершения визарда будет создан проект и скачены все необходимые библиотеки (это обеспечивается использованием Maven). В итоге дерево проекта представлено справа.


Шаблон проекта содержит файл GWT модуля AppWidgetSet.gwt.xml, который нужен для создания собственных компонент-виджетов. Vaadin фреймфорк содержит большое количество готовых компонент-виджетов, которые можно использовать в своём приложении без необходимости создавать свои собственные. Здесь можно посмотреть доступные компоненты и примеры их использования. В тех случаях, когда есть необходимость реализовать свою нестандартную компоненту или использовать Add-On (компонента реализованная кем-то, но не доступная в стандартном фреймворке) необходим файл модуля AppWidgetSet.gwt.xml. В данный момент в нём нет необходимости, поэтому этот файл нужно удалить. Подобнее нестандартные компоненты будут затронуты позже.

Удаление упомянутого файла позволит сэкономить время на сборке проекта. Дело в том, что если проект содержит клиентскую часть (нестандартные виджеты), то их необходимо скомпилировать в JavaScript, что выполняется прозрачно при сборке, если в проекте есть файл модуля. Стандартные компоненты поставляются уже скомпилированными как часть фреймфорка, так что нет нужды в компиляции. Компиляция же требует значительного времени и будет замедлять разработку.

Внимание: Удалять файл необходимо через команду "Refactor->Safe Delete". Если удалить файл через команду "Delete" то сам файл будет удалён, но ссылка на него останется в конфигурации сервлета: в параметре @WebInitParam аннотации @WebServlet или widgetset значении аннотации @VaadinServletConfiguration. Проект будет собираться без проблем, но если его запустить (см. ниже), то страница приложения будет 'висеть'. И это первая из простых и труднодиагностируемых проблем, на которой может споткнуться неопытный программист. Плагины для других IDE не поддерживают рефакторинг для файла модуля GWT. Поэтому удаление этого файла всегда будет вести к описанной проблеме. NetBeans плагин же позволяет увидеть проблему даже если была использована команда обычного удаления "Delete". Если открыть файл MyVaadinUI, который определяет точку входа для Vaadin приложения и содержит определение сервлета, сконфигурированного для использования Vaadin, то редактор покажет ошибку:

File:Vaadin-widgetset-absent.png

Итак, после удаления файла проект можно собрать и запустить. Выполните "Build" через всплывающее меню проектного узла. Для того чтобы запустить проект можно использовать зарегистрированный в IDE сервер (вкладка Services, узел Servers) или использовать Maven плагин с легковесным быстрым Jetty сервером. В первом случае проверьте что у вас установлен JEE сервер в свойствах проекта (Properties во всплывающем меню проектного узла, Run категория, Server опция). Если сервер не выбран, то его необходимо зарегистрировать (через вкладку Services) если это ещё не сделано и выбрать в этом диалоге. После того как сервер выбран выполните "Run". Будет запущен выбранный сервер и на нём будет развёрнуто приложение. Также будет запущен броузер и открыт URL приложения в новой вкладке.

Можно также использовать Jetty сервер. Для этого воспользуйтесь командой "Embedded Jetty->Run". В этом случае будет запушен Jetty сервер через Maven плагин. Для этого действия нет встроенной возможности автоматического открытия броузера с URL приложения, так что нужно запустить броузер самостоятельно и открыть страницу "http://localhost:8080/{context_path}", где {context_path} - имя проекта приложения. Его можно посмотреть в консоли запуска Jetty, найдите строку Context path = xxxxx.

Замечание: если вы не используете Jetty, то его можно отключить. Откройте диалог с конфигурацией: Tools->Options. Выберите Java и вкладку Vaadin. Отключите Jetty с помощью опции "Show Jetty Popup Menu Action".

Расширенные возможности

Наиболее распостранённые ошибки

Если вернуться к созданному приложению, то его результат выполнения на сервере это кнопка, которая при нажатии добавляет вниз текст.

Приложение простое и, как уже упоминалось, этот текст не ставит цели ознакомления с самим фреймворком. Поэтому, разработку на серверной стороне ограничим этим примером.

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

Итак, предположим, что мы хотим расширить приложение и сделать так, чтобы при наведении на добавленный текст показывалась подсказка. Это можно сделать, не прибегая к реализации на стороне клиента. Можно, к примеру отнаследоваться от класса Label и выставить текст подсказки в подклассе.

File:Vaadin-label-ex.png File:Vaadin-label-ex-usage.png

Результат:

Предположим теперь, что текст подсказки может быть очень большим. Но большой текст подсказки неудобен в UI. Поэтому, имеет смысл выводить короткий текст в виде подсказки при наведении указателя мыши и, если указатель переместился на подсказку и на ней была нажата клавиша мыши, то подсказка должна развернуться в полнотекстовый режим и показать весь текст (таким образом, к примеру, работают Javadoc подсказки в Eclipse).

Реализовать это можно несколькими способами и на примере пары этих способов можно споткнуться всё на тех же мелких недочётах, которые приведут к наработающему приложению. Их и разберём поробнее. Начнём с напрашивающегося способа: расширение существующей функциональности класса Label. Тем более что его подкласс уже создан (LabelEx).

Требуемая функциональность не может быть реализована исключительно дополнением кода серверной компоненты Label. Необходимо расширить код клиентской части этого класса. Как же это сделать ? Плагин даёт возможность создать свои виджеты через визард.

Визарды удобно использовать для вновь создаваемых комнонент с нуля. Одним из шаблонов визарда мы воспользуемся в качестве второго способа. Сейчас же мы хотим расширить класс Label и для этого неудобно использовать визарды: придётся править все созданные классы. Класс же LabelEx у нас уже есть и хочется идти от этого класса.

Для того чтобы расширить клиентскую часть кода для класса Label нужно создать коннектор и реализовать его метод getWidget(), который должен вернуть собственную реализацию клиентской части виджета. Создадим отдельный пакет для клиентской части, назовём его "com.mycompany.vaadin.labelex". И создадим в нём класс LabelExConnector. После создания его нужно поправить так, чтобы он наследовался от класса LabelConnector. Этот суперкласс используется для связывания серверного класса Label и его клиентской части.

Созданный класс наследует от класса LabelConnector метод getWidget(), который возвращает виджет VLabel, доступный во основе фреймфорка Vaadin. Таким образом, мы пока не добавили никакой функциональности классу Label, но сделали рутинную работу по связыванию клиентской и серверной частей, требуемую фреймворком и теперь нужно только написать код, решающий задачу. Это значит, как минимум, что если сейчас построить проект, то все должно работать как и раньше. Это полезно сделать для самопроверки. Если это сделать, то выяснится что LabelExConnector не задействуется. В чём же проблема ? На деле проблем масса. И все они не очевидны сходу в этом конкретном случае.



package com.mycompany.vaadin.labelex;

import com.vaadin.client.ui.label.LabelConnector;


public class LabelExConnector extends LabelConnector{
    
}

Код созданного класса здесь намерено показан текстом а не картинкой, потому что NetBeans (а точнее плагин) покажет все проблемы в виде ошибок редактора. Приведём здесь список проблем с описанием, а затем изображение того как эти проблемы выявлены радактором. Итак вот они:

  1. Коннектор не достаточно отнаследовать от класса LabelConnector (или, к примеру абстрактного AbstractComponentConnector, используемого визардами по умолчанию). Для того чтобы связать серверную компоненту с коннектором необходима аннотация @Connect(LabelEx.class), даже если она уже объявлена для LabelConnector. Ведь там она связывает не LabelEx, а Label. Этой проблемы бы не было, если бы класс коннектора создавался бы через визард. В этом случае аннотация была бы вставлена в сгенерированном классе с нужным значением.
  2. Исправление предыдущей ошибки решит проблему связывания логически. Но логическое связывание необходимо сделать фактическим с помощью GWT компилятора на стадии сборки проекта. И этого сделано не будет потому что GWT компилятор не найдёт класса коннектора. Он не сможет сделать этого ввиду того что отсутствует файл модуля (.gwt.xml). Мы же его удалили в самом начале ! Опять же этого можно было бы избежать если бы был использован визард. Файл модуля был бы создан автоматически.
  3. Если создать файл модуля вручную через визард, то этого опять будет недостаточно. Его необходимо прописать в конфигурации сервлета (про это уже было упомянуто в начале). Иначе, при запуске приложения, оно не будет знать какой файл модуля использовать. И опять же визард всё это сделает автоматически.
  4. Наконец, последняя проблема: после того как создан файл модуля, его местоположение определяет пакеты, где GWT компилятор будет искать классы, которые он будет компилировать. По умолчанию пакет с клиентскими классами определяется как подпакет с именем "client" пакета, содержащего файл модуля. То есть если файл AppWidgetSet.gwt.xml содержится в пакете "com.mycompany.vaadin" то клиентские классы (в том числе и коннектор) будут искаться только в пакете "com.mycompany.vaadin.client". Клиентские пакеты также можно задавать с помощью элемента <source path="относительный путь"/>. В нашем же частном случае коннектор создан в пакете "com.mycompany.vaadin.labelex", который клиентским не является. И снова этой проблемы бы не было, если бы был использован визард.

Последний пункт является одной из самых распостранённых проблем, допускаемых случайно или по незнанию деталей фреймворка новичками. Она является следствием соглашения, определяемого GWT компилятором, которое нужно просто постоянно помнить и знать. Как результат, мы получаем наиболее популярную и очень труднодиагностируемую ошибку.

Возникает вопрос: если всё может сделать визард, то почему бы не воспользоваться визардом с самого начала? Разумеется, им можно воспользоваться, но визард генерирует файлы по шаблону и не даёт гибко и полно конфигурировать сгенерированный код. В противном случае требовалось бы задать кучу параметров и UI был бы пересыщен элементами. Визард же должен быть максимально прост для быстрого использования.

С другой стороны: получается что необходимо использовать визард для любого действия, связанного с написанием кода для клиентской части. В противном случае вы постоянно рискуете допустить простеньку ошибку. Требование всегда использовать визард довольно странно. Среда разработки должна заниматься отслеживанием подобного рода ошибок самостоятельно, помогая разработчику быстро решать рутинные вопросы, связанные со следованием требованиям фреймворка, и сосредоточиться на бизнес логике самого приложения. Именно в этом и заключается смысл импользования среды разработки. К счастью, плагин именно этим и занимается. Если открыть файл коннектора в редакторе, то он пакажет ошибки и предложит их исправить.

Пакет с клиентскими классами невозможно определить без файла модуля, поэтому сначала будут показаны проблемы отсутствия этого файла и отсутствия аннотации связывания. File:Vaadin-wrong-labelex-connector.png File:Vaadin-wrong-labelex-connector-fixes.png

После устранения этих проблем будет показана ошибка нахождения клиентского класса в неверном пакете.

File:Vaadin-wrong-labelex-package.png File:Vaadin-wrong-labelex-pckg-fixes.png

Можно пройти по всем ошибкам, показанным в редакторе и исправить их предложенными способами через подсказки ассистента. Это даёт возможность не держать в памяти постоянно все подробности конфигурации фреймворка. Среда разработки сгенерирует все необходимые артефакты и правки прозрачно для пользователя. Но, придерживаясь цели показать функциональность плагина, оставим этот путь, подчистив за собой все созданные файлы класса LabelEx и LabelExConnector.

Визарды и использование ассистента редактора для реализации виджетов

Пойдём по второму пути: используем визард. Визард позволяет создавать компоненты как с нуля, так и основываясь на уже существующих компонентах. Существует два шаблона для создания компонент: шаблон, который создаёт серверную и клиентскую часть компоненты вместе с заглушками взаимодействия между ними (RPC интерфейсы и их имплементация, класс состояния) и простой шаблон серверной компоненты и коннектора. Первый шаблон удобно использовать только тогда когды вы заранее знаете о том, что вам понадобится полное взаимодействие между сервером и клиентом в обе стороны. Обычно разработка идёт итеративно и взаимодействие дописывается пошагово. Поэтому этот шаблон неудобен, потому что генерирует всё и сразу, а это не требуется: какие то заглушки останутся неимплементированными долгое время пока до них не дойдут руки. Это именно наш случай, поэтому используем второй шаблон, который создаст минимальную базу, на которой можно продолжить разработку.

При выборе этого шаблона в визарде можно сгенерировать компоненту, используя кнопку "Finish" прямо на текущей странице, а можно нажать "Next>" и выбрать на третьей странице существующую компоненту, отнаследовавшись от неё. Первый вариант полезен для быстрого создания компоненты с нуля. Второй удобен тем, что сгенерированные классы будут сразу расширять возможности существующей компоненты. Если закончить визард сразу на втором шаге, то сгенерированный код будет отнаследован от класса AbstractComponent. Поэтому придётся пройтись по обоим сгенерированным классам и поменять суперклассы. Третий же шаг сгенерирует код сразу таким какой нам нужен и не придётся ничего править:выберите на третьем шаге класс com.vaadin.ui.Label. В результате визард сгенерирует класс серверной компоненты, коннектора с нужной аннотацией, в нужном пакете, создаст файл GWT модуля если он отсутствует и пропишет его в конфигурацию сервлета.

File:Vaadin-labelex-location-wizard.png File:Vaadin-labelex-super-wizard.png

В результате работы визарда будут сгенерированы три файла: серверный класс LabelEx, коннектор LabelExConnector и класс виджета LabelExWidget.

       package com.mycompany.vaadin;

       import com.vaadin.ui.Label;

       public class LabelEx extends Label {
    
           public LabelEx() {
           }
       }


       package com.mycompany.vaadin.client;

       import com.vaadin.client.ui.VLabel;


       public class LabelExWidget extends VLabel {
    
       }

Если теперь собрать проект и запустить его, то он будет вести себя также, как будто используется стандартный класс Label. Подсказка на тексте показываться не будет потому что сгенерированный конструктор LabelEx не вызывает метода setDescription(). Но мы его использовать и не будем, чтобы не углубляться в детали того как реализована функциональность подсказки в Vaadin. Вместо этого возьмём статический текст и расширим клиентскую часть: допишем свой код в классе LabelExWidget. Всё необходимое связывание этого класса с его серверной компонентой уже выполнено, так что ничего кроме класса LabelExWidget менять не придётся. Не будем останавливаться на деталях реализации нужной функциональности. Ниже приведён код, использующий GWT API и клиентские классы Vaadin, который делает то, что нужно.

       package com.mycompany.vaadin.client;

       import com.google.gwt.event.dom.client.ClickEvent;
       import com.google.gwt.event.dom.client.ClickHandler;
       import com.google.gwt.event.dom.client.MouseOutEvent;
       import com.google.gwt.event.dom.client.MouseOutHandler;
       import com.google.gwt.event.dom.client.MouseOverEvent;
       import com.google.gwt.event.dom.client.MouseOverHandler;
       import com.google.gwt.user.client.Timer;
       import com.google.gwt.user.client.ui.Label;
       import com.google.gwt.user.client.ui.TextArea;
       import com.google.gwt.user.client.ui.Widget;
       import com.vaadin.client.ui.VLabel;
       import com.vaadin.client.ui.VOverlay;


       public class LabelExWidget extends VLabel {
    
           private static final String SHORT_TOOLTIP = "Short tooltip";
    
           private static final String FULL_TEXT = "Lorem ipsum dolor sit amet, "
                   + "consectetuer adipiscing elit, sed diam nonummy nibh euismod "
                   + "tincidunt ut laoreet dolore magna aliquam erat volutpat. "
                   + "Ut wisi enim ad minim veniam, quis nostrud exerci tation "
                   + "ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo "
                   + "consequat.";
    
           private final Timer timer = new Timer() {

               @Override
               public void run() {
                   overlay.hide();
               }
           };
    
           private final Label tooltip;
    
           private final TextArea textArea;
    
           private VOverlay overlay;
    
           LabelExWidget(){
               overlay = new VOverlay(true);
               tooltip = new Label(SHORT_TOOLTIP);
               overlay.add(tooltip);
               overlay.setOwner(this);
        
               textArea = new TextArea();
               textArea.setText(FULL_TEXT);
               textArea.setReadOnly(true);
        
               MouseListener listener = new MouseListener();
               addHandler(listener, MouseOverEvent.getType());
               addHandler(listener, MouseOutEvent.getType());
        
               overlay.addDomHandler(listener, MouseOverEvent.getType());
               overlay.addDomHandler(listener, MouseOutEvent.getType());
               overlay.addDomHandler(listener, ClickEvent.getType());
           }
    
           private class MouseListener implements MouseOverHandler, MouseOutHandler, ClickHandler{

               @Override
               public void onMouseOver(MouseOverEvent event) {
                   timer.cancel();
                   if ( !overlay.isShowing()){
                       overlay.clear();
                       overlay.add(tooltip);
                       overlay.showRelativeTo(LabelExWidget.this);
                   }
               }

               @Override
               public void onMouseOut(MouseOutEvent event) {
                   timer.schedule(100);
               }

               @Override
               public void onClick(ClickEvent event) {
                   Widget widget = overlay.getWidget();
                   if ( tooltip.equals(widget)){
                       overlay.clear();
                       overlay.add(textArea);
                   }
               }
           }
       }

Единственная деталь, на которую здесь нужно обратить внимание для последующего: строка короткой подсказки и её полный текст заданы статически в файле класса и не могут быть динамически изменены. Это отличается, к примеру, от строки содержимого класса Label, который можно задать на серверной стороне и менять в процессе выполнения приложения динамически. Займёмся реализацией такого же поведения для текста подсказки: нужна возможность устанавливать короткий и полный текст подсказки со стороны сервера.

Требуемое взаимодействие сервера и клиента решается использованием класса состояния, подкласса SharedState. Нужно создать методы в классе LabelEx для выставления короткого и полного текстов подсказки и инициализировать поля класса состояния, которое будет передано клиенту при запросе сервера. Как создать нужный класс состояния? Можно создать класс вручную, незабыв отнаследоваться от нужного суперкласса: компонента LabelEx расширяет компоненту Label, у которой уже есть свой собственный класс состояния. Так, что создание потребует нескольких действий: создание класса, модификация его супрекласса. Визарда для этого нет, да он и не нужен: использование визарда также требует нескольких действий: вызов визарда, выбор нужного типа файла, указание местоположения, выбор суперкласса. Вся информация, нужная для создания класса, кроме его имени, уже известна в контексте серверной компоненты или её коннектора. Поэтому ассистент редактора предлагает подсказку для создания этого класса, доступную в качестве текстовой аннотации класса коннектора и серверного класса. Для того чтобы получить эту подсказку нужно выбрать класс в качестве контекста в редакторе (курсор должен быть в теле класса), например, поставить курсор на имя класса.

File:Vaadin-labelex-component-state.png File:Vaadin-labelex-connector-state.png

File:Vaadin-labelex-component-state-fixes.png File:Vaadin-labelex-connector-state-fixes.png

В качестве правки можно выбрать создание класса и метода getState() как только в текущем контекстном классе, так одновременно и в его парном классе. При выборе нужной правки появится диалог где можно задать имя для класса состояния.

File:Vaadin-labelex-new-state.png

В результате будет создан класс с нужным суперклассом и имплементированы методы getState() в самом классе и парном ему (если был выбран соответствующий пункт). Добавим реализацию логики использования класса состояния между сервером и клиентом. Ниже приведён код, который позволяет задавать строки подсказки со стороны сервера.

File:Vaadin-labelex-component-connector.png File:Vaadin-labelex-widget-state.png

Таким образом, имплементирована заявленная функциональность с возможностью выставлять тексты подсказок со стороны серверной компоненты. Предположим теперь, что нужно получить событие о щелчке мышью на подсказке с коротким текстом, так чтобы можно было добавлять листенеры на стороне сервера. Это можно, к примеру, использовать для того, чтобы менять текст полной подксказки в тот момент, когда произошло нажатие мышью, а не выставлять его заранее.

Не будем здесь останавливаться на деталях того как это можно сделать, ограничась наброском использования среды разработки для создания классов взаимодействия между клиентом и сервером. Итак, метод, отслеживающий момент нажатия мышью уже реализован в приватном классе MouseListener внутри класса LabelExWidget (onClick). Нужно только оповестить сервер о произошедшем событии. Для этого нужно использовать RPC вызовы. Нужно создать интерфейс, наследующий com.vaadin.shared.communication.ServerRpc. Этот интерфейс представляет серверную сторону взаимодействия. Вызовы методов этого интерфейса будут преобразованы в отражённые вызовы классов, имплементирующих интерфейс на стороне сервера.

Можно создать RPC интерфейс, задействовав его на клиентской стороне вручную в несколько приёмов. Есть визард, который создаёт компоненту с нуля со всеми необходимыми классами для полного взаимодействия между клиентом и сервером. Он уже упоминался и он неудобен здесь: взаимодействие нужно только в одну сторону, да и компонента у нас уже почти готова. Не использовать же визард для того чтобы взять оттуда куски кода. Это именно то самое итеративное создание компоненты, с которым чаще всего и приходится иметь дело. Ситуация эта схожа с ситуацией создания класса состояния. В данном случае визард также не нужен, потому что контекст даёт всю информацию кроме имени RPC интерфейса.

В качестве контекста может выступать класс коннектора (ссылка на него необходима, чтобы создать прокси для интерфйеса, см. далее) или класс серверной компоненты. Начинать можно с любого класса, но в данном случае логично начать с коннектора. Точно также как и с классом состояния ассистент редактора предлагает подсказку и правку в контексте класса коннектора.

File:Vaadin-labelex-connector-rpc.png File:Vaadin-labelex-connector-rpc-fix.png

Если воспользоваться подсказкой и предлагаемой правкой, то будет создан пустой интерфейс и метод в коннекторе, возвращающий ссылку на прокси этого интерфейса для его использования (вызова его методов) на клиентской стороне.

File:Vaadin-labelex-connector-rpc-used.png

Всё что теперь нужно сделать - добавить нужный метод в сгенерированный интерфейс и вызвать его в коде коннектора в нужном месте. Только этого среда разработки сделать и не может самостоятельно: это уже бизнес логика приложения. Вся же рутинная работа по связыванию была выполнена с минимальным количеством действий. Итак, на стороне клиента осталось только вызвать метод RPC интерфейса (который нужно туда добавить) из класса MouseListener. Единственная оставшаяся сложность заключается в том, что прокси доступен в классе коннектора, а не в классе виджета LabelExWidget. Но это тривиальная имплементационная подробность, которая может быть решена многими способами. Оставшиеся подробности здесь опустим и перейдём к серверной компоненте.

На стороне сервера необходимо создать имплементацию RPC интерфейса и написать логику метода, который вызывается клиентской стороной. Если открыть в редакторе класс серверной компоненты LabelEx, то опять же ассистент покажет подсказку на этом классе, сообщающую об отсутствии задействования серверного RPC интерфейса.

File:Vaadin-labelex-component-rpc.png File:Vaadin-labelex-component-rpc-fix.png

Ассистент находит уже существующий серверный RPC интерфейс и предлагает его использовать, а также предлагает создать новый с нуля. Если воспользоваться правкой с уже существующим интерфейсом, то будет создан анонимный класс-заглушка, переменная с присвоенной ссылкой на этот класс и в конструкторе класса LabelEx будет вызван метод registerRpc, который зарегистрирует анонимный класс для того, чтобы он вызывался когда его запрашивает клиентская сторона.

File:Vaadin-labelex-component-rpc-implemented.png

Таким образом, опять вся рутинная работа была выполнена ассистентом. Остаётся только написать имплементацию созданного анонимного класса, ссылка на который присвоена полю rpc. Последний класс пустой потому что мы не стали здесь добавлять метод в RPC интерфейс. Этот метод нужно добавить и имплементировать в этом анонимном классе (если бы интерфейс содержал метод до того как была применена правка, то сгенерированный анонимный класс содержал бы заглушку-имплементацию этого метода). Грамотная имплементация должна разослать серверное событие листенерам, зарегистрированным в серверной компоненте. Это всё детали бизнес логики и API компоненты, которые остаются на усмотрение программиста.

Цель же предыдущего описания была продемонстрировать как можно, не прибегая к визардам, осуществить итеративную разработку, опираясь на ассистент редактора. При разработке взаимодействия сервер-клиент есть ещё некоторое количество мест, где можно кое-что забыть/не учесть и где поможет ассистент. Здесь описаны ещё несколько таких подсказок, срабатывающих при реализации такого взаимодействия. В частности: аналогично описанному выше способу можно организовать взаимодействие через RPC от сервера к клиенту, вопросы, относящиеся к классу состояния (сериализации), методов и их аргументов в RPC интерфейсах.

Компиляция, запуск и отладка в режиме разработки

Если приложение содержит клиентский код, то он требует компиляции в JavaScript. Это осуществляется автоматически при сборке проекта, также компиляцию можно запустить через команду всплывающего меню проекта Compile Widgetset and Theme. Помимо этой команды всплывающее меню содержит следующие действия, относящиеся к запуску проекта:

  • Compile Theme - компилирует тему. Стили приложения могут быть описаны в файлах SCSS. В итоговом приложении они должны быть скомпилированы в обычные файлы CSS. Эта команда именно это и выполняет. Упомянутая выше команда выполняет как компиляцию кода в JavaScript так и компиляцию темы.
  • Run Dev Mode - запускает сервер режима разработки, который позволяет исполнять клиентский код приложения в JVM вместо JavaScript движка броузера. Это помогает диагностировать проблемы с клиентским кодом.
  • Run Dev Mode (debug) - делает тоже самое что и предыдущая команда, но запускает сервер режима разработки с возможностью отладки и подсоединяет к нему отладчик NetBeans. В результате можно ставить точки останова в клиентском коде и отлаживать его как обычное Java приложение.
  • Run Super Dev Mode - запускает режим разработки в super режиме. Этот режим позволяет отлаживать клиентский код прямо в броузере через отладчик броузера. За подробностями об этом режиме обратитесь к документации по GWT.
Помимо команд для функциональности Vaadin есть также команды для Jetty сервера, доступного через Maven плагин. Он уже упоминался в самом начале. Этот пункт меню можно отключить (см. выше). Он содержит команды:
  • Run - запускает сервер.
  • Debug - запускает сервер в режиме отладки. Это даёт возможность отлаживать серверный код как обычное Java приложение.
  • Run with Dev Mode - запускает Jetty сервер и сервер режима разработки клиентского кода. Действие этой команды не эквивалентно вызову двух команд по отдельности (соответственно Run для Jetty и Run Dev Mode). При запуске Jetty производится сборка проекта. Это значит, что команда Run запустит компиляцию клиентских классов перед тем, как запустить сервер Jetty. Режим разработки же используется для быстрого диагностирования проблем, минуя необходимость в перекомпиляции. Если вы меняете код, то он тут же подхватывается режимом разработки. Что позволяет не терять время. В итоге быстро это сделать не получится: код будет подхватываться на лету, но уже только после того как сервер запустился. А при запуске придётся дожидаться перекомпиляции. Это скорее всего не то, чего бы хотелось. Поэтому рассматриваемая команда запускает сборку проекта перед стартом сервера со специальным флагом, предотвращающим компиляцию клиентского кода. В этом и состоит отличие от запуска двух команд по отдельности.

Помимо режима разработки есть ряд опций, которые позволяют влиять на процесс компиляции клиентских классов и на его скорость. Опции доступны через свойства проекта: категория Vaadin содержит несколько подкатегорий, которые позволяют конфигурировать работу компилятора.

Вот описание этих опций категории Compiler and GWT (здесь доступно описание опций в других категориях):
  • Extra JVM Arguments - дополнительные аргументы JVM, используемые при запуске GWT.
  • JavaScript Style - Задаёт стиль для сгенерированного JavaScript кода.
  • Compiler Threads - Задаёт число параллельных потоков, которые могут быть использованы для компиляции кода, спецефичного для разных броузеров, то есть permutations в терминологии GWT.
  • Logging Level - уровень, который будет использоваться журналом для вывода сообщений.
  • Faster compilation (draft) - Ускоряет процесс компиляции засчёт меньшей оптимизации. Опцию полезно включать во время разработки и отключать на финальной стадии сборки приложения.


Использование каталога дополнений

Если в вашем приложении требуется использовать компоненту, которой нет среди стандартных компонент Vaadin фреймворка, то прежде чем писать свою собственную можно попробовать поискать готовую. Каталог дополнений содержит большое количество как UI компонент, так и других библиотек, позволяющих расширить функциональность приложения. Открыв указанный адрес каталога, можно использовать доступный поиск по параметрам, найти нужное дополнение (Add-On), скопировать предоставляемую часть XML содержимого для Maven и вставить её в нужное место POM файла. При сборке проекта нужные файлы будут скачены из репозитория, где они содержатся и добавлены в проект в качестве библиотек.

Вместо того, чтобы использовать внешнюю программу (web броузер) для поиска дополнений, копирования XML вставки и редактирования POM файла вручную можно использовать броузер дополнений, предоставляемый плагином. Для этого нужно использовать действие "Vaadin->Open Add-Ons Browser".

File:Vaadin-action-addon-browser.png

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

File:Addon-browser.png

Результаты поиска можно сортировать по столбцам таблицы, а выбранное дополнение можно добавить в проект в качестве зависимости через кнопку "Add". В результате POM файл будет нужным образом отредактирован и все необходимые библиотеки будет скачены из соответствующего репозитория прозрачно для пользователя.

Ещё быстрее добавить и использовать нужное дополнение можно если вы знаете какие именно классы содержатся в нём (вы его уже использовали ранее или просто угадываете): стандартная возможность дополнения кода (code completion) в редакторе расширена с помощью классов, доступных через каталог. То есть, даже если класса нет в путях классов вашего проекта (classpath), но его имя начинается с введённого в редакторе префикса, он будет показан среди других предложений дополнения (code completion): введите префикс класса, нажмите Ctrl-Space и предложенный список классов будет содержать доступные классы из каталога (если, конечно, они имеют нужный префикс).

File:Code-completion-items.png

При выборе класса каталога из предлагаемого списка сработает обычная функциональность дополнения кода (code completion), а также будет добавлен необходимый XML код в POM файл, а файлы выбранного дополнения (Add-Onа) будут скачены и добавлены в проект в качестве библиотек.

Классы из каталога, которые показываются в списке предложенных дополнений кода подразделяются на три категории: серверные классы, клиентские классы и классы для тестов. Серверные классы будут показаны только для кода внутри серверного класса, клиентские классы, соответственно, только для клиентских классов и классы из дополнений каталога категории тестов только для тестовых классов.

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

File:Config.png

Все настройки, связанные с обновлением кеша с удалённых серверов имеют 5 значений:

  • Use cached data - не обновлять данные вообще, а использовать только кешированные данные, доступные с последнего обновления или те, с которыми поставляется плагин, если обновлений не было вообще.
  • Download on each IDE start - обновлять кеш один раз в пределах каждого запуска NetBeans.
  • Download daily - обновлять кеш ежедневно.
  • Download weekly - обновлять кеш еженедельно.
  • Download monthly - обновлять кеш ежемесячно.

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

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

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo