Chapter 16. Seeing the Grails light

 

Human beings, who are almost unique in having the ability to learn from the experience of others, are also remarkable for their apparent disinclination to do so.

 
 --Douglas Adams

The book is now officially over, but because you bought the whole album, we’ll throw in an additional bonus track for you.

The bonus track is a recording of two developers, Guillaume (G) and Dierk (D), who work together on a full-blown web application in Groovy. By eavesdropping on their conversation and looking at the code they produce, you’ll witness the evolution of the application from first ideas until deployment.

G and D work for ACME Software, an independent software vendor. Their boss wants each product to be accompanied by an “interactive tutorial.” At least, these were his last words before heading for the golf course. While leaving the room, he grumbled something about “needed by Monday morning” and “only highest quality accepted.”

Setting the stage

In which our heroes are given an assignment, make a bold decision, install Grails, and create their first page.

It’s Friday afternoon, right after lunch, when Guillaume enters Dierk’s office.

G:

Hi!

D:

Hi, Mr. G. What’s up?

G:

Didn’t you hear the boss? We have to do something about the “interactive tutorial.”

D:

He can’t be serious. I’m not gonna spend the weekend on this.

G:

Me neither. We need to find the quickest way to make this happen tonight.

D:

You mean “quick and dirty”? Not with me.

G:

No, quick and clean. I suggest we use Grails and see how much we can achieve this afternoon.

D:

Grails? What’s that?

G:

Grails is a web application framework.

D:

Oh no! Not another one. I’ve seen so many of them. They all claim to do everything in no time. Please spare me another disappointment.

G:

Well, all I’ll promise is that it will be fun working with it. I’ve used it in some other projects, and it worked well. I even think there’s a good chance we’ll have something running by this evening—and if we don’t, what have we got to lose?

D:

I wouldn’t follow anyone but you after this pitch. [laughs] Okay then. We’ll give it a try. What’s next? Download and install?

Installing Grails

Guillaume takes a seat next to Dierk. They start working together at Dierk’s machine, sharing the same keyboard, mouse, and monitor.

G:

Point your browser to http://grails.codehaus.org. Downloading and installing is explained there. We will go for the latest version, which is 0.3.1 at the moment.

Dierk opens the browser and navigates to the web site.

D:

Okay, it needs Java 1.4+ and Ant as prerequisites. I’ve got them already. Strange that it doesn’t need Groovy to be installed. [raises right eyebrow]

G:

Grails comes with the embeddable groovy-all.jar included to avoid any version conflicts.

D:

Wise decision. Now how about installation? Aha—setting a GRAILS_HOME environment variable and adding GRAILS_HOME/bin to the Path environment variable. So far so good—no surprises. But what’s that next thing here: going to GRAILS_HOME and typing ant?

G:

That means we are building Grails from the sources. Isn’t that neat?

D:

Neat or not, it’s a bit odd. But hey—as long as it works, it’s fine with me.

G:

That was it for installation.

D:

Eh—no. Wait. We’re gonna need a database.

G:

Yep. That’s included.

D:

And this will be a web application, right? So we also need a web server.

G:

No, that’s all included. Grails comes with everything you need for development.

D:

That can’t possibly be right. In the end, we’ll have to deploy it on our corporate SphereLogic webserver and the DBacle/2 database! That’s all very special. How could we ever develop against a different environment?

G:

Well, first of all, Grails will produce a full J2EE-compliant web application as a web archive file. We can throw that into any compliant server. Second, Grails uses Hibernate to take care of the database mapping. That means we have a huge variety of databases that we can choose from.

D:

Pretty impressive. Now, how do we start?

Getting your feet wet

G:

Go to any directory you like, and open a command shell.

D:

I’ll take one that’s already under version control.

G:

[nods] Sure. Now create a new application by typing

grails create-app

G:

and enter our application name when asked. I think Tutor would be appropriate.

D:

It’s created a lot of directories. Any idea what they all do?

G:

Yes, it looks like this. [produces table 16.1]

Table 16.1. Directory structure below the Tutor application directory

Directory

Content

grails-app

The grails-specific part of the web application

 

conf

Configuration data sources and bootstrapping

 

controllers

All Grails controllers; initially empty

 

domain

All Grails domain classes (models); initially empty

 

i18n

Message bundles for internationalization

 

services

All Grails service classes; initially empty

 

taglib

All Grails tag libraries

 

views

All Grails views (GSP or JSP); initially empty

  

layouts

All sitemesh layouts

grails-tests

All Grails unit tests; initially empty

hibernate

Optional Hibernate configuration and mapping files

lib

Additional libraries as jar files

spring

Spring configuration file(s)

src

 
 

groovy

Additional Groovy sources; initially empty

 

java

Additional Java sources; initially empty

web-app

Web application document root directory

 

css

Resource directory for Cascading Style Sheets

 

images

Resource directory for images

 

js

Resource directory for JavaScript files

 

WEB-INF

J2EE meta information (web.xml, and so on)

  

classes

Target for compiled classes; initially empty

  

tld

Resource directory for compiled tag libraries

D:

Gosh, it’s lucky you had that table with you. I see that the layout of the grails-app directory suggests that Grails obeys the good-old Model-View-Controller (MVC) separation or even enforces it.

G:

Yes, we will see that throughout the whole project. In general, the model is made by the domain objects, which drive the whole process.

D:

It’s created a whole bunch of files, too.

G:

Those are defaults for our application so that we can start right away.

D:

You mean we can start the application without having done anything?

G:

Yes, we can. [takes the keyboard] Go to the application directory

cd tutor

G:

and run the application.

grails run-app

D:

That’s a heck of a lot of console output. Is anyone meant to understand that?

G:

Well, we’re running at warning log level per default. I bet you’d be glad if anything went wrong.

D:

Now it waits and says: watch-context. What does that mean?

