Boost your productivity.

Grails yourself!

Manuel Ángel Quindimil

@quindimildev

https://goo.gl/gzOHZb

Who I am?

  • Senior web developer at Salenda
  • Working with Grails since 2011
  • Scrum Master Certified
  • Discovering Angular 2

https://goo.gl/gzOHZb

What is Grails?

  • Powerful web framework
  • Java platform
  • Based on Convention over Configuration
  • Integrates with JVM
    • Immediately productive
    • Integrated ORM

https://goo.gl/gzOHZb

Resolving the web development puzzle

https://goo.gl/gzOHZb

Resolving the web development puzzle

  • Embedded Tomcat container with fly reloading
  • Dependency injection. Spring container
  • Internationalization (i18n) built on Spring's core MessageSource concept

https://goo.gl/gzOHZb


Start!

Creating a new project

Creating a new project

$ grails create-app my-app

project name

https://goo.gl/gzOHZb

Running our new project

$ grails run-app

or using the gradle wrapper

$ ./gradlew bootRun

https://goo.gl/gzOHZb

GORM

Grails Object Relational Mapping

 

What is GORM?

  • Data access toolkit
  • Allow access for relational and non-relational data such as:
    • SQL
    • MondoDB
    • Cassandra
    • Neo4j
    • Redis

https://goo.gl/gzOHZb

Creating a domain

Domain class could be also created directly into the domain folder

$ grails create-domain-class es.example.Book
package es.example

class Book {

    String title
    String isbn
    String description

}

https://goo.gl/gzOHZb

Creating a domain

Domain class could be also created directly into the domain folder

$ grails create-domain-class es.example.Author
package es.example

class Author {

    String name
    String email

}

https://goo.gl/gzOHZb

Constraints

Defining validation rules, schema generation

class Book {

    String title
    String isbn
    String description

    static constraints = {
        isbn nullable: true
    }
}
class Author {

    String name
    String email

    static constraints = {
        email email: true, nullable: true
    }

}

https://goo.gl/gzOHZb

Database mapping

Defining the way a domain is mapped to the DB

class Book {

    String title
    String isbn
    String description

    static constraints = {
        isbn nullable: true
    }

    static mapping = {
        table "books"
        description type: "text"
    }
}

https://goo.gl/gzOHZb

One-to-many association

Creating a 1:n relation between two classes

class Book {

    String title
    String isbn
    String description

    static belongsTo = [author: Author]

    static constraints = {
        isbn nullable: true
    }

    static mapping = {
        table "books"
        description type: "text"
    }
}
class Author {

    String name
    String email

    static hasMany = [books: Book]

    static constraints = {
        email email: true, nullable: true
    }

}

https://goo.gl/gzOHZb

Database connection

Setting a MySQL connection

Include the JDBC driver

dependencies {
        // ...

        runtime "mysql:mysql-connector-java:5.1.36"

        // ...

    }

build.gradle

https://goo.gl/gzOHZb

Setting a MySQL connection

Setting DB configuration

application.yml

#..

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: com.mysql.jdbc.Driver
    username: example
    password: example

environments:
    development:
        dataSource:
            dbCreate: create-drop
            url: jdbc:mysql://localhost:3306/example

#..

https://goo.gl/gzOHZb

Programmatic start-up configuration

Bootstrap class

Programmatic start-up configuration can be added

class BootStrap {

    //to do when app is launched
    def init = { servletContext ->

        50.times{ authorIndex ->
            Author author = new Author(name: "Author ${authorIndex}", 
                    email: "email${authorIndex}@salenda.es")
            3.times{ bookIndex ->
                Book book = new Book(title: "Book${authorIndex}-${bookIndex}",
                        description: "description ${authorIndex}-${bookIndex}")
                author.addToBooks(book)
            }
            author.save()
        }

    }

    //to de when app is destroyed
    def destroy = {
    }
}

BootStrap.groovy

https://goo.gl/gzOHZb

Scaffolding

Scaffolding

Generates some basic CRUD interfaces for a domain class

$ grails generate-all "*"
  • Generates:
    • ​The necessary views (GSPs)
    • Controller actions for:
      • Create, Edit, Show & Delete an instance

https://goo.gl/gzOHZb

Scaffolding

If we do not want to use it....

dependencies {
        // ...

        //remove this
        compile "org.grails.plugins:scaffolding"

        // ...

    }

build.gradle

Do not remove it if you have used cause includes some required dependencies

