В клиент-серверной архитектуре место Java-приложения — преимущественно на серверной стороне, при этом веб-интерфейс пишется отдельной группой фронт-енд разработчиков на JavaScript. Java не предлагает адекватных средств для создания современного веб-интерфейса (когда в последний раз ты видел Java-апплет..?) ни с точки зрения дизайна, ни с точки зрения реализации клиент-серверного взаимодействия.

А что, если бы все клиент-серверное приложение целиком писалось на Java, но его клиентская часть была бы «нативной» для браузера и соответствовала бы самым современным представлениям о юзабилити?

Введение



Рис. 1. Логотип Vaadin

Vaadin (кстати, в переводе с финского это слово означает «олениха») поддерживает все распространенные браузеры как обычных компьютеров, так и мобильных устройств и планшетов. Вся разработка ведется на Java, но Java-код выполняется только на сервере, на клиенте же выполняется чистый JavaScript.

Структурно Vaadin состоит из серверного API, клиентского API, набора компонентов пользовательского интерфейса с обеих сторон, механизма тем для оформления интерфейса и модели данных, позволяющей связывать серверные компоненты непосредственно с данными. Можно применять две основные модели разработки: на стороне сервера и на стороне клиента (браузера).


Рис. 2. Архитектура Vaadin

На рис. 2 показаны основные архитектурные компоненты веб-приложения, построенного с использованием Vaadin.

Серверная модель разработки


Оптимизировано для производительности


Серверная модель разработки для Vaadin является основной и позволяет создавать законченные приложения без разработки на стороне клиента. При этом используется AJAX-движок Vaadin Client-Side Engine, который формирует пользовательский интерфейс в браузере. Серверный подход позволяет фактически забыть про то, что разработка ведется под веб, и разрабатывать пользовательский интерфейс почти как традиционную Java-программу с непосредственным доступом к данным и сервисам на сервере. При этом серверная часть Vaadin позаботится и о формировании пользовательского интерфейса в браузере, и об AJAX-взаимодействии между браузером и сервером. Движок Vaadin осуществляет рендеринг пользовательского интерфейса приложения серверной стороны в браузере и реализует все детали обмена клиента и сервера.

Серверная часть приложения Vaadin исполняется как обычный сервлет сервера приложений Java. Она представляет собой чистую Java в JAR-файле, который может добавляться к любому стандартному веб-приложению и работает на любом контейнере сервлетов или портлетов от Tomcat до Oracle WebLogic. Сервлет принимает HTTP-запросы от клиента и интерпретирует их как события конкретной пользовательской сессии. События ассоциированы с компонентами пользовательского интерфейса и доставляются к обработчикам (event listeners), определенным в приложении. Если логика пользовательского интерфейса вносит изменения в компоненты пользовательского интерфейса со стороны сервера, сервлет рендерит их для отображения в веб-браузере и формирует ответ. Движок клиентской части, выполняемый в браузере, получает ответ и на его основе производит изменения в загруженной в браузере веб-странице.

Клиентская модель разработки


Оптимизировано для контроля


Клиентская модель позволяет разрабатывать виджеты и приложения на языке Java, которые затем компилируются в выполняемый в браузере JavaScript с помощью компилятора Vaadin Compiler, основанного на Google Web Toolkit (GWT). Можно использовать и непосредственно JavaScript. Это предоставляет полный доступ к структуре DOM и максимальный контроль над браузером.

Подготовка среды разработки


Ниже описывается использование Vaadin в среде NetBeans 8.0.2 (версия Vaadin Plug-in for NetBeans — 1.1.3); во врезке есть ссылки на обучающие видео для работы в IntelliJ IDEA и Eclipse (плагин для Eclipse включает в себя графический редактор пользовательского интерфейса).

Первым шагом в NetBeans IDE будет установка плагина (Tools -> Plugins -> Available Plugins, ввести vaadin в поле Search, установить галочку у 'Vaadin Plug-in for NetBeans' и нажать Install, согласившись со всеми вопросами).

Теперь при создании нового проекта (File -> New Project) стала доступна новая категория Vaadin. Выберем Vaadin Web Application Project, нажмем Next и укажем имя нового проекта, например myvaadin.


Рис. 3. Структура проекта

После нажатия Finish будет создана группа проектов приложения Vaadin по умолчанию. Основной файл с минимальным примером исходного кода приложения Vaadin расположен в проекте myvaadin-ui, файл /Source Packages/com.mycompany.myvaadin/MyUI.java; его ключевая часть выглядит так (опущены инструкции package и import):

