Programing Assignment 1 – Building Your First Grails Apps

The purpose of this assignment is to introduce you to Grails, including:

  • Grails 5 Framework and the use of Domains, Controllers and Views
  • IntelliJ IDEA integrated development environment (IDE)
  • H2 Database and h2-console
  • Writing and running unit tests
  • Writing and running integration tests

You are to complete this programming assignment individually. In this assignment, you will build a simple web app for a book store’s administrative back end, meaning administrators will be able to add books and authors to the book store’s database.

Step 1: Introduction to Grails

Grails

What is Grails?

Grails is a Java Enterprise web framework built on top of the Spring Boot framework using the Groovy scripting language.

That is a mouthful and probably not very illuminating. We’ll dissect the above sentence.

  • A “web framework” provides structure and packages for developing web applications. Some web frameworks you might have heard of are WordPress and Rails.
  • “Java Enterprise” (Java EE) is the set of Java API for constructing enterprise applications which includes the technology for Servlets (the Java API for implementing a web server), Java Server Pages (JSP, the Java API for building web pages), Java Database Connector (JDBC, the Java API or interface for databases), and many more APIs.
  • “Spring Boot” is a Java EE web framework built using the Spring framework. The Spring Framework was the first popular framework implementing dependency injection for constructing large applications. Lectures in this course will describe Java Servlet and how Spring Boot uses them to construct a web server.
  • “Groovy” is a scripting language based on Java. A programmer can almost always write Java code in a Groovy script. Some of the differences between Groovy and Java are that Groovy is loosely typed and eliminates many of Java’s syntax.  Groovy scripts simplifies code. Groovy was originally developed to define Domain Specific Languages (DSL). An example of a DSL is Gradle which is a tool that you’ll get to use to describe how the web application should be built. There is also a Groovy lecture in this course.

That was a lot. Maybe it helps to explain the sentence describing Grails. Try reading the sentence again and see if it makes any more sense. The above explanations should make it clear that Grails is built on top of quite a few technologies. But what does Grails provide that Java EE and Spring Boot do not already supply?

Some Grails benefit:

  • GORM, which means Grails Object Relational Mapping (ORM). An ORM is an interface between the database and the application code. It simplifies schema descriptions and database queries. GORM is built on top of the Hibernate package. The Hibernate package provides an ORM, databases, and consoles for querying databases. In particular Hibernate provides a database instantiated in memory.
  • Model View Controller (MVC) structure for building the web application. MVC is a design pattern separating the concerns of information or data (the Model), preparing the information and responding to user interaction (the Controller) and presenting the information to the user (the Controller). In this programming assignment, you will learn Grails’ MVC pattern. Note there are many variations on the MVC design pattern implemented by different web frameworks.
  • Grails provides scaffolding or automatic code generation.  Grails commands will automatically write code for many of the components that you can build on. This saves a lot of coding labor.
  • Gradle is a tool for building your applications. Gradle uses groovy to specific the build. Using groovy the build description can mix procedural and declarative descriptions of the build. More important Gradle uses plugin, so Grails Gradle plugin can specify the build for your code and minimize your coding for the build. You will interact with Gradle in later programming assignments.
  • Grails plugins, which provides additional functionality to the framework. The most notable Grails plugins are GORM and Spring Security. Spring Security provides authentication and authorization for web application. In later programming assignments, you will get to use Spring Security.
  • “Convention over Configuration” is a technique for simplifying code without limiting flexibility of the framework. In essence, appropriate defaults are applied to the code. For example, where you place code and how you name the class file will automatically configure much of the code for you. But you can always override the defaults by coding configurations. In the programming assignments, you will use both convention and configuration. You should always use convention when you can because it will make the code easier to read and maintain.  
  • Much more.

To summarize, I’ll state again what is Grails.

Grails is a web framework. It use the Java EE and the MVC design pattern. Developers can write their code using the groovy scripting language. Grails provides many tools to simply the construction of your web application.

Hopefully, now you have a good idea of what is Grails and why this course uses it.

If you need more resources consult the Grails User Documentation

Grails Documentation

http://grails.org/doc/latest/

Please note that Grails’ documentation URLs always point to the latest version of Grails, as of October 18, 2022, Grails 5.2.4. This tutorial will use Grails 5.2.4. But if you are using a later version of Grails, you may want to read the documentation specific for the Grails version used in the project. The core features of Grails 3, 4 and 5 generally agree, but you should be cautious. For example if you are using Grails 3, you’ll want to refer to

http://docs.grails.org/3.3.11/guide/single.html

The title of the page (appearing in the browser window tab) gives the Grails version number.

You will want to study chapter 7 about GORM and the dedicated GORM manuel:

http://grails.org/doc/latest/guide/GORM.html

http://gorm.grails.org/6.1.x/hibernate/manual/

For more details about Grails views and controllers, you will want to study chapter 8 about the web layer

http://docs.grails.org/latest/guide/single.html#theWebLayer

Also on the right hand side of all the documentation web pages is the Quick Reference, but you need to have a wide browser window to view the links in the Quick Reference.

The documentation is a good reference but is very technical, so you will probably find it hard to understand when you first begin programming. There are good books that give easy explanation of grails, only a few books are about Grails 3, and no books for Grails 4 or 5.

https://grails.org/learn.html#books

There are also a large collection of Grails Guides:

http://guides.grails.org/#/index

Be careful of the Grails version used in the guides. Although for the most part, guides are written for Grails 4 and 5, but some guides have not been update from Grails 3.

Step 2: Install IntelliJ IDE and Java 8 on your home machine

1. Download Java 8 on your home machine

Note, you will need to remember where you download Java 8 on your home machine because you will need to know the location when you create your project. Also be sure that your team is using the same version of Java.

There are two options for installing Java. You can download from Oracle or you can use SDKMAN to install and manage different Java versions. I use SDKMAN.

To download Java 8 from Oracle site go to

https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html