G:

That’s the Jetty web server, which is included in the distribution. Grails has generated a full-blown J2EE web application and started the server on it.

D:

Very helpful. No tinkering with server configuration files. That’s a big plus. And where is it running now?

G:

http://localhost:8080/tutor/.

D:

[grabs the keyboard] Here we go. [figure 16.1 is displayed]

Grails welcome page for the Tutor application

Figure 16.1. Grails welcome page for the Tutor application

G:

Okay, the setup works.

D:

Yes, that’s a good installation check. How did you know the URL?

G:

8080 is the default port, but we can of course change it if we want. The /tutor part comes from the name of the J2EE “application context” that has been created for us.

D:

I see. And now we hack away some static HTML pages to create a prototype?

G:

Why would we want to write static HTML pages? Don’t you want to see real data?

D:

Of course, eventually, but surely a few dummy pages come first, don’t they?

G:

No. If we were going to do any more coding right away, we would create domain classes, but I think we should lay out some plans first.

D:

[nods] Does that mean you’re calling a coffee break?

G:

Or tea. 15 minutes after lunch is the perfect time for this.

Laying out the domain model

In which our friends learn of requirements and dream up a schema.

Guillaume and Dierk are standing at a round table next to a whiteboard in the coffee corner, with mint tea and cappuccino.

D:

This can’t be a coincidence.

G:

What do you mean?

D:

We’re working with Grails and standing at a round table...

G:

Oh, please. This isn’t Camelot.

D:

[grins] Okay then. What about the “interactive tutorial”?

G:

I guess the tutorial needs to consist of at least some text and code examples...

D:

... that we need to create and display.

Thinking through the use cases

G:

Yes. We have authors who create a tutorial and users who read it. To make the authors’ lives easier, it would be nice if they could post tutorial pages through the web application.

D:

You mean like posting to a blog or a wiki?

G:

Yes, exactly. We’ll find out what works best. We’ll also need a tree-like structure for the tutorial entries to organize the tutorial and show a table of contents.

D:

And where is the interactive part?

G:

I talked with our boss about that before. He has the idea that logged-in users should be able to see what tutorial elements they’ve already worked through so that they can concentrate on the new ones.

D:

Hm, that’s a bit tricky. A user can scan through the material without reading much of it. That shouldn’t count as “reading.”

G:

Maybe the user clicks a button, indicating that they have visited the page.

D:

Hm, sounds doable—with considerable effort.

Designing relations

G:

We don’t have much time, so we will follow the simplest possible route. Let’s see what we have. [sketches figure 16.2 on the whiteboard]

Relational design of TutorialEntry and Author with many-to-one relations

Figure 16.2. Relational design of TutorialEntry and Author with many-to-one relations

D:

That’s rather simple.

G:

It will become more complex over time. You know: every complex solution that works is based on a simple solution that works.

D:

So, what have we got? TutorialEntry seems to be the central abstraction.

G:

Yes, it holds most of the information and will be the hub of most references. After all, the tutorials are the entities we’re doing all this for.

D:

And every TutorialEntry refers to an Author?

G:

Yes, each one refers to exactly one Author, but many TutorialEntries can refer to the same one.

D:

Therefore the relation from TutorialEntry to Author is many-to-one.

G:

Right, and it is unidirectional. That means we assume that every TutorialEntry directly references its Author, but a single Author doesn’t explicitly store a reference to all their TutorialEntries.

D:

But what if we want to show all TutorialEntries of a given Author?

G:

Then we can query for all TutorialEntries where the author property matches the given one.

D:

Ah, okay. No need to keep track of all the back references, because we can rely on the relational model below the surface.

G:

Exactly. It means less work with the bookkeeping but at the expense of performance. A query is always slower than following a reference.

D:

Hmm. [scratches head] So which is better then: unidirectional or bidirectional?

G:

I wish it were that easy. That’s an engineering decision. Making such tricky decisions is what we are paid for in the first place—besides knowing the tools and mechanics.

D:

You’re making me nervous. What if we decide wrong?

G:

They will hang us. [laughs] No, honestly, the cool thing about Grails is that you can change the design at a later stage without excessive costs. This is where the knowledge of the tools pays off. They give us leeway to defer and correct decisions.

D:

Good to know. What about that other reference between the TutorialEntries? That seems to be the parent-child relationship.

G:

All TutorialEntries have a parent TutorialEntry except the root Tutorial-Entry, which has a null parent. Many TutorialEntries can have the same parent. They are siblings, so to speak.

D:

And the relation is also unidirectional. Every TutorialEntry knows its parent but not its children? Is that a wise decision?

G:

For the moment, it should be good enough. Introducing back references from the parent to its children is an optimization issue. Grails grants us enough leeway to care about that later, when we have a better idea of what the actual access patterns are.

D:

I think I need to see some code to understand all this.

G:

All right. Let’s go over and get a first version running.

Implementing the domain model

In which Guillaume creates the first domain class, and Dierk is astonished by the scaffolding and testing capabilities of Grails.

Back at Dierk’s machine, he unlocks the screensaver.

D:

Now, how do we start implementing the domain model? Are we creating POJOs?[1] Do we have to follow any conventions?

[1] Plain Old Java Objects.

Scaffolding domain classes

G:

Both, actually. [grabs the keyboard] But Grails gives us all support we need. Let’s first create the Author class:

grails create-domain-class

    [input] Enter domain class name:
author

D:

Ah, you entered the class name in lowercase?

G:

Yes, but that’s only because I’m a lazy typist.

D:

And Grails says it has created two classes for us: grails-app/domain/Author.groovy and grails-tests/AuthorTests.groovy.

G:

That’s a scaffolded domain class and a corresponding unit test to give us something to start with. Author.groovy looks like this. [opens the file in an editor—listing 16.1 is displayed]

