Groovy

Groovy is a very appealing language that I really enjoy using from time to time. Unfortunately, as many other languages, Groovy lacks decent development environments. Even though this shortcoming is being addressed by the Eclipse and IntelliJ, the Groovy Console remains a valuable tool to quickly test an expression or a script.

The console works fine and achieve its purpose really well. However, its GUI is somewhat lacking and integrates badly with Mac OS X. The following screenshot shows what the ordinary Groovy Console looks like on Mac OS X:

Groovy Console on Mac OS X

Mac OS X users will easily spot the problems with this user interface. The menu bar belong to the window rather than to the screen and the shortcuts are wrong (using Ctrl instead of Cmd.) Also, not shown in the screenshot, the About and Quit menu items are not in the appropriate location. After a few tweaks, here is the new UI:

New Groovy Console on Mac OS X

There are three steps required to achieve this kind of integration with Mac OS X, two of them which you are probably already aware of. First, you need to tell Apple’s VM to move the menu bar to the top of screen, out of the application’s frame. At this stage, you also need to give your application its real name, otherwise Mac OS X will use the main class’ fully qualified name (in this case, groovy.ui.Console.) The Groovy code to achieve this is the following:

System.setProperty("apple.laf.useScreenMenuBar", "true")
System.setProperty("com.apple.mrj.application.apple.menu.about.name", "GroovyConsole")
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())

Make sure to set the properties before you initialize any frame and you will get the desired result. The second part of our “Mac OS Xification” is to use the appropriate shortcut keys in the menus. The java.awt.Toolkit class offers the getMenuShortcutKeyMask() method which returns the system’s default shortcut modifier (Ctrl on Windows and Linux, Cmd on Mac OS X.) Again, using this in your application is quite simple:

def createShortcutWithModifier = { key, modifier -> KeyStroke.getKeyStroke(key,
    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | modifier) }
def createShortcut = { key -> createShortcutWithModifier(key, 0) }

// Uses Cmd-Shift-N/Ctrl+Shift+N
def newWindowAction = action(
    name:'New Window', closure: this.&fileNewWindow, mnemonic: 'W',
    accelerator: createShortcutWithModifier(KeyEvent.VK_N, KeyEvent.SHIFT_DOWN_MASK)
)

// Uses Cmd-O/Ctrl+O
def openAction = action(
    name:'Open', closure: this.&fileOpen, mnemonic: 'O',
    accelerator: createShortcut(KeyEvent.VK_O)
)

In this sample code, we just defined Swing actions with correct menu shortcuts. Creating a menu bar with those actions is easy with Groovy’s SwingBuilder. Building the menus is also the opportunity for us to introduce the last trick regarding Mac OS X integration: handling the About and Quit menu items correctly.

def isMac = { System.getProperty("mrj.version") != null }

menuBar {
    menu(text:'File', mnemonic: 'F') {
        menuItem() { action(newFileAction) }
        menuItem() { action(newWindowAction) }
        menuItem() { action(openAction) }
        separator()
        menuItem() { action(saveAction) }
        if (!isMac()) {
            separator()
            menuItem() { action(exitAction) }
        }
    }
    // More menus...
}

This piece of code does not add the exit menu item when the application is running on Mac OS X. Every Mac OS X application shows this menu item in the system controlled menu named after the application itself. This menu appears in bold on the second screenshot. The main problem is to figure out to hook our code to this menu. In my modified version of the Groovy console, here is how I did it:

if (isMac()) {
    ConsoleMacOsSupport.handleMacOs(this.&exit, this.&showAbout)
}

The isMac() could be avoided if ConsoleMacOsSupport was cleverer, but I decided to go with a simple yet effective implementation. As you can see, this class exposes a static method to which you can pass two method references. The methods bound that way will be invoked by the system when the user clicks either the About or Quit menu item in the application’s menu. The support class is also written in Groovy:

package groovy.ui

import com.apple.mrj.*

class ConsoleMacOsSupport implements MRJQuitHandler, MRJAboutHandler {
    static initialized = false
	
    def quitHandler
    def aboutHandler

    public void handleAbout() {
        aboutHandler()
    }

    public void handleQuit() {
        quitHandler()
    }

    public static void handleMacOs(quit, about) {
        if (!initialized) {
            initialized = true
            def handler = new ConsoleMacOsSupport(quitHandler:quit, aboutHandler:about)
            MRJApplicationUtils.registerAboutHandler(handler)
            MRJApplicationUtils.registerQuitHandler(handler)
        }
    }
}