Using SDKMAN to download Java 8

SDKMAN is a command line tool for controlling the version of many Groovy and Java development software.

http://sdkman.io/

To install and use SDKMAN, it is convenient to use a BASH terminal. I like to use the Git Bash terminal.

https://git-scm.com/downloads

Download git for your machine’s platform and follow the instructions.

In the terminal, use the “curl” command to install SDKMAN.

Java comes in many version and implementations. So first use SDKMAN to see what is available by entering

sdk list java

A table with 5 columns: Vender (first column), Use, Version, Dist, Status and Identifier (the last column). I generally use the Zulu because it offers the most legacy versions. The “Identifier” code is what you use to install the java version. So if you want the latest Zulu Java 8 version, type “q” to exit the list, and enter

sdk install java 8.0.345-zulu

SDKMAN will make a “.sdkman” directory in the root of your user home directory. In the .sdkman directory, there is a “candidate” directory. The candidate directory will contain the different software and versions of the software.

2. Installing IntelliJ IDEA Ultimate

The only Integrated Development Environments (IDE) that runs Grails is IntelliJ IDEA. Although it is possible to develop with Grails without an IDE, I recommend that your team use the IntelliJ IDEA. Then, your team and instructors can help you if there are IDE challenges.

Below are detail instructions for IntelliJ IDEA installation. The instructions are specific to a Windows machine. If you are using Mac or Linux it might differ.

Many of you already have IntelliJ IDEA Ultimate edition, so you can skip this step.

The Grails Developer Team recommends using IntelliJ for your development environment. IntelliJ comes in two favors: Community and Ultimate. The community version is free for anyone and can run Grails, but it does not have many of the convenient features that the ultimate version has.  Jet Brains offers an one year academic licenses for the ultimate version, and our IT has installed the ultimate version on the school lab machines. I assume that you want to download and install the ultimate version on your home machine

i. Apply for JetBrains student License

Apply for a student licence at

https://www.jetbrains.com/student/

Clicking the “Apply Now” button in the middle of the page will take you to this form

https://www.jetbrains.com/shop/eform/students

Use the “University email address” tab to apply. Be sure to use your MTU email address. Click the “Apply for Free Products” button at the bottom of the page.

You should get an email from Jet Brains. The email will be from “sales.us@jetbrains.com”, and there should be a link in the email to activate your JetBrains account.

ii. Download and Install IntelliJ IDEA Ultimate

IntelliJ IDEA download website is at

https://www.jetbrains.com/idea/

Clicking the “Download” button in the middle of the page, takes you to the “chooseYourEdition” section of the page:

https://www.jetbrains.com/idea/#chooseYourEdition

Download the “Ultimate” version. For windows this will download an installer. Run the installer as administrator. Follow the wizard, you can use all the default options.

Jet Brain has good documentation for IntelliJ IDEA. You can find installation instructions at:

https://www.jetbrains.com/help/idea/installation-guide.html

iii. Install the Grails plugin for IntelliJ IDEA

Install the Grails plugin for IntelliJ IDEA by visiting the page below and clicking the blue “Install intelliJ IDEA” button the top right of the page

https://plugins.jetbrains.com/plugin/18504-grails

If you need help with the Grails plugin or want to learn its feature, visit the help in later IntelliJ IDEA for Grails

https://www.jetbrains.com/help/idea/2021.1/getting-started-with-grails3.html

In general, the left panel of IntelliJ IDEA shows the project file directories. Also the right panel (main panel) is the editor view. IntelliJ has a bottom panel. This may be hidden, but you can open different views using the buttons along the bottom of IntelliJ IDEA. The relevant view at the bottom occurs when you run your Grails app within IntelliJ IDEA. Running the Grails app will open a “Run” view that allows you to stop and start your app and also clear and print the terminal output. There are also terminals for accessing your system terminal for executing system commands. There is a far right panel which you access by clicking the buttons on the right boarder of IntelliJ IDEA and gives you access to build tasks and commands.

Step 3: Make the Grails project using IntelliJ IDEA

1. Make “workspace” and Project Directories

Although IntelliJ IDEA does not have the notion of a “workspace,” I like to make a “workspace” directory in order to clearly indicate the location of my code and to keep it separate from other files, such as programming notes.

In a directory of your choosing, make the directories

C: .../cs4760progassign-Workspace/cs4760progassign/

Note that the directory “cs4760progassign” is a sub-directory of the “cs4760progassgin-Workspace” directory.  From experience, I have learned that the IntelliJ IDEA will automatically name the project and package of the app by the name of the directory that the project is created in. So it is easier to first name the project directory within the workspace directory first.

Note: Make sure that there are no spaces in the path to your project workspace. Spaces can cause some of the Grails command to fail.

2. Use the Grails App Forge to make the project

Run IntelliJ IDEA and point it to the “cs4760progassign-Workspace/cs4760progassign/” directory. If IntelliJ IDEA opens in the wrong directory do not worry. Within IntelliJ IDEA select “File” from the top menu. Then select “New” and then “Project …”. The wizard should open. In the left hand panel of the wizard, select “Grails App Forge“.

i. Specify the Package SDK download in the Wizard

In the right panel, click the down arrow in the “Project JDK” text field. In the dropdown menu, select “Add JDK”. In the pop up window, navigate to to the directory with you JDK. For my case using sdkman to download Java on a Window’s machine is

C:\Users\pastel\.sdkman\candidates\java\8.0.255-zulu

ii. Specify rest of the project options

For “Project type” dropdown, select “Application”.

For “Grails version”, you can only select “5.2.4”.

For “Profile”, select “web”.

For “Features”, just use the default/required check boxes

Then click the “Next” button at the bottom right.

iii. Specify the Project Location

In the next window, you specify the “Project location” by clicking the “…” button. Yet another file browser opens. Navigate to your workspace and project directory,  “cs4760progassign-Workspace/cs4760progassign/”. Make sure there are no spaces in this path. Click the “OK” button at the bottom of the file browser. Note that the “Project name” is automatically filled in the text field to match “cs4760progassign”.

