Practical applications of Vaadin - in words

Published on August 2, 2016 by Aleksandr Pchelintcev



Magnolia Conference this year seems to have been a huge success and is surely the most memorable one for myself since I finally got to present something.

One of the topics that I know fairly well is the Vaadin framework and how to use it with Magnolia so my talk was devoted to this. My primary goal was to introduce the audience to some more advanced aspects of Vaadin, like client-server communication mechanics and development of custom extensions and components. I prepared a couple of demos, one of which was coded in real time ( for the first time in my life!) It was a risky thing to do on the one hand, but great fun on the other! The one thing I regret a little is that I had so much to say but only half an hour to say it in, which forced me to go through the content quite fast. In this post I would like to recap several parts of my talk in order to complement the things I said on the stage.

You can also watch my talk directly here.

 

A bit of theory

During the first part of the presentation we discussed Vaadin's underlying communication mechanism. The ultimate goal was to explain the magic that allows Vaadin users to separate the network communication part from their web application development.

We talked about Vaadin's well known server-side Components and their less well known client-side counterparts called ComponentConnectors and how those communicate over the shared state pattern and Vaadin RPC. During the talk we learnt that with these mechanisms it is quite easy to handle the component life cycle, i.e.

  • maintaining and synchronising the component's internal state between the client and the server
  • handling the user-initiated events via dedicated interfaces, 
  • arranging components into complex layouts and hierarchies 

We won't spend too much time repeating those topics here, but for those interested in learning more, I would like to suggest some nice in-depth articles provided by the Vaadin team (can be found at the end of the post).

 

Raptorizr

Since we didn't have time to demonstrate the development of something useful step-by-step, I decided to demonstrate the development of something useless... but fun!

