4 months ago

You have your superdevmode development environment up and running. All is peachy and world is unicorns until you figure out that some pesky bug is only reproducible on a cell phone. Bam. What now?

No worries! The totally awesome thing about PC Chrome is how easy it is to access the phone's Chrome browser log, modify DOM, everything. All this from the comfort of your PC. Just follow this tutorial and prepare for a major wow effect: https://developers.google.com/web/tools/chrome-devtools/remote-debugging/ . Simply by clicking that Inspect button you will be able to use the full power of Chrome Dev Tools on your PC, of a page displayed and running in Android's Chrome.

Usually both the PC and Android are connected to the same wifi, and thus Android will see the PC. I'm going to assume here that your PC's IP is 192.168.100.17. Naturally you will type in http://192.168.100.17:8080?superdevmode into Android's Chrome and naturally it won't work.

The usual approach is to add tons of configuration options as stated below. TL;DR: skip them and use the port forwarding solution below which is way easier and less error-prone.

The old and traditional solution which you'll want to avoid unless port forwarding is not an option for you for some reason

We need to configure stuff first.

  1. First we need to open up the codeserver so that it accepts connections from all over the world, not just from localhost. Open your mvn vaadin:run-codeserver launch configuration in intellij, and set Command line to vaadin:run-codeserver -Dgwt.bindAddress=0.0.0.0
  2. To force the Android Chrome browser to connect to the proper codeserver one needs to set it in the URL. Just make the Android Chrome Browser to open http://192.168.100.17:8080?superdevmode=192.168.100.17
  3. To avoid this pesky message "Ignoring non-whitelisted Dev Mode URL:" in your Android's Chrome (some security precaution introduced in GWT 2.6) you need to bootstrap from a slightly modified widgetset.gwt.xml. Just follow http://stackoverflow.com/questions/18330001/super-dev-mode-in-gwt and add the following into your widgetset.gwt.xml and recompile, so that you will bootstrap with proper settings:
    <set-configuration-property name="devModeUrlWhitelistRegexp" value=".*" />
    

Now what to do if you don't have your own widgetset but instead you are just using the precompiled one from vaadin-client-compiled.jar? Obviously you will bootstrap off the default unmodified DefaultWidgetSet.gwt.xml from vaadin-client-compiled.jar. You could do mvn clean install of modified Framework client/src/main/resources/com/vaadin/DefaultWidgetSet.gwt.xml but we can be smarter and use port forwarding.

Port Forwarding

Instead of twiddling with countless configuration options we will pretend that your phone is the one who is actually running the server. By doing that we can just type in http://localhost:8080?superdevmode into Android's browser and thus bypass those pesky security precautions. This can be done by somehow listening on TCP ports 8080 and 9876 on your phone; when someone connects, just send all data to the PC. This technique is called port forwarding.

Make sure you have Android SDK installed. You can download that for free here: https://developer.android.com/studio/index.html - you can either download the full-blown Android Studio which comes with the SDK itself, or just scroll down to the end of the page and install the sdk-tools, the choice is yours. We will only need the adb binary anyway.

Now open up your console and type in the following:

$ adb reverse tcp:9876 tcp:9876
$ adb reverse tcp:8080 tcp:8080
$ adb reverse --list
(reverse) tcp:9876 tcp:9876
(reverse) tcp:8080 tcp:8080

If adb complains that error: no devices/emulators found just close your PC's Chrome, so that it will close its connections to Android and will thus allow adb to connect.

Now fire up Chrome, Remote, and just type http://localhost:8080?superdevmode into your Android Chrome and you should be good to go.

FAQ

Q: The widgetset compiles but my changes aren't applied

A: Simply use the port forwarding solution. Else make sure there is no "Ignoring non-whitelisted Dev Mode URL:" message in the browser's console. If there is, you will need to bootstrap off a modified widgetset.gwt.xml.

Q: The widgetset recompilation fails

A: Simply use the port forwarding solution. Else make sure the codeserver listens on all interfaces (netstat -tnlp|grep 9876 will give you :::9876 instead of 127.0.0.1:9876). Also make sure you're using ?superdevmode=192.168.100.17 in the Android Chrome's URL.

Q: adb list won't show my phone and/or complains with error: no devices/emulators found

A: Make sure that your PC's Chrome is not connected to Android. Close all Chrome Remote windows; close all the "Remote Devices" tabs in PC Chrome. Or simply close Chrome while we configure port forwarding.

 
4 months ago