@Theme("mytheme")
@Widgetset("com.mycompany.myvaadin.MyAppWidgetset")
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        setContent(layout);

        Button button = new Button("Click Me");
        button.addClickListener(new Button.ClickListener() {
            @Override
            public void buttonClick(ClickEvent event) {
                layout.addComponent(new Label("Thank you for clicking"));
            }
        });
        layout.addComponent(button);

    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

В этом простейшем проекте объявляется класс MyUI, являющийся наследником класса UI. В нем переопределяется метод init(). Внутри него создается вертикальная компоновка VerticalLayout, у нее включается отступ (margin), создается новая кнопка с обработчиком нажатия, который добавляет компонент типа Label с текстовой строкой. Затем кнопка добавляется к компоновке вызовом метода addComponent(). Директива Theme(«mytheme») задает используемую тему оформления (о них чуть ниже).

Перед первым запуском пересоберем все проекты (правый клик на 'myvaadin — myvaadin-parent' -> Build)

Для запуска в отладочном режиме можно использовать плагин Jetty или интегрированный в NetBeans сервер GlassFish Server
Щелкнем правой кнопкой на проекте -> Debug –> в окне Select Deployment Server из выпадающего списка выберем GlassFish Server.

После того как будут установлены все зависимости и перекомпилированы компоненты приложения, запустится сервлет и автоматически откроется окно браузера.


Рис. 4. Минимальное приложение Vaadin

Темы и стили


Взглянем непосредственно в инспекторе или в Firebug, что собой представляет кнопка на нашей форме (рис. 5).


Рис. 5. Кнопка пользовательского интерфейса

<div tabindex="0" role="button" class="v-button v-widget">
  <span class="v-button-wrap">
  <span class="v-button-caption">Click Me</span>
  </span>
</div>


Рис. 6. Просмотр HTML и стилей в Firebug

Все стили кнопки берутся из файла styles.css. Этот файл находится в разделе /Web Pages/VAADIN/themes/mytheme/ проекта myvaadin-ui и генерируется из SASS-файлов styles.scss, mytheme.scss и addons.scss, размещенных в том же каталоге. В них за основу берется базовый стиль, называющийся Valo (его предыдущая версия называлась Reindeer и местами по-прежнему упоминается в документации). Почитать про Valo можно здесь, а здесь можно посмотреть примеры всех компонентов.

Основные параметры темы вынесены в переменные, и для полной смены внешнего вида приложения достаточно изменения считаного числа параметров. Например, цвет шрифта определяется автоматически на основании цвета фона. Сам же цвет фона задается переменной $v-background-color. Чтобы изменить его, добавим в начало файла mytheme.scss следующую строку:

$v-background-color: #000;

Затем нужно щелкнуть правой кнопкой мышки на проекте myvaadin-ui и выбрать Vaadin -> Compile Widgetset and Theme или Compile Theme, после чего обновить в браузере страницу.
При этом фоновый цвет большинства элементов проекта изменится на черный, цвет шрифта изменится автоматически.

Благодаря такому подходу для того, чтобы полностью переоформить приложение, скажем, под плоский стиль Metro, достаточно двух десятков строк, переопределяющих значения переменных, без изменения собственно стилей. Результат можно посмотреть здесь (выбрав тему Metro в правом верхнем углу), а исходный текст — здесь:

