Prepare for Vaadin, an extremely powerful Java framework for enterprise web

In a client-server architecture, Java applications are most often located on the server side, and web interfaces are created by separate groups of front-end developers using JavaScript. Java does not provide any useful tools to develop modern Web interfaces (Do you still remember what Java applets look like?) — neither in terms of design, nor in terms of the client-server interaction. But what if the entire client-server application was developed using Java? Why not to make the client part “native” to the browser in compliance with the most modern usability concepts?

Figure 1. Vaadin logo

Vaadin (by the way, this word means “deer” in Finnish) supports all common browsers for traditional PCs, as well as mobile devices and tablets. Developers utilize only Java, but the Java code is executed on the server, the client uses “pure” JavaScript.

Vaadin includes the following components: server API; client API; a set of user interface components on both sides; a subsystem user interface themes; a data model allowing to bind server components directly to the data. Two basic development models can be used: on the server side and on the client (browser) side.

Figure 2. Vaadin's architecture

Figure 2 contains basic architectural components of a web application created with the help of Vaadin.

Server-centric development model

Optimized for high performance

Server-centric development model is the basic model for Vaadin, it allows you to create finished applications without a single line of code on the client side. AJAX-based Vaadin Client-Side Engine is used to generate user interface in the browser.
Server-side approach allows you to develop user interface for your web application in almost the same way as the interface of traditional Java applications with direct access to data and services on the server. In this case, the server-side part of Vaadin will take care of the user interface in the browser, and the AJAX interaction between the browser and the server. Vaadin engine renders the user interface of the server-side application in the browser and takes care of all the aspects of the client-server communications.

Server part of the application is executed as a traditional Vaadin servlet of the Java application server. It is a pure Java in a JAR file that can be added to any traditional web application, this JAR file is executed in any servlet or portlet container — from Tomcat to Oracle WebLogic. The servlet receives HTTP requests from the client and interprets them as events of the specific user session. Events are associated with components of the user interface and delivered to event listeners defined in the application. When components of the user interface on the server side are changed by the logic of the user interface, the servlet renders those changes to be displayed in the web browser and generates a response. The client-side engine receives the response and uses it to show the changes on the webpage loaded into the browser.

Client-centric development model

Optimized for effective monitoring

Client-centric model enables the development of widgets and applications with the help of Java. A JavaScript compiler – Vaadin Compiler – that is based on Google Web Toolkit (GWT) is used to compile the widgets and applications in order to make them executable in the browser. JavaScript itself can also be used. It provides full access to the DOM structure and allows you to have maximum control over the browser.

Preparing the development environment

Principles of using Vaadin in the NetBeans 8.0.2 environment (version of the Vaadin Plugin for NetBeans — 1.1.3) are described below; in the sidebar, you can find links to video tutorials for IntelliJ IDEA and Eclipse (the plugin for Eclipse includes an editor for the graphical user interface).

To get started with NetBeans IDE, it is necessary to install the plugin: in the Tools -> Plugins -> Available Plugins menu, enter “vaadin” in the Search field, check “Vaadin Plugin for NetBeans” and click “Install,” and then answer “Yes” to all the prompts.

A new category — Vaadin — is now available when creating new projects (File -> New Project). Select Vaadin Web Application Project, click Next and enter a name for the new project, for example “myvaadin.”

13. Project Structure

Upon clicking “Finish,” the default project group for the Vaadin application is created. The main file with the minimum example of the Vaadin application source code is located in the “myvaadin-ui” project in the following file: /Source Packages/com.mycompany.myvaadin/MyUI.java; its main part looks as follows (“package” and “import” instructions are omitted):

@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 {
    }
}

In this simple project, the MyUI class that is an heir to the UI class is declared. The init() method is overridden in it. Inside it, a vertical layout (VerticalLayout) is generated with enabled margin. In addition, a new button is created with a click handler that adds a Label component with a text string. Then, the button is added to the layout by calling the addComponent() method. The @Theme(“mytheme”) directive defines the theme to be used (the themes will be described later in this article).