https://goo.gl/gzOHZb

Plugins

Plugins

We can add functionality installing plugins or create our own

https://goo.gl/gzOHZb

Securing our app

Installing Spring Security Core plugin

https://goo.gl/gzOHZb

Installing Spring Security Core pluging

dependencies {

        // ...

        compile 'org.grails.plugins:spring-security-core:3.1.1'

        // ...

}

build.gradle

Setting the user and role classes

$ grails s2-quickstart es.example User Role

https://goo.gl/gzOHZb

Creating a user with a certaing role

BootStrap.groovy

//..

    if(!Role.count){
        ['ROLE_ADMIN', 'ROLE_USER'].each{ roleName ->
            new Role(authority: roleName).save(flush:true)
        }
    }

    if(!User.count){
        User admin = new User(username: 'admin', password: 'admin')
        admin.save(flush: true, failOnError: true)
        UserRole.create(admin, Role.findByAuthority('ROLE_ADMIN'))

        User user = new User(username: 'user', password: 'user')
        user.save(flush: true, failOnError: true)
        UserRole.create(user, Role.findByAuthority('ROLE_USER'))
    }

//..

https://goo.gl/gzOHZb

Securing controllers/actions

AuthorController.groovy

package es.example

import grails.plugin.springsecurity.annotation.Secured

@Secured(['ROLE_ADMIN', 'ROLE_USER'])
class AuthorController {

    //..

    @Secured('ROLE_ADMIN')
    def delete() { }

    //..
}

https://goo.gl/gzOHZb

Custom config

AuthorController.groovy

//..

grails.plugin.springsecurity.logout.postOnly = false

//..

Logout is only allow by default but can be set

//..
//We haven't created this 'email' property yet!
grails.plugin.springsecurity.userLookup.usernamePropertyName = 'email'

//..

Changing login by username for other user property instead

application.groovy

application.groovy

https://goo.gl/gzOHZb

Rest API

Grails profiles

Running "create-app" the web profile is used by default

A profile encapsulates an application structure, set of commands, plugins and capabilities

https://goo.gl/gzOHZb

Grails profiles

  • Some of them are:
    • ​Angular profile
    • Rest-api

How to use:

$ grails create-app my-rest-app --profile=rest-api

https://goo.gl/gzOHZb

JSON Views

Comes by default with the rest-api profile

but we can use them on our app with web profile

buildscript {
    //..
    dependencies {
        //..
        classpath "org.grails.plugins:views-gradle:1.0.12"
    }
}

//Insert after Grails core gradle plugins
apply plugin: "org.grails.plugins.views-json"

//..
dependencies {
    //..
    compile 'org.grails.plugins:views-json'
    //..
}

build.gradle

https://goo.gl/gzOHZb

JSON Views

package es.example

import grails.transaction.Transactional

@Transactional
class AuthorService {

    def search(String name, String email) {
        // Dynamic finders are the easiest way for retrieving data from DB
        // Author.findAllByNameLike("%${name}%")
        // Criterias have better performance, and do more complex queries
        Author.withCriteria{
            like("name", "%${name}%")
            if(email){
                like("email", "%${email}%")
            }
        }
    }
}

AuthorService.groovy

Creating a service for searching Authors

$ grails create-service es.example.AuthorService

https://goo.gl/gzOHZb

JSON Views

Injecting the service bean in the controller

//@Secured(['ROLE_ADMIN', 'ROLE_USER'])
@Secured('permitAll')
class AuthorController {

    AuthorService authorService

    //..

    def search(){
        [authors: authorService.search(params.name, params.email)]
    }

AuthorController.groovy

Allowing free access to the controller

Invokes the service method

The request parameters map

Creating an action

https://goo.gl/gzOHZb

JSON Views

import es.example.Author
model{
    Iterable<Author> authors
}
json tmpl.author(authors)

search.gson

Creating JSON Views with a template

import es.example.Author
model{
    Author author
}
json{
    name author.name
    numBooks author.books.size()
}

_author.gson

https://goo.gl/gzOHZb

Deploying the app

WAR building

..

version "0.1"

..

build.gradle

Specifying the app version

Building a runnable war

$ grails dev package
$ ./gradlew assemble -Dgrails.env=dev

or using the gradle wrapper

https://goo.gl/gzOHZb

Deploying

$ java -jar my-app-0.1.war

Runnable WAR

https://goo.gl/gzOHZb

We are hiring!

hello@salenda.es

Thanks!