Over a million developers have joined DZone.

How Griffon Helps MigLayout

DZone's Guide to

How Griffon Helps MigLayout

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

A surprisingly undervalued aspect (judging by how little one seems to hear of it) of Groovy is its support for web services. Literally, this is all it takes for you to access a web service to retrieve a piece of Shakespearean wisdom:

import groovyx.net.ws.WSClient

class ShakesWSClient {

* Searches for a Shakespeare speech,
* which will be in this form:
    * <SPEECH>
    *        <SPEAKER>ALL</SPEAKER>
    *          Fair is foul, and foul is
    *          fair: Hover through the fog
    *          and filthy air.
    * </SPEECH>

    String processRequest(searchString) {
def proxy = new WSClient

        def xml = proxy.GetSpeech(searchString)
        def XmlParser parser = new XmlParser()
        def speech = parser.parseText (xml)
        ["PLAY: ${speech.PLAY.text()}\n",
        "SPEAKER: ${speech.SPEAKER.text()}\n",
        "TEXT: ${speech.text()}"].sum("")


Note: The final line above is a return statement because the 'return' keyword is optional in the final line of a Groovy method. The final line above also parses the speech retrieved from the web service, adds linebreaks, and prepends an explanatory text to each part.

Now, bringing us closer to the point of this article, let's create a GUI that sends in a search string to the above Groovy method and displays the parsed payload. The GUI's end result should be the following:

Let's use MigLayout and let's use Java to create the above GUI. Heads up, newbies to MigLayout! MigLayout works with an invisible grid placed on the container to which you're adding components. By default, each component you add to the container takes up one new cell in the grid, from left to right, ad infinitum. However, you're pretty unlikely to want all your components to be arranged from left to right ad infinitum, although that's a nice default pattern to begin with. When you get a bit further, though (i.e., after about the third component or so), you will want to, as you add each component, specify constraints, per component, along the lines of "the next row should begin after this component", "the cell taken up by this component should also be shared by the next component", "the space between this component and the next one should be X", etc. You can also specify these constraints on the layout level, e.g., "after every third component the next row should begin".

All of these constraints are called, well, exactly that. In slightly more detail, these are the MigLayout constraints that will be used in the code that follows:

  • align. Aligns the component, 'left' or 'right', for example, such as the Search button above, which is aligned right within its cell, which covers the whole row because of the 'span' constraint, as discussed below.
  • growx. Sets whether the component will automatically grow within the available space, such as the JScrollPane and the JSeparator above, which both use this constraint to automatically grow within their cell.
  • insets. Sets the space around the component, such as in this case the JPanel in the screenshot above, which (at the point where it is initialized) has an argument of 'insets 10' to create some space around itself. Instead of 'insets 10', you could specify specific insets per side, such as 'insets 0 25 10 15'.
  • span. Specifies how many cells a component will cover. E.g., "span 2" means it will span two cells. If no integer is specified, the whole row will be covered by the component. If you specify two integers, you have set an x and a y value, i.e., a width and a height. In the screenshot above, the first row, containing the string 'Enter the search string' and a JSeparator (creating the line) [which are there together because of the "split", described below], would not show the JSeparator if "span" had not been set as a constraint for the label, because without "span", only one cell is covered, which would not have provided room for both the label and the separator. The "Search" button also has a "span" constraint, so that it has the whole row to play in, with "align right" added, to make it align to the right side of the cell, i.e., the right side of the row.
  • split. Splits cells so that multiple components can share one cell. In the screenshot above, one of the constraints applied to the first label is "split", so that the line, which is a JSeparator, can share the same space. If there had been no "split" constraint set for the label, the JSeparator (i.e., the line) would automatically have appeared on the next row.
  • wrap. Puts the next component on the next row. Alternatively, you can autowrap at a specific column, by setting a layout constraint when initializing the layout manager. Add an integer right after "wrap" and then you've specified the gap between this row and the new one. If "wrap" had not been set as a constraint for the JSeparator in the screenshot above, the "Search" label, with the related JTextField, would have appeared in the same row.

That's all we'll need to know about MigLayout for purposes of the code that follows. (Curious for more? Go here for Mikael Grev's introduction here on Javalobby; go here to look through the HTML Cheat Sheet, which in my humble opinion would look even better as a DZone Refcard.) Okay, so below follows all of the code that creates the above GUI:

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;

public class ShakesMigWSChooser extends JFrame {

ShakesWSClient client = new ShakesWSClient();

JTextField searchField = new JTextField(50);
JTextArea resultArea = new JTextArea(50, 30);
JScrollPane scrollPane = new JScrollPane();

//Main method:
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new ShakesMigWSChooser().setVisible(true);

public ShakesMigWSChooser() {

private void initComponents() {

//Set up the JFrame:
setSize(new java.awt.Dimension(600, 200));
setTitle("Shakespeare Searcher");

//Create the JPanel in MigLayout:
JPanel p = new JPanel(new MigLayout("insets 10"));

//Add the JLabel with "Enter the search string",
//let its cell be shared by the next component,
//and let it cover the whole row.
p.add(new JLabel("Enter the search string"), "split, span");

//Add the JSeparator,
//let it grow to 100,
//and let the next component start a new row:
p.add(new JSeparator(), "growx, wrap");

//Add the JLabel with "Search":
p.add(new JLabel("Search:"));

//Add the Search Field,
//and let the next component start on a new row:
p.add(searchField, "wrap");

//Add the JLabel with "Response":
p.add(new JLabel("Response:"));

//Set up the JTextArea in a JScrollPane:

//Add the JScrollPane,
//let it grow,
//and let the next component begin a new row:
p.add(scrollPane, "growx, wrap");

//Create the "Search" JButton,
//with an ActionListener to interact with the Groovy web service:
JButton searchButton = new JButton("Search");
searchButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
String searchString = client.processRequest(searchField.getText());

//Add the JButton to the JFrame,
//let it play in the whole row,
//and within that row align it to the right of the cell:
p.add(searchButton, "span, align right");

//Add the JPanel to the JFrame:



There are two points to make about the above, in the context of this article. Firstly, MigLayout rocks. With only six MigLayout constraints under our belt we've built a nice GUI for our application. Secondly, though, there's a lot of intermingling of different concerns in the above code. Firstly, there's the code that initializes the application; secondly, there's the code that displays our GUI; thirdly, there's our action stuck in the middle of our GUI code; fourthly, on the Groovy side, there's the interaction with the web service. Really a mess, especially in the light of Griffon, of course, which is where this article is inexorably leading. Therefore, let's untangle all of the above into more manageable Griffonic parts, while continuing to benefit from Groovy web services and the MigLayout. It will also be a first introduction of how Groovy web services can be handled by Griffon, i.e., an introduction to a simple usecase for the lifecycle support that Griffon provides out of the box.

Quick Prototype

Let's start really small. We'll do no more than get the web service snugly located in Griffon. To prove our success in doing so we'll hard code a search string and then display the result (without even parsing it) in an absolutely minimal view:

OK, how to get to this first stage? Use Startup.groovy to do the heavy lifting. Simply add the following to it:

import groovyx.net.ws.WSClient

def rootModel = app.models.root

def proxy = new WSClient

rootModel.message = proxy.GetSpeech("fair is foul")

So the above will set our model, based on the hard coded search string "fair is foul". The model, in turn, is simply as follows:

import groovy.beans.Bindable

class ShakesWSClientModel {

    @Bindable message

The @Bindable gives us PropertyChangeListener support for free, allowing the payload provided by Startup.groovy to set the value of "message" in the model. As a result, we can bind the above domain object to the view:

application(title:'ShakesWSClient',  size:[500, 50],
    location:[50,50], pack:true, locationByPlatform:true) {

    label(text:bind(source:model, sourceProperty:'message'))

Note: The label could also be bound as follows, which is shorter, but possibly less clear than the above:


Finally, be aware that the above code is all I added after running "griffon create-app". Nothing more than the above is needed to create the dialog-with-Shakespeare-goodness you see at the start of this section.

Our prototype is complete. Let's use it as the basis for our real application, which we'll start below.

Coding the View

Now, it's time to create the view. Since we're going with MigLayout and we already have a Java version of it, transferring to Griffon is easy, and with Groovy the world is a saner place:

import net.miginfocom.swing.MigLayout;

application(title:'Shakespeare Searcher', size:[600, 200],
location:[50,50], pack:true, locationByPlatform:true,) {

panel(layout:new MigLayout('insets 10')) {
label('Enter the search string', constraints: 'split, span')
separator(constraints: 'growx, wrap')
textField(id:'searchField', columns: 50, constraints:'wrap')
scrollPane(constraints:'growx, wrap'){
textArea(rows: 30, columns: 50,
editable:false, autoscrolls:true,
wrapStyleWord:true, lineWrap: true,
background: javax.swing.UIManager.getDefaults().getColor("TextArea.selectionBackground"))
button("Search", constraints:'span, align right')


The above is more or less the point of this article. One can see how Griffon helps MigLayout by the fact that our view is "viewy" (i.e., extremely view oriented, no other code to be seen). The main difference with the Java version of our MigLayout design is that we don't have any superfluous (and noisy, in retrospect) "add" calls, because whatever is within the curly braces is added to whatever container is nearest. Look at the JScrollPane for some nice nesting, i.e., the JTextArea is added to the JScrollPane because it is nested within it; likewise, the JScrollPane is added to the JPanel, which is added to the application... all without one instance of "add" being found in our code. (Don't you want to switch to this saner world right away?)

As previously, the result is as follows:

Coding the Controller

Next, since our "Search" button currently does nothing, we need to think about where to move our original ActionListener.

Here's a nice rule of thumb: Anything that controls anything belongs in the controller.

Let's put it in the controller as follows, very reminiscent of the code with which this article started (note that this is why you needed 'id:searchField' and 'id:resultArea' as properties in the JTextField and JTextArea in your view, to make things such as 'view.searchField.text' in the the code below possible):

class ShakesWSClientController {

    // these will be injected by Griffon
    def model
    def view

    def searchAction = { evt ->

        def xml = model.proxy.GetSpeech(view.searchField.text)

        def XmlParser parser = new XmlParser()

        def speech = parser.parseText (xml)

        view.resultArea.text =  ["PLAY: ${speech.PLAY.text()}\n",
         "SPEAKER: ${speech.SPEAKER.text()}\n",
         "TEXT: ${speech.text()}"].sum("")


The model needs to change and so does the Startup.groovy file. Why? Because, initially, we had hard coded the search string in the Startup.groovy file and then bound the domain object to it. Instead of that, we'll simply define our proxy as the domain object, without needing to bind it to anything since it isn't going to change:

import groovy.beans.Bindable

class ShakesWSClientModel {

def proxy


Now our Startup.groovy is simply as follows:

import groovyx.net.ws.WSClient

def rootModel = app.models.root

rootModel.proxy = new WSClient

That's it. Our controller is done and we've tweaked our model so that we're able to perform our action event in the controller. Next, all we need to do is hook our action into our button. Remember how our button simple had the "Search" text in it, together with two constraints? Simply replace the text with "action: searchAction" and type "build(ShakesWSClientActions)" below the view's import statement. Then create a file (the first file you've had to create manually) named 'ShakesWSClientActions' and add this content:

actions {

action( id: 'searchAction',
name: "Search",
closure: controller.searchAction,
accelerator: shortcut('S'),
mnemonic: 'S',
shortDescription: "Looking for Shakespeare"


Your application, now all in Groovy, using MigLayout for the UI design, with the sources structured very rigidly according to the Griffon framework... is complete. Run it and you'll have the same result as before:


This article has provided a brief introduction to MigLayout, both in the context of Java and in the context of Griffon. The advantages of Groovy over Java have been pointed out. The coolness of MigLayout has been demonstrated. The integration of a Groovy web service has also been shown, within a Java/Groovy application, as well as within a Griffon application.

Postscript: Interested in creating the samples discussed here yourself? In addition to the JDK and Griffon, you'll also need ant.jar on your classpath together with groovyws-standalone.jar (currently groovyws-standalone-0.3.1.jar) and miglayout-swing.jar (currently miglayout-3.6-swing.jar).


Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.


Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}