Можно переопределять стиль и напрямую. К примеру, изменить цвет надписи на кнопке можно, добавив в файл mytheme.scss следующие строки (ниже строки «// Insert your own theme rules here»):

$textcolor: red;

.v-button-caption {
  color: $textcolor;
}

Затем перекомпилировать темы и обновить страницу браузера.

Вместо того чтобы создавать свою тему, можно воспользоваться и одной из готовых, изменив название темы с mytheme на одно из следующих: valo, runo, reindeer, chameleon, liferay.

Подробнее о темах можно прочитать здесь.

Создание браузерного файл-менеджера


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

Отображение файловой системы — знакомство с TreeTable и контейнерами


Container — интерфейс Vaadin, представляющий собой источник табличных или иерархических данных. Подробнее о контейнерах можно почитать здесь. Для доступа к базе данных SQL предназначен контейнер SQLContainer. Для файловой системы также существует готовый контейнер FilesystemContainer, который мы и используем в этом проекте.

Контейнер можно задавать в качестве источника данных для элементов типа Table (табличные данные), Tree и TreeTable (для отображения иерархических данных) и других.

Начнем с того, что создадим новый проект с названием fileman. Добавим классу MyUI метод создания и инициализации элемента TreeTable, который будет отображать структуру каталога (если строка в исходном тексте подсвечивается красным, это значит, что для данного класса нет соответствующей строки import; чтобы ее добавить, можно нажать <Alt + Enter> и выбрать «Add import for...». Нужно уточнить, что ниже для класса File потребуется выбрать из предложенных именно java.io.File):

public class MyUI extends UI
{
. . .
    private TreeTable treetable;

    private void initFileTree(ComponentContainer parentLayout) {
        // Создадим объект TreeTable для отображения иерархических данных в табличном виде
        treetable = new TreeTable("File System");
        treetable.setSelectable(true);
        treetable.setColumnCollapsingAllowed(true);
        treetable.setColumnReorderingAllowed(true);
        treetable.setSizeFull();
        parentLayout.addComponent(treetable);
    }
. . .
}    

Добавим метод установки новых данных TreeTable из FilesystemContainer

private void updateFileTree(File sourcePath) {
    // Создаем контейнер файловой системы
    FilesystemContainer currentFileSystem = new FilesystemContainer(sourcePath);
    currentFileSystem.setRecursive(false); // Отключаем рекурсивное считывание подкаталогов

    // Связываем его с объектом TreeTable, отображающим файловую систему
    treetable.setContainerDataSource(currentFileSystem);
    treetable.setItemIconPropertyId("Icon");
    treetable.setVisibleColumns(new Object[]{"Name", "Size", "Last Modified"}); // Для того чтобы скрыть колонку с идентификатором иконки, укажем нужные колонки
}

Также добавим метод определения каталога проекта по умолчанию.

private File currentPath;

// Вспомогательная функция для получения каталога приложения по умолчанию
// ~/NetBeansProjects/fileman/target/fileman-1.0-SNAPSHOT/
private void getDefaultDirectory() {
    UI ui = MyVaadinUI.getCurrent();
    VaadinSession session = ui.getSession();
    VaadinService service = session.getService();
    currentPath = service.getBaseDirectory();
}

Создадим новый метод initAll, добавив в него вызовы объявленных выше методов:

// Инициализация всех элементов
private void initAll(VerticalLayout layout) {
    initFileTree(layout);
    getDefaultDirectory();
    updateFileTree(currentPath);
}

В методе init() удалим все, что связано с кнопкой button, и добавим в конце вызов нового метода initAll() так, чтобы init() выглядел следующим образом:

@Override
protected void init(VaadinRequest request) {
    final VerticalLayout layout = new VerticalLayout();
    layout.setMargin(true);
    setContent(layout);
    initAll(layout);
}

Добавим заготовки под функции для

// Обновление всех элементов
private void updateAll() {
    updateFileTree(currentPath);
    updateInfo();
}

// Обновление информации о файле/каталоге (при изменении файла/каталога)
private void updateInfo() {
}

Сохраним файл и запустим приложение в отладочном режиме. Если приложение уже было запущено раньше, то после сохранения и завершения развертывания (deploy) достаточно просто обновить страницу в браузере.

Обработка событий компонента TreeTable


В конце метода initFileTree добавим обработчик одинарного и двойного нажатия кнопок мыши:

// Добавляем обработчик нажатия
treetable.addItemClickListener(new ItemClickEvent.ItemClickListener() {
    @Override
    public void itemClick(ItemClickEvent itemClickEvent) {
        String clickedFilename = itemClickEvent.getItemId().toString(); // Элемент, на котором была нажата кнопка мыши
        System.out.println("ItemClick: pathname:" + clickedFilename);

        // Если двойной клик
        if (itemClickEvent.isDoubleClick()) {
            doChangeDir(clickedFilename);
        } else {
            doSelectFile(clickedFilename);
        }
    }
});

Добавим методы классу MyUI для обработки действий пользователя

private String selectedFilename;

// Пользовательское действие — обновление каталога
private void doRefresh() {
    updateAll();
}

// Пользовательское действие — переход в другой каталог
private void doChangeDir(String path) {
    currentPath = new File(path);
    if (currentPath.isDirectory()) {
        selectedFilename = null;
        updateAll();
    }
}

// Пользовательское действие — переход в каталог на уровень выше
private void doUpLevel() {
    currentPath = currentPath.getParentFile();
    selectedFilename = null;
    updateAll();
}

// Пользовательское действие — выбор файла
private void doSelectFile(String filename) {
    selectedFilename = filename;
    updateInfo();
}

С этого момента по двойному клику уже можно переходить в каталог, находящийся уровнем ниже.

Главное меню — компонент MenuBar


Добавим главное меню с пунктами Refresh и Up Level в подменю File, подобное горизонтальным меню традиционных приложений:

private void initMenuBar(Layout parentLayout) {
    // Описание объекта MenuBar
    // https://vaadin.com/book/-/page/components.menubar.html

    // Создаем главное меню
    MenuBar menuBar = new MenuBar();    // Создаем объект
    menuBar.setWidth("100%");           // Растягиваем на 100% доступной ширины
    parentLayout.addComponent(menuBar); // Добавляем в layout    

    // Добавляем в главное меню подменю File
    final MenuItem fileMenuItem = menuBar.addItem("File", null, null);

    // Добавляем в меню File элемент Refresh и обработчик при его выборе
    fileMenuItem.addItem("Refresh", FontAwesome.REFRESH, new MenuBar.Command() {
        @Override
        public void menuSelected(MenuItem selectedItem) {
            doRefresh();
        }
    });

    // Добавляем в меню File элемент Up Level и обработчик при его выборе
    fileMenuItem.addItem("Up Level", FontAwesome.ARROW_UP, new MenuBar.Command() {
        @Override
        public void menuSelected(MenuItem selectedItem) {
            doUpLevel();
        }
    });
}

private void updateMenuBar() {
    // Пока ничего не делать
}

И вызов этих методов в метод InitAll() первой строкой (иначе меню окажется ниже всех других элементов):

initMenuBar(layout);

и в updateInfo():

updateMenuBar();

Верхняя и нижняя панели


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

private Label labelFileName;

// Инициализация верхней панели, содержащей кнопки и текущий путь / выбранный файл
private void initTopPanel(Layout parentLayout) {
    // Создаем новую горизонтальную компоновку, которая будет служить панелью инструментов
    HorizontalLayout topPanelLayout = new HorizontalLayout();
    // Растягиваем на 100% доступной ширины
    topPanelLayout.setWidth("100%");
    // Между элементами будет пустое пространство
    topPanelLayout.setSpacing(true);
    // Добавляем к основной компоновке
    parentLayout.addComponent(topPanelLayout); 

    // Создаем кнопку Refresh
    // Создаем сам объект
    Button button = new Button("Refresh");
    // Задаем иконку из FontAwesome
    button.setIcon(FontAwesome.REFRESH);
    // Есть стили разных размеров
    // button.addStyleName(ValoTheme.BUTTON_SMALL);
    // Добавляем в компоновку
    topPanelLayout.addComponent(button);                 
    // Добавляем обработчик нажатия
    button.addClickListener(new Button.ClickListener() { 
        @Override
        public void buttonClick(Button.ClickEvent event) {
            doRefresh();
        }
    });

    // Создаем кнопку Up Level
    // Создаем сам объект
    button = new Button("Up Level");
    // Задаем иконку из FontAwesome
    button.setIcon(FontAwesome.ARROW_UP);
    // Есть стили разных размеров
    // button.addStyleName(ValoTheme.BUTTON_SMALL);
    // Добавляем в компоновку
    topPanelLayout.addComponent(button);
    // Добавляем обработчик нажатия
    button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent event) {
            doUpLevel();
        }
    });

    // Добавляем текст с именем выбранного файла
    // Создаем сам объект
    labelFileName = new Label();
    // Добавляем в компоновку
    topPanelLayout.addComponent(labelFileName);
    topPanelLayout.setComponentAlignment(labelFileName, Alignment.MIDDLE_CENTER);
    // Данный компонент будет занимать все доступное место
    topPanelLayout.setExpandRatio(labelFileName, 1);
}