ConsoleMacOsSupport simply delegates two Apple VM’s specific handlers to our Groovy methods. Since Groovy won’t even bother with this class at runtime if it’s not required, it is safe to keep it in the Windows and Linux distribution. This kind of work would usually be achieved through reflection in Java to avoid compile-time headaches.

Groovy makes the use of those handles even sweeter thanks to optional method parameters. For instance, Mac OS X’s quit handler does not expect any argument when invoked. However, we would like to bind the same method to a regular JMenuItem when the application is executed on Linux or Windows. In this case we are actually writing an ActionListener, and a parameter of type EventObject is therefore expected. Groovy is here to make it simple:

// The action's closure (action listener) is bound to the exit(EventObject) method
def exitAction = action(
    name:'Quit', closure: this.&exit, mnemonic: 'Q',
    accelerator: createShortcut(KeyEvent.VK_Q)
)

// The MRJQuitHandler is bound to the exit() method
ConsoleMacOsSupport.handleMacOs(this.&exit, this.&showAbout)

// Optional evt argument
void exit(EventObject evt = null) {
    // Cleanup, etc.
}

Even though I did not used this feature in the Groovy Console, you can also install a handler to hook into Mac OS X’s Preferences menu items. If your application offers some sort of options panel or configuration dialog, make sure to bind it to this menu item because that’s where users expect to find this feature. Last but not least, all those examples work also in pure Java and should not required much work to transpose.

Improving Groovy Console’s GUI

The first two pictures below show Guillaume Laforge, Groovy‘s project manager, speaking at G2One, the Groovy event that took place during Community One for JavaOne 2007 (gotta love those ones.) The third picture show Chet, the funny guy with whom I co-authored Filthy Rich Clients. More pictures from JavaOne 2007 to come.

Guillaume Laforge

Guillaume Laforge at Groovy One

Chet

Guillaume Laforge and Chet Haase

The No Fluff Just Stuff folks are hosting a meeting to gather Groovy and Grails community in San Francisco early next week. The day before JavaOne actually. It seems like a great opportunity to know more about Groovy and Grails or meet their community leaders. I will definitely attend and I highly suggest you to go to Guillaume Laforge’s blog for more information. Registration is free, but a charity donation is highly appreciated.

Groovy and Grails Meetup in San Francisco

I was recently looking into using JPA (Java Persistence API) in desktop applications to persist the UI’s state. After all, that’s what JSR 296‘s reference implementation relies on to save the frame’s size and position. There are several JPA implementations available out here and I have chosen TopLink from Oracle, as it comes bundled with NetBeans 5.5.

Using JPA in a sample Java application proved to be very easy, particularly with NetBeans wizards and editors. No need to worry about writing the POJOs by hand, no need to worry about writing the XML configuration file, everything is handled for you. And I like that.

What struck me though is how clean and easy the code looks. Yet, I wanted something even easier and cleaner so I tried to use JPA/TopLink with Groovy. I encountered a few issues but thanks to Doug Clarke, Alexandru Popescu and Thomas Risberg I finally have something that works.

The following example requires Java DB (or Apache Derby as you prefer), Oracle TopLink Essentials and a snapshot of Groovy 1.1. To install Groovy 1.1, first install Groovy 1.0 by following the instructions from the website, remove groovy-1.0.jar from the lib/ directory and replace it with groovy-all-1.1-SNAPSHOT.jar. Groovy 1.1 is optional but brings support for annotations, thus avoiding some XML mess.

The full source code of this example, along with Java DB and TopLink, is available for download.

The very first step to use JPA with Groovy is to write your entity bean that you will persist to the database and later retrieve. Being very original, I create the following Person class. The annotations on the id field tell JPA to auto-generate an ID column in the database. Here is the code of Person.groovy:

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id

@Entity
class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id

    String firstName
    String lastName
}

Because we are using Groovy, the getters, setters and constructors are automatically generated for us. This bean can now be used to persist data in the database, as the following script, PopulateAddressBook.groovy demonstrates:

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

def factory = Persistence.createEntityManagerFactory("AddressBookStore")
def manager = factory.createEntityManager()

manager.getTransaction().begin()

manager.persist new Person(firstName: "Alexis", lastName: "Moussine")
manager.persist new Person(firstName: "Ludovic", lastName: "Champenois")
manager.persist new Person(firstName: "Francois", lastName: "Orsini")
manager.persist new Person(firstName: "Eric", lastName: "Mahe")
manager.persist new Person(firstName: "Roman", lastName: "Strobl")
manager.persist new Person(firstName: "Tor", lastName: "Norbye")
manager.persist new Person(firstName: "James", lastName: "Gosling")
manager.persist new Person(firstName: "Chet", lastName: "Haase")
manager.persist new Person(firstName: "Richard", lastName: "Bair")