Example 16.1. Scaffolded domain class Author.groovy

class Author {

}

D:

There’s not a lot there, really. Is that it?

G:

That’s all it takes to start with. Grails will inject id and version properties at runtime for internal purposes. It also adds a toString method for the standard display of the object, showing class name and id. We should change that to something more meaningful for our domain class.

D:

You mean we should make it return something that makes sense to a user rather than to the computer?

G:

[nods] Exactly. Also, every such class will automatically be persistent—that is, backed by the database and managed by Hibernate.

D:

Aha, then let’s create the database, the tables, the schema, and the mapping descriptor.

G:

No, no, no! That’s why I said “automatically.” That’s all done for us behind the scenes. There’s really nothing we have to do.

D:

Wow! Very impressive. But how about the name attribute that we have to add?

G:

That’s as simple as this. [edits the file to make it listing 16.2]

Example 16.2. Customized domain class Author.groovy

Customized domain class Author.groovy

D:

And no mapping at all?

G:

As I said—it’s all automatic. Grails follows the “convention over configuration” paradigm, which makes all this possible.

D:

I would like to see something tangible before we proceed with the domain model. Can we look at the database?

G:

Yes, we can. We can even look at it through the web application.

D:

You’re kidding! We don’t have a web application.

Scaffolding views and controllers

G:

We don’t have a web application yet, but we will have one in a minute. See here:

grails generate-all

    [input] Enter domain class name:
author

D:

Lots more screenfuls of output, I see. And this is creating a web application?

G:

Start the server with

grails run-app

G:

and point the browser to http://localhost:8080/tutor/author. We need to add “author” to the URL because that is our domain class of interest. [browsing around, the screens in figure 16.3 are displayed]

Screenshots of scaffolded views to list, create, show, edit, and delete Authors

Figure 16.3. Screenshots of scaffolded views to list, create, show, edit, and delete Authors

D:

Hey, this really works! I can see the initially empty list of authors, create a new one, see it in the list, and edit and delete it. Not bad for having written only two lines of code!

G:

Yes, this is called scaffolding. It allows us to get something running quickly.

D:

Ah, you mean all these views are scaffolded, as you call it?

G:

Not only the views but also the controllers...

D:

... which call the database operations.

G:

Kind of. They work on the domain objects rather than calling the database directly. Each change to the domain object gets automatically propagated to the database.

D:

Does that mean we can have a campfire with all our SQL reference manuals being burned in a big ceremony?

G:

Certainly not. We don’t need them for the standard cases any longer, but when times get tough, you’ll be glad to have them.

D:

Right, right. Now, what’s next? Proceed with the other domain classes?

G:

Well, we could. But I’d like to show you another feature first.

D:

And that is?

G:

Functional testing.

Testing the web application

D:

You mean we can also test the web application automatically?

G:

Yes, and it’s remarkably easy. Look here. We’ll create the webtest, then scaffold the author tests, and finally run it. Creating the webtest support is a one-liner:

grails create-webtest

G:

This fetches Canoo WebTest if necessary and installs it. The download size is pretty big. Luckily we have good network connectivity.

G:

Now scaffold a webtest for the Author class:

grails generate-webtest

    [input] Enter domain class name:
author

G:

And now we run it:

grails run-webtest

D:

[as figure 16.4 is displayed] Ah, it brings up a test report. That’s pretty. Does it work by clicking through the web application on my behalf?

Excerpt of the WebTest report for the scaffolded Author CRUD operations

Figure 16.4. Excerpt of the WebTest report for the scaffolded Author CRUD operations

G:

Yes, and we can run it after every tiny change to the code, asserting that we haven’t broken anything.

D:

That means the tests will fail if our code doesn’t compile?

G:

It will even fail if our implementation doesn’t add or delete an author properly to the database.

D:

Very impressive. That’s good-old engineering practice made easy. When we proceed with changing the application logic, do we have to adapt the tests as well?

G:

Of course—and the best practice is to adapt the test before we change any functionality. [smiles]

D:

How do I change the tests, then?

G:

See for example webtest/tests/AuthorTest.groovy. It specifies the test like in lines like these:

clickLink  (label:'New Author')
verifyText (text: 'Create Author')
clickButton(label:'Create')
verifyText (text: 'Show Author', description:'Detail page')
clickLink  (label:'List',        description:'Back to list view')

D:

That reads like a checklist of what to do for manual testing, but it does it automatically.

G:

It also is all plain Groovy, which makes it convenient to work with.

D:

I feel confident enough to proceed with the domain model.

G:

Shall I type?

D:

Please let me do it. I think I understand the pattern...

Completing the domain model

Guillaume leans back and folds his hands.

D:

We’re going to scaffold TutorialEntry, right?

G:

Yep.

D:

[starts typing]

grails create-domain-class

    [input] Enter domain class name:
tutorialEntry

D:

All fine. Now scaffolding views and controllers?

G:

We could do that, but the views are scaffolded from what’s available in the domain classes, so we should complete them first.

D:

Okay, Author is already complete. Now let’s do the TutorialEntry class. It’s stored in the domain directory, if I remember correctly. [opens the file] Now what?

G:

Add a property for every attribute that we defined in our design: title, text, author, and parentEntry.

D:

Do we need to declare property types?

G:

Yes. Grails uses the type information for building the model. Having the type information available is a big plus.

D:

Title and text are plain Strings. Is author of type Author then?

G:

Of course.

D:

And parentEntry is of type TutorialEntry. Very straightforward.

G:

[grabs the keyboard] Here we go. [edits TutorialEntry to listing 16.3]

Example 16.3. Domain class TutorialEntry

class TutorialEntry {

    String        title
    TutorialEntry parentEntry
    String        text
    Author        author
    String toString() { title }
}

D:

Can I scaffold the views and controllers now?

G:

Go ahead.

D:

Piece of cake. [starts typing]

grails generate-all

    [input] Enter domain class name:
tutorialEntry

D:

Now starting the server...

grails run-app

[wait-forever]:

D:

... and open the browser and create a TutorialEntry.

G:

Every TutorialEntry needs an Author, so we’d better create one first.

Guillaume opens the browser at the URL http://localhost:8080/tutor/author and creates an Author.

G:

Now head over to http://localhost:8080/tutor/tutorialEntry/create. [figure 16.5 is shown]

Scaffolded create page for tutorial entries

Figure 16.5. Scaffolded create page for tutorial entries

D:

Wow! We already have the core of the application. And it’s working. We have the full lifecycle for authors and tutorials. The views are still not what we really want, though.

G:

Yes. Scaffolding is only to get you started and provide you with something that you can build on. It’s not uncommon to progressively replace all scaffolded artifacts in the course of the project.

D:

Then let’s see what we can do about the TutorialEntry’s list and details view.

Customizing the views

In which our heroes create some sample data, change a view in GSP, and gain wiki-like formatting using a tag library.

D:

We’ll need a set of example tutorials that we can test the views with. Shall we create five or so through the interface?

G:

We could, but there is an easier way. We can add them to the bootstrapping.

D:

Ah, does that mean we write a SQL script that fills the database on server startup?

G:

Almost. But not via SQL scripts.

Bootstrapping data

G:

We create our domain objects programmatically. Look here.[creates config/ApplicationBootStrap.groovylisting 16.4]

Example 16.4. Creating sample data in ApplicationBootStrap.groovy

Creating sample data in ApplicationBootStrap.groovy

D:

You create your objects with references and such, and when you say “save()” it all goes to the database?

G:

Isn’t that nice? That’s the typical Groovy style of having a smart configuration. Just imagine if this were XML like in other systems. Then we wouldn’t be able to define any amount of example data as we do in the little for loop.

D:

Well, then let’s display it. [types]

grails run-app

D:

Now see what we have in the TutorialEntry list. [web browser displays figure 16.6]

Scaffolded list view for tutorial entries showing bootstrapped data

Figure 16.6. Scaffolded list view for tutorial entries showing bootstrapped data

Working with Groovy Server Pages

G:

I think the scaffolded list view of tutorials shows too many details. It should only show titles, like in a table of contents, but with indentations.

D:

Okay. How do we change it?

G:

It’s all here. [opens views/tutorialEntry/list.gsp]

D:

Ah, the directory structure reflects the URL of the view. Very convenient.

G:

At least for the standard views as scaffolded. There are alternatives like creating them dynamically, but for our little thing here the standard way is all we need.

D:

And this GSP is the Groovy version of JSP?

G:

Yes, only better. [grins] Look at the snippet here that renders the list of tutorials. [opens the file, showing listing 16.5]

Example 16.5. Snippet of the scaffolded tutorialEntry/list.gsp

Snippet of the scaffolded tutorialEntry/list.gsp

D:

Looks familiar. I know nothing about GSP, but I can guess that this renders a table row for each TutorialEntry with table cells for every property. Not surprising. I assume we delete all cells that we don’t want to see?

G:

Yes. And don’t forget the corresponding table header cells.

D:

Done. Now restart the server and see what it looks like?

G:

No, no. We don’t need to restart the server. Just save the file and reload the browser page.

D:

Wonderful—very convenient. I like this micro-iteration development.

G:

Yes. Instant feedback is really helpful. You do a little change and verify the result without losing your concentration. This is what gets you into the flow of programming.

D:

I know what you mean. When everything seems to just fit.

G:

That reminds me—our list view doesn’t fit yet. It doesn’t show indentation.

D:

Hm, we need to indent each title as much as its nesting depth counted from root.

G:

And that’s equivalent to the number of parents it has.

D:

Ah, you mean we count the parents and indent by that number? But that’s a full-blown tree algorithm that will require a lot of work!

G:

Not necessarily. Let me try something. [grabs the keyboard—edits the file to listing 16.6].

Example 16.6. Snippet adapting tutorialEntry/list.gsp with title indentations

Snippet adapting tutorialEntry/list.gsp with title indentations

D:

Magic, magic. I’m impressed. What’s that? A scriptlet?

G:

Yes, the same as you would do in Java for JSP but in Groovy. The depth variable is calculated in the scriptlet for every TutorialEntry before rendering and implicitly stored in the binding. Therefore, we can reuse it in the padding argument of the title table cell style.

D:

It works well, and the output looks nice, but it has a smell. I think a view should never contain logic, and this view does.

G:

I’m not so sure. We could easily refactor this code into the TutorialEntry domain class or into a tag library. On the other hand, although this scriptlet contains logic, this is only view logic. It has nothing to do with the TutorialEntry model as such nor with the flow of the application. That makes it justifiable to leave it here.

D:

But I don’t like the mix of logic here. It doesn’t look well factored. I agree it’s view logic, but it should go to a separate place.

G:

If that’s the only thing that makes you feel uneasy, then we can move it into a template. Templates are little view pieces that are reused with changing data.

D:

Like a row template used for each TutorialEntry?

G:

In fact, that is a good name. We make a new file tutorialEntry/_row.gsp and put all the <tr>-enclosed code there.

D:

The leading underscore is a naming convention?

G:

That separates it from the normal views and suggests that it doesn’t produce a full page but only a part of it.

D:

And the tutorialEntry/list.gsp refers to it by name?

G:

Yes, via the render method. The old line

<g:each var="page" in="${tutorialEntryList}">

G:

is replaced with

<g:render template="row" collection="${tutorialEntryList}" />

G:

This will pass all entries of the tutorialEntryList one after the next into the row template for partial rendering.

D:

I like that much better. It clearly shows that the extracted logic affects only one line.