iv. Create the Project and the Basic App

Click the “Finish” button at the bottom in the Wizard. A notice appears asking if you want to open the new project in “This Window” or in a “New Window.” Click the blue “This Window” button.

IntelliJ IDEA will move to the new project directory, and IntelliJ IDEA begins building the basic Grail app directory structure and files within the project directory. This will take some time, 5 minutes or more. Be sure that you let IntelliJ build the complete directory and files. You can watch the build in the “Build” console at the bottom of IntelliJ IDEA.  

If you are developing on a Windows machine. “Window Defender” will ask permission to download files. Give it access using your admin account.

Setting up and building the project files the first time takes a long time, several minutes. Let Gradle do its work.

iv. Setup Local Git Repository

You should at least setup a local repository for your project. IntelliJ IDEA setups the project with an gitignore file. IntelliJ comes with version control system functionality. You can find them under “VCS” in the menu bar. I prefer to use the Git Bash terminal. Open Git Bash terminal at the root of the project file, cs4760prograssign/, and enter

$ git init
$ git checkout -b main
$ git add -A
$ git commit -m "initial commit"
$ git checkout -b progassign-1

IntelliJ IDEA VCS commands integrate with the the git commands that you enter through the Git Bash terminal.

v. Study the Project Directories

After Grails has finished creating the new app, the “Project” view shows the project directories files:

  • .gradle/
  • .idea/
  • build/
  • gradle/
  • grails-app/ <- This is the main directory that you will be coding in.
    • assets/ <- asset-pipeline plugin uses this directory. You will add css and JS files here.
      • images/
      • javascript
      • stylesheet/
    • conf/
      • spring/
      • application.yml <- Configuration file for the running app
      • logback.groovy
    • controllers/ <- Controllers talk to domains and views
      • cs4760progassign/
        • UrlMappings.groovy <- configure the url mapping for the app
    • domain/ <- domain models for interfacing with the data store
    • i18n/
      • Resource Bundle ‘messages”
        • messages.property <- English language files.
        • other language files
    • init/
      • cs4760progassign/
        • Application.groovy <- file that initialize the program
        • Bootstrap.groovy <- groovy code for initializing the app
    • services/ <- for groovy and Java code that run outside the controllers
    • taglib/ <- for app specific “g-tags”
    • views/ <- for gsp files, like html files.
      • Layouts/ <- gsp files for structuring views
        • main.gsp
      • error.gsp
      • index.gsp <- default home view
      • notFound.gsp
  • src/
    • integration-test/
    • main/
    • test/ <- Where unit tests are made
  • .gitignore
  • build.gradle <- configuration file for building the app
  • cs4760progassign.iml
  • gradle.properties
  • gradlew <- Command file for running grails tasks
  • gradlew.bat <- Command file for running grails tasks
  • grails-wrapper.jar
  • grailsw
  • grailsw.bat
  • settings.gradle

I have marked a few important directories and files. For the most part, you will do most of your coding in the grails-app/ directory. You will make domain models in the domain/ directory to interface with the data store (database). You will generate controller files to act as the intermediary between the domain classes and the views.  Also you will make views, gsp (like html) files in the views/ directory. You will want to know the directory structure below well:

  • grails-app/
    • assets/
    • conf/
      • application.yml
    • controllers/
      • UrlMappings.groovy
    • domain/
    • i18n/
    • intit/
      • Bootstrap.groovy
    • views/
      • layout/
        • main.gsp
      • index.gsp

There are a few configuration files you will use:

  • application.yml in grails-app/conf/ – for configuring the running app including the data source
  • Bootstrap.groovy in grails-app/init/ – for setting initial values for the running app
  • UrlMapping.groovy in grails-app/controller – for configuring the URL
  • build.gradle in the project root directory – for specifying plugins and building the app

vi. Run the app

The code generated by IntelliJ IDEA will run. To run the basic app, you only need to click the right pointing green arrow at the top of IntelliJ IDEA in the toolbar. Make sure that the adjacent text field says “Grails: cs4760progassign”. This will open a terminal view at the bottom of IntelliJ IDEA. You can watch the build. When the build is done you should see this message:

|Running application...
Grails application running at http://localhost:8080 in environment: development

Also your default browser window should open to http://localhost:8080. If it does not, just open a browser window and paste the URL into address bar. You should see the default index page for a Grails basic app. Congratulations you made your first basic app.

A lot of useful information will appear on this page, including

  • Application Status <- Specifics Grails version
  • Artifacts <- number of controllers, domains and services.
  • Installed Plugins <- list of plugins and there versions.
  • Available Controllers <- List of controller links.

Currently, there are no controllers. The app is showing the index.gsp. You can see the code by clicking on grails-app/views/index.gsp in the Project panel of IntelliJ IDEA. The code will appear in the edit window. For the most part the code is html, but it is accessing some app variables. They are written using ${…} syntax. More about that in other assignments and lectures.

Grails Commands

On the side, we should talk about the Grails commands for two reasons.

  • So you understand what you are doing while developing your applications.
  • Sometimes invoking a Grails command fails (especially when the command is invoked using the IDE), so you will need alternative ways to invoke the command.

Much of Grails functionality is provided by invoking a Grails command. You can see a list of all the Grails command at

https://docs.grails.org/latest/ref/Command%20Line/Usage.html

Look at the panel on the right to see the list of commands.

For the most part, Grails commands can be categorized into one of two types.

  • Commands that build and run the web app
  • Commands that generate artifacts for the web application

Artifacts are components of the web applications, such as controllers, domains, services, html files, JavaScript files etc.  Generally, commands that begin with “create” or “generate” are commands that will make artifacts for your project. Most of the other commands are for building or running the application. The “grails run-app” which you just invoked by clicking the green arrow in IntelliJ IDEA toolbar is an example of a Grails build command.

