Boost your productivity.

Grails yourself!

Manuel Ángel Quindimil

@quindimildev

http://goo.gl/KpIYUo

Who is Manuel Quindimil?

  • Senior web developer at Salenda
  • Scrum Master Certified
  • Has worked with Grails since 2011

http://goo.gl/KpIYUo

What is Grails?

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

http://goo.gl/KpIYUo

Resolving the web development puzzle

  • Object Relational Mapping (ORM) layer built on Hibernate
  • Groovy Server Pages (GSP)
  • A controller layer built on Spring MVC
  • An interactive command line environment and build system based on Gradle

http://goo.gl/KpIYUo

Resolving the web development puzzle

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

http://goo.gl/KpIYUo


Start!

Creating a new project

Creating a new project

$ grails create-app my-app

project name

http://goo.gl/KpIYUo

Running our new project

$ grails run-app

or using the gradle wrapper

$ ./gradlew bootRun

http://goo.gl/KpIYUo

GORM

Grails Object Relational Model

 

What is GORM?

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

http://goo.gl/KpIYUo

Creating a domain

Domain class could be also created directly into the domain folder

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

class Book {

    String title
    String isbn
    String description

}

http://goo.gl/KpIYUo

Creating a domain

Domain class could be also created directly into the domain folder

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

class Author {

    String name
    String email

}

http://goo.gl/KpIYUo

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
    }

}

http://goo.gl/KpIYUo

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"
    }
}

http://goo.gl/KpIYUo

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
    }

}

http://goo.gl/KpIYUo

Database connection

Setting a MySQL connection

Include the JODB driver

dependencies {
        // ...

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

        // ...

    }

build.gradle

http://goo.gl/KpIYUo

Setting a MySQL connection

Setting DB configuration

application.yml

#..

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

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

#..

http://goo.gl/KpIYUo

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 ->
                author.addToBooks(title: "Book${authorIndex}-${bookIndex}", 
                    description: "description ${authorIndex}-${bookIndex}")
            }
            author.save()
        }

    }

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

BootStrap.groovy

http://goo.gl/KpIYUo

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

http://goo.gl/KpIYUo

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

http://goo.gl/KpIYUo

Plugins

Plugins

We can add functionality installing plugins or create our own

http://goo.gl/KpIYUo

Securing our app

Installing Spring Security Core plugin

http://goo.gl/KpIYUo

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.jbcnconf User Role

http://goo.gl/KpIYUo

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'))
    }

//..

http://goo.gl/KpIYUo

Securing controllers/actions

AuthorController.groovy

package es.jbcnconf

import grails.plugin.springsecurity.annotation.Secured

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

    //..

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

    //..
}

http://goo.gl/KpIYUo

Custom config

AuthorController.groovy

//..

grails.plugin.springsecurity.logout.postOnly = false

//..

http://goo.gl/KpIYUo

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

Rest API

Grails profiles

http://goo.gl/8s89PA

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

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

Grails profiles

http://goo.gl/8s89PA

  • Some of them are:
    • ​Angular profile (1.x) - 2.x soon
    • Rest-api

How to use:

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

JSON Views

http://goo.gl/8s89PA

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.grails-web"
apply plugin: "org.grails.plugins.views-json"

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

build.gradle

JSON Views

http://goo.gl/8s89PA

package es.jbcnconf

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.jbcnconf.AuthorService

JSON Views

http://goo.gl/8s89PA

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

JSON Views

http://goo.gl/8s89PA

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

search.gson

Creating JSON Views

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

_author.gson

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

http://goo.gl/KpIYUo

Deploying

$ java -jar my-app-0.1.war

Runnable WAR

http://goo.gl/KpIYUo

Thanks!

Made with Slides.com