When trying to fix some browser-side issue in Vaadin built-in components, it is often useful to modify those component sources while your app is running. This way, you can modify e.g. VButton.java, refresh your browser with F5 and immediately see the changes done in Vaadin Button. This blogpost describes how to configure Intellij to do just that.

We will need two things:

  • A standard Java Vaadin WAR project which employs the component we are going to modify, e.g. com.vaadin.ui.Button. You can use your own project; if you don't have one, for the purpose of this article you can easily generate one at https://vaadin.com/maven
  • The Vaadin Framework sources checked out from git. I'll guide you later on.

First, start by running your Vaadin-based WAR application from your IDE as you usually do. I assume here that you use only the default widgetset, with no added widgets; I need to write another blogpost on how to debug your custom widgetset.

Now that your app is up and running and accessible via the browser at, say, http://localhost:8080, we are going to employ GWT superdevmode. Generally, the idea here is that once a special Vaadin switch is activated (?superdevmode added to your url), the browser will be configured by the Vaadin Framework to serve widgetset javascript not from your project's WAR, but instead from a GWT so-called codeserver which we will launch in a minute. The codeserver does two things:

  • when you press F5 in your Chrome, the codeserver will perform a hot javascript recompilation of Vaadin client-side widgets modified by you, and it will feed the compiled javascript to your Chrome.
  • the codeserver will also provide so-called sourcesets for Chrome browser so that you will see an actual original Java sources in Chrome and you will be able to debug them from Chrome.

Let us start by grabbing Vaadin sources:

$ git clone git@github.com:vaadin/framework.git

Make sure that you checkout appropriate branch (e.g. 8.0 if you use Vaadin 8.0.x, 7.7 if you use Vaadin 7.7.x etc), then compile:

$ mvn clean install -DskipTests

Then just open the main framework/pom.xml in Intellij. There will be compilation errors regarding import elemental.json.JsonObject missing, you can safely ignore those.

Launching the codeserver properly is tricky and produces lots of errors when not done right. To put it bluntly, codeserver is a bitch, prepare for lots of cursing and wasted time, you've been warned. The easiest option is to use maven vaadin plugin. Just open the Maven tool window in the Intellij in which you have the framework sources opened, go into vaadin-client-compiled / Plugins / vaadin / vaadin:run-codeserver, right-click and select Create "vaadin-client-compiled..." launch configuration. The most important part is the Resolve Workspace artifacts:

  • When it is unchecked, Maven will use vaadin-client.jar from ~/.m2/repository, meaning that you will need to run mvn clean install inside the client project every time you modify Vaadin GWT sources. This is definitely suboptimal and we can do better.
  • When it is checked, Maven will prefer client sources over jars from ~/.m2/repository. However, activating this mode and Debugging this launch configuration will sometimes fail to compile the DefaultWidgetSet with some ridiculous error message. If this happens, just read below for a workaround.

Workaround for vaadin:run-codeserver not starting: So, what now? Simple - just attach the sources as resources :-) Uncheck the Resolve Workspace artifacts, save the launch configuration. Then, open the client-compiled/pom.xml file and add the following snippet right below the <build> element:

    <resources>
        <resource>
            <directory>../client/src/main/java</directory>
        </resource>
    </resources>

Now it will be possible to launch the vaadin:run-codeserver task, and with it, the codeserver. The codeserver should eventually print something like this to the console:

[INFO] --- vaadin-maven-plugin:8.1-SNAPSHOT:run-codeserver (default-cli) @ vaadin-client-compiled ---
[INFO] Turning off precompile in incremental mode.
[INFO] Super Dev Mode starting up
[INFO]    workDir: /tmp/gwt-codeserver-1604961084994240944.tmp
[INFO]    [WARN] Deactivated PrecompressLinker
[ERROR] SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
[ERROR] SLF4J: Defaulting to no-operation (NOP) logger implementation
[ERROR] SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[INFO]    Loading Java files in com.vaadin.DefaultWidgetSet.
[INFO]    Module setup completed in 14374 ms
[INFO] 
[INFO] The code server is ready at http://127.0.0.1:9876/

Now that the codeserver is running, let us use it. In your Chrome, modify the URL to http://localhost:8080?superdevmode (or http://localhost:8080?superdevmode#!someview if you are using Navigator). The browser should say that it is recompiling the widgetset, and after a while, your app should appear as usual. To prove that the app is indeed using the codeserver, we will modify the VButton.java a bit.

Open the VButton.java in Intellij where the codeserver is running, and head to the constructor. At line 118, at the end of the constructor, just add the following line:

addStyleName("woot-my-style");