// Обновление верхней панели
private void updateTopPanel(File currentPath, String selectedFilename) {
    if (selectedFilename != null) {
        labelFileName.setValue(selectedFilename);
    } else {
        labelFileName.setValue(currentPath.toString());
    }
}

Инициализация нижней панели, содержащей информацию о выбранном файле

  Label[] bottomLabels;
  private void initBottomPanel(Layout parentLayout) {
    final String[] captions = new String[]{
        "File Size (Bytes)", "File Date", "Usable Space (Bytes)", "Total Space (Bytes)", "Free Space (Bytes)"
    };

    HorizontalLayout bottomPanelLayout = new HorizontalLayout();
    // Растягиваем на 100% доступной ширины
    bottomPanelLayout.setWidth("100%"); 
    parentLayout.addComponent(bottomPanelLayout);

    // Создаем объекты Label для отображения информации о файле
    bottomLabels = new Label[captions.length];
    for (int i = 0; i < captions.length; i++) {
        bottomLabels[i] = new Label();
        bottomLabels[i].setCaption(captions[i]);
        bottomLabels[i].setValue("NA");
        bottomPanelLayout.addComponent(bottomLabels[i]);
    }
}

// Обновление нижней панели
private void updateBottomPanel(String pathname) {
    try {
        File file = new File(pathname);
        // Присваиваем значения объектам Label — информация о файле
        bottomLabels[0].setValue(Long.toString(file.length()));
        bottomLabels[1].setValue((new Date(file.lastModified())).toString());
        // Информация о диске
        bottomLabels[2].setValue(Long.toString(file.getUsableSpace()));
        bottomLabels[3].setValue(Long.toString(file.getTotalSpace()));
        bottomLabels[4].setValue(Long.toString(file.getFreeSpace()));
    } catch (Exception e) { 
        // Скроем исключительную ситуацию
        for (Label bottomLabel : bottomLabels) {
            bottomLabel.setValue("NA");
        }
    }
}