https://docs.grails.org/latest/ref/Command%20Line/run-app.html

There are multiple ways to run a Grails command depending on the command types.

Build and Run Grails Commands

Commands that build and run the applications can be invoke

  • in the IDE
  • by invoking the corresponding Gradle task
  • by invoking the Grails command in a Bash shell

By clicking the green arrow in IntelliJ IDEA menu bar is an example of using the IDE to invoke the command. Alternatively, we could have ran the app by invoking the corresponding Gradle task, bootRun. There are two way to invoke a Gradle task

  • in the IDE
  • by invoking the Gradle task in a terminal

We can invoke the Gradle task in the IDE through the Gradle panel in the IntelliJ IDEA. You open the Gradle panel by clicking the tab on the right boarder of IDEA. Then search for the task in panel. In the case of “grails run-app” command, the corresponding Gradle task is “bootRun” found in the “application” category. Double clicking on the Gradle task will open the “Run” console at the bottom of IntelliJ IDEA, where you can watch the progress and have access to modify the command. Invoking the Gradle task using the Gradle panel in the IDE will also create a command in the “Run/Debug” list in the IntelliJ toolbar.

You can also invoke the Gradle task in a terminal. You can either use a Bash terminal (at the root project) or use the “Terminal” provided by IntelliJ IDEA. Either case you use the Gradle wrapper command, “gradlew”. The wrapper assures that you are using the correct Gradle version for your platform. To run the “grails run-app” command, enter

./gradlew bootRun

You can find the corresponding Gradle task for the grails build and run command at

https://docs.grails.org/latest/guide/commandLine.html#gradleTasks

Finally, you can also run the Grails command in a Bash terminal by using the Grails wrapper. Unlike the Gradle wrapper, the Grails wrapper does not run the Power Shell terminal provided by IntelliJ IDEA on Windows machines. I suspect that it will work for macOS and Linux. From the root directory of the project type

./grailsw run-app

My preferred way to invoke a run or build grails command is to use the Gradle panel in IntelliJ IDEA because I do not have to type and the “Run” console that opens at the bottom of IntelliJ IDEA is an excellent presentation for viewing the execution.  

Generate Artifacts Grails Command

There are only two ways to invoke Grails command that make application artifacts.

  • in the IDE
  • by invoking the grails command in a Bash terminal

IntelliJ IDEA offers many techniques to invoke grails command. We will use the “grails create-domain-class” command as an example.  (You’ll use the “grails create-domain-class” command in Step 5 of this programming assignment.)

https://docs.grails.org/latest/ref/Command%20Line/create-domain-class.html

For the “grails create-domain-class” commands there should be three ways to invoke the command in IntelliJ IDEA.

  • using the context menu by right clicking the “domain” directory in the project panel, and then selecting “new” followed by “Grails Domain Class”
  • using the Run Grails Command tool by clicking “Tools” in the menu, then selecting “Run Grails Command”.
  • by adding a command to the “Run/Debug” list in the toolbar and then invoking the command by clicking the green arrow.

Unfortunately, the first two techniques, “using the context menu” and “using the Run Grails Command tool”, do not work with this version of IntelliJ IDEA Grails plugin. Even more unfortunately, they fail silently.

Creating the Grails command in the “Run/Debug” list does work, but the process is tedious, so I’ll not describe the technique for the “grails create-domain-class” command. Creating a “Run/Debug” command is appropriate for Grails command that you will run more than once.

So when using the “context menu” in IntelliJ IDEA fails to run a Grails command, the best technique to invoke the command is to use the Grails wrapper in a Bash terminal. The technique is identical to the description for invoking a build grails command. Open a Bash terminal in project’s root directory and type

./grailsw create-dmain-class Author

I was provoked to write this long discussion on the alternative for invoking a Grails command because using the context menu to create domain classes failed.   Using the context menu to create Grails artifacts is very convenient and used to work. I had to figure out alternative techniques and try to understand what was going on.

By the way, there is yet another way to create Grails artifacts. You could just code them from scratch in the proper directory with the proper name. But in the case of creating domain classes, you would also have to create the unit test files from scratch. So there is a benefit to using the grails command invoked in a Bash terminal.

In following steps of this assignments, I’ll illustrate different ways to invoke Grails commands.

Build Errors

Sometimes when adding a lot of artifacts to you project, the build fails. You will get an error similar to:

Execution failed for task ':bootRunMainClassName'.
> Unable to find a single main class from the following candidates [application, cs4760progassign.Application]

Gradle is confused. The solution is to clean the project by invoking the clean task found in the “build” category.

vii. Modify the Basic App: set the context path

Although the index view looks good, I’m not happy with the URL of the index or home view. The URL should be:

http://localhost:8080/cs4760progassign/

and NOT:

http://localhost:8080

The production server for your project will need the app URL to include the app name, so that the router knows where to route the request. So you will need to configure the server’s context path.

We need to edit grails-app/conf/application.yml by adding to the bottom of the file

server:
    servlet:
        context-path: /cs4760progassign

Note that there is a space between the colon and the quote. Without the space the YML parser will silently fail. An hint that the formatting is correct is that the “/cs4760progassign” will be a different color form “context-path” Also “server:”, “servlet:” and “context-path: /cs4760progassin” should be on separate lines. Finally, there must be a leading slash, “/”, or you’ll get an error.

The file application.yml is written in YML which is a markup language:

https://fdik.org/yml/

It is not a particularly complex markup language and more readable than XML. Gradle and Grails use YML. You’ll not need to learn too much of the language. For reference see:

http://stackoverflow.com/questions/32976039/how-do-you-change-the-application-name-in-grails-3

Now stop and rerun Grails. Note IntelliJ IDEA automatically saves edits to a file, so you do not have to save the file. Stop the app by clicking the red square either in the Run view or the tool bar at the top of IntelliJ IDEA. You can also restart the app clicking the small black square with a  green arrow.