Save and hit recompile CTRL+F9. There may be compilation errors regarding import elemental.json.JsonObject missing, you can safely ignore those - the VButton.java will be hot-redeployed to the codeserver anyway.

Now, head to the browser, press F5, then Ctrl+Shift+C and click any Vaadin button. The appropriate div should now have the woot-my-style class. If not, kill the codeserver and make sure you are using the <resources> workaround above, with Resolve Workspace artifacts unchecked.

This concludes the hot-compilation part. Now for the debugging part. Press F12 in Chrome and head to the Sources tab. Now patiently unpack com.vaadin.DefaultWidgetSet / 127.0.0.1:9876 / sourcemaps/com.vaadin.DefaultWidgetSet / com / vaadin (at this point I start to wonder whether coping with all this is really worthwile, now that we have Kotlin2JS) / client / ui / VButton.java. You see java sources in a browser. Cool. Now place a breakpoint at line 118 where the addStyleName() resides, and press F5. Chrome should now stop at that line. You can use leftmost debugger buttons to fiddle with the execution flow.

FAQ

Q: The codeserver fails to start with

[INFO] [ERROR] cannot start web server
[INFO] java.net.BindException: Address already in use

A: CodeServer loves to stay running even when killed by Intellij. Just do netstat -tnlp|grep 9876 and then kill the offending java CodeServer process.

Q: The codeserver does not pick up the changes I made

A: Make sure you are launching the codeserver in Debug mode. Then, make sure you saved the java file and hit CTRL+F9. Also make sure you are using the <resources> workaround above, with Resolve Workspace artifacts unchecked.

Q: The codeserver fails to compile widgetset because of some ridiculous compilation error

Make sure you are using the <resources> workaround above, with Resolve Workspace artifacts unchecked.

 
5 months ago

When doing mobile development with Vaadin, you often develop the app on your dev machine and view the page on a tablet or a phone. Yet, entering your dev machine IP into the phone browser may be tedious and error-prone. There is a way though, to enter an URL to your phone. Via a camera, as a QR Code.

Imagine having a special PopupView in your menu, which would upon clicking reveal the QR code to your machine :-)

With Vaadin On Kotlin this is easy. Just use the QRCode Vaadin Component and add the following code:

val hostIPAddress: String? get() {
    val iface = NetworkInterface.getNetworkInterfaces().toList().filter { it.isUp && !it.isLoopback && !it.isVirtual }.firstOrNull() ?: return null
    val address = iface.inetAddresses.toList().filterIsInstance<Inet4Address>().firstOrNull() ?: return null
    return address.hostAddress
}

fun HasComponents.qrcode(value: String, block: QRCode.()->Unit = {}) = init(QRCode()) {
    this.value = value
    block()
}

Then, in your UI somewhere:

if (hostIPAddress != null) {
    popupView("QR Code") {
        verticalLayout {
            val url = "http://${hostIPAddress}:8080/"
            label(url)
            qrcode(url) { w = 300.px; h = 300.px }
        }
    }
}

And voila! Just launch the Barcode Scanner app and point your tablet or your cellphone towards your screen.

 
6 months ago

Let's discuss the most useful cases for the extension methods. First use-case: extension methods allow to apply themselves only selectively. For example, the following method only applies to a list of Person:

fun Iterable<Person>.averageAge(): Double = map { it.age }.filterNotNull().average()

No longer it is necessary to create classes which only perform summarization functionality over a collection of objects - you can now attach that functionality directly to a Iterable or a Collection.

Second use-case is even more interesting. Let's create a Session object as follows:

object Session {
    operator fun get(key: String): Any? = VaadinSession.getCurrent().getAttribute(key)
    operator fun set(key: String, value: Any?) = VaadinSession.getCurrent().setAttribute(key, value)
}

This will allow to store stuff into the Session as follows: Session["key"] = value. But this is not the interesting part. The interesting part comes here:

fun login(person: Person) {
    Session["user"] = person
}
fun logout() {
    Session["user"] = null
}
val Session.loggedInUser: Person? get()= Session["user"] as Person?

Yes, yes, login/logout, standard stuff, what's your point? The main point is that we extended the Session with the loggedInUser property. And by "we" I mean that anyone can do this - another class, another jar file, another module. Consider this: lots of jars, lots of modules contributing stuff to the central Session class. You can use that stuff, as long as those jars are on your classpath. This is not about pluggability, this is about modularity and about discoverability of services. Your IDE will auto-discover all services and will provide you a neat list in the form of auto-completion on the Session object. And in some applications, this approach may even replace simple injections - that is, injections with one implementation and with no interceptors. But - don't we strive for all injections to be simple?