I picked an awesome Raptorize jQuery plugin (which I've wanted to use somewhere for a long time anyway) and added it to Magnolia.  

The trick here is that we don't just simply throw in a JS script that runs in parallel with Magnolia UI, but actually create an API which allows us to assemble and trigger the raptor animation through Magnolia interfaces according to some custom events. As a result I got a simple mixture of JavaScript, Vaadin and Magnolia UI framework communicating with each other through easy to understand interfaces without any magic involved. In the same way it's possible to create more sophisticated components (e.g. an AppSwitcher which we will consider later) and integrate existing JS solutions into Magnolia apps effortlessly. 

Let's recall what the coding part was all about.

 

The Vaadin extension

We'll start with one of the main classes of our small project called Raptorizr, which is going to be a custom JavaScript extension. In Vaadin 7, extension is like a component plugin which can add functionality both on the client and the server sides. It is pretty much the same concept as components themselves except that extensions do not have their own 'UI block', but instead attach to the parent's one. When we intend to extend the web app on the whole, it is convenient to attach the extension directly to the root component (called UI in Vaadin 7 and is extended by AdminCentralUI in Magnolia). 

 

@JavaScript({
    "jquery.raptorize.1.0.js",
    "raptorizr_connector.js"
})
public class Raptorizr extends AbstractJavaScriptExtension {

    public static Raptorizr raptorize(final UI ui) {
        final Raptorizr raptorizr = new Raptorizr();
        raptorizr.extend(ui);
        return raptorizr;
    }

    public void unleashRaptor() {
        getRpcProxy(RaptorizrRpc.class).unleashRaptor();
    }

    public void setRaptorImage(String imageUrl) {
        getState().raptorImage = imageUrl;
    }

    public void setRaptorSound(String raptorSound) {
        getState().raptorSound = raptorSound;
    }
 
    @Override
    protected RaptorizrState getState() {
        return (RaptorizrState) super.getState();
    }
}

The server-side Vaadin extension is a really simple class without much of the logic involved. What it does is the following:

  • provides API for setting the image and sound resource URLs
  • allows you to easily attach Raptorizr to the UI (or speaking in Vaadin terms - extend)
  • finally - declares the API which instructs the JS client-side via a remote call to start the raptor fun (Raptorizr#unleashRaptor() method)

It is also worth mentioning the @JavaScript annotation in the head of the class. This is something crucial for binding the extension with its client-side counterpart. @JavaScript annotation makes sure to include a script listed in the annotation's value to the page as soon as Raptorizr is attached to the parent component and becomes present on the client-side. In our case such list includes connector script raptorizr_connector.js which we will consider shortly and the actual JQuery plugin script which executes the raptor animation.

Communication to the client

Before we see some JavaScript - let's have a look at the communication layer.

Shared state class called RaptorizrState is responsible for buffering some common data between the server and client. In our case such data would include the aforementioned resource URLs required to render the raptor and let it roar. This class is a merest POJO and the parent class JavaScriptExtensionState is also a simple POJO which contains some framework defined common fields.

public class RaptorizrState extends JavaScriptExtensionState {
    public String raptorImage;
    public String raptorSound;
}

RaptorizrRpc interface exposes a method which can be invoked to the client as a signal to action.

public interface RaptorizrRpc extends ClientRpc {
    void unleashRaptor();
}

The JavaScript connector

Okay, so we have a server-side extension class and are ready to use with our Magnolia code and we also have the communication interface. Now let's see how the visual part is handled in the browser with a JS connector script:

info_magnolia_raptorizr_Raptorizr = function() {
    
    var connectorId = this.getConnectorId();
    var parentConnectorId = this.getParentId(connectorId);
    var handle = {}
    
    $(this.getElement(parentConnectorId)).raptorize({
        raptorImage: this.getState().raptorImage,
        raptorSound: this.getState().raptorSound,
        handle: handle
    });

    this.registerRpc({
        unleashRaptor: function() {
            setTimeout(handle.unleashRaptor, 1000);
        }
    });
}

The funny looking name of the script's function is not a coincidence: in order for framework to know which server-side Java class to bind this script to, its f.q.n is resolved from the connector's function name. Otherwise the script is really straightforward: we simply do the following:

  • take resource URLs provided by the server via shared state
  • resolve the parent's element through standard JavaScriptExtension connector's API (this.getConnectorId(), this.getParentId(..) and this.getElement(..) calls)
  • invoke the jQuery plugin (notice that jQuery comes for free here and also that the plugin script is surely loaded by now)
  • register an 'implementation' of RaptorizrRpc which would trigger the raptor after a short delay

Glue to the framework

In order to automatically attach Raptorizr whenever an instance of AdminCentral is started, we define a singleton component which attaches an instance of Raptorizr to the current UI instance, prepares the resource URLs via resources module and unleashes the raptor upon the pages app is started.

@Singleton
public class RaptorizrStarter {
    @Inject
    public RaptorizrStarter(@Named(AdmincentralEventBus.NAME) EventBus eventBus, ResourcesModule resourcesModule) {
        final Raptorizr raptorize = Raptorizr.raptorize(UI.getCurrent());
        raptorize.setRaptorImage(resourcesModule.getDownloadPath() + "raptorizr/raptor.png");
        raptorize.setRaptorSound(resourcesModule.getDownloadPath() + "raptorizr/raptor-sound.mp3");
        eventBus.addHandler(AppLifecycleEvent.class, new AppLifecycleEventHandler.Adapter() {
            @Override
            public void onAppStarted(AppLifecycleEvent event) {
                if (event.getAppDescriptor().getName().equalsIgnoreCase("pages")) {
                    raptorize.unleashRaptor();
                }
            }
        });
    }
}

The source code of Raptorizr module can be found here.

 

AppSwitcher

App switcher component has been referenced as an example of a custom Vaadin component which improves the user experience of AdminCentral. It has similar underlying principles to the Raptorizr, but more complex and closer to real life. You can take a look at the sources (here) or try it out yourself  (nexus coordinates). Right now,  let us briefly take a look at how it works under the hood.

App switcher is actually an assembly of a Vaadin custom component (the switcher with app tiles) and the Vaadin extension which monitors the key events and expects the switcher shortcut to be triggered. It's actually quite easy to wire such a component with the UI framework we have (with apps, events and etc).

Binding to app controller

All we need to know in order to make app switcher work is what currently running apps there are, what order are they in and which one is active at the moment. Luckily this information is exposed by the AppController via event system. Hence, we throw in a simple AppSwitcherService which would subscribe to the AppLifecycleEvent (the one fired when an app is started, stopped or activated) via the admin-central scoped event bus (@Inject @Named(AdmincentralEventBus.NAME) final EventBus) admincentralEventBus). The events conveniently provide the app descriptors, so all the data required for the app switcher view can be fetched from them.Monitor

Also whenever there is at least a single app running - the services makes sure that we listen to a specific shortcut sequence: by default - alt+shift to launch the switcher, then alt be held to keep it visible. Since it is much easier and cheaper to deal with the key press events on the client-side, we implement such a monitor also as a JS extension, which will only tell our server-side service whether the switcher needs to be displayed or on the contrary - removed.

Monitor

Whenever there is at least a single app running - the service makes sure that we listen to a specific shortcut sequence: by default - alt+shift to launch the switcher, then alt be held to keep it visible. Since it is much easier and cheaper to deal with the keyboard events exclusively on the client-side, we implement such a monitor also as a JS extension, which will only tell our server-side service whether the switcher needs to be displayed or on the contrary - removed.

Presenter

Once the monitor signals that the switcher needs to be displayed our service simply passes the data aggregated from the app controller to an app switcher presenter instance which in turn composes an app switcher view. In order to display it - we simply ask an injectable component called MagnoliaShell to open the view with an AppSwitcher component as an overlay. 

 

Declarative layouts

The last (but not least) feature I spoke about was Vaadin's declarative layouting. A relatively recent feature (added in Vaadin 7.4), it allows for defining the view layouts in an HTML-like language and gluing them with the Java logic and event handling.

@DesignRoot
public class LoginViewLayout extends VerticalLayout{

	// these fields will be mapped based on _id attribute in the design
	protected TextField emailField;
	protected PasswordField passwordField;
	protected Button signInButton;

	public LoginViewLayout(){
		// There is a markup file somewhere nearby in the classpath!
		Design.read(this);
		emailField.addValueChangeListener(e -> "Hello Magnolia!");
	}
}

This feature is particularly interesting to Magnolia due to its editing capabilities: such declarative layout snippets can be stored as resources or as content and edited on a live instance, opening the possibilities for really dynamic apps. 

For the conference I hacked together a quick demo of a master-detail app which can bind to a JCR workspace and display a grid with the data and an editing form for the elements.

By means of a declarative layout it is possible to re-arrange the parts of the app view on the screen, add, remove and re-configure fields of the form and much more.

Of course, the solution is limited (and rather fragile), but I believe that the possibilities it opens are quite tempting!

The sources of the declarative layout demo can be found here.

 

Vaadin documentation reference:

https://vaadin.com/docs/-/part/framework/gwt/gwt-connector.html

https://vaadin.com/docs/-/part/framework/gwt/gwt-shared-state.html

https://vaadin.com/docs/-/part/framework/gwt/gwt-rpc.html

https://vaadin.com/docs/-/part/framework/gwt/gwt-extension.html



Comments



{{item.userId}}   {{item.timestamp | timestampToDate}}

About the author Aleksandr Pchelintcev

Aleksandr is in the Magnolia development team and is responsible for tasks around UI and core features. In his spare time he likes playing football and reading about technology. His posts will focus on tinkering and extending Magnolia APIs and improving user experience with the Vaadin framework.


See all posts on Aleksandr Pchelintcev

Demo site Contact us Free trial