Добавляем вызов этих методов в метод InitAll(), приведя его к следующему виду:

private void initAll(VerticalLayout layout) {
    initMenuBar(layout);
    initTopPanel(layout);

    initFileTree(layout);
    getDefaultDirectory();
    updateFileTree(currentPath);
            
    initBottomPanel(layout);
}


и в updateInfo(), приведя его к следующему виду:

private void updateInfo() {
    updateMenuBar();
    updateTopPanel(currentPath, selectedFilename);
    updateBottomPanel(selectedFilename);
}

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

Предварительный просмотр и сплиттер — компоненты HorizontalSplitPanel, Embedded


Добавим нашему файл-менеджеру панель предварительного просмотра графических файлов. По аналогии несложно сделать предварительный просмотр для текстовых файлов с помощью компонента TextArea.

private HorizontalLayout previewLayout;
private Embedded previewEmbedded;

// Инициализация основной панели, содержащей просмотр файловой структуры и предварительный просмотр файла
private void initMainPanels(VerticalLayout parentLayout) {
    HorizontalSplitPanel mainPanels = new HorizontalSplitPanel();
    mainPanels.setSizeFull();
    parentLayout.addComponent(mainPanels);
    parentLayout.setExpandRatio(mainPanels, 1);

    initFileTree(mainPanels);
    initPreview(mainPanels);
}

// Инициализация панели предварительного просмотра файла
private void initPreview(ComponentContainer parentLayout) {
    previewLayout = new HorizontalLayout();
    previewLayout.setSizeFull();
    parentLayout.addComponent(previewLayout);
    
    // Создаем элемент для предпросмотра изображений
    // Создаем объект Embedded
    previewEmbedded = new Embedded("Preview area", null); 
    // Задаем видимость
    previewEmbedded.setVisible(true);    
    // Добавляем в компоновку
    previewLayout.addComponent(previewEmbedded); 
    // Располагаем по центру
    previewLayout.setComponentAlignment(previewEmbedded, Alignment.MIDDLE_CENTER); 
}

// Скрыть предварительный просмотр файла
private void clearPreview() {
    previewEmbedded.setSource(null);
    previewEmbedded.setVisible(true);
}

// Обновить предварительный просмотр файла
private void updatePreview(String pathname) {
    if (pathname == null || pathname.length() == 0) {
        clearPreview();
        return;
    }
    // Выделим расширение файла
    File file = new File(pathname);
    int lastIndexOf = pathname.lastIndexOf(".");
    String extension = (lastIndexOf == -1) ? "" : pathname.substring(lastIndexOf);
    // Ограничение на размер файла для предпросмотра — до 128 Кб
    final int PREVIEW_FILE_LIMIT = 128 * 1024;
    // Расширения файлов для предпросмотра с помощью объекта Embedded (изображения, Flash и так далее)
    final String[] imageExtensions = new String[]{
        ".gif", ".jpeg", ".jpg", ".png", ".bmp", ".ico", ".cur", "swf", "svg"
    };
    // Скроем объект, используемый для предпросмотра
    previewEmbedded.setVisible(false);
    // Проверим, не превышает ли размер файла пороговый
    if (file.length() > PREVIEW_FILE_LIMIT) {
        clearPreview();
        return;
    }
    // Если расширение файла — в списке изображений
    if (Arrays.asList(imageExtensions).contains(extension)) {
        Resource resource = new FileResource(file); // Создаем файловый ресурс
        previewEmbedded.setSource(resource);        // Задаем источник для объекта Embedded
        previewEmbedded.setVisible(true);           // Показываем объект
        previewLayout.setExpandRatio(previewEmbedded, 1.0f); // Будет занимать все доступное место 
    }
}

И добавляем метод initMainPanels() в метод InitAll(), вместо вызов метода инициализации дерева файлов initFileTree(), так как теперь он вызывается из initMainPanels:

private void initAll(VerticalLayout layout) {
    initMenuBar(layout);
    initTopPanel(layout);
    // initFileTree(layout);
    initMainPanels(layout);
    getDefaultDirectory();
    updateFileTree(currentPath);                
    initBottomPanel(layout);
}

и добавляем в updateInfo() строку

updatePreview(selectedFilename);      

Не забудь скопировать изображение в папку по умолчанию (<каталог проектов NetBeans (NetBeansProjects)>/fileman/fileman-ui/target/fileman-ui-1.0-SNAPSHOT
).

Ну вот и все, с помощью нашего файл-менеджера можно перемещаться по файловой системе, просматривать файлы и их свойства.

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


Рис. 6. Интерфейс приложения

Заключение


В целом фреймворк оставляет очень приятное впечатление своей продуманностью и документированностью, большим количеством примеров с исходными кодами на GitHub. Журнал «Хакер» (на данный момент под этим утверждением подписался автор, редактор и главред) рекомендует тебе его использование, в том числе внутрь и в неограниченных количествах!

Google Web Toolkit (GWT)


Google Web Toolkit (GWT) — библиотека с открытым кодом, предоставляющая набор Java API и визуальных компонентов, позволяющих разрабатывать AJAX-приложения на Java и затем компилировать их исходные тексты в высоко оптимизированный JavaScript, работающий на всех основных браузерах, включая мобильные браузеры для Android и iPhone. Подробнее — здесь.

VIDEO



WWW


  • Подробно начало работы описано в разделе Getting Started with Vaadin электронной книги Book of Vaadin.
  • Отличным дополнением книге служит Book of Vaadin Examples, с фрагментами исходного кода для подавляющего большинства разделов книги и элементов Vaadin.
  • Небольшой учебный пример приложения можно посмотреть здесь.
  • Более сложные демоприложения.
  • Документация по API доступна здесь.

Vaadin TouchKit


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

Vaadin TestBench


Базируется на библиотеке Selenium, что позволяет управлять браузером непосредственно из кода Java.
С помощью Vaadin TestBench реализуется автоматизированное тестирование на всех уровнях и фазах разработки вплоть до сравнения скриншотов. Более подробно можно прочитать здесь.

Дополнения Vaadin


В Vaadin Directory на данный момент почти 500 различных дополнений, среди которых можно, например, отметить компонент Vaadin Charts, предназначенный для отрисовки графиков и диаграмм.

Для доступа к каталогу дополнений в контекстном меню проекта есть пункт Open Add-Ons Browser.

image

Впервые опубликовано в журнале «Хакер» от 02/2015.
Автор: Александр Лыкошин, alykoshin@gmail.com


Подпишись на «Хакер»