Interesting times we live in ;) And that's all folks, thank you for reading these ramblings ;) If you wish to see these ideas in practice, please check out the following Github project: https://github.com/mvysny/vaadin-on-kotlin - it includes the production-ready db{} function, Grid integration for rendering of your DB tables in your page and more. Check out the sample project as well.

 
6 months ago

Now if there was some way to get rid of that pesky em -> which needs to be written every time one calls the db {} function. If only there was a way we could tell a block to "know" some variables implicitly... Maybe we could throw those "variables" into a class and tell the block to run as some kind of an extension method in that class? That could work - extension methods have direct access to the receiver object methods and variables. Remember our findAll() method?

fun <T: Any> EntityManager.findAll(clazz: KClass<T>): List<T> = createQuery("select a from ${clazz.java.simpleName} a", clazz.java).resultList

The findAll() extension method is apparently able to access the createQuery() method which belongs to the EntityManager. So. How about we create a object which has em as its field? Right on:

class PersistenceContext(val em: EntityManager)

Now we modify the db() method as follows:

fun <R> db(block: PersistenceContext.() -> R): R {
    val em = emf.createEntityManager()
    try {
        em.transaction.begin()
        val result = PersistenceContext(em).block()
        em.transaction.commit()
        return result
    } catch (t: Throwable) {
        try {
            em.transaction.rollback()
        } catch (t2: Throwable) {
            t2.printStackTrace()
        }
        throw t
    } finally {
        em.close()
    }
}

The block definition changed from (EntityManager)->R (that is, take EntityManager as its parameter and return R) into PersistenceContext.()->R, that is, pretend that the block is some weird kind of extension method of PersistenceContext. And we will call it in this new fashion:

PersistenceContext(em).block()

After these modifications you'll have compiler errors - this is because the db {} block now has no parameters. Just fix them by removing the em -> stanza from your db {} method calls.

With this approach, we can toss in some additional variables which may come handy. For example, sometimes it is handy to circumvent EntityManager and go directly into JDBC. No problem:

class PersistenceContext(val em: EntityManager) {
    val session: SessionImpl get() = em.unwrap(SessionImpl::class.java)
    val connection: Connection get() = session.connection()
}

From now on, you can use connection in your db {} calls. Just like that. Awesome, isn't it?

Now, I need you to play with this thing a bit, so that it has the time to soak in and become native to you. Because in the next lines we will combine everything you have learned, into a mind-blowing big ball which may get too overwhelming. Just tell me when you're ready :-)

Ready? Now I want you to read this article and understand everything from it: https://kotlinlang.org/docs/reference/type-safe-builders.html. Don't worry - once you grok that article, that's it - you just learned everything I need you to know, and further text will be completely straightforward.

So, now you grok this DSL thingy. Cool, huh? You can basically model a tree structure type-safe with Kotlin. Hey, Vaadin components are arranged in a kinda tree fashion! You know, VerticalLayouts containing HorizontalLayouts containing other components... Let's build a Vaadin DSL!

Let's create a Kotlin file named VaadinDSL and let's start by introducing a builder method for VerticalLayout:

fun HasComponents.verticalLayout(init: VerticalLayout.()->Unit): VerticalLayout {
    val vl = VerticalLayout()
    add(vl)
    vl.init()
    return vl
}

I chose to add this extension method into HasComponents, so that both ComponentContainers+Layouts and SingleComponentContainers are supported. Since VerticalLayout is also HasComponents, you can now nest vertical layouts. But there is a problem: HasComponents has no "add" method! Easily fixable though:

fun HasComponents.add(component: Component) = when (this) {
    is ComponentContainer -> this.addComponent(component)
    is SingleComponentContainer -> this.content = component
    else -> throw IllegalArgumentException("Don't know how to add items to $this")
}

Let's introduce a few more builders for Button, TextField and PersonEditor, so that we can rewrite our MyUI, builder-style!

fun HasComponents.textField(caption: String? = null, init: TextField.()->Unit = {}): TextField {
    val component = TextField(caption)
    add(component)
    component.init()
    return component
}

fun HasComponents.button(caption: String, init: Button.()->Unit = {}): Button {
    val component = Button(caption)
    add(component)
    component.init()
    return component
}

fun HasComponents.personEditor(init: PersonEditor.()->Unit = {}): PersonEditor {
    val component = PersonEditor()
    add(component)
    component.init()
    return component
}

