GRAILS LAUNCHPAD
from Ground Zero to Orbit
ABOUT ME
• Zachary Klein
• Based in St Louis, MO
• GrailsTeam at OCI
• Developing with Grails since 2010
• Grails Profile, plugin, documentation
author
guides.grails.org
start.grails.org
start.grails.org
Create An Application Using The Latest Release
curl -O start.grails.org/myProject.zip
Create A Plugin Using The Latest Release
curl -O start.grails.org/myPlugin.zip -d type=plugin
Specify A Grails Version
curl -O start.grails.org/myProject.zip -d version=3.2.5
Specify A Grails Profile
curl -O start.grails.org/restPproject.zip -d profile=rest-api
HISTORY
• Created in 2005
• Grails 3, based on Spring Boot, released in 2015
• Sponsored by OCI since 2015
• Grails 3.3 released in 2017
WHAT IS GRAILS?
• Java web application framework
• Based on Spring Boot
• Follows Model-View-Controller
pattern
• Leverages proven technology
• Convention-over-configuration
• Modular plugin-based architecture
• Prioritizes developer productivity
Model
ControllerView
User
uses
manipulatesupdates
sees
• Extensive plugin library
• Application Profiles
• JSON views
• Native multi-tenancy
• Angular & React support
WHAT IS GRAILS?
Unique Features
• JVM Platform
• Spring & Spring Boot
• Hibernate
• Quartz
• Gradle
Proven Technology
FOUNDATIONS
JavaVirtual Machine
Java JDK
Groovy
JEE GORMBoot
SPRING BOOT
• Rapid development for Spring-based applications
• Convention-over-configuration
• Embedded container
• First-class metrics & monitoring
• Custom “starters” for popular libraries & frameworks
SPRING BOOT ACTUATOR
SPRING BOOT ACTUATOR
DEMO
http://mrhaki.blogspot.com/2015/04/grails-goodness-adding-health-check.html
https://objectpartners.com/2016/08/30/monitoring-grails-applications-the-easy-way/
GRAILS & SPRING BOOT
Grails 3 for Spring Developers Screencast
GROOVY
• Java-based, optionally-typed programming language
• Can be dynamic or statically-compiled
• Emphasizes developer-productivity
• First-class DSL (domain-specific language) support
• Supports multiple programming styles - OO, functional,
runtime & compile-time meta-programming
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World");
}
}
Java
println “Hello, World"
Groovy
Java POJO
public class Person {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String setLastName(String value) {
this.lastName = value;
}
public void setFirstName(String value) {
this.firstName = value;
}
}
Groovy POGO
class Person {
String firstName
String lastName
}
Groovy Closures
Integer item = 0
String name = "My name"
{ item++ }
{ println it }
{ name -> println name }
{ String x, int y ->
println "hey ${x} the value is ${y}"
}
{ reader ->
def line = reader.readLine()
line.trim()
}
Groovy Web Console
GORM
• Groovy-based data access & persistence framework
• Originally an ORM framework based on Hibernate
• Supports MongoDB, Neo4J, Cassandra
• Supports RxJava for Reactive applications
• Supports multiple datasources
DATASOURCE CONFIG
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
environments:
development:
dataSource:
dbCreate: create-drop
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
test:
dataSource:
dbCreate: update
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
production:
dataSource:
dbCreate: update
url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
properties:
jmxEnabled: true
initialSize: 5
grails-app/conf/application.yml
DATASOURCE CONFIG
dataSource:
pooled: true
jmxExport: true
driverClassName: com.mysql.jdbc.Driver
dialect: org.hibernate.dialect.MySQL5InnoDBDialect
username: sa
password: testing
environments:
development:
dataSource:
dbCreate: update
url: jdbc:mysql://127.0.0.1:3306/myapp
grails-app/conf/application.yml
MULTIPLE DATASOURCES
dataSource:
pooled: true
jmxExport: true
driverClassName: org.h2.Driver
username: sa
password:
dataSources:
lookup:
dialect: org.hibernate.dialect.MySQLInnoDBDialect
driverClassName: com.mysql.jdbc.Driver
username: lookup
password: secret
url: jdbc:mysql://localhost/lookup
dbCreate: update
grails-app/conf/application.yml
DOMAIN CLASSES
package org.grails.guides
class Vehicle {
String name
String make
String model
static constraints = {
name maxSize: 255
make inList: ['Ford', 'Chevrolet', 'Nissan']
model nullable: true
}
}
grails-app/domain/demo/Vehicle.groovy
CONSTRAINTS
Constraint Description Example
blank Validates that a String value is not blank login(blank:false)
creditCard Validates that a String value is a valid credit card number cardNumber(creditCard: true)
email Validates that a String value is a valid email address. homeEmail(email: true)
inList Validates that a value is within a range or collection of constrained values. name(inList: ["Joe"])
matches Validates that a String value matches a given regular expression. login(matches: "[a-zA-Z]+")
max Validates that a value does not exceed the given maximum value. age(max: new Date()) price(max:
999F)
maxSize Validates that a value’s size does not exceed the given maximum value. children(maxSize: 25)
min Validates that a value does not fall below the given minimum value. age(min: new Date()) price(min:
0F)
minSize Validates that a value’s size does not fall below the given minimum value. children(minSize: 25)
notEqual Validates that that a property is not equal to the specified value login(notEqual: "Bob")
nullable Allows a property to be set to null - defaults to false. age(nullable: true)
range Uses a Groovy range to ensure that a property’s value occurs within a specified range age(range: 18..65)
scale Set to the desired scale for floating point numbers (i.e. the number of digits to the right of the salary(scale: 2)
http://gorm.grails.org/latest/hibernate/manual/index.html#constraints
CUSTOM CONSTRAINTS
class Response {
Survey survey
Answer answer
static constraints = {
survey blank: false
answer blank: false, validator: { val, Response obj ->
val in obj.survey.answers }
}
}
ASSOCIATIONS
package org.grails.guides
class Model {
String name
static belongsTo = [ make: Make ]
static constraints = {
}
String toString() {
name
}
}
ASSOCIATIONS
package org.grails.guides
class Make {
String name
static hasMany = [ cars: Car ]
static constraints = {
}
}
QUERYING
def books = Book.list()
def books = Book.list(offset:10, max:20)
def books = Book.list(sort:"title", order:"asc")
def book = Book.get(23)
def book = Book.findByTitle(“Grails In Action")
book = Book.findByTitleLike(“Groovy%")
book = Book.findByReleaseDateBetween(firstDate, secondDate)
book = Book.findByReleaseDateGreaterThan(someDate)
book = Book.findByTitleLikeOrReleaseDateLessThan("%Something%", someDate)
QUERYING
def query = Person.where {
firstName == "Bart"
}
Person bart = query.find()
//Note the query is “lazy” - it’s not instantiated until we
actually call find(), list(), get()
def results = Person.findAll {
lastName == "Jeff"
}
def results = Person.findAll(sort:"firstName") {
lastName == "Brown"
}
Person p = Person.find { firstName == "Bart" }
def query = Person.where {
(lastName != "Simpson" && firstName != "Fred") ||
(firstName == "Bart" && age > 9)
}
def results = query.list(sort:"firstName")
http://gorm.grails.org/latest/hibernate/manual/index.html#querying
PERSISTENCE & UPDATES
//Create
def newPerson = new Person(name: “Person name”)
newPerson.save()
//Update
def p = Person.get(1)
p.name = “A New Name”
p.save()
//If you want to persist the change immediately:
p.save(flush: true)
//Delete
p.delete() //or delete(flush:true)
DATA SERVICES
• Interface-Based approach to persistence and querying logic
• Introduced in GORM 6.1
• You define interfaces with methods that follow a naming
convention
• GORM will implement those methods
• You can optionally override the implementation
• Statically-compiled, type-checked, transaction-managed
DATA SERVICES
@Service(Book)
interface BookService {
Book getBook(Serializable id)
}
class MyController {
@Autowired BookService bookService
def index() {
render bookService.getBook(1)
}
}
grails-app/services/demo/BookService.groovy
src/main/groovy/demo/BookService.groovy
src/main/groovy/demo/BookService.groovy
http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
DATA SERVICES
@Service(Book)
interface BookService {
List<Book> findBooks(String title, Map args) //args can supply pagination
List<Book> findByTitleAndPublishDateGreaterThan(String title, Date publishDate)
@Where({ title ==~ pattern && releaseDate > fromDate })
Book searchBooks(String pattern, Date fromDate)
@Query("from $Book as book where book.title like $pattern")
Book searchByTitle(String pattern)
}
grails-app/services/demo/BookService.groovy
src/main/groovy/demo/BookService.groovy
http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
DATA SERVICES
@Service(Book)
interface BookService {
Book saveBook(String title)
Book saveBook(Book newBook)
Book updateBook(Serializable id, String title)
@Query(“update ${Book book} set ${book.title} = $newTitle
where book.title = $oldTitle")
Number updateTitle(String newTitle, String oldTitle)
Number deleteAll(String title)
void delete(Serializable id)
}
grails-app/services/demo/BookService.groovy
src/main/groovy/demo/BookService.groovy
http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
Model
ControllerView
User
uses
manipulatesupdates
sees
CONTROLLERS
package demo
class MyController {
def index() {
render “Hello world”
}
}
grails-app/controllers/demo/MyController.groovy
http://localhost:8080/my
Hello world
CONTROLLERS
package demo
class MyController {
def index() {
[message: “Hello world”]
}
}
grails-app/controllers/demo/MyController.groovy
VIEWS
<html>
<html>
<head>
<title>Home Page</title>
</head>
<body>
<div>
${message}
</div>
</body>
</html>
grails-app/views/my/index.gsp
Model
ControllerView
User
uses
manipulatesupdates
sees
start.grails.org
SDKMAN.IO
BUILDYOUR
FIRST GRAILS
APP
http://guides.grails.org/
creating-your-first-grails-app
INTERCEPTORS
• Interceptors allow you to add logic before/after a targeted
request
• Allow blocking or redirecting of a request
• By default, interceptors will target requests made a controller
of the same name
• You can implement custom request “matching” in the
constructor
https://objectcomputing.com/resources/publications/sett/september-2015-grails-3-interceptors/
Model
ControllerView
User
sees
manipulatesupdates
Interceptors
Interceptors
package com.example
class BookInterceptor {
 
boolean before() { true }
 
boolean after() { true }
 
void afterView() { }
}
grails-app/controller/com/example/BookInterceptor.groovy
class CustomInterceptor {
 
public CustomInterceptor() {
// match all requests to the reporting controller...
match controller: 'reporting'
 
//match requests to create action, person controller
match controller: 'person', action: 'create'
 
//match all requests to accounting or payroll controller
match controller: ~/(accounting|payroll)/
}
boolean before() { true }
 
boolean after() { true }
 
void afterView() { /.../ }
}
class CustomInterceptor {
 
int order = HIGHEST_PRECEDENCE + 100 //or LOWEST_PRECEDENCE
boolean before() { true }
 
boolean after() { true }
 
void afterView() { /.../ }
}
INTERCEPTORS
Grails Quickcast #1 - Grails Interceptors
JSONVIEWS
http://views.grails.org/latest/
• JSON views make it easy to express the desired format of
your controller endpoints
• Markup views are also available for expressing XML
responses
• JSON views are statically compiled, type-checked, and reload
instantly
JSONVIEWS
json.message {
hello "world"
}
{"message":{ "hello":"world"}}
grails-app/views/hello.gson
Response:
JSONVIEWS
model {
Person person
}
json {
name person.name
age person.age
}
{“name": “John Doe”, “age”: 34}
grails-app/views/person/_person.gson
Response:
buildscript {
...
dependencies {
...
classpath "org.grails.plugins:views-gradle:1.2.6"
}
}
...
apply plugin: "org.grails.grails-web"
apply plugin: "org.grails.plugins.views-json"
dependencies {
...
compile "org.grails.plugins:views-json:1.2.6"
In your build.gradle file:
model {
Person person
}
json tmpl.person(person)
grails-app/views/person/show.gson
model {
Person person
}
json {
name person.name
age person.age
}
grails-app/views/person/_person.gson
model {
List<Person> people = []
}
json tmpl.person(“person", people)
grails-app/views/person/index.gson
model {
Person person
}
json {
name person.name
age person.age
}
grails-app/views/person/_person.gson
HAL+JSON
http://www.cnblogs.com/coderland/p/5902997.html
model {
Book book
}
json {
hal.links(book)
hal.embedded {
author( book.authors.first() ) { Author author ->
name author.name
}
}
title book.title
}
grails-app/views/book/_book.json
HAL+JSON
{
"_links": {
"self": {
"href": "http://localhost:8080/book/show/1",
"hreflang": "en",
"type": "application/hal+json"
}
},
"_embedded": {
"author": {
"_links": {
"self": {
"href": "http://localhost:8080/author/show/1",
"hreflang": "en",
"type": "application/hal+json"
}
},
"name": "Stephen King"
}
},
"title": "The Stand"
}
HAL+JSON
http://guides.grails.org/using-
hal-with-json-views/guide
JSONVIEWS
Grails Quickcast #2 - JSONViews
SINGLE PAGE APPS
SINGLE PAGE APPS
•GORM
•Convention/Config
•Spring Boot
•Profiles
•Plugins
•Gradle
•URL mappings
•JSON views
•Hypermedia
•GORM for GraphQL
•WebSockets
GRAILS PROFILES
• Include default config, plugins, project structure
• Custom commands/scripts for code-generation
• Optional “features” allow further control of the
generated project
• Profile inheritance
• Packaged/resolved as Maven dependencies
SPA GRAILS PROFILES
angularjs Angular 1.x https://grails-profiles.github.io/angularjs/latest/
guide/index.html
angular Angular 2+ https://grails-profiles.github.io/angular/latest/guide/
index.html
react React 16.x https://grails-profiles.github.io/react/latest/guide/
index.html
react-webpack React 16.x, using
Webpack
https://grails-profiles.github.io/react-webpack/
latest/guide/index.html
webpack `Webpack 2.x, no
JavaScript framework
https://grails-profiles.github.io/webpack/latest/
guide/index.html
ANGULAR SCAFFOLDING
https://grails-plugins.github.io/grails-angular-scaffolding/latest/
• Generate Angular controllers, services and
views based on your domain model
• Full CRUD functionality
• Installed automatically in the Angular Profile
buildscript {
...
dependencies {
...
classpath "org.grails.plugins:angular-scaffolding:2.0.0.RC2"
}
}
dependencies {
...
compile "org.grails.plugins:angular-scaffolding:2.0.0.RC2"
In your build.gradle file:
package demo
import grails.rest.Resource
@Resource(uri='/book')
class Book {
String title
Integer pages
Date publishDate
String author
static constraints = {
}
}
grails ng-generate-all demo.Book
Rendered template service.ts to destination /client/src/app/book/book.service.ts
Rendered template domain.ts to destination /client/src/app/book/book.ts
Rendered template module.ts to destination /client/src/app/book/book.module.ts
Rendered template routing.module.ts to destination /client/src/app/book/book-routing.module.ts
Rendered template persist.component.ts to destination /client/src/app/book/book-persist.component.ts
Rendered template persist.component.html to destination/client/src/app/book/book-persist.component.html
Rendered template list.component.ts to destination /client/src/app/book/book-list.component.ts
Rendered template list.component.html to destination /client/src/app/book/book-list.component.html
Rendered template show.component.ts to destination /client/src/app/book/book-show.component.ts
Rendered template show.component.html to destination /client/src/app/book/book-show.component.html
Added Book as a dependency to your bootstrap module
ANGULAR SCAFFOLDING
https://grails-plugins.github.io/grails-angular-scaffolding/latest/
Grails Quickcast #4 - AngularJS Scaffolding With Grails 3
DEMO
ANGULAR SCAFFOLDING
Grails Quickcast #4 - AngularJS Scaffolding With Grails 3
REACT PROFILE
• Create a React app backed by a RESTful
Grails API
• Uses the create-react-app CLI
• Preconfigures CORS support
• Provides working React components & test
grails create-app myReactApp -profile react
REACT PROFILE
https://grails-profiles.github.io/react/latest/guide/
DEMO
REACT PROFILE
Grails Quickcast #8 - Grails React Profile
MULTI-TENANCY
• GORM provides native support for multi-tenant applications
• Multi-tenancy allows an application to service multiple clients
(tenants) with isolated datasets
• Three modes:
• DATABASE - Separate databases for each tenant
• SCHEMA - Single database, separate schema for each tenant
• DISCRIMINATOR - Single database, with a discriminator column
to distinguish between tenants
MULTI-TENANCY
DATABASE
SCHEMA
DISCRIMINATOR
MULTI-TENANCY
grails:
gorm:
multiTenancy:
mode: DATABASE
tenantResolverClass:
org.grails.datastore.mapping.multitenancy.web.SessionTenantResolver
MULTI-TENANCY
SessionTenantResolver Resolves the tenant id from the HTTP session using an attribute
called gorm.tenantId
CookieTenantResolver Resolves the tenant id from the HTTP cookie using an attribute
called gorm.tenantId
SubDomainTenantResolver Resolves the tenant id from the current sub-domain. For example if
the subdomain is foo.mycompany.com, the tenant id would be foo
SystemPropertyTenantResolver Resolves the tenant id from a system property
called gorm.tenantId. Mainly useful for testing
package example
import grails.gorm.MultiTenant
class Vehicle implements MultiTenant<Vehicle> {
String model
Integer year
static hasMany = [engines: Engine]
static constraints = {
model blank:false
year min:1980
}
}
package example
import grails.gorm.MultiTenant
class Vehicle implements MultiTenant<Vehicle> {
String model
Integer year
String manufacturer
static hasMany = [engines: Engine]
static constraints = {
model blank:false
year min:1980
}
static mapping = {
tenantId name:'manufacturer'
}
}
import grails.gorm.multitenancy.*
// resolve the current tenant for every method
@CurrentTenant
class TeamService {
// execute the countPlayers method without a tenant id
@WithoutTenant
int countPlayers() {
Player.count()
}
// use the tenant id "another" for all GORM logic within the method
@Tenant({"another"})
List<Team> allTwoTeams() {
Team.list()
}
List<Team> listTeams() {
Team.list(max:10)
}
@Transactional
void addTeam(String name) {
new Team(name:name).save(flush:true)
}
}
import grails.gorm.multitenancy.*
// resolve the current tenant for every method
@CurrentTenant
class TeamService {
// execute the countPlayers method without a tenant id
@WithoutTenant
int countPlayers() {
Player.count()
}
// use the tenant id "another" for all GORM logic within the method
@Tenant({"another"})
List<Team> allTwoTeams() {
Team.list()
}
List<Team> listTeams() {
Team.list(max:10)
}
@Transactional
void addTeam(String name) {
new Team(name:name).save(flush:true)
}
}
MULTI-TENANCY
GRAILS GUIDES
Discriminator perTenant
Guide
Database perTenant Guide
Custom JWTTenant Resolver
Guide
Dynamic Datasources Multi-
tenancy Guide
GRAPHQL
GRAPHQL
• Allows JavaScript components to declaratively indicate their
data requirements
• GraphQL consists of a server-side schema + client operations
• Queries: specify the required inputs/outputs of a request
• Mutations: specify create/update/delete operations against the
GraphQL schema.
• GORM GraphQL plugin generates a GraphQL schema for
domain classes
GRAPHQL
https://medium.freecodecamp.org/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf?gi=e256cd305c64
QUERIES
//List speaker ids
query {
speakerList(max: 10) {
id
}
}
//Return total speakers
query {
speakerCount
}
//Retrieve speaker details by id
query {
speaker(id: 1) {
firstName
lastName
bio
}
}
//Return list of talks with speaker details embedded
query {
talkList(max:10) {
title
speaker {
firstName
lastName
}
}
}
MUTATIONS
//Update speaker by id, return any error messages
mutation {
speakerUpdate(id: 1, speaker: {
bio: "Updated bio!"
}) {
id
bio
errors {
field
message
}
}
}
//Delete speaker by id
mutation {
speakerDelete(id: 2) {
error
}
}
//Create speaker
mutation {
speakerCreate(speaker: {
firstName: "James"
lastName: "Kleeh"
}) {
id
firstName
lastName
errors {
field
message
}
}
}
GORM FOR GRAPHQL
https://grails.github.io/gorm-graphql/snapshot/guide
compile “org.grails.plugins:gorm-graphql:1.0.0.BUILD-SNAPSHOT”
package demo
class Book {
String title
Integer pages
Date publishDate
Author author
static graphql = true
}
In your build.gradle file:
In your domain class:
GORM, GRAPHQL,
REACT & APOLLO GUIDE
https://tinyurl.com/GormGraphQLReact
Step-by-step guide to building a GraphQL
backend using Grails, with a React frontend
Soon to be published at: http://guides.grails.org/gorm-graphql-with-react-and-apollo/guide
SUMMARY
• Grails core framework (MVC, data access/persistence)
• Spring Boot interoperability (Actuator)
• Advanced request handling (Interceptors)
• Expressive JSON rendering (JSON Views)
• SPA support for Angular & React (and others)
• Native Multi-tenancy support via GORM
• Effortless GraphQL support via GORM
SUMMARY
• Grails is alive and well!
• The frameworks offers a suite of unique and compelling
features that go above and beyond the competition
• Developer-productivity is the #1 benefit of Grails
• The Grails community is vibrant, engaged and active
• Head to start.grails.org and start your new Grails project!
THANKYOU
kleinz@objectcomputing.com

Grails Launchpad - From Ground Zero to Orbit

  • 1.
  • 2.
    ABOUT ME • ZacharyKlein • Based in St Louis, MO • GrailsTeam at OCI • Developing with Grails since 2010 • Grails Profile, plugin, documentation author
  • 3.
  • 4.
  • 5.
    start.grails.org Create An ApplicationUsing The Latest Release curl -O start.grails.org/myProject.zip Create A Plugin Using The Latest Release curl -O start.grails.org/myPlugin.zip -d type=plugin Specify A Grails Version curl -O start.grails.org/myProject.zip -d version=3.2.5 Specify A Grails Profile curl -O start.grails.org/restPproject.zip -d profile=rest-api
  • 6.
    HISTORY • Created in2005 • Grails 3, based on Spring Boot, released in 2015 • Sponsored by OCI since 2015 • Grails 3.3 released in 2017
  • 7.
    WHAT IS GRAILS? •Java web application framework • Based on Spring Boot • Follows Model-View-Controller pattern • Leverages proven technology • Convention-over-configuration • Modular plugin-based architecture • Prioritizes developer productivity
  • 8.
  • 9.
    • Extensive pluginlibrary • Application Profiles • JSON views • Native multi-tenancy • Angular & React support WHAT IS GRAILS? Unique Features • JVM Platform • Spring & Spring Boot • Hibernate • Quartz • Gradle Proven Technology
  • 10.
  • 11.
    SPRING BOOT • Rapiddevelopment for Spring-based applications • Convention-over-configuration • Embedded container • First-class metrics & monitoring • Custom “starters” for popular libraries & frameworks
  • 12.
  • 13.
  • 14.
    GRAILS & SPRINGBOOT Grails 3 for Spring Developers Screencast
  • 15.
    GROOVY • Java-based, optionally-typedprogramming language • Can be dynamic or statically-compiled • Emphasizes developer-productivity • First-class DSL (domain-specific language) support • Supports multiple programming styles - OO, functional, runtime & compile-time meta-programming
  • 16.
    public class HelloWorld{ public static void main(String[] args) { System.out.println("Hello, World"); } } Java
  • 17.
  • 18.
    Java POJO public classPerson { private String firstName; private String lastName; public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String setLastName(String value) { this.lastName = value; } public void setFirstName(String value) { this.firstName = value; } }
  • 19.
    Groovy POGO class Person{ String firstName String lastName }
  • 20.
    Groovy Closures Integer item= 0 String name = "My name" { item++ } { println it } { name -> println name } { String x, int y -> println "hey ${x} the value is ${y}" } { reader -> def line = reader.readLine() line.trim() }
  • 21.
  • 22.
    GORM • Groovy-based dataaccess & persistence framework • Originally an ORM framework based on Hibernate • Supports MongoDB, Neo4J, Cassandra • Supports RxJava for Reactive applications • Supports multiple datasources
  • 23.
    DATASOURCE CONFIG dataSource: pooled: true jmxExport:true driverClassName: org.h2.Driver username: sa password: environments: development: dataSource: dbCreate: create-drop url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE test: dataSource: dbCreate: update url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE production: dataSource: dbCreate: update url: jdbc:h2:prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE properties: jmxEnabled: true initialSize: 5 grails-app/conf/application.yml
  • 24.
    DATASOURCE CONFIG dataSource: pooled: true jmxExport:true driverClassName: com.mysql.jdbc.Driver dialect: org.hibernate.dialect.MySQL5InnoDBDialect username: sa password: testing environments: development: dataSource: dbCreate: update url: jdbc:mysql://127.0.0.1:3306/myapp grails-app/conf/application.yml
  • 25.
    MULTIPLE DATASOURCES dataSource: pooled: true jmxExport:true driverClassName: org.h2.Driver username: sa password: dataSources: lookup: dialect: org.hibernate.dialect.MySQLInnoDBDialect driverClassName: com.mysql.jdbc.Driver username: lookup password: secret url: jdbc:mysql://localhost/lookup dbCreate: update grails-app/conf/application.yml
  • 26.
    DOMAIN CLASSES package org.grails.guides classVehicle { String name String make String model static constraints = { name maxSize: 255 make inList: ['Ford', 'Chevrolet', 'Nissan'] model nullable: true } } grails-app/domain/demo/Vehicle.groovy
  • 27.
    CONSTRAINTS Constraint Description Example blankValidates that a String value is not blank login(blank:false) creditCard Validates that a String value is a valid credit card number cardNumber(creditCard: true) email Validates that a String value is a valid email address. homeEmail(email: true) inList Validates that a value is within a range or collection of constrained values. name(inList: ["Joe"]) matches Validates that a String value matches a given regular expression. login(matches: "[a-zA-Z]+") max Validates that a value does not exceed the given maximum value. age(max: new Date()) price(max: 999F) maxSize Validates that a value’s size does not exceed the given maximum value. children(maxSize: 25) min Validates that a value does not fall below the given minimum value. age(min: new Date()) price(min: 0F) minSize Validates that a value’s size does not fall below the given minimum value. children(minSize: 25) notEqual Validates that that a property is not equal to the specified value login(notEqual: "Bob") nullable Allows a property to be set to null - defaults to false. age(nullable: true) range Uses a Groovy range to ensure that a property’s value occurs within a specified range age(range: 18..65) scale Set to the desired scale for floating point numbers (i.e. the number of digits to the right of the salary(scale: 2) http://gorm.grails.org/latest/hibernate/manual/index.html#constraints
  • 28.
    CUSTOM CONSTRAINTS class Response{ Survey survey Answer answer static constraints = { survey blank: false answer blank: false, validator: { val, Response obj -> val in obj.survey.answers } } }
  • 29.
    ASSOCIATIONS package org.grails.guides class Model{ String name static belongsTo = [ make: Make ] static constraints = { } String toString() { name } }
  • 30.
    ASSOCIATIONS package org.grails.guides class Make{ String name static hasMany = [ cars: Car ] static constraints = { } }
  • 31.
    QUERYING def books =Book.list() def books = Book.list(offset:10, max:20) def books = Book.list(sort:"title", order:"asc") def book = Book.get(23) def book = Book.findByTitle(“Grails In Action") book = Book.findByTitleLike(“Groovy%") book = Book.findByReleaseDateBetween(firstDate, secondDate) book = Book.findByReleaseDateGreaterThan(someDate) book = Book.findByTitleLikeOrReleaseDateLessThan("%Something%", someDate)
  • 32.
    QUERYING def query =Person.where { firstName == "Bart" } Person bart = query.find() //Note the query is “lazy” - it’s not instantiated until we actually call find(), list(), get() def results = Person.findAll { lastName == "Jeff" } def results = Person.findAll(sort:"firstName") { lastName == "Brown" } Person p = Person.find { firstName == "Bart" } def query = Person.where { (lastName != "Simpson" && firstName != "Fred") || (firstName == "Bart" && age > 9) } def results = query.list(sort:"firstName") http://gorm.grails.org/latest/hibernate/manual/index.html#querying
  • 33.
    PERSISTENCE & UPDATES //Create defnewPerson = new Person(name: “Person name”) newPerson.save() //Update def p = Person.get(1) p.name = “A New Name” p.save() //If you want to persist the change immediately: p.save(flush: true) //Delete p.delete() //or delete(flush:true)
  • 34.
    DATA SERVICES • Interface-Basedapproach to persistence and querying logic • Introduced in GORM 6.1 • You define interfaces with methods that follow a naming convention • GORM will implement those methods • You can optionally override the implementation • Statically-compiled, type-checked, transaction-managed
  • 35.
    DATA SERVICES @Service(Book) interface BookService{ Book getBook(Serializable id) } class MyController { @Autowired BookService bookService def index() { render bookService.getBook(1) } } grails-app/services/demo/BookService.groovy src/main/groovy/demo/BookService.groovy src/main/groovy/demo/BookService.groovy http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
  • 36.
    DATA SERVICES @Service(Book) interface BookService{ List<Book> findBooks(String title, Map args) //args can supply pagination List<Book> findByTitleAndPublishDateGreaterThan(String title, Date publishDate) @Where({ title ==~ pattern && releaseDate > fromDate }) Book searchBooks(String pattern, Date fromDate) @Query("from $Book as book where book.title like $pattern") Book searchByTitle(String pattern) } grails-app/services/demo/BookService.groovy src/main/groovy/demo/BookService.groovy http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
  • 37.
    DATA SERVICES @Service(Book) interface BookService{ Book saveBook(String title) Book saveBook(Book newBook) Book updateBook(Serializable id, String title) @Query(“update ${Book book} set ${book.title} = $newTitle where book.title = $oldTitle") Number updateTitle(String newTitle, String oldTitle) Number deleteAll(String title) void delete(Serializable id) } grails-app/services/demo/BookService.groovy src/main/groovy/demo/BookService.groovy http://gorm.grails.org/latest/hibernate/manual/index.html#savingAndUpdating
  • 38.
  • 39.
    CONTROLLERS package demo class MyController{ def index() { render “Hello world” } } grails-app/controllers/demo/MyController.groovy http://localhost:8080/my Hello world
  • 40.
    CONTROLLERS package demo class MyController{ def index() { [message: “Hello world”] } } grails-app/controllers/demo/MyController.groovy
  • 41.
  • 42.
  • 43.
  • 44.
  • 47.
  • 48.
    INTERCEPTORS • Interceptors allowyou to add logic before/after a targeted request • Allow blocking or redirecting of a request • By default, interceptors will target requests made a controller of the same name • You can implement custom request “matching” in the constructor https://objectcomputing.com/resources/publications/sett/september-2015-grails-3-interceptors/
  • 49.
  • 50.
    package com.example class BookInterceptor{   boolean before() { true }   boolean after() { true }   void afterView() { } } grails-app/controller/com/example/BookInterceptor.groovy
  • 51.
    class CustomInterceptor {   publicCustomInterceptor() { // match all requests to the reporting controller... match controller: 'reporting'   //match requests to create action, person controller match controller: 'person', action: 'create'   //match all requests to accounting or payroll controller match controller: ~/(accounting|payroll)/ } boolean before() { true }   boolean after() { true }   void afterView() { /.../ } }
  • 52.
    class CustomInterceptor {   intorder = HIGHEST_PRECEDENCE + 100 //or LOWEST_PRECEDENCE boolean before() { true }   boolean after() { true }   void afterView() { /.../ } }
  • 53.
    INTERCEPTORS Grails Quickcast #1- Grails Interceptors
  • 54.
    JSONVIEWS http://views.grails.org/latest/ • JSON viewsmake it easy to express the desired format of your controller endpoints • Markup views are also available for expressing XML responses • JSON views are statically compiled, type-checked, and reload instantly
  • 55.
    JSONVIEWS json.message { hello "world" } {"message":{"hello":"world"}} grails-app/views/hello.gson Response:
  • 56.
    JSONVIEWS model { Person person } json{ name person.name age person.age } {“name": “John Doe”, “age”: 34} grails-app/views/person/_person.gson Response:
  • 57.
    buildscript { ... dependencies { ... classpath"org.grails.plugins:views-gradle:1.2.6" } } ... apply plugin: "org.grails.grails-web" apply plugin: "org.grails.plugins.views-json" dependencies { ... compile "org.grails.plugins:views-json:1.2.6" In your build.gradle file:
  • 58.
    model { Person person } jsontmpl.person(person) grails-app/views/person/show.gson model { Person person } json { name person.name age person.age } grails-app/views/person/_person.gson
  • 59.
    model { List<Person> people= [] } json tmpl.person(“person", people) grails-app/views/person/index.gson model { Person person } json { name person.name age person.age } grails-app/views/person/_person.gson
  • 60.
  • 61.
    model { Book book } json{ hal.links(book) hal.embedded { author( book.authors.first() ) { Author author -> name author.name } } title book.title } grails-app/views/book/_book.json HAL+JSON
  • 62.
    { "_links": { "self": { "href":"http://localhost:8080/book/show/1", "hreflang": "en", "type": "application/hal+json" } }, "_embedded": { "author": { "_links": { "self": { "href": "http://localhost:8080/author/show/1", "hreflang": "en", "type": "application/hal+json" } }, "name": "Stephen King" } }, "title": "The Stand" }
  • 63.
  • 64.
  • 65.
  • 66.
    SINGLE PAGE APPS •GORM •Convention/Config •SpringBoot •Profiles •Plugins •Gradle •URL mappings •JSON views •Hypermedia •GORM for GraphQL •WebSockets
  • 67.
    GRAILS PROFILES • Includedefault config, plugins, project structure • Custom commands/scripts for code-generation • Optional “features” allow further control of the generated project • Profile inheritance • Packaged/resolved as Maven dependencies
  • 68.
    SPA GRAILS PROFILES angularjsAngular 1.x https://grails-profiles.github.io/angularjs/latest/ guide/index.html angular Angular 2+ https://grails-profiles.github.io/angular/latest/guide/ index.html react React 16.x https://grails-profiles.github.io/react/latest/guide/ index.html react-webpack React 16.x, using Webpack https://grails-profiles.github.io/react-webpack/ latest/guide/index.html webpack `Webpack 2.x, no JavaScript framework https://grails-profiles.github.io/webpack/latest/ guide/index.html
  • 69.
    ANGULAR SCAFFOLDING https://grails-plugins.github.io/grails-angular-scaffolding/latest/ • GenerateAngular controllers, services and views based on your domain model • Full CRUD functionality • Installed automatically in the Angular Profile
  • 70.
    buildscript { ... dependencies { ... classpath"org.grails.plugins:angular-scaffolding:2.0.0.RC2" } } dependencies { ... compile "org.grails.plugins:angular-scaffolding:2.0.0.RC2" In your build.gradle file:
  • 71.
    package demo import grails.rest.Resource @Resource(uri='/book') classBook { String title Integer pages Date publishDate String author static constraints = { } }
  • 72.
    grails ng-generate-all demo.Book Renderedtemplate service.ts to destination /client/src/app/book/book.service.ts Rendered template domain.ts to destination /client/src/app/book/book.ts Rendered template module.ts to destination /client/src/app/book/book.module.ts Rendered template routing.module.ts to destination /client/src/app/book/book-routing.module.ts Rendered template persist.component.ts to destination /client/src/app/book/book-persist.component.ts Rendered template persist.component.html to destination/client/src/app/book/book-persist.component.html Rendered template list.component.ts to destination /client/src/app/book/book-list.component.ts Rendered template list.component.html to destination /client/src/app/book/book-list.component.html Rendered template show.component.ts to destination /client/src/app/book/book-show.component.ts Rendered template show.component.html to destination /client/src/app/book/book-show.component.html Added Book as a dependency to your bootstrap module
  • 73.
  • 74.
    ANGULAR SCAFFOLDING Grails Quickcast#4 - AngularJS Scaffolding With Grails 3
  • 75.
    REACT PROFILE • Createa React app backed by a RESTful Grails API • Uses the create-react-app CLI • Preconfigures CORS support • Provides working React components & test grails create-app myReactApp -profile react
  • 76.
  • 77.
    REACT PROFILE Grails Quickcast#8 - Grails React Profile
  • 78.
    MULTI-TENANCY • GORM providesnative support for multi-tenant applications • Multi-tenancy allows an application to service multiple clients (tenants) with isolated datasets • Three modes: • DATABASE - Separate databases for each tenant • SCHEMA - Single database, separate schema for each tenant • DISCRIMINATOR - Single database, with a discriminator column to distinguish between tenants
  • 79.
  • 80.
  • 81.
    MULTI-TENANCY SessionTenantResolver Resolves thetenant id from the HTTP session using an attribute called gorm.tenantId CookieTenantResolver Resolves the tenant id from the HTTP cookie using an attribute called gorm.tenantId SubDomainTenantResolver Resolves the tenant id from the current sub-domain. For example if the subdomain is foo.mycompany.com, the tenant id would be foo SystemPropertyTenantResolver Resolves the tenant id from a system property called gorm.tenantId. Mainly useful for testing
  • 82.
    package example import grails.gorm.MultiTenant classVehicle implements MultiTenant<Vehicle> { String model Integer year static hasMany = [engines: Engine] static constraints = { model blank:false year min:1980 } }
  • 83.
    package example import grails.gorm.MultiTenant classVehicle implements MultiTenant<Vehicle> { String model Integer year String manufacturer static hasMany = [engines: Engine] static constraints = { model blank:false year min:1980 } static mapping = { tenantId name:'manufacturer' } }
  • 84.
    import grails.gorm.multitenancy.* // resolvethe current tenant for every method @CurrentTenant class TeamService { // execute the countPlayers method without a tenant id @WithoutTenant int countPlayers() { Player.count() } // use the tenant id "another" for all GORM logic within the method @Tenant({"another"}) List<Team> allTwoTeams() { Team.list() } List<Team> listTeams() { Team.list(max:10) } @Transactional void addTeam(String name) { new Team(name:name).save(flush:true) } }
  • 85.
    import grails.gorm.multitenancy.* // resolvethe current tenant for every method @CurrentTenant class TeamService { // execute the countPlayers method without a tenant id @WithoutTenant int countPlayers() { Player.count() } // use the tenant id "another" for all GORM logic within the method @Tenant({"another"}) List<Team> allTwoTeams() { Team.list() } List<Team> listTeams() { Team.list(max:10) } @Transactional void addTeam(String name) { new Team(name:name).save(flush:true) } }
  • 86.
    MULTI-TENANCY GRAILS GUIDES Discriminator perTenant Guide DatabaseperTenant Guide Custom JWTTenant Resolver Guide Dynamic Datasources Multi- tenancy Guide
  • 87.
  • 88.
    GRAPHQL • Allows JavaScriptcomponents to declaratively indicate their data requirements • GraphQL consists of a server-side schema + client operations • Queries: specify the required inputs/outputs of a request • Mutations: specify create/update/delete operations against the GraphQL schema. • GORM GraphQL plugin generates a GraphQL schema for domain classes
  • 89.
  • 90.
    QUERIES //List speaker ids query{ speakerList(max: 10) { id } } //Return total speakers query { speakerCount } //Retrieve speaker details by id query { speaker(id: 1) { firstName lastName bio } } //Return list of talks with speaker details embedded query { talkList(max:10) { title speaker { firstName lastName } } }
  • 91.
    MUTATIONS //Update speaker byid, return any error messages mutation { speakerUpdate(id: 1, speaker: { bio: "Updated bio!" }) { id bio errors { field message } } } //Delete speaker by id mutation { speakerDelete(id: 2) { error } } //Create speaker mutation { speakerCreate(speaker: { firstName: "James" lastName: "Kleeh" }) { id firstName lastName errors { field message } } }
  • 92.
    GORM FOR GRAPHQL https://grails.github.io/gorm-graphql/snapshot/guide compile“org.grails.plugins:gorm-graphql:1.0.0.BUILD-SNAPSHOT” package demo class Book { String title Integer pages Date publishDate Author author static graphql = true } In your build.gradle file: In your domain class:
  • 93.
    GORM, GRAPHQL, REACT &APOLLO GUIDE https://tinyurl.com/GormGraphQLReact Step-by-step guide to building a GraphQL backend using Grails, with a React frontend Soon to be published at: http://guides.grails.org/gorm-graphql-with-react-and-apollo/guide
  • 94.
    SUMMARY • Grails coreframework (MVC, data access/persistence) • Spring Boot interoperability (Actuator) • Advanced request handling (Interceptors) • Expressive JSON rendering (JSON Views) • SPA support for Angular & React (and others) • Native Multi-tenancy support via GORM • Effortless GraphQL support via GORM
  • 95.
    SUMMARY • Grails isalive and well! • The frameworks offers a suite of unique and compelling features that go above and beyond the competition • Developer-productivity is the #1 benefit of Grails • The Grails community is vibrant, engaged and active • Head to start.grails.org and start your new Grails project!
  • 96.