1. Overview

In this tutorial, we’ll learn how to create a simple web application using Grails.

Grails (more precisely it’s latest major version) is a framework built on top of the Spring Boot project and uses the Apache Groovy language to develop web apps.

It’s inspired by the Rails Framework for Ruby and is built around the convention-over-configuration philosophy which allows reducing boilerplate code.

2. Setup

First of all, let’s head over to the official page to prepare the environment. At the time of this tutorial, the latest version is 3.3.3.

Simply put, there are two ways of installing Grails: via SDKMAN or by downloading the distribution and adding binaries to the PATH environment variable.

We won’t cover the setup step by step because it is well documented in the Grails Docs.

3. Anatomy of a Grails App

In this section, we will get a better understanding of the Grails application structure. As we mentioned earlier, Grails prefers convention over configuration, therefore the location of files defines their purpose. Let’s see what we have in the grails-app directory:

  • assets – a place where we store static assets files like styles, javascript files or images
  • conf – contains project configuration files:
    • application.yml contains standard web app settings like data source, mime types, and other Grails or Spring related settings
    • resources.groovy contains spring bean definitions
    • logback.groovy contains logging configuration
  • controllers – responsible for handling requests and generating responses or delegating them to the views. By convention, when a file name ends with *Controller, the framework creates a default URL mapping for each action defined in the controller class
  • domain – contains the business model of the Grails application. Each class living here will be mapped to database tables by GORM
  • i18n – used for internationalization support
  • init – an entry point of the application
  • services – the business logic of the application will live here. By convention, Grails will create a Spring singleton bean for each service
  • taglib – the place for custom tag libraries
  • views – contains views and templates

4. A Simple Web Application

In this chapter, we will create a simple web app for managing Students. Let’s start by invoking the CLI command for creating an application skeleton:

grails create-app

When the basic structure of the project has been generated, let’s move on to implementing actual web app components.

4.1. Domain Layer

As we are implementing a web application for handling Students, let’s start with generating a domain class called Student:

grails create-domain-class com.baeldung.grails.Student

And finally, let’s add the firstName and lastName properties to it:

class Student {
    String firstName
    String lastName
}

Grails applies its conventions and will set up an object-relational mapping for all classes located in grails-app/domain directory.

Moreover, thanks to the GormEntity trait, all domain classes will have access to all CRUD operations, which we’ll use in the next section for implementing services.

4.2. Service Layer

Our application will handle the following use cases:

  • Viewing a list of students
  • Creating new students
  • Removing existing students

Let’s implement these use cases. We will start by generating a service class:

grails create-service com.baeldung.grails.Student

Let’s head over to the grails-app/services directory, find our newly created service in the appropriate package and add all necessary methods:

@Transactional
class StudentService {

    def get(id){
        Student.get(id)
    }

    def list() {
        Student.list()
    }

    def save(student){
        student.save()
    }

    def delete(id){
        Student.get(id).delete()
    }
}

Note that services don’t support transactions by default. We can enable this feature by adding the @Transactional annotation to the class.

4.3. Controller Layer

In order to make the business logic available to the UI, let’s create a StudentController by invoking the following command:

grails create-controller com.baeldung.grails.Student

By default, Grails injects beans by names. It means that we can easily inject the StudentService singleton instance into our controller by declaring an instance variable called studentsService.

We can now define actions for reading, creating and deleting students.

class StudentController {

    def studentService

    def index() {
        respond studentService.list()
    }

    def show(Long id) {
        respond studentService.get(id)
    }

    def create() {
        respond new Student(params)
    }

    def save(Student student) {
        studentService.save(student)
        redirect action:"index", method:"GET"
    }

    def delete(Long id) {
        studentService.delete(id)
        redirect action:"index", method:"GET"
    }
}

By convention, the index() action from this controller will be mapped to the URI /student/index, the show() action to /student/show and so on.

4.4. View Layer

Having set up our controller actions, we can now proceed to create the UI views. We will create three Groovy Server Pages for listing, creating and removing Students.

By convention, Grails will render a view based on controller name and action. For example, the index() action from StudentController will resolve to /grails-app/views/student/index.gsp