Step 4: Move the Controller List view

1. Generate the New Controller

The default home page, the controller list view, is useful but not appropriate for a home page, so we should move it. We would like the URL for the control list view to be:

http://localhost:8080/cs4760progassign/controllerList

Create a controller by right clicking the “controllers” directory in “Project” left hand panel, and select “New” and then “Grails Controller”. In the pop-up menu enter

ControllerList

The command creates several files. One of the files is ControllerListController.groovy in grails-app/controllers/ directory:

package cs4760progassign

class ControllerListController {

    def index() { }
}

The generated controller has only one method, “index” and the method is blank. This is all we need.

2. Move index.gsp

Also notice that Grails also made a new directory, /views/controllerList/, in the views directory. All the views for the ControllerListController should go into this directory. The names of the views should correspond to the method names in the controller. In this case, we want a view file called, “index.gsp.” To make this view all we have to do is move “views/index.gsp” to “views/controllerList/index.gsp. You can do this by dragginng “views/index.gsp” to the “views/controllerList/” directory. After dragging, IntelliJ IDEA asks if you want to perform any checks and change references. In this case, you don’t, so unclick, “check for references”. Then click “OK.”

Rerun the app.

You should get a 500 navigating the browser to

http://localhost:8080/cs4760progassign

We should not be surprised, currently there is no root “index.gsp”. If you do not get a 500 response then Intellj IDEA has refactored the URLMappings.groovy file, and added the line

      "/"(view:"/controllerList/index")

Correct the mapping by editing it to be

      "/"(view:"/index")

Now you should get a 500.

In the browsers address bar, add “/controllerList” and you will get to the controller list view. Now the list of available controllers has one item in it:

cs4760progassign.ControllerListController

You can click on the link to go to the controller’s index view or action. In this case, clicking on the link will take you to this same view. You can notice that the URL has changed to

localhost:8080/cs4760progassign/controllerList/index

The part of the URL after the controller name is the controller action, in this case the “index” action. With an empty action all that the Grails app will do is display the index view, “index.gsp”.

3. Make Temporary Root Index View

We would like to eliminate the 500 error when the browser is pointed to the root of the app. We only need to make a new “index.gsp” file in the views root directory. Right click on the “views/” directory in the project panel, select “New” and then “GSP”. When the window pops up asking for the name of the file, enter “index”. After clicking the “OK” button, the editor window should show a template gsp file for “views\index.gsp.” Cut and paste the html code below into the editor window

<!DOCTYPE html>
<html>
<head></head>
<body>

 <h1>Base index.gsp </h1>
 <p>Under construction</p>
 <p><g:link controller="controllerList">Go to ControllerList</g:link></p>

</body>
</html>

The html code should look very familiar to you, if not study, html at w3schools

http://www.w3schools.com/html/default.asp

There is only one tag that should be unfamiliar to you, the g-tag, or more specifically the g:link tag. The g:link tag is a Grails tag that is similar to the html anchor tag. It will make portable links using controller names and actions. Whenever possible you should use g:link tags in your code rather then html anchor tags. This will make it safer to deploy your app. You can read more about the g:link tag at

http://docs.grails.org/latest/ref/Tags/link.html

You do not need to rerun the app when adding a new view. The development server will just try to serve the view again. This time it will find it. Now there is no longer a 500 error, and you can click the link that will take you to the controller list view.

4. Fix Unit Test

You should also notice notice that a new directory, test/, is made in the src/ directory. Navigate into src/test/groovy/cs4760progassign/ directory, and you should see the auto generated groovy file, ControllerListControllerSpec.groovy. This is a Specification file for specifying unit tests for the ControllerListController. Grails uses a Groovy testing platform, Spock:

http://spockframework.org/

The documentation is at

https://spockframework.org/spock/docs/2.3/index.html

You should read the primer

https://spockframework.org/spock/docs/2.2/spock_primer.html

https://docs.grails.org/latest/guide/testing.html

There are two ways to run the all the unit test. You can run all the tests for the app by running the grails command. In menu bar, click “Tools” and select “Grails” -> “Run Grails Command”. In the pop up window, type “test-app”, and click “OK”.

One way to run all the test is to make the command in IntelliJ IDEA. Click the field to the left of the green right run arrow or black box with green rerun arrow at the top. Select “Edit Configuration …”. In the window that pops up, click the plus, at the top and select “Grails”. In the right panel, name the test “test-app”, and in the “Command line” field enter “test-app”. Be sure to select the “Application”, “cs4760progassign”, in the top field just below the Grails tab. Click “OK”. The Grails Console should open in the bottom of the IDEA and show the running tasks. But this technique does not always run and when it does run the output is not very informative.

The better way to run all the unit test is to use gradle. Along the right boarder of intelliJ IDEA, click the “Gradle” tab. This will open a panel of all the Gradle task for this build, click the arrow for “cs4760progassign”, then click the arrow for “Tasks”, then the arrow for “verification”, finally click “test”. This will open the “Run” console at the bottom of the IDEA. You will see the task executes.

To run the tests, select “test-app” in the “Run/Debug” field then click the green arrow. A “test-app” console will open at the bottom which will show the program searching for tests. Eventually, you will see in the console displaying this message:

...
:test
cs4760progassign.ControllerListControllerSpec > test something FAILED
Condition not satisfied:

true == false
     |
     false

Condition not satisfied:

true == false
     |
     false

	at cs4760progassign.ControllerListControllerSpec.test something(ControllerListControllerSpec.groovy:16)

ControllerListControllerSpec > test something FAILED
    org.spockframework.runtime.SpockComparisonFailure at ControllerListControllerSpec.groovy:16
Java HotSpot(TM) Client VM warning: TieredCompilation is disabled in this release.

1 test completed, 1 failed
> Task :test FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':test'.
> There were failing tests. See the report at: file:///E:/Data-2012/Classes/cs4760/s23/ProgrammingAssignment/workspace-4/cs4760progassign/build/reports/tests/test/index.html
* Try:
...

