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.

← Tutorial: Writing Vaadin apps in Kotlin Part 3 Tutorial: Writing Vaadin apps in Kotlin Part 5 →