Let’s start with implementing the view */grails-app/*views/student/index.gsp, which will display a list of students. We’ll use the tag <f:table/> to create an HTML table displaying all students returned from the index() action in our controller.

By convention, when we respond with a list of objects, Grails will add the “List” suffix to the model name so that we can access the list of student objects with the variable studentList:

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main" />
    </head>
    <body>
        <div class="nav" role="navigation">
            <ul>
                <li><g:link class="create" action="create">Create</g:link></li>
            </ul>
        </div>
        <div id="list-student" class="content scaffold-list" role="main">
            <f:table collection="${studentList}" 
                properties="['firstName', 'lastName']" />
        </div>
    </body>
</html>

We’ll now proceed to the view */grails-app/**views/*student/create.gsp, which allows the user to create new Students. We’ll use the built-in <f:all/> tag, which displays a form for all properties of a given bean:

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main" />
    </head>
    <body>
        <div id="create-student" class="content scaffold-create" role="main">
            <g:form resource="${this.student}" method="POST">
                <fieldset class="form">
                    <f:all bean="student"/>
                </fieldset>
                <fieldset class="buttons">
                    <g:submitButton name="create" class="save" value="Create" />
                </fieldset>
            </g:form>
        </div>
    </body>
</html>

Finally, let’s create the view */grails-app/**views/*student/show.gsp for viewing and eventually deleting students.

Among other tags, we’ll take advantage of <f:display/>, which takes a bean as an argument and displays all its fields:

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main" />
    </head>
    <body>
        <div class="nav" role="navigation">
            <ul>
                <li><g:link class="list" action="index">Students list</g:link></li>
            </ul>
        </div>
        <div id="show-student" class="content scaffold-show" role="main">
            <f:display bean="student" />
            <g:form resource="${this.student}" method="DELETE">
                <fieldset class="buttons">
                    <input class="delete" type="submit" value="delete" />
                </fieldset>
            </g:form>
        </div>
    </body>
</html>

4.5. Unit Tests

Grails mainly takes advantage of Spock for testing purposes. If you are not familiar with Spock, we highly recommend reading this tutorial first.

Let’s start with unit testing the index() action of our StudentController.

We’ll mock the list() method from StudentService and test if index() returns the expected model:

void "Test the index action returns the correct model"() {
    given:
    controller.studentService = Mock(StudentService) {
        list() >> [new Student(firstName: 'John',lastName: 'Doe')]
    }
 
    when:"The index action is executed"
    controller.index()

    then:"The model is correct"
    model.studentList.size() == 1
    model.studentList[0].firstName == 'John'
    model.studentList[0].lastName == 'Doe'
}

Now, let’s test the delete() action. We’ll verify if delete() was invoked from StudentService and verify redirection to the index page:

void "Test the delete action with an instance"() {
    given:
    controller.studentService = Mock(StudentService) {
      1 * delete(2)
    }

    when:"The domain instance is passed to the delete action"
    request.contentType = FORM_CONTENT_TYPE
    request.method = 'DELETE'
    controller.delete(2)

    then:"The user is redirected to index"
    response.redirectedUrl == '/student/index'
}

4.6. Integration Tests

Next, let’s have a look at how to create integration tests for the service layer. Mainly we’ll test integration with a database configured in grails-app/conf/application.yml.

By default, Grails uses the in-memory H2 database for this purpose.

First of all, let’s start with defining a helper method for creating data to populate the database:

private Long setupData() {
    new Student(firstName: 'John',lastName: 'Doe')
      .save(flush: true, failOnError: true)
    new Student(firstName: 'Max',lastName: 'Foo')
      .save(flush: true, failOnError: true)
    Student student = new Student(firstName: 'Alex',lastName: 'Bar')
      .save(flush: true, failOnError: true)
    student.id
}

Thanks to the @Rollback annotation on our integration test class, each method will run in a separate transaction, which will be rolled back at the end of the test.

Take a look at how we implemented the integration test for our list() method:

void "test list"() {
    setupData()

    when:
    List<Student> studentList = studentService.list()

    then:
    studentList.size() == 3
    studentList[0].lastName == 'Doe'
    studentList[1].lastName == 'Foo'
    studentList[2].lastName == 'Bar'
}

Also, let’s test the delete() method and validate if the total count of students is decremented by one:

void "test delete"() {
    Long id = setupData()

    expect:
    studentService.list().size() == 3

    when:
    studentService.delete(id)
    sessionFactory.currentSession.flush()

    then:
    studentService.list().size() == 2
}

5. Running and Deploying

Running and deploying apps can be done by invoking single command via Grails CLI.

For running the app use:

grails run-app

By default, Grails will setup Tomcat on port 8080.

Let’s navigate to http://localhost:8080/student/index to see what our web application looks like:

list

If you want to deploy your application to a servlet container, use:

grails war

to create a ready-to-deploy war artifact.

6. Conclusion

In this article, we focused on how to create a Grails web application using the convention-over-configuration philosophy. We also saw how to perform unit and integration tests with the Spock framework.

As always, all the code used here can be found over on GitHub.


« 上一篇: Java Weekly, 第221期