Prior to the first launch, we must rebuild all the projects (right click on ‘myvaadin – myvaadin-parent’ -> Build)

To run in debug mode, you can use the Jetty plugin or GlassFish Server integrated into NetBeans.
Right click on the project -> Debug –> Select Deployment Server (select GlassFish Server from the drop-down list).

After all the interdependencies are defined and all the application components are recompiled, the servlet is launched and a browser window is automatically opened.

Minimal project

Themes and styles

Let’s see what a button on our form looks like directly in the Inspector or Firebug (Fig. 5).

Button Click Me-styles

<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>

All styles of the button are retrieved from the styles.css file. This file is located in the /Web Pages/VAADIN/themes/mytheme/ section of the myvaadin-ui project, the file is generated using the SASS files (styles.scss, mytheme.scss and addons.scss) in the same directory. SASS files use the basic style called Valo (the previous version of this style — Reindeer — is still sometimes referred to in the documentation). Click here to read more about Valo, or use this link to get examples for all the components.

Basic parameters of the theme can be customized with the help of the corresponding variables. By simply setting several parameters, you can completely change the appearance of your application. For example, the font color is automatically defined taking into account the background color. The background color itself is set in the $v-background-color variable. To change it, let’s add the following line to the beginning of the file mytheme.scss:

$v-background-color: #000;

Then right-click on the myvaadin-ui project and select Vaadin -> Compile Widgetset and Theme or Compile Theme, and refresh the page in your browser.
Most of the elements in the project will change their background color to black, and the font color will also be changed automatically.

Using such an approach, it is enough to write two dozen lines of code to override the variables in order to completely change the application’s appearance, for example, in accordance with the flat style Metro. The styles themselves remain unchanged. You can find the resulting page here (select the Metro theme in the upper right corner). The original text is here:

You can also override the style directly. For example, to change the color of the label on the button, you can add the following lines to the mytheme.scss file (under the “// Insert your own theme rules here” line):

$textcolor: red;

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

Then, you should recompile the themes and refresh the page in your browser.

Instead of creating your own theme, you can use one of the existing ones by changing the name (mytheme) to one of the following: valo, runo, reindeer, chameleon, liferay.

Click here to learn more about the themes.

Creating a browser-based file manager

To demonstrate the elegance of the approach offered by Vaadin, let’s create a prototypal file manager.

Displaying the file system — let’s get acquainted with TreeTable and containers

Container is an interface of Vaadin which is a source of tabular or hierarchical data. Click here to learn more about the containers. SQLContainer is used to access the SQL database. There is also a ready-to-use container for the file system (FilesystemContainer), which is suitable for this project.

You can set the container as a data source for elements of the following types: Table (tabular data), Tree or TreeTable (hierarchical data) and others.

First, let’s create a new project and give it a name: fileman. Then we’ll add a method to create and initialize TreeTable in MyUI class. TreeTable will display the directory structure (if the line is highlighted in red in the source code, it means that this class does not have a corresponding “import” line; to add it, you can press and select “Add import for…” It should be noted that that java.io.File must be selected from the proposed options for the File class):

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

    private void initFileTree(ComponentContainer parentLayout) {
        // Create a TreeTable object to display hierarchical data in a tabular format
        treetable = new TreeTable("File System");
        treetable.setSelectable(true);
        treetable.setColumnCollapsingAllowed(true);
        treetable.setColumnReorderingAllowed(true);
        treetable.setSizeFull();
        parentLayout.addComponent(treetable);
    }
. . .
}

Let’s add a method to get new data for the TreeTable from the FilesystemContainer