manager.getTransaction().commit()

This script cannot be run yet as you need to configure the persistence unit that describes the data store. Persistence units are defined in a file called META-INF/persistence.xml. In the example above I have defined a persistence unit called AddressBookStore:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
     http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="AddressBookStore" transaction-type="RESOURCE_LOCAL">
        <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
        <properties>
            <property name="toplink.jdbc.url" value="jdbc:derby:address-book;create=true" />
            <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
            <property name="toplink.jdbc.user" value="app" />
            <property name="toplink.jdbc.password" value="app" />
            <property name="toplink.ddl-generation" value="create-tables" />
            <property name="toplink.application-location" value="./db-schema"/>
        </properties>
    </persistence-unit>
</persistence>

As you can see, this file contains several properties specific to TopLink. You can refer to the online documentation to understand the meaning of each of them. However, it is important to note that this configuration requires to have a folder called db-schema/ in the execution directory. You can either create it or remove the property toplink.application-location.

We also need to tell JPA what beans we want to persist. When using annotations, this is usually done in persistence.xml by adding a <class /> element containing the name of your bean. Unfortunately, some class loading conflict between TopLink and Groovy prevent us from doing so. The solution is to use another configuration file called META-INF/orm.xml. This file can be used to override the beans annotations. It can also be used to describe the persisting field and get rid of the annotations altogether. This is what you want to use with Groovy 1.0 which does not support annotations. In our case, however, we simply need to declare the name of our bean:

<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0" xmlns="http://java.sun.com/xml/ns/persistence/orm"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
     http://java.sun.com/xml/ns/persistence/orm_1_0.xsd">
    <entity class="Person" name="Person" metadata-complete="false" access="FIELD">
    </entity>
</entity-mappings>

You can now run the script to populate the database with a few entries:

groovy -cp ../lib/derby.jar:../lib/toplink-essentials.jar:. PopulateAddressBook.groovy

If you encounter an error, check that you are using Groovy 1.1 and that both XML configuration files are correctly set up. When everything runs smoothly, you can proceed to the last step, querying the database.

The code to query the database and bring back POJOs is surprisingly short. The following asks for the list of all persons and print them by descending order of first names:

import javax.persistence.EntityManager
import javax.persistence.EntityManagerFactory
import javax.persistence.Persistence
import javax.persistence.Query

def factory = Persistence.createEntityManagerFactory("AddressBookStore")
def manager = factory.createEntityManager()

def personQuery = manager.createQuery("SELECT p FROM Person p ORDER BY p.firstName DESC")
personQuery.getResultList().each { println "${it.firstName} ${it.lastName}" }

Instead of writing our query in this snippet, we can move it to the bean itself and give it a name:

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.Id
import javax.persistence.NamedQueries
import javax.persistence.NamedQuery

@Entity
@NamedQueries(value = [
    @NamedQuery(name = "Person.findByFirstName",
                query = "SELECT p FROM Person p WHERE p.firstName = :firstName")
])
class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id

    String firstName
    String lastName
}

The bean now contains a query named Person.findByFirstName which accepts one parameter called firstName. The following snippet shows how to use it to list all of the persons whose first name is “Alexis:”

personQuery = manager.createNamedQuery("Person.findByFirstName")
personQuery.setParameter("firstName", "Alexis")
personQuery.getResultList().each { println "${it.firstName} ${it.lastName}" }

I hope this example made you want to use Groovy and JPA together. Remember you can download the full example.

Persistence Made Easy with Groovy and JPA

I have recently been playing with JPA, TopLink and Java DB (Apache Derby if you prefer) to manage date in Java SE applications. And I love those tools! It then dawned to me that I could make things even easier by using Groovy.

Unfortunately, TopLink doesn’t seem to play nice with Groovy as none of my persistent classes can be found by TopLink. I have tried to write the POJO in Groovy, write the POJO in Groovy and compile a .class and finally write the POJO in Java but nothing worked. TopLink keep sending me a ClassNotFoundException at startup. TopLink finds the persistence.xml file so it’s not the problem. Incidentally, all my Groovy code works just fine when compiled and ran with javac/java.

Does any of you every tried to use TopLink with Groovy? If so, I’d love to get some help. (And I don’t want to use Hibernate’s JPA implementation by the way :-)

Update: the source code of both Java and Groovy version is available. The Groovy version cannot find the Person POJO no matter what I do (use a Groovy bean, compile the Groovy source to a .class, use a Java bean, use a Java bean in a JAR, etc.)

JPA and Groovy