G:

It also makes the view logic reusable. Any view that needs to render a TutorialEntry in this fashion can refer to the template.

D:

What other views need improvement? Ah, the detail view should present tutorial entries in a more readable format. I assume it resides in views/tutorialEntry/ show.gsp.

Working with tag libraries

D:

This looks all very comprehensive. Rearranging the fields a little should be all that’s necessary.

G:

I have an idea. In addition to having our authors possibly provide HTML tags in their contribution, wouldn’t it be nice if they could use some wiki-like markup for the simple cases? We could then display the transformed markup on this page.

D:

Hm, sounds complicated.

G:

For a start, we allow some simple markup: treating newlines as breaks, two newlines as a paragraph, and support bold, underline, and italic styles.

D:

That’s still a lot of work—more than we can do in a scriptlet.

G:

Yes. That’s a job for a taglib.

D:

Hm. [shakes head] I did some taglib stuff with JSP—lots of configuration and not a lot of fun.

G:

I’ll show you Grails’ no-configuration solution. Go to the taglib dir, and create a new file WikiTagLib.groovy.

D:

And now?

G:

We implement the intended tag as a closure property with the respective name. How about “wikify”? The tag would have a text attribute with the raw text that is to be transformed to HTML.

D:

And how do we pass the result back?

G:

We add it to the out result writer. Look here. [creates taglib/WikiTagLib. groovy, listing 16.7]

Example 16.7. Implementing a wiki tag library in taglib/WikiTagLib.groovy

Implementing a wiki tag library in taglib/WikiTagLib.groovy

D:

Pretty slick. All plain Groovy. But how do we use it?

G:

In show.gsp we replace

${tutorialEntry?.text}

G:

with

<g:wikify text="${tutorialEntry?.text}"/>

D:

That’s all logical. I guess we now have to declare our WikiTagLib in the page header and in web.xml?

G:

No, no. That’s all done automatically. Save the files, and reload the page, [David Copperfield gesture] et voilà, there it is. [figure 16.7 appears on the screen]

Tutorial entry detail view with wikified text from WikiTagLib

Figure 16.7. Tutorial entry detail view with wikified text from WikiTagLib

D:

You know what? This is really how web development should always be: simple and efficient. Why wasn’t this invented earlier?

G:

[nods] I would say: Ruby on Rails invented the idea, and it took the appearance of Groovy to make this possible on the Java platform. The availability of Hibernate and Spring was also a prerequisite. But it’s not the pure power of the Java packages that counts. The dynamic nature of Groovy is just as important.

D:

And now the idea can grow.

G:

Just like our application. Let’s use it a little to see how it feels.

Working with controllers and finder methods

In which Dierk and Guillaume take control of the application and ask searching questions of their database.

D:

As a Tutor user, I would like to go through the tutorials in sequence. How about providing Previous and Next links in the detail page?

G:

Yes, you’re right. That’s missing. But how do we determine which tutorial entry is previous or next?

D:

We could guess from the containment in the parent/child relationship or from conventions about the title.

G:

That’s all too shaky. We need to have that in the model itself. After all, the page sequence is a central part of any tutorial, right?

D:

But changing the model is always critical.

G:

Not necessarily. Let’s add this line to TutorialEntry

TutorialEntry predecessor

G:

and then restart the server.

D:

Changing the domain class requires a restart?

G:

Not always. Simple changes don’t. Grails tries to avoid making you restart unless it’s absolutely essential. However, my personal style is to always do it after changes of the domain classes—just in case, you know.

D:

Fair enough. I guess we should also provide our bootstrapped TutorialEntries with a sensible value for predecessor.

G:

Ah, right. Thanks. I would have forgotten about that.

D:

Okay. All done. The server is restarted.

G:

Then let’s change the show.gsp to include Previous and Next buttons. I’d say we will use menu buttons for this. [edits file to listing 16.8]

Example 16.8. Snippet of views/tutorialEntry/show.gsp introducing Previous and Next menu buttons

<span class= "menuButton">
    <g:link action= "index">Home</g:link></span>
<span class= "menuButton">
    <g:link action= "previous" id="${tutorialEntry?.id}">Previous
    </g:link></span>
<span class= "menuButton">
    <g:link action= "next" id="${tutorialEntry?.id}">Next
    </g:link></span>

D:

And this means?

G:

When we click the Previous button, the previous action of the TutorialEntryController will be invoked. We provide the id of the currently displayed TutorialEntry as the id attribute.

D:

That means we have to extend the TutorialEntryController with that action. I can guess where the Controller will be: in the controller directory. Yes, there it is: TutorialEntryController.groovy.

G:

You see all the scaffolded actions in it? Does it look familiar?

D:

They are implemented as closures and assigned to properties—the same way we defined tags for the WikiTagLib. Looks like a pattern to me.

G:

[smiles] Seems to be more than a coincidence at least. [takes the keyboard] Let me try this. [makes the controller resemble listing 16.9]

Example 16.9. Previous action in the TutorialEntryController

def previous = {
    def entry = TutorialEntry.get( params.id )
    if (entry.predecessor) {
        entry = entry.predecessor
    }  else {
        flash.message = "Top of tutorials reached."
    }
    redirect(action: show, id: entry.id)
}

D:

I don’t understand a single line.

G:

Well, we first fetch the current TutorialEntry instance.

D:

Is get a database access?

G:

Kind of. It “gets” you the instance from the database or from the cache. Chances are that when we reach this point, the instance is already in the cache.

D:

And params.id is the id we provided in the <g:link/> tag?

G:

So it is.

D:

Then we work with the object references as usual. That’s okay. What is the flash object?

G:

The flash is a scope that lives until the next request and is used for relaying information from one controller call to the next. It is mostly used in situations like this, for relaying messages such as information, warnings, errors, and so on. In the scaffolded views, you will find references to the flash scope to find out whether there are any messages to be shown.