So a test failed. Even this is not the easiest test report to read. There are several ways to get to the more attractive test report allured to in the message “1 test completed, 1 failed …”. One way is to navigate to build/reports/tests/test/index.html in the Project panel. Select it and right click. Select “run index.html” in the context menu.  The test report will appear in your web browser. At the same time, “index.html” is added to the “Run/Debug” commands, so the next time you run the tests you can access the report by clicking the “Run/Debug” field and select “index.html” and finally click the green arrow. You may want to rename this command by selecting “Edit Configuration…”. You will find the “index.html” command under “JavaScript Debug” category on the left. Change the “Name:” field to “test-app results”.

Inspect ControllerListControllerSpec.groovy

package cs4760progassign

import grails.testing.web.controllers.ControllerUnitTest
import spock.lang.Specification

class ControllerListControllerSpec extends Specification implements ControllerUnitTest<ControllerListController> {

    def setup() {
    }

    def cleanup() {
    }

    void "test something"() {
        expect:"fix me"
            true == false
    }
}

The test, called a “feature” in Spock, is a method named by the string “test something”:

true == false

This test Is certain to fail. Best practice in unit testing is not to test auto generate code because with 99% confidence we believe it to be correct, so we could remove the test or the feature. But a worthwhile feature/test might be to test that ControlerListController.index() returns status 200 OK. Then running the test assures us that action is still rendering. So replace the “test something” test with

void "test 200 response"() {
    when:
    controller.index()

    then:
    status == 200
}

Rerun the gradle task, by clicking the green arrow in the Run terminal, or selecting “cs4760progassign [test]” run command in tool bar, and clicking the green arrow . Now the console returns

...

BUILD SUCCESSFUL in 8s
7 actionable tasks: 3 executed, 4 up-to-date
1:12:23 PM: Execution finished 'test'.

This is not very informative, but we can guess that the test did not fail. The test report is still generated and you can view it by selecting the “index.html” in the “Run/Debug” and clicking the right green arrow. You can also see the results by selecting the “test-app results” in the “run/debug” field and click the right arrow.

Step 5: Make Domain Classes

You will implement a simple database scheme. You need two tables in the database, an Author table and a Book table:

Database Scheme:

Author:

  • String name
  • has many books (list of entries in the Book table)

Book:

  • String title
  • Integer publishYear
  • belongs to author (entry in the Author table)

Read the chapter about GROM in the Grails and GORM documentation to learn how to specify the Domain classes, Author and Book.

http://docs.grails.org/latest/guide/GORM.html

http://gorm.grails.org/latest/hibernate/manual/index.html

Specifically, you’ll want to read carefully the section about GROM association:

http://gorm.grails.org/latest/hibernate/manual/index.html#gormAssociation

You do not need to understand the entire GORM Manual to complete this assignment. The assignment can be completed without deeply understanding GORM.

You should notice that the Author class references the Book class and the Book class references the Author class. This is a cyclic reference and can confuse the auto compiler in the IDE, if you just write the code for the classes sequentially. If you type up the Author class before there is a Book class then your code will have an error at the line referencing the Book class.

Additional challenges is that many of the Grails commands have not been integrated into the current Grails plugin for IntelliJ IDEA. In particular, selecting “tools” from menu bar and then “Run Grails Command” will just silently fail. Fortunately, you can still access the grails command through the Grails wrapper provided by the project. Open a Bash terminal in project root directory. (Note that if you are using Windows, you cannot use terminal provided by the IntelliJ IDEA. The IntelliJ IDEA terminal is a Power Shell terminal. I use the git bash terminal.) In the Bash terminal type

./grailsw create-domain-class Author

Then enter

./grailsw create-domain-class Book

These commands will make 4 files. Two domain groovy files in the grails-app/domain/cs4760progassign/ directory. One for domain/cs4760progassign/Author.groovy:

package cs4760progassign

class Author {

    static constraints = {
    }
}

Another file is made for domain/cs4760progassign/Book.groovy:

package cs4760progassign

class Book {

    static constraints = {
    }
}

Also the commands create two test files in the src/test/groovy/cs4760progassign/ which we will discuss later.

Now you can add the properties to the Author and and Book domain classes to make the tables in the ORM.

Step 6: Inspect the Database

You will want to inspect the database to verify that it has really made the tables. You can inspect the database directly to see if the SQL actions are preformed as you expect.

Rerun the Grails app. You will need to rerun run-app whenever you make changes to the Domain objects. Point your browser to

http://localhost:8080/cs4760progassign/h2-console

This will bring up the database login. Note that in the login screen the JDBC URL should match what is specified in the “grails-app/conf/application.yml” configuration file for the development data soruce entry. So if you running in a “development” environment the JDBC URL should be

jdbc:h2:mem:devDb

Note the login in screen has a link for “JDBC URL”, click on it and more explanation for the entry.

The user name should be “sa” and password should be blank. Click “Connect” button and data source browser will appear. You should see the Author and Book tables listed in the left panel. Expanding them you’ll see the fields for each table. Clicking the Author table will enter the SQL command:

SELECT * FROM AUTHOR

You can click the “Run” button to execute the command. Do so, and you’ll see that the table is empty because you have not add entries to the table.

Step 7: Add Entries to the Tables.

1. Write Specification Class for AuthorBook

When we made the Author and Book domain classes, Grails auto generated the specification classes  AuthorSpec.groovy and BookSpec.groovy in the src/test/groovy/cs4760progassign/ folder. Inspect these specification files. If we run the tests they will fail. The Author and Book domain classes are very simple. If we made persistence test for Author and Book, we would just be testing the GORM code, which is not necessary.  If the domain classes had significant constraints specified in the constraint clause, the constraints would be worthy of unit testing. Grails has an excellent guide on unit testing constraints:

http://guides.grails.org/grails-test-domain-class-constraints/guide/index.html

