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:
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.