(There is some code pattern, repeating again and again. I'll leave the refactoring to you ;). Note the default values for the init parameters. This way, the block is not required and we can leave that out if we wish so. This allows for simple textField("name") instead of having to write textField("name") {}. And thus, we can rewrite the init() method as follows:

val persons = Person.findAll()

verticalLayout {
    setMargin(true); isSpacing = true
    val layout = this
    val name = textField(persons.joinToString())
    button("Click Me Kotlin") {
        addClickListener { layout.add(Label("Thanks ${name.value}, it works!")) }
    }
    personEditor {
        edit(persons.last())
    }
}

The structure of the Vaadin component graph is now more visible than in the previous "flat" code. Please play with the code a bit. Next, yet another application of the extension methods.

 
6 months ago

I'll get to the forms right away. But first, I want to talk extension methods. They are immensely helpful in adding functionality to objects. Note that Kotlin is not a dynamic language - the methods will not really be added to target classes, it is just a syntactic sugar. Quite delicious, though. So, EntityManager misses the findAll method - let's fix that. Open the DB.kt file and add this to the end:

fun <T: Any> EntityManager.findAll(clazz: KClass<T>): List<T> = createQuery("select a from ${clazz.java.simpleName} a", clazz.java).resultList

Read more here: https://kotlinlang.org/docs/reference/extensions.html. Basically, what we just did is that we "kinda added" a findAll method to the EntityManager, which invokes the createQuery() method of the EntityManager itself and runs given query. From Java's perspective we have created a static method findAll(EntityManager $receiver, Class<T> clazz): List<T> on a class named DBKt - this is what's actually produced by the Kotlin compiler. Let's use it and let's implement findAll() which finds all Persons. I personally believe that such code belongs to the class dealing with personnel, as per OOP imperative "code shall be with the data", don't you agree? So, let's create a finder right on the Person class:

@field:Column(nullable = false)
var age: Int? = null
) {
companion object {
    fun findAll(): List<Person> = db { em -> em.findAll(Person::class) }
}
}

Companion object is Kotlin's way of doing Java static stuff. Seems more complicated than static methods, until you realize that the companion object is also an object, and as such it may inherit from other objects, implement an interface, so it is immensely more powerful. Read more here: https://kotlinlang.org/docs/reference/object-declarations.html. Let's head back to MyUI and modify the init() method:

db { em -> em.persist(Person(name = "Zaphod", surname = "Beeblebrox", age = 42)) }
val persons = Person.findAll().joinToString()

There. More easy to read, and one can just intuitively head to the Person class and auto-complete to find this useful function. We'll leave extension methods for now, but we'll return to them as they are immensely useful.

Tip: Now maybe is the time to purchase IDEA Ultimate. The Ultimate edition has a built-in database browser which also integrates with Java/Kotlin editors and provides auto-completion and validation of your database queries. Let's browse the embedded H2 database by opening the upper-right Database Tool Window, clicking the green + button and import from sources. IDEA auto-discovers the persistence.xml, extracts the connection string from that so you only need to provide the username "sa" and password "sa". If the Test Connection button is greyed out, see below - the H2 driver may be missing. Just click the Download link, IDEA will download the driver and you will be able to add the database. Then, expand the database, click More Schemas - PUBLIC, select the PERSON table and press F4 - you will see the table contents. This kind of connection will work because H2 is in mixed mode: http://www.h2database.com/html/features.html#auto_mixed_mode

Now, back to the forms. We will employ Vaadin's BeanFieldGroup to edit the bean. Let's create a Kotlin class PersonEditor:

package org.test.myvaadin

import com.vaadin.data.fieldgroup.BeanFieldGroup
import com.vaadin.data.util.converter.StringToIntegerConverter
import com.vaadin.ui.Button
import com.vaadin.ui.FormLayout
import com.vaadin.ui.TextField

class PersonEditor : FormLayout() {
    private val name = TextField()
    private val surname = TextField()
    private val age = TextField()
    private val fieldGroup = BeanFieldGroup(Person::class.java)
    private val save = Button("Save")

    init {
        save.addClickListener {
            fieldGroup.commit()
            db { em -> em.merge(fieldGroup.itemDataSource.bean) }
        }
        age.setConverter(StringToIntegerConverter())
        fieldGroup.bind(name, "name")
        fieldGroup.bind(surname, "surname")
        fieldGroup.bind(age, "age")
        addComponents(name, surname, age, save)
    }

    fun edit(person: Person) {
        fieldGroup.setItemDataSource(person)
    }
}