We will save the AuthorSpec.groovy and BookSpec.groovy specification files just in case we add constraints worthy of testing. So comment out the feature tests in both AuthorSpec.groovy and BookSpec.groovy. Because creating Book entries belonging to an Author entry is non trivial, we could test the combined creation of Author and Book entries. This specification really only test our understanding of the syntax for creating Author and Book entries, but running the unit test is faster then running the entire app. Create a new Specification file in the src/test/groovy/cs4760progassign/ folder called AuthorBookSpec.groovy. Add the following code:

package cs4760progassign

import grails.testing.gorm.DataTest
import spock.lang.Specification

class AuthorBookSpec extends Specification implements DataTest {
    void setupSpec(){
        mockDomains Author, Book
    }

    void "test basic Author Book persistence"(){
        setup:
        new Author(name:"Stephen King")
                .addToBooks(new Book(title:"The Stand", publishYear:1978))
                .addToBooks(new Book(title:"The Shining", publishYear:1977))
                .save(flush: true, failOnError: true)

        new Author(name:"Mark Twain")
                .addToBooks(new Book(title:"Tom Sawyer", publishYear:1876))
                .addToBooks(new Book(title:"Huckelberry Finn", publishYear:1884))
                .save(flush: true, failOnError: true)


        expect:
        Author.count == 2
        Book.count == 4

        Author.findByName("Stephen King").books.size() == 2
        Author.findByName("Mark Twain").books.size() == 2
    }

}

Note that AuthorBookSpec implements DataTest rather then DomainUnitTest. This is because we need to use mockDomains for Author and Book to create our database entries. In the feature test “test basic Author Book persistence”, we use the “setup” block to create the entries. While creating the entries note that the Book objects must be created before the Author object. The save for the Author object also saves the Book objects to the database. The “expect” block specifies the feature tests. Note that in

        Author.findByName("Stephen King").books.size() == 2
        Author.findByName("Mark Twain").books.size() == 2

the parentheses after “size” is necessary because otherwise Groovy interrupts “size” as a property of the Book object rather than a method of the list for books.

Run the tests. You can run just this unit test by clicking the double green arrows just to the left of the code for the class in the AuthorBookSpec.groovy file. The test should be successful.

2. Bootstrapping the Database

You can use the Bootstrap.groovy file to add entries to the table when the web app is installed on the server. Open “grails-app/init/cs4760progassign/BootStrap.groovy” in the editor, by double clicking the file in the project panel. Add the code to the init closure, by replacing the file content with below.

package cs4760progassign

class BootStrap {

    def init = { servletContext ->
        new Author(name:"Stephen King")
                .addToBooks(new Book(title:"The Stand", publishYear:1978))
                .addToBooks(new Book(title:"The Shining", publishYear:1977))
                .save()

        new Author(name:"Mark Twain")
                .addToBooks(new Book(title:"Tom Sawyer", publishYear:1876))
                .addToBooks(new Book(title:"Huckelberry Finn", publishYear:1884))
                .save()

    }
    def destroy = {
    }
}

Now stop the app and run it again. Use dbconsole to inspect the tables. You should see the entries in both the Author and Book tables.

Step 8: Generate the Controller and Views

Grails will generate the basic controllers and all the views for the domain classes. To make the controllers and views for the Author domain class, right click the project or select “tools” in the menu bar then select “Grails” and then “RunGrails Command”.  In the pop up window, type the command below in the command text field.

generate-all cs4760progassign.Author

Make sure the correct project is specified in the “Application” text field. You do not need any “VM options.”

This will make several files for you:

  • grails-app/controller/cs4760progassin/AuthorController.groovy
  • grails-app/views/author/create.gsp
  • grails-app/views/author/edit.gsp
  • grails-app/views/author/edit.gsp
  • grails-app/views/author/index.gsp
  • grails-app/views/author/show.gsp
  • src/test/groovy/cs4760progasign/AuthorControllerSpec.groovy
  • services/cs4760progassign/AuthorService.groovy
  • src/integration-test/groovy/cs4760progassign/AuthorServiceSpec.groovy

You should inspect these files and learn from them. The generated controller uses Services to interact with the Domain object and make the model sent to the view. When interaction with domain objects run for extended time, consider making the Service transnational. See the documentation for Grails Services:

https://docs.grails.org/latest/guide/services.html

The file AuthorService.groovy is an interface for the service used by the AuthorController.

The file AuthorServiceSpec.groovy is an integration test specification for the implementation of AuthorService.

Generate the Book controller and all the views, and  inspect the generated files. Note you can also use the Bash terminal to generate the controller and views by entering

./grailsw generate-all cs4760progassign.Book

Step 9: Fix Integration Tests

The files in src/integration-test/groovy/cs4760progassign/ directory, AuthorServiceSpec.groovy and BookServiceSpec.groovy, are integration tests. Integration tests are used to test the communication between classes or components of the application. Because the roles of services are typically to interface between controllers and domain objects it is appropriate to use integration tests. Integration tests run the application before any of the tests are ran. All tests use the same application context. See the Grails documentation about integration testing:

https://docs.grails.org/latest/guide/testing.html#integrationTesting

There are several ways to run the integration test. In the Bash terminal you can run the Grails command

./grailsw test-app -integration

You can also run the integration test by running the gradle command by opening the gradle panel and double clicking the “integrationTest” task in “verification” category.

Run “integration test” now. It fails because of the integration tests specified in AuthorServiceSpec.groovy and BookServiceSpec.groovy. You can view the integration report by opening files:

  • build/test-results/integrationTest/TEST-cs4760prograssign.AuthorServiceSpec.xml
  • build/test-results/integrationTest/TEST-cs4760prograssign.BookServiceSpec.xml

The reports are in xml format, so they are not very readable. Nevertheless, you can determine the failing tests.

A more human readable report is available by opening build/reports/test/test/index.html in a browser.

The first cause of the failure in the BookServiceSpec is in the setupData()