D:

Aha—and finally we redirect to the show action to render the show.gsp as usual but for the predecessor entry.

G:

Exactly, possibly showing the flash message if there is no predecessor.

D:

After the explanation, it makes sense.

G:

I hope so. The next action will be even more fun. [edits the controller to become listing 16.10]

Example 16.10. Next action in the TutorialEntryController

Next action in the TutorialEntryController

D:

What’s different?

G:

Look closely at the second line.

D:

findByPredecessor? Can that be true? We didn’t define any method with that name!

G:

No, we didn’t, but Grails is smart enough to know what we’re after. That’s a dynamic finder method made from the domain class information. This expression is roughly equivalent to this pseudo SQL statement:

SELECT * FROM TutorialEntry AS te WHERE entry.predecessor.id = te.id

D:

Why do you say “roughly”?

G:

Because strictly speaking, SQL has no notion of “entry” that we used here. Behind the scenes, Hibernate is doing all the work of providing us with nice object-oriented query facilities and optimization. Grails makes them available to us in the most convenient fashion.

D:

That is really amazing, but also very unfamiliar. Is there a reference about all these dynamic methods?

G:

Yes, it’s all on the web under http://grails.org.

D:

I know what to read over the weekend, then. Are we finished with the Tutor application?

G:

Not yet. The “interactive” part is still missing. We have no model about our users, yet, and how they visit TutorialEntries.

D:

That sounds like we should have a coffee break for another modeling session.

G:

Good idea.

Guillaume and Dierk walk over to the coffee corner.

Elaborating the model

In which Guillaume and Dierk discover a new entity in their midst.

The coffee corner’s whiteboard still shows figure 16.2.

D:

We should add the new predecessor attribute to the picture.

G:

[nods silently]

D:

And we will have a User class with a name attribute for each User.

G:

[nods silently]

D:

You’re so quiet. What are you thinking about?

G:

About the relation between User and TutorialEntry. I’m afraid it’s a bit complicated.

D:

Why? One User can visit several TutorialEntries. That’s a simple one-to-many relation, isn’t it?

G:

It’s not as simple, because many Users can visit the same TutorialEntry. So it’s actually many-to-many.

D:

Ah, I see. You’re right. If we ask a TutorialEntry for all Users that have seen it, we will get many Users. If we ask one User for all TutorialEntries he has visited, we will also get many TutorialEntries. That’s many-to-many. What worries you about it?

G:

The rule of thumb is that when you hit a many-to-many relation, you have missed an important concept in your design.

D:

Aha. So we know at least there’s something missing. Any hint how to find it?

G:

The trick is to picture an object that encapsulates the whole many-to-many relationship. If we had an object that takes TutorialEntries on the one hand and Users on the other hand to care for their relation, what would that object represent?

D:

Eureka! I have it! That’s a Visit! Look here. [sketches figure 16.8]

Relational design of the Tutor application completed with User and Visit

Figure 16.8. Relational design of the Tutor application completed with User and Visit

G:

Good job! That makes sense. Every time a User marks a TutorialEntry as visited, we create a new Visit for the unique User/TutorialEntry combination.

D:

And the relations become much simpler. Many Visits can refer to the same TutorialEntry. That’s a standard many-to-one relation.

G:

And many visits can be stored for the same User. That’s many-to-one as well.

D:

In other words, we have split the original many-to-many relation into two many-to-one relations. How exciting! We invented a whole new world-changing pattern!

G:

I don’t like pouring water in your cappuccino, but this pattern isn’t new at all. It’s as old as relational databases—if not older.

D:

Still, I think we did a really good job modeling the domain. I’m impatient to see how that looks in code.

Working with the session

In which Guillaume and Dierk ask their users to introduce themselves, and allow them to record their visits.

Back on Dierk’s machine.

D:

Scaffolding User and Visit is a breeze. Here’s the scaffolded User class, after customization. [User class is edited to listing 16.11]

Example 16.11. Scaffolded and customized User domain class

class User {
    String name

    String toString() { name }
}

D:

And here’s the Visit class, created and similarly customized. [Visit class is edited to listing 16.12]

Example 16.12. Scaffolded and customized Visit domain class

class Visit {
    User           user
     TutorialEntry entry

     String toString() { user.toString() + ' : ' + entry.toString() }
}

G:

The user needs some device to log in.

D:

To begin with, they could choose their name from the list of users. Ah, wait—we have scaffolded the user list view that we can hijack for that purpose. It’s in views/user/list.gsp.

G:

How should that work?

D:

We add a new action button to each table row like so:

<span class="actionButton">
    <g:link action="select" id="${it.id}"> That's me </g:link>
</span>

G:

I see. And we add a select action...

D:

... to the scaffolded UserController.

G:

Okay, let me do it. [takes the keyboard] We need get hold of the selected User object and store it in the session. I’d suggest the key user. [UserController becomes listing 16.13]

Example 16.13. select action in UserController

select action in UserController

D:

Session is a simple map that can store objects?

G:

It would be more accurate to say that it can be used like a map.

D:

And if everything is okay, we forward to the list view of the TutorialEntries.

G:

Yes, via the TutorialEntryController.

D:

The error handling seems a bit too much. Can it really be that a bad id is passed into the select action?

G:

You’re right. It’s unlikely, but you never know. Call me paranoid, but in controllers with all the redirections and chaining that might happen, I prefer to keep the code defensive.

D:

You’re the boss. What’s next?

G:

Users need some device to mark a TutorialEntry as “visited.” They could do that best when in the detail view of TutorialEntry. Therefore, I’d suggest adding a Visited button to views/tutorialEntry/show.gsp.

D:

Let me do that GUI stuff. [keyboard switch] I will add this line to the <g:form> section:

<span class="button"><g:actionSubmit value="Visited"/></span>

G:

If we click this, it will be handled by the visited action of the TutorialEntryController.

D:

Let me try to implement this. I will keep it as defensive as you like it. [twinkles as he edits to listing 16.14]

Example 16.14. visited action in TutorialEntryController

visited action in TutorialEntryController

G:

Good. You create the corresponding Visit object and save it. Perfect.

D:

And I added a convenience feature in the last line. After the button click, there is no point in staying on the same page. We go directly to the next one.

G:

Your users will love you.

D:

I hope so. Are we done?

G:

Almost. We still need to provide a filtered view for the TutorialEntry list that only shows links that haven’t been visited yet. That’s what the boss demanded.

D:

That concerns views/tutorialEntry/list.gsp. I’d say the simplest solution is to add the filtering functionality as menu buttons in the top row. [does so to make listing 16.15]

Example 16.15. Snippet of menu button section in tutorialEntry/list.gsp for providing filtering views

<span class="menuButton">
    <g:link controller="user" action="list">
        <%= (session?.user) ? "${session.user.name}": 'Log in' %>
    </g:link>
</span>
<g:if test="${session?.user}">
    <span class="menuButton">
        <g:link action="listVisited">List visited</g:link>
     </span>
     <span class="menuButton">
         <g:link action="listUnvisited">List unvisited</g:link>
     </span>
</g:if>

G:

I see you’re going to like GSP.

D:

Is that too much?

G:

You’re doing a little more than we need. First, you’re displaying the current user.

D:

Or “Log in” if there is no user in the session yet. The link behind that button goes to the user list view.

G:

And then two filtering buttons to restrict the current view to only the visited of the unvisited links.

D:

But only if the user is known. Otherwise there is no point in showing them.

G:

Very user-friendly. But none of this will work until we have the listVisited and listUnvisited actions in the TutorialEntryController.

D:

Can you help me with this?

G:

Sure. Remember, it’s all plain Groovy object work. [edits the controller to listing 16.16]

Example 16.16. listVisited and listUnvisited actions in TutorialEntryController

listVisited and listUnvisited actions in TutorialEntryController

D:

Cool. You use the dynamic finder methods to select all visits for the current user. But why is the simple expression .entry working?

G:

The finder method returns a list of Visit objects, and .entry is a GPath expression that returns a list of all entry properties of each Visit—that is, our list of TutorialEntries.

D:

And in listUnvisited, you subtract this list from the list of all TutorialEntries. This almost reads as if we were working on an object database!

G:

Simple and efficient. That’s Grails.

D:

I really see the light, now. We’re done, aren’t we?

G:

Yes, we’re done with the functionality. But because we still have some time left, I’d suggest doing some spit’n’polish before finally deploying it.

D:

You really want to deploy it? Isn’t that too much work? All the packaging and so on? Also, I have never before deployed an application that only took an afternoon to create.

G:

Why not? It’s all good and stable, and deployment will be easy. I promise you can go home early today.

D:

Okay then. What do we have to do to finish up?

Finishing up

In which Guillame and Dierk protect themselves from the forces of evil (well, evil input), deploy the application to a live system, and go their separate ways.

G:

Currently, the user needs to know the URL scheme to handle the application.

I’d say that’s okay for administrators who create new author and user accounts, but not so nice for the casual reader.

D:

We need a welcome page in our corporate design with quick links to the frequent entry points. I think a static HTML page would do it.

G:

We can store that as index.html in the web-app directory, and Grails will pick it up automatically.

D:

I expected nothing less. [laughs] I will copy the start page of our corporate web site for that purpose and change the body to be

<a href="tutorialEntry">Tutorials</a>

D:

That should be okay for the moment. We can refine that later. Anything else for the finishing touch?

G:

Well, we could go over the style in web-app/css/main.css to adapt it to our corporate style.

D:

I think we’ll leave that to the web designer. I’m happy with how it is right now.

G:

Okay, just in case the designer asks, we can change the whole page layout for all the pages at once by adapting views/layout/main.jsp.

D:

That’s good to know. Anything else?

Validating constraints

G:

Yep. Finally, I’d like to go over field validation to protect the application from bad user input.

D:

I would normally complain that this is too much work, because we have to go over all the affected actions and views, but I guess you have another ace up your sleeve?

G:

Well, I haven’t—but Grails has. It allows easy declaration of constraints in your model that are automatically used for validation. Let’s take the User class, for example. The name property should never be empty, neither should it be too long or too short. And most importantly, it must be unique in the database.

D:

Looks like we have to write some tricky conditionals and database access code here.

G:

Or a simple constraints property like so that goes directly in User.groovy:

def constraints = {
    name(length: 5..15, blank: false, unique: true)
}

D:

Marvelous! And how does it work on the user interface?

G:

Let’s try to add a user that already exists and see what happens. [tries it, resulting in figure 16.9]

Validation error message when trying to create a new user with a name that already exists in the database

Figure 16.9. Validation error message when trying to create a new user with a name that already exists in the database

D:

Really slick and very intuitive. Can you tell me what kind of validations are available?

G:

There’s a fairly long list in the online docs. So far, I’ve been able to do everything I needed, even regular expression matches.

D:

That’s fine. Regular expressions should at least cover everything we need to validate on the syntactic level. But what if I want to have other error messages?

G:

Then you can adapt the file i18n/defaultErrorMessages.properties or provide your own localized message bundle. It all goes through the standard Java way of internationalization.

D:

Perfect. I’d say we are ready for take-off.

G:

So would I.

Deploying the application

D:

Now, what do we have to do in order to deploy the application?

G:

Adapt the database configuration to match the settings of your deployment target, run the grails prod war command, and use the production server mechanics to install the generated web archive file.

D:

Where is the database configuration?

G:

It’s in conf/ProductionDataSource.groovy, just beneath the data source definitions for testing and development. Look here:

class ProductionDataSource {
   boolean pooling = true
   String dbCreate = "update"
   String url = "jdbc:hsqldb:file:prodDb"
   String driverClassName = "org.hsqldb.jdbcDriver"
   String username = "sa"
   String password = ""
}

D:

I guess we change this to our production settings. What’s the purpose of the dbCreate property?

G:

This mimics what is in Hibernate: the hibernate.hbm2ddl.auto property. We can set it to create,create-drop, or update, meaning we want the database schema to be created automatically.

D:

Will that mean that all existing data is lost?

G:

Not if we set it to update, which is the best choice for our purpose. Because we were running with the in-memory test database before, create-drop was the fastest solution.

D:

Where do we get all the information from?

G:

We’d better ask our database administrator, Martin. For production, we’ll need his help anyway, because he needs to grant us access rights and provide a username and password.

D:

And that’s all?

G:

Martin may have some further advice about the best possible driver to use.

D:

Do we need to package that driver class with Grails?

G:

If we need to package it, we can store it in the lib directory, and it will be packaged with the web archive. However, production systems typically have their drivers installed in a shared library, so we can rely on having it available. All we need to know is the class name, and we can add it to the configuration.

D:

I will head over to Martin and ask him.

G:

Meanwhile, I’ll prepare a text message for the boss, saying that the initial version of the Tutor application is up and running in production. I guess they have Internet access in the clubhouse, and he will be able to connect from there.

D:

He will be surprised.

G:

Definitely.

Dierk leaves to contact the database administrator and returns with a paper sheet of the configuration details.

D:

Here we go. I owe him a beverage.

G:

[smiles] We’ll have one later.

D:

Okay, I’ve fixed up the configuration. What was next? Creating the web archive?

G:

Yes. Here it is:

grails prod war

G:

We now have the tutor.war web archive ready for deployment.

D:

I know how this goes. The server has a web interface where we can upload the file and start the application. That’s easy. Just a minute. [uploads the file] Okay. Done.

G:

Let’s do some simple click-through testing to verify it’s fine. Open the browser to http://groovy.canoo.com/tutor.

D:

Looks okay. [clicks through the application] Perfect.

G:

Gimme a high five. [clap]

D:

Okay. Let’s invite Martin and call it a day.

Farewell

Guillaume, Dierk, and the database administrator Martin assemble in the nearest beer garden.

D:

The work with you and Grails was a ton of joy. Thanks for pointing me at it. I think we did an awesome good job this afternoon.

G:

Hm, we could have done a little better.

M:

I saw your application and used it a little. Really nice. Others need more than a week to achieve anything comparable.

G:

Thanks, but I would have liked it better if we’d worked in a more test-driven style. You know: writing unit tests and functional tests as we go along.

D:

You’re demanding too much. That’s hardly possible.

G:

With all the scaffolded tests, it’s quite easy.

M:

And you are testing from a user’s perspective?

G:

Yes. Those are the so-called webtests. They are testing the view. We did a little of that. But we can also have unit tests for our models and controllers.

D:

But we tested the views all the time, because we were constantly switching between coding and using the application.

M:

Sounds like an agile programming thing.

D:

And the models have no methods at all, so there’s no point in testing them.

G:

True; they could have had some methods, though.

D:

I agree that testing the controllers automatically would have been beneficial. They contain the beef of the application logic.

M:

Testing controllers is always tricky. You have to set up a server environment for that, because controllers rely on request parameters, session information, and such. That’s an awful lot of work.

G:

Actually, it’s simple. In Grails, all the information is in common maps that you can pass into the controller method when testing.

M:

Hm, okay. Then it should be easily possible.

D:

I was surprised so many times this afternoon that I believe everything.

G:

[laughs]

M:

You must have had some real fun today.

D:

You bet. It felt like everything was just falling in place.

G:

Well, almost. Remember how we discussed the design of the domain model? That was the crucial part.

M:

The drawings I saw in the coffee corner? They didn’t look overly complicated.

G:

But that’s where the work is: making the complex simple.

D:

Yes. I think my first attempt would have been to go for more bidirectional references. Would that have been possible at all?

G:

Of course. It’s also straightforward. The online docs show how to do it.

M:

How about database performance? I certainly don’t want you to drag down my db when your application becomes popular. [twinkles]

G:

We still have room for optimization, whether by using bidirectional references or by optimizing Hibernate usage with direct mappings. However, I expect at least 90% of our database accesses to hit the Hibernate cache anyway.

M:

Perfect. I’ll look into the database logs to see what you produce, anyway.

G:

Thanks. I would have asked you for that favor otherwise. [grins]

D:

What are we going to do on Monday? I’d like to do some more work with Grails.

G:

We’ll write some more tests first. After that, I’ll say it would make sense to hook in security.

D:

That is possible?

G:

Of course; Grails is pure J2EE, and we can use every feature of that platform: administration, operation surveillance, logging, load balancing, failover, and so forth, and also security. There is no need to reinvent the wheel just because we’re groovy.

M:

I can help you with the security setup.

G:

Great!

D:

The little wiki-like markup we implemented in the WikiTagLib made me think we’re re-inventing the wheel, though. Would it be possible to include something like the Radeox Wiki engine?

G:

Good idea! Yes, of course, that’s possible. We can use any Java library we fancy. We throw its jar file in the lib directory and rework the WikiTagLib to use that functionality!

M:

You have so many options that virtually anything is possible.

D:

Friends, [stands up and raises his glass] I’m afraid the party is over, and I have to leave now. It was an honor to work with you. I know that working with me has not always been easy. I appreciate your forbearance when overlooking my little mistakes. Thank you very much for spending your precious time with me. I hope we will soon meet again and share the joy of dynamic programming.

Cheers!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.102.50