Be sure to study the BeanFieldGroup javadoc how this class works and how it binds properties (e.g. "name" is really the name field in the Person class) to Vaadin Fields. To use this class, modify MyUI.init() as follows:

override fun init(vaadinRequest: VaadinRequest) {
    db { em -> em.persist(Person(name = "Zaphod", surname = "Beeblebrox", age = 42)) }
    val persons = Person.findAll()

    val layout = VerticalLayout()

    val name = TextField()
    name.caption = persons.joinToString()

    val button = Button("Click Me")
    button.addClickListener { e ->
        layout.addComponent(Label("Thanks " + name.value
                + ", it works!"))
    }

    layout.addComponents(name, button)
    layout.setMargin(true)
    layout.isSpacing = true

    val personEditor = PersonEditor()
    personEditor.edit(persons.last())
    layout.addComponent(personEditor)

    content = layout
}

This will edit the last person in the list. Feel free to play with the editor a bit, and check the database in the Database explorer, to see the last person being modified by your changes. But what about validations? Try clearing the age field and press Save. What happens is that Vaadin happily stores null into Person.age which is supposedly non-null, and thus Hibernate will complain with an exception.

To fix this, we will employ data validations, or JSR-303. Head to pom.xml and add the following dependency:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-validator</artifactId>
   <version>5.3.4.Final</version>
</dependency>

We'll add the validation annotations to the Person class as follows:

package org.test.myvaadin

import org.hibernate.validator.constraints.Range
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.validation.constraints.NotNull
import javax.validation.constraints.Size

@Entity
data class Person(
    @field:Id
    @field:GeneratedValue
    @field:NotNull
    var id: Long? = null,

    @field:Column(nullable = false, length = 100)
    @field:NotNull
    @field:Size(min = 2, max = 100)
    var name: String? = null,

    @field:Column(nullable = false, length = 100)
    @field:NotNull
    @field:Size(min = 2, max = 100)
    var surname: String? = null,

    @field:Column(nullable = false)
    @field:NotNull
    @field:Range(min = 15, max = 90)
    var age: Int? = null
) {
    companion object {
        fun findAll(): List<Person> = db { em -> em.findAll(Person::class) }
    }
}

The BeanFieldGroup will pick the annotations up automatically and will add appropriate validators to the Fields themselves. To get rid of the horrible stack trace hovering over the Save button, just modify the click listener as follows:

save.addClickListener {
    save.componentError = null
    try {
        fieldGroup.commit()
        db { em -> em.merge(fieldGroup.itemDataSource.bean) }
    } catch (ex: FieldGroup.CommitException) {
        save.componentError = UserError("There are invalid fields in the form")
    }
}

Again, please feel free to play with the code and to study the BeanFieldGroup. Up next, juicy stuff coming. We'll learn how to build the UI properly.

 
6 months ago