private Long setupData() {
    // TODO: Populate valid domain instances and return a valid ID
    //new Book(...).save(flush: true, failOnError: true)
    //new Book(...).save(flush: true, failOnError: true)
    //Book book = new Book(...).save(flush: true, failOnError: true)
    //new Book(...).save(flush: true, failOnError: true)
    //new Book(...).save(flush: true, failOnError: true)
    assert false, "TODO: Provide a setupData() implementation for this generated test suite"
    //book.id
}

Because the entire application will be running at the time of tests, we do not use MockDomains to make the Book entries. Instead we create the Books as we would normally in the program. The @Rollback annotation will treat each test method as a transaction that should be rolled back at the end of the test. Below is what we should write for the setupData method:

private Long setupData() {
    // Need to remove all data from tables just in case hibernate is already running. 
    // https://stackoverflow.com/questions/8160296/grails-delete-all-data-from-table-domain-class-i-e-deleteall
    // Note even the deletes will be rolled backed.
    Book.executeUpdate('delete from Book')
    Author.executeUpdate('delete from Author')
 
    // Add to the tables
    Author testAuthor = new Author(name: "Test Author").save(flush: true, failOnError: true)
    Book book0 = new Book(title: "Test Book 0", publishYear: 1999, author: testAuthor).save(flush: true, failOnError: true)
    new Book(title: "Test Book 1", publishYear: 1999, author: testAuthor).save(flush: true, failOnError: true)
    new Book(title: "Test Book 2", publishYear: 1999, author: testAuthor).save(flush: true, failOnError: true)
    new Book(title: "Test Book 3", publishYear: 1999, author: testAuthor).save(flush: true, failOnError: true)
    new Book(title: "Test Book 4", publishYear: 1999, author: testAuthor).save(flush: true, failOnError: true)

    book0.id
}

The integration test will use the same application context that run-app uses. So the app should not be running at the time of the test. Even if the app is shut down by the “stop” command, hibernate is still running in the background. Consequently, we need to clear all the tables. The fastest way to do this is to use HQL directly. See

https://stackoverflow.com/questions/8160296/grails-delete-all-data-from-table-domain-class-i-e-deleteall

The Rollback will even rollback the deletes from the table, so in fact, the app could be running doing integration tests. As long as the app is inactive, its state will be restored.

Because the Book domain is defined as “belongsTo” to an Author domain object, we need to create an Author first and include it in the constructor call for the new Book. Note that the save methods includes flush and failOnError true. We need to flush to assure that the Book object is made immediately, and we want failOnError because this is a test and should fail on the save rather than later.

The “test get” feature test was written assuming that no other operations were performed on the database. But if you have added to the database, for example using Bootstrap and running the program, then the first id will not be 1. So we patch the “test get” feature test

void "test get"() {
    Long bookId = setupData()

    expect:
    bookService.get(bookId) != null
}

We also need to patch the “test list” feature test:

    void "test list"() {
        setupData()

        when:
        List<Book> bookList = bookService.list(max: 2, offset: 2)

        then:
        bookList.size() == 2
//        assert false, "TODO: Verify the correct instances are returned"
        bookList[0].title.contains("Test Book")
        bookList[1].title.contains("Test Book")
    }

Also, we need to patch the “test save” feature test:

    void "test save"() {
        when:
//        assert false, "TODO: Provide a valid instance to save"
        Author author = new Author(name: "Author").save(flush: true, failOnError: true)
        Book book = new Book(title: "Test Book", publishYear: 1990, author: author)
        bookService.save(book)

        then:
        book.id != null
    }

Run “integration test” command. You can run just the BookServiceSpec tests by double clicking the double green arrow pointing at the class declaration. The BookServiceSpec does not fail any test.

You are to patch AuthorServiceSpec.groovy on your own. HINT: an Author object does not need to have any Books to be created.

Step 10: Use the Web App

You can run the web app. Stop and start the app again. The controller list page should show the Author and Book controllers. Click on these controller links. Inspect the index views for the Author and Book controllers. You should navigate and play with the interface.

Use the app to create a new author and add a book to the author. View the Author and Book list to see that it is in database. Also go to dbconsole to inspect the tables directly.

Delete the author “Stephen King” and then view the book list. The books “The Stand” and “The Shining” should be gone from the list because of the static property “belognsTo” in the Book domain class.

Step 11: Change Cross Referencing List Displays

Using the web app, you should have noticed that when you view the list of authors that the list of books are referenced by cs4760progassign.Book and the id. Clicking a book reference will show the authors by the cs4760progassign.Author and the id. This is not an error, but it might not be what you want. You probably expect that the book reference ought to be the book title and the author reference should be the author’s name. We can change to this behavior by adding a toString method to the domain model.

In the Author domain class, add the lines below:

String toString(){
 "${name}"
}

In the Book domain class, add the lines

String toString(){
 "${title}"
}

You’ll need to stop and run the web app for the changes to the domain to take place. Now try the web app, you notice that Author is referenced by name and Book by the title. The code in the toString method tells the view how to render the view. The dollar sign, $, is a grails tag that alert the view that within the curly bracket is groovy code, in this case a property of the class.

In general, you should be careful making these changes. The problem with our change is that the name of the author may not be unique, meaning two authors may have the same name. So, this will cause ambiguity when associating a book with an author. In the next assignment, you’ll see that my general strategy is to use the generated views only for the administrated backend of the website and that we’ll make public views by hand coding. The admin should understand the references to book and author are table and id, and consequently the admin would not be confused.

Step 12: Make Screen Shots and Submit on Canvas

After creating a new author, adding a book and deleting the author “Stephen King”, make a screen shot of the book list. The URL is

http://localhost:8080/cs4760progassign/book/index

Also make a screen shot of AuthorServiceSpec.groovy that you have patched.

Submit the screen shots in canvas for the “Programming Assignment 1 – Building Your First Grails App”.