Комментарии (38)


  1. AndersonDunai
    23.04.2015 12:43
    -4

    У меня в голове засело «DVA-ADIN» и не выходит…


    1. AndersonDunai
      27.04.2015 13:17
      -2

      Moar minuses!


  1. P0WERMIC
    23.04.2015 12:44
    +15

    Да ну его этот Vaadin. Строили на нем front-end для enterprise решения и он реально очень тяжелый на нем все очень долго (считай в 3 раза дороже) делать, если нужно что-то большее, чем стандартные компоненты. В общем, я не рекомендую.


    1. Dr_XaoS
      23.04.2015 13:28
      +3

      Да, это так. Все очень радужно, пока укладываешься в небольшой набор стандартных компонентов. Но шаг в сторону — и надо либо добавлять css/js, чтобы пдоравнивать расползающиеся элементы, либо запиливать свои, да так чтобы вписаться в архитектуру ваадина.
      Еще не понравилась система layout'ов — задумка хорошая, но выливается это в сотню вложенных div'ов.


      1. Calc
        23.04.2015 13:41
        +1

        Не знаю как в Vaadin, но в GWT документации четко сказано «Используйте виджет, а GWT сам придумает как его отобразить», и да, это выливается во множество div. «GWT думает за вас» — как основной функционал приподносится.


        1. Minoru
          24.04.2015 11:25
          +1

          сколько бы он ни думал за нас, клиент всегда думает по-другому


          1. Borz
            24.04.2015 11:57

            клиенту всё равно сколько div/table использованы чтобы показать ему элемент интерфейса — для него главное, чтобы скорость отображения и взаимодействия с этим элементом не выходила за зону его комфорта.


            1. dea
              24.04.2015 17:47
              -1

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


      1. hardex
        23.04.2015 13:41
        +1

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


    1. DbLogs
      24.04.2015 01:51

      А на что перешли? Мы вот на Apache Wicket — все тоже самое, но ооочень быстро. Ну и в Wicket можно докапаться до таких глубин, если надо — что Vaadin'у и не снилось.


      1. P0WERMIC
        24.04.2015 07:12

        перешли на чистый html5 и Angular


        1. FaNtAsY
          24.04.2015 12:11
          +1

          Мы тоже пришли к выводу, что для клиентской стороны нужен HTML5 после того, как некоторое время поработали с JSF + Primefaces.
          Итог простой: как только нужно что-то посложнее стандартных компонентов, моментально возрастает сложность и время разработки.
          Вместо ангуляра можно и React использовать — на любителя.


  1. vaniaPooh
    23.04.2015 13:06

    Мне в GWT не нравится то, что получаемый Javascript очень трудно отлаживать и я согласен с POWERMIC, что он действительно очень тяжелый.


    1. gagoman
      23.04.2015 13:18
      +2

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


      1. Calc
        23.04.2015 13:23
        +2

        На GWT еще хорошо прототипы делать, особенно если ты не WEB программист и не знаком с js напрямую.


        1. FaNtAsY
          24.04.2015 12:12

          И получается, что ты программируешь нечто, не имея представления, как это работает. Не надо так :)


          1. Calc
            24.04.2015 13:17

            Не факт
            У меня очень хорошая база по web bakcend программированию на PHP(8 лет) и других интерпретируемых языках.
            А когда ты уходишь в проектировани
            frontend терпеть не могу, но знаю как работает.
            Давно живу на java (desktop, android и т.д.), так что GWT дает хороший и простой инструмент для отработки некоторых элементов + после это можно включать в ТЗ для web программиста, либо использовать в том виде как есть.
            И да, я не работаю программистом :)


            1. FaNtAsY
              24.04.2015 13:21

              Похоже, мы недопоняли друг друга.

              Из вашей фразы

              особенно если ты не WEB программист и не знаком с js напрямую.

              я сделал, как мне кажется, логичный вывод:
              получается, что ты программируешь нечто, не имея представления, как это работает.

              , подразумевая не конкретно вас, а того, программирует web-интерфейс с JS, но при этом не знаком с JS.

              Сейчас вы пишете
              frontend терпеть не могу, но знаю как работает.

              Следовательно, мой комментарий не про вас :)


    1. TimReset
      23.04.2015 18:40
      +1

      А Вы давно на GWT писали? А то тут опции компиляции pretty появились и SuperDevMode с source map. Я сам source map пользовался в 2.6 и 2.7. Отлаживал JS как Java код в IDEA. Т.е. фактически исполнялся JS в Chrome (не как Dev mode с исполнением клиентского Java кода на сервере), а IDEA точки останова переносила в Java код и выглядело как отладка обычного Java кода. Соглашусь, что работает хуже в плане отладки, чем в Dev Mode, но пользоваться можно.


      1. dea
        23.04.2015 20:22
        +1

        Поддержу, в 2.7 отладка с source map доведена до приемлемого качества. По моему, главной проблемой GWT является долгая перекомпиляция, но и тут в 2.7 заметные улучшения в компиляции на лету.


  1. vit1251
    23.04.2015 14:49

    А есть какой-то простой Framework для Servlet+ORM+Template? А то использовать таких монстриков как Spring и Play! очень уж не хочется.
    Все больше думаю о написании костыльного FrontContoller+Templater+собственный частный ORM (частный значит реализует преобразования SQL в POJO), но конечно хотелось бы уже готовые решения посмотреть. Может быть кто-то подскажет?


    1. Borz
      23.04.2015 15:09

      шаблонизатор: JMTE или SiteMesh
      ORM: OrmLite или MyBatis


  1. AxianLTD
    23.04.2015 16:28
    +1

    Мне JSF и компания вполне подходят. И вручную немного писать приходится, и проектировать интерфейс достаточно просто.


    1. dea
      23.04.2015 20:26

      Порог входа слишком высокий. Но согласен, оно того стоит, если правильно организовать разделение в команде между версткой и логикой. Я по facelet'ам сильно скучаю.


  1. Acvilon
    23.04.2015 19:41
    +1

    Я очень удивлен, что кто-то еще использует таких монстров как: Vaadin, JSF и другие подделки, где люди пытаются жизненный цикл Frontend'а написать на backend'е.


    1. dea
      23.04.2015 20:30
      +2

      Просто не надо на них как на swing писать, тогда все получится.


  1. rtorsten
    23.04.2015 21:04
    +4

    Мне очень не понравился Vaadin, как фреймворк для enterprise приложений. К сожалению сейчас проект на нем написали и приходится поддерживать сам Vaadin и кучу кастылей для него, чтобы он хоть как-то работал.

    Фреймворк очень тяжелый и не гибкий. Например, можно сделать generated column, а потом узнать что при открытии страницы на сервере исчезает 100 Мб на каждую, потому что где-то там внутри вызывается некий refresh row cache, который перегенерит каждую ячейку 100500 раз и создаст столько layout-ов и прочего стафа для нее.
    Кастомные компоненты — писать тоже очень не удобно.
    Очень много дефектов на UI связанных с zooming, scrollbars и resize'нгом для IE бразуеров (которые наиболее часто используются в корпоративном секторе, для которого собственного вроде как и делалася Vaadin).
    Очень много проблем так же возникает с интеграцией в корпоративную инфраструктуру. Любые лаги в сети делают Vaadin абсолютно неработоспособным.

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


  1. dax
    23.04.2015 23:57
    +1

    Подозреваю, что множества «Ява-Разработчики» и «Профессиональные Верстальщики» практически не пересекаются. Оттого смотрю на Vaadin с большой долей скепсиса.


    1. alexmay
      24.04.2015 03:00

      Очень. Медленно. Работает.


    1. Borz
      24.04.2015 10:41
      +1

      можно пересекаться. например через чистый Spring WebMVC, ZK Framework (с темой atlantic), JMTE или SiteMesh. А GWT, Vaadin и иже с ними (как и вышеозвученный ZK) это для Rich Internet application, где в первую очередь функциональность, а потом уже «рюшечки».


  1. tinhol
    24.04.2015 06:54
    +1

    Мы используем Vaadin, как механизм создания веб-интерфейса в нашей платформе. По мне, так он неплохо подходит для создания корпоративных приложений, где красота явно не на первом месте. Ведь такие приложения — это в первую очередь очень много функционала, который постоянно меняется. Верстать каждый раз с нуля такое — очень утомительно, на Vaadin гораздо быстрее.
    Как мне кажется, для создания публичных приложений лучше использовать что-то другое, Vaadin с его «топорностью» тут проигрывает.


    1. P0WERMIC
      24.04.2015 07:17

      Это пока их UI не интересует, придет время и они начнут просить сделать все красиво и удобно и придется страдать.


      1. tinhol
        24.04.2015 07:38
        +1

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


      1. Borz
        24.04.2015 10:44
        +1

        когда заинтересует «сделать красиво», разработают тему и просто её применят. ИМХО, для корп. приложений не требуются футуристические полёты мыслей дизайнера.


    1. Throwable
      24.04.2015 17:31

      Не совсем понятны утверждения насчет некрасивости? Тема Valo вполне неплохо смотрится и легко кастомизируется. Reindeer тоже ничего, за исключением кнопок. У приложений единый лук, стиль везде выдержан.


      1. tinhol
        24.04.2015 17:45
        +1

        Проблема (на мой взгляд) в том, что интерфейс зачастую подогнан под требования Vaadin, то есть использует VerticalLayout, HorizontalLayout и так далее. Чтобы уйти от этого (например использовать кастомную html верстку), требуется усилий ничуть не меньше, чем верстать html с нуля на каком нибудь client-side фреймворке. То есть приложение вынужденно подгоняется под возможности фреймворка и имеет соответствующий вид. Для корпоративных приложений это приемлемо, для публичных — вряд ли.


  1. cyberorg
    24.04.2015 16:51

    >Vaadin (кстати, в переводе с финского это слово означает «олениха»…
    Если немного подробнее, то Vaadin — это богиня-олениха из финской мифологии.
    В Book of Vaadin есть любопытное приложение «Songs of Vaadin»


  1. squonk
    24.04.2015 18:19

    Всегда интересно было, что сподвигло авторов делать запроы на сервер на каждый «чих» приложения. Что-то подобное видел давно в iceFaces.