Let's add some database to our hello-world. I'll use the pure-Java embedded database called H2 (http://www.h2database.com) which will run as a part of our application, so there is no need for you to install any database engine. Open the pom.xml file and add the following lines at the end of the dependencies element:

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-core</artifactId>
   <version>5.2.6.Final</version>
</dependency>
<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
   <version>1.4.193</version>
</dependency>

Tip: You can add any dependency on any jar hosted in Maven central this way. Just head to the artifactId element and press Ctrl+Space, to offer the artifacts as you type. To auto-complete from the maven repo, one needs to index it first though, so open File / Settings in IDEA, then Build, Execution, Deployment / Build Tools / Maven / Repositories, select the https://repo1.maven.org/maven2 repo and click update. The update will take a while.

What we just did is that we added two Java libraries:

  • The Hibernate library, which maps Java (or Kotlin) objects to rows in a standard relational database (or RDBMS since Java people love acronyms ;)
  • The H2 database implementation

Now, let's configure Hibernate to use H2. Create the following file: src/main/resources/META-INF/persistence.xml:

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
             http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
             version="2.1">
    <persistence-unit name="h2" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
            <property name="hibernate.connection.driver_class" value="org.h2.Driver" />
            <property name="hibernate.connection.url" value="jdbc:h2:~/temp/h2/myvaadinapp;AUTO_SERVER=TRUE" />
            <property name="hibernate.connection.user" value="sa" />
            <property name="hibernate.connection.password" value="sa" />
            <!-- <property name="hibernate.show_sql" value="true"/> -->
            <property name="hibernate.hbm2ddl.auto" value="update" />
        </properties>
    </persistence-unit>
</persistence>

Tip: to force Intellij to show the currently edited class in the Project tree view, click on the little cog-wheel located in the header of the Project Tool window and check Autoscroll from source.

Now right-click the org.test.myvaadin package in the Project Tool Window and select New / Kotlin File/class. Type in DB and paste the following contents:

package org.test.myvaadin

import javax.persistence.EntityManager
import javax.persistence.Persistence

private val emf = Persistence.createEntityManagerFactory("h2")

fun <R> db(block: (EntityManager) -> R): R {
    val em = emf.createEntityManager()
    try {
        em.transaction.begin()
        val result = block(em)
        em.transaction.commit()
        return result
    } catch (t: Throwable) {
        try {
            em.transaction.rollback()
        } catch (t2: Throwable) {
            t2.printStackTrace()
        }
        throw t
    } finally {
        em.close()
    }
}

First, we have created an entity manager factory, which produces entity managers which look after the JPA objects and map them to the database. Next, we defined a very simple db function which runs a block in a transaction. Note that this function is not present in any class and is thus "global" - this seems like an anti-pattern but at some places this is immensely helpful. More on these blocks or lambdas here: https://kotlinlang.org/docs/reference/lambdas.html. Now that we have the necessary machinery in place, let's create some tables. Let's define a person - create new Kotlin class Person and paste in the following code:

package org.test.myvaadin

import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id

@Entity
data class Person(
    @field:Id
    @field:GeneratedValue
    var id: Long? = null,

    @field:Column(nullable = false, length = 100)
    var name: String? = null,

    @field:Column(nullable = false, length = 100)
    var surname: String? = null,

    @field:Column(nullable = false)
    var age: Int? = null
)

This is a standard JPA so-called entity - an object mapped to a table row. See here how exactly that is done: https://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html. Let's now use all the pieces. Open the MyUI class and insert the following code right at the beginning of the init method:

val persons = db { em ->
    em.persist(Person(name = "Zaphod", surname = "Beeblebrox", age = 42))
    em.createQuery("select p from Person p").resultList.joinToString()
}

So, we run the db function which starts the database transaction and gives us the Entity Manager which is the central point for storing stuff into the database. We have created a new person and stored it into the database; then we queried all personnel, returned it from the block and stored into the persons variable. Let's show the variable, in a bit of a stupid way, but nevertheless: just change the code in the MyUI.init() function appropriately:

val name = TextField()
name.caption = persons

Again, please play with the code a bit. The db function has many shortcomings - it does not cache/reuse Entity Managers, it does not cache JDBC Connections, it can't handle nested calls and will create new transactions. No worries - this is just for demo purposes. Later on we will use a real, production-ready stuff. Stay tuned. In the next article we will show how to create forms which will edit our Personnel.

 
6 months ago

Kotlin provides some very interesting language features which are immensely helpful when writing Vaadin apps. Let us start from scratch, building necessary functionality as we progress. The result of this exercise will be a Kotlin-based simple web app, built with Gradle, using Hibernate+JPA to store entities into the database. I will not use Spring nor JavaEE here - the whole thing will run in a pure Servlet environment like Tomcat or Jetty. The complete exercise may take some 60 minutes. I will be using Intellij IDEA Community which you can download for free here: https://www.jetbrains.com/idea/download. When installing, just press the "Skip remaining" button to install Intellij with the default settings.

Originally I intended to write the tutorial using Gradle, but there were bugs in the Gradle Vaadin Plugin which would disallow the app to launch without using IntelliJ Ultimate. Therefore I have chosen Maven with more stable support for tooling.

Let's start by creating a Maven-based Java Vaadin project by following this tutorial: https://vaadin.com/maven (I am using the "org.test.myvaadin" groupId and "vaadin-app" as artifactId throughout this tutorial). Just install Maven 3, follow the steps in the Vaadin tutorial and you're good to go. After you have run the project from the command-line, just fire up IDEA, open the pom.xml file with File / Open; then select Open as Project.

To run the project in IDEA, click on the bottom-left touchpad-like button in the lower-left corner, then click Maven Projects in the upper-right corner. Unwrap the tree: vaadin-app / Plugins / jetty, right-click jetty:run and select Run (or Debug). Then, point your browser to http://localhost:8080/.

Now it may be a good idea to start versioning, so that you can play with the project safely and just revert the changes, should things go banana. Just run this in your command-line, inside the project directory (vaadin-app):

$ git init .

Create a .gitignore with the following contents:

.idea
target
styles.css
*.iml
$ git add .
$ git commit -m "Initial import"

Intellij will detect a git repo, just click "Add Root" in the popup dialog.

Finally, let's Kotlinify the project. Open the MyUI class (by pressing Ctrl+N) and press Ctrl+Alt+Shift+K - this will automatically convert the Java class to Kotlin class. Intellij will show "Kotlin not configured" popup - click "Kotlin" (not Kotlin-Javascript), then just press OK - the pom.xml will be modified automatically.

Tip: to see the cheat-sheet with the keyboard shortcuts in IDEA, just open the Help / Keymap reference menu.

Now, head back to the MyUI class and change the following line: val button = Button("Click Me Kotlin"). Now, when you run the project using the jetty:run as before (or just press Shift+F10 since the Maven launcher for this task has been created, just see the upper-right corner in the IDEA window; this way you can also launch the project in debug mode), Kotlin should auto-compile the class and the page saying "Click Me Kotlin" should open. Good job!

Please feel free to experiment around. When you alter some code, press Ctrl+F9 to recompile - the code should be deployed to Jetty immediately, just press F5 in the browser. If not, just head down to the Debug Tool Window and then press the green arrow "Rerun 'vaadin-app'", to recompile the project and restart the Jetty web server. If something's not working right or I am not explaining myself properly, please see the example application here: https://github.com/mvysny/vaadin-app

In the next blog post, we will add database support.

 
6 months ago

When one tries to migrate her project to a newer version of a framework, it usually helps if the transition is smooth and the original code works without any modifications. This way, you can convert the code to the new API gradually, and never encounter this OMG-shit-doesn't-compile-everybody-blocked period for a long time.

Migration from Vaadin 7 to Vaadin 8 isn't as straightforward since some modifications to Java source files are required, but the guide of migrating to Vaadin 8 may assist you well in converting your imports from Vaadin 7's com.vaadin.ui.* components to their Vaadin 8's compatibility counterparts in the com.vaadin.v7.ui.* package.

Unfortunately the actual problem lies with Add-ons since they depend on com.vaadin.ui.* components and need to be modified as well. For example all Container-based addons need to import com.vaadin.v7.data.Container instead of com.vaadin.data.Container - a small change indeed, but will cause the addon to malfunction in runtime.

Luckily, the change is straightforward. I have converted the JPAContainer 3.2.0 to be compatible with Vaadin 8 and thus to be compatible with the com.vaadin.v7.ui.Grid. This will allow you to gradually convert your Grids to v8 Grids and the old Container-based code to the DataProvider niceness.

EDIT: the changes have been merged to the official JPAContainer Add-on, so just use JPAContainer 4.0.0 with Vaadin 8: https://vaadin.com/directory#!addon/vaadin-jpacontainer

 
6 months ago

If you ever used Selenium (or TestBench, which is based on Selenium), you know the pain of configuring the testing browser driver:

  • One simply cannot use the preinstalled Firefox since the Driver <-> Firefox protocol breaks basically on every Firefox release
  • Selenium developers eventually gave up supporting newer Firefoxes directly and started to use Gecko Driver which is pain to build
  • Setting up PhantomJS on every dev+test machine is a pain
  • Using other browsers require appropriate drivers preinstalled

Basically, every machine has different browser preinstalled, on different path, and when you throw in different OSes the configuration starts to get nasty.

Fortunately, there is a browser, installed on every machine with Java 8 - the JavaFX Browser component. It's not a full-blown browser per se, but it can render html pages, is webkit-based and works regardless of OS. Luckily, there is a specialized Selenium driver based on this browser: the JBrowserDriver. We can take advantage of that.

You can find the demo project here: https://github.com/mvysny/testbench-simplest-demo . It's a simplest project possible, with just one UI class and one testing class. To get it up and running, just follow these steps:

  1. Obtain TestBench license at the TestBench AddOn Page - just search for "Trial license"
  2. git clone https://github.com/mvysny/testbench-simplest-demo
  3. mvn clean verify
  4. Find the proof that the test ran, in target/screenshot.png

Headless mode

The browser even works in headless mode. You can quick-proof this by running the test in the Docker. First, run:

docker run -it --rm -v "$PWD":/usr/src/mymaven -w /usr/src/mymaven -v "$HOME/vaadin.testbench.developer.license":/root/vaadin.testbench.developer.license maven:3.3.9-jdk-8 /bin/bash

in the testbench-simplest-demo directory. Then, in the docker:

  1. We'll need to install the JavaFX to the docker container. Run apt update and apt install openjfx, press y when prompted
  2. Still in Docker, run mvn clean verify
  3. Docker will run maven using your host OS's filesystem and will produce target/screenshot.png. Just open the target/screenshot.png file with your favourite image viewer.