private void updateFileTree(File sourcePath) {
    // Create the filesystem container
    FilesystemContainer currentFileSystem = new FilesystemContainer(sourcePath);
    currentFileSystem.setRecursive(false); // Disable the recursive reading of the subdirectories

    // Bind it to the TreeTable object displaying the file system
    treetable.setContainerDataSource(currentFileSystem);
    treetable.setItemIconPropertyId("Icon");
    treetable.setVisibleColumns(new Object[]{"Name", "Size", "Last Modified"}); // To hide the column with ID icon, let's specify the desired columns
}

We also need a method to determine the default project directory.

private File currentPath;

// An auxiliary function for obtaining the default directory of the application
// ~/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();
}

Let’s create a new “initAll” method by adding the calls to the methods declared above:

// Initialization of all elements
private void initAll(VerticalLayout layout) {
    initFileTree(layout);
    getDefaultDirectory();
    updateFileTree(currentPath);
}

In the “init()” method, delete everything associated with the “button” button and add a call to the new “initAll()” method, so that the “init()” is as follows:

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

Add “unfinished” functions for

// Refreshing of all elements
private void updateAll() {
    updateFileTree(currentPath);
    updateInfo();
}

// Update the information on the file/directory (upon changing the file/directory)
private void updateInfo() {
}

Let’s save the file and execute the application in debug mode. If the application is already running, it is enough to refresh your browser after you save the file and complete the deployment.

TreeTable events handling

Let’s add a handler for single and double mouse clicks at the end of the “initFileTree” method:

    // Adding a handler for mouse clicks
    treetable.addItemClickListener(new ItemClickEvent.ItemClickListener() {
        @Override
        public void itemClick(ItemClickEvent itemClickEvent) {
            String clickedFilename = itemClickEvent.getItemId().toString(); // The element over which the mouse button is pressed down
            System.out.println("ItemClick: pathname:" + clickedFilename);

            // Upon double clicking
            if (itemClickEvent.isDoubleClick()) {
                doChangeDir(clickedFilename);
            } else {
                doSelectFile(clickedFilename);
            }
        }
    });

Let’s add methods to handle user actions into the MyUI class

private String selectedFilename;

// The user performs an action related to update of the directory
private void doRefresh() {
    updateAll();
}

// The user performs an action related to opening of another directory
private void doChangeDir(String path) {
    currentPath = new File(path);
    if (currentPath.isDirectory()) {
        selectedFilename = null;
        updateAll();
    }
}

// The user performs an action related to transition to a higher-level directory
private void doUpLevel() {
    currentPath = currentPath.getParentFile();
    selectedFilename = null;
    updateAll();
}

// The user performs an action related to selection of a file
private void doSelectFile(String filename) {
    selectedFilename = filename;
    updateInfo();
}

From this moment, double-clicking allows to open a lower-level directory.

Let’s add the main menu with “Refresh” and “Up Level” options in the “File” submenu — similar to horizontal menus in traditional applications:

private void initMenuBar(Layout parentLayout) {
    // MenuBar object description
    // https://vaadin.com/book/-/page/components.menubar.html

    // Creating the main menu
    MenuBar menuBar = new MenuBar();    // Creating the object
    menuBar.setWidth("100%");           // Stretching to 100% of the available width
    parentLayout.addComponent(menuBar); // Adding to the layout

    // Adding to the "File" submenu of the main menu
    final MenuItem fileMenuItem = menuBar.addItem("File", null, null);

    // Adding the "Refresh" element and the corresponding handler to the "File" menu
    fileMenuItem.addItem("Refresh", FontAwesome.REFRESH, new MenuBar.Command() {
        @Override
        public void menuSelected(MenuItem selectedItem) {
            doRefresh();
        }
    });

    // Adding the "Up Level" element and the corresponding handler to the "File" menu
    fileMenuItem.addItem("Up Level", FontAwesome.ARROW_UP, new MenuBar.Command() {
        @Override
        public void menuSelected(MenuItem selectedItem) {
            doUpLevel();
        }
    });
}

private void updateMenuBar() {
    // Do nothing at the moment
}

These methods must be called in the first line of the “InitAll()” method, otherwise the menu is displayed below all other elements:

    initMenuBar(layout);

and in the “updateInfo()”:

    updateMenuBar();

Top and bottom panels

Then, let’s add a top panel with buttons and information on the current path / name of the selected file, as well as a bottom panel to display information about the file. Add the following into the MyUI class:

private Label labelFileName;

// Initialization of the top panel with buttons and information on the current path / selected file
private void initTopPanel(Layout parentLayout) {
    // Creating a new horizontal layout to be used as a toolbar
    HorizontalLayout topPanelLayout = new HorizontalLayout();
    // Stretching to 100% of the available width
    topPanelLayout.setWidth("100%");
    // There will be empty space between the elements
    topPanelLayout.setSpacing(true);
    // Adding to the main layout
    parentLayout.addComponent(topPanelLayout);

    // Creating the "Refresh" button
    // Creating the object itself
    Button button = new Button("Refresh");
    // Define an icon from the FontAwesome
    button.setIcon(FontAwesome.REFRESH);
    // There are styles of different sizes
    // button.addStyleName(ValoTheme.BUTTON_SMALL);
    // Adding to the layout
    topPanelLayout.addComponent(button);
    // Adding a handler for mouse clicks
    button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent event) {
            doRefresh();
        }
    });

    // Creating the "Up Level" button
    // Creating the object itself
    button = new Button("Up Level");
    // Define an icon from the FontAwesome
    button.setIcon(FontAwesome.ARROW_UP);
    // There are styles of different sizes
    // button.addStyleName(ValoTheme.BUTTON_SMALL);
    // Adding to the layout
    topPanelLayout.addComponent(button);
    // Adding a handler for mouse clicks
    button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent event) {
            doUpLevel();
        }
    });

    // Adding text for the selected filename
    // Creating the object itself
    labelFileName = new Label();
    // Adding to the layout
    topPanelLayout.addComponent(labelFileName);
    topPanelLayout.setComponentAlignment(labelFileName, Alignment.MIDDLE_CENTER);
    // This component will occupy all the available space
    topPanelLayout.setExpandRatio(labelFileName, 1);
}

// Refreshing the top panel
private void updateTopPanel(File currentPath, String selectedFilename) {
    if (selectedFilename != null) {
        labelFileName.setValue(selectedFilename);
    } else {
        labelFileName.setValue(currentPath.toString());
    }
}

Initialization of the bottom panel with information on the file

  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();
    // Stretching to 100% of the available width
    bottomPanelLayout.setWidth("100%");
    parentLayout.addComponent(bottomPanelLayout);

    // Creating a Label object to display information on the file
    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]);
    }
}

// Refreshing the bottom panel
private void updateBottomPanel(String pathname) {
    try {
        File file = new File(pathname);
        // Assign values to the Label objects to display information on the file
        bottomLabels[0].setValue(Long.toString(file.length()));
        bottomLabels[1].setValue((new Date(file.lastModified())).toString());
        // Information on the disk
        bottomLabels[2].setValue(Long.toString(file.getUsableSpace()));
        bottomLabels[3].setValue(Long.toString(file.getTotalSpace()));
        bottomLabels[4].setValue(Long.toString(file.getFreeSpace()));
    } catch (Exception e) {
        // Hide the exception
        for (Label bottomLabel : bottomLabels) {
            bottomLabel.setValue("NA");
        }
    }
}

These methods must be called in the “InitAll()” method, the method itself will be as follows:

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

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

    initBottomPanel(layout);
}

and in the “updateInfo()” method, which will be as follows:

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

After saving and refreshing the page in your browser, the file manager will contain a menu, a toolbar and a status bar.

Preview and splitter — the HorizontalSplitPanel and Embedded components

Let’s add a panel to preview image files in our file manager. Then, using this example, it would be easy to make a preview for text files in the TextArea component.

private HorizontalLayout previewLayout;
private Embedded previewEmbedded;

// Initialization of the main panel displaying the file structure and file preview
private void initMainPanels(VerticalLayout parentLayout) {
    HorizontalSplitPanel mainPanels = new HorizontalSplitPanel();
    mainPanels.setSizeFull();
    parentLayout.addComponent(mainPanels);
    parentLayout.setExpandRatio(mainPanels, 1);

    initFileTree(mainPanels);
    initPreview(mainPanels);
}

// Initialization of the file preview
private void initPreview(ComponentContainer parentLayout) {
    previewLayout = new HorizontalLayout();
    previewLayout.setSizeFull();
    parentLayout.addComponent(previewLayout);

    // Creating a component to preview image files
    // Creating the "Embedded" object
    previewEmbedded = new Embedded("Preview area", null);
    // Setting the visibility
    previewEmbedded.setVisible(true);
    // Adding to the layout
    previewLayout.addComponent(previewEmbedded);
    // Centering
    previewLayout.setComponentAlignment(previewEmbedded, Alignment.MIDDLE_CENTER);
}

// Hide the file preview
private void clearPreview() {
    previewEmbedded.setSource(null);
    previewEmbedded.setVisible(true);
}

// refresh the file preview
private void updatePreview(String pathname) {
    if (pathname == null || pathname.length() == 0) {
        clearPreview();
        return;
    }
    // Retrieving the file extension
    File file = new File(pathname);
    int lastIndexOf = pathname.lastIndexOf(".");
    String extension = (lastIndexOf == -1) ? "" : pathname.substring(lastIndexOf);
    // Maximum file size for preview: 128 KB
    final int PREVIEW_FILE_LIMIT = 128 * 1024;
    // Extensions of files for preview in the "Embedded" object (images, Flash, etc.)
    final String[] imageExtensions = new String[]{
        ".gif", ".jpeg", ".jpg", ".png", ".bmp", ".ico", ".cur", "swf", "svg"
    };
    // Hide the object for preview
    previewEmbedded.setVisible(false);
    // Make sure that the file size threshold is not exceeded
    if (file.length() > PREVIEW_FILE_LIMIT) {
        clearPreview();
        return;
    }
    // If the file extension is in the list of image files
    if (Arrays.asList(imageExtensions).contains(extension)) {
        Resource resource = new FileResource(file); // Creating the file resource
        previewEmbedded.setSource(resource);        // Defining the source for the "Embedded" object
        previewEmbedded.setVisible(true);           // Show the object
        previewLayout.setExpandRatio(previewEmbedded, 1.0f); // The object will occupy all the available space
    }
}

Let’s add the “initMainPanels()” method into the “InitAll()” method instead of the “initFileTree()” method for initialization of the file tree, since it is now called from the “initMainPanels”:

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

Add the following line in the “updateInfo()”:

    updatePreview(selectedFilename);

Do not forget to copy the image to the default directory (/fileman/fileman-ui/target/fileman-ui-1.0-SNAPSHOT
).

That’s all. Our file manager allows you to navigate through the file system, as well as preview files and their properties.

We have created a client-server application for browsers without writing a single line in JavaScript or spending time to implement AJAX interaction. Moreover, we do not take into account the peculiarities of web development at all.

12. Application

Conclusion

In general, the framework leaves a very good impression, it is well thought-out and documented. In addition, there are many examples of source code on GitHub. The Hacker magazine (author of this article, editor and chief editor) recommends that you use it, including ingest and in unlimited quantities!


3 Responses to “Prepare for Vaadin, an extremely powerful Java framework for enterprise web”

  1. Alex Punen

    Vaadin is a mistake that we made in our large enterprise by some projects; It is good in its defaults, but changing theme , both look and feel , as well as slight customization became a pain very soon. Plus sluggish UI response, Java developers having no idea about basic web development hammering away production code meant to run on the web, on different browsers, relying purely on the strength of the framework , is scary. Plus all the reasons you read here by real users; http://stackoverflow.com/questions/1183801/should-i-use-vaadin-framework

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>