Grails Multi-Project Builds - Multitenancy

Grails Multi-Project Builds - Multitenancy

Slides Deck of GR8Conf EU 2017 by Sergio del Amo

30995fcc876f40073628b63ea5cfab59?s=128

Sergio del Amo

June 01, 2017
Tweet

Transcript

  1. GRAILS MULTI-PROJECT BUILDS & MULTI TENANCY objectcomputing.com/grails

  2. http://sergiodelamo.es Sergio del Amo @sdelamo

  3. objectcomputing.com/grails http://groovycalamari.com

  4. http://guides.grails.org

  5. MULTI-PROJECT BUILS

  6. objectcomputing.com/grails BUILD TOOL

  7. A multi-project build in Gradle consists of one root project,

    and one or more subprojects that may also have subprojects
  8. objectcomputing.com/grails DEMO APP PLUGIN GROOVY LIBRARY ROOT PROJECT

  9. objectcomputing.com/grails DEMO multiproject$ mkdir app multiproject$ cd app multiproject/app$ grails

    grails> create-app --profile web --inplace | Application created at multiproject/app | Resolving Dependencies. Please wait… CONFIGURE SUCCESSFUL Total time: 12.262 secs grails> exit multiproject/app$ cd ..
  10. objectcomputing.com/grails DEMO multiproject$ mkdir plugin multiproject$ cd plugin multiproject/plugin$ grails

    grails> create-app --profile plugin --inplace | Application created at multiproject/plugin | Resolving Dependencies. Please wait… CONFIGURE SUCCESSFUL Total time: 12.262 secs grails> exit multiproject/plugin$ cd ..
  11. objectcomputing.com/grails DEMO - MOVING GRADLE FILES TO ROOT multiproject$ mv

    app/gradlew . multiproject$ mv app/gradlew.bat . multiproject$ mv app/gradle.properties . multiproject$ mv app/gradle .
  12. objectcomputing.com/grails DEMO - SETUP SETTINGS.GRADLE multiproject$ touch settings.gradle multiproject$ echo

    "include 'app'" >> settings.gradle multiproject$ echo "include 'plugin'" >> settings.gradle multiproject$ echo "include 'groovylib'" >> settings.gradle multiproject$ cat settings.gradle include ‘app' include ‘plugin' include 'groovylib'
  13. objectcomputing.com/grails DEMO - CLEAN-UP plugin FOLDER multiproject$ rm plugin/gradlew multiproject$

    rm plugin/gradlew.bat multiproject$ rm plugin/grailsw.bat multiproject$ rm plugin/grailsw multiproject$ rm plugin/grailsw.bat multiproject$ rm plugin/grails-wrapper.jar multiproject$ rm plugin/settings.gradle multiproject$ rm plugin/gradle.properties multiproject$ rm -rf plugin/gradle
  14. objectcomputing.com/grails DEMO - CLEAN-UP app FOLDER multiproject$ rm app/grailsw multiproject$

    rm app/grailsw.bat multiproject$ rm app/grails-wrapper.jar multiproject$ rm app/settings.gradle multiproject$ rm app/.gitignore
  15. objectcomputing.com/grails DEMO - PLAIN GROOVY PROJECT multiproject$ mkdir groovylib multiproject$

    cd groovylib multiproject/groovylib$ touch build.gradle apply plugin: 'groovy' repositories { jcenter() } dependencies { compile 'org.codehaus.groovy:groovy-all:2.4.11' }
  16. objectcomputing.com/grails OPEN FOLDER WITH INTELLIJ IDEA

  17. objectcomputing.com/grails app/build.gradle dependencies { … compile project(':plugin') } plugin/build.gradle dependencies

    { … compile project(':groovylib') } APP PLUGIN GROOVY LIB
  18. objectcomputing.com/grails app/build.gradle dependencies { … compile project(':plugin') } grails {

    plugins { compile project(':plugin') } } HOT-RELOADING
  19. objectcomputing.com/grails APP PLUGIN GROOVY LIB PersonController.groovy RandomPersonService.groovy OCI.groovy

  20. objectcomputing.com/grails app/grails-app/controllers/app/PersonController.groovy package app import demo.RandomPersonService import groovy.transform.CompileStatic @CompileStatic class

    PersonController { RandomPersonService randomPersonService def index() { render randomPersonService.randomOciPersonName() } }
  21. objectcomputing.com/grails plugin/grails-app/services/demo/RandomPersonService.groovy package demo import groovy.transform.CompileStatic @CompileStatic class RandomPersonService {

    String randomOciPersonName() { List<String> people = OCI.PEOPLE Collections.shuffle(people) people.first() } }
  22. objectcomputing.com/grails groovylib/src/main/groovy/demo/OCI.groovy package demo import groovy.transform.CompileStatic @CompileStatic class OCI {

    public static final List<String> PEOPLE = [ 'Ryan', 'Jeff', 'Paul', 'Søren', 'Sergio' ] }
  23. objectcomputing.com/grails KEPP THE BUILD DRY

  24. buildscript { repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" }

    } dependencies { classpath "org.grails:grails-gradle-plugin:$grailsVersion" classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}" classpath "com.bertramlabs.plugins:asset-pipeline-gradle:2.14.2" } } ext { grailsApps = ['app'] grailsPlugins = ['plugin'] } subprojects { project -> boolean isGrailsApp = grailsApps.contains(project.name) boolean isGrailsPlugin = grailsPlugins.contains(project.name) boolean isGrailsProject = isGrailsApp || isGrailsPlugin if ( isGrailsProject ) { apply plugin:"eclipse" apply plugin:"idea" if ( isGrailsApp ) { apply plugin:"war" apply plugin:"org.grails.grails-web" apply plugin:"asset-pipeline" apply plugin:"org.grails.grails-gsp" } if ( isGrailsPlugin ) { apply plugin:"org.grails.grails-plugin" apply plugin:"org.grails.grails-plugin-publish" } repositories { mavenLocal() maven { url "https://repo.grails.org/grails/core" } } dependencies { objectcomputing.com/grails multiproject/build.gradle version "0.1" group "app" grails { plugins { compile project(':plugin') } } multiproject/app/build.gradle version "0.1" group "plugin" dependencies { compile project(':groovylib') } multiproject/plugin/build.gradle
  25. FEATURES AS GRADLE FILES objectcomputing.com/grails gradle/geb.gradle project.ext.seleniumVersion = '2.53.1' project.ext.htmlUnitVersion

    = '2.18' project.ext.htmlUnitDriverVersion = '2.47.1' project.ext.phantomJsDriverVersion = '1.3.0' dependencies { testCompile 'org.grails.plugins:geb' testRuntime "net.sourceforge.htmlunit:htmlunit:$htmlUnitVersion" testRuntime "org.seleniumhq.selenium:selenium-support:$seleniumVersion" testCompile "org.seleniumhq.selenium:selenium-htmlunit-driver:$htmlUnitDriverVersion" testCompile "com.codeborne:phantomjsdriver:$phantomJsDriverVersion" testCompile "org.seleniumhq.selenium:selenium-chrome-driver:$seleniumVersion" testCompile "org.seleniumhq.selenium:selenium-firefox-driver:$seleniumVersion" } apply from: "${rootProject.projectDir}/gradle/geb.gradle" build.gradle
  26. objectcomputing.com/grails APP PLUGIN SPRING SECURITY CORE APP PLUGIN SPRING SECURITY

    CORE loadBefore property to specify one ore more plugins that your plugin should load before loadAfter property to specify one ore more plugins that your plugin should load after UNDERSTANDING PLUGIN ORDER dependsOn dependencies will be loaded before the plugin and if all dependencies do not load, then the plugin will not load.
  27. GORM DATA SERVICES

  28. Introduced in GORM 6.1, Data Services take the work out

    of implemented service layer logic by adding the ability to automatically implement abstract classes or interfaces using GORM logic.
  29. MULTI-TENANCY

  30. Multi-Tenancy, as it relates to software developments, is when a

    single instance of an application is used to service multiple clients (tenants) in a way that each tenants' data is isolated from the other.
  31. MULTI-TENANCY MODE • DATABASE - Use a distinct database connection

    per tenant • SCHEMA - Use a single database, but different physical schemas per tenant • DISCRIMINATOR - Use a single database, but partition the data using a discriminator column
  32. None
  33. objectcomputing.com/grails grails-app/conf/application.yml grails: gorm: multiTenancy: mode: DATABASE tenantResolverClass: org.grails.datastore.mapping.multitenancy.web.SessionTenantResolver gradle.properties

    gormVersion=6.1.3.RELEASE
  34. objectcomputing.com/grails Type DESCRIPTION SessionTenantResolver Resolves the tenant id from the

    HTTP session using an attribute called gorm.tenantId CookieTenantResolver Resolves the tenant id form the HTTP cookie using an attribute called gorm.tenantId SubDomainTenantResolver Resolves the tenant id from the current subdomain. For example if the subdomain is foo.mycompany.com the tenant id would be foo SystemPropertyTenantResolver Resolves the tenant id form a System Property called gorm.tenantId. Mainly useful for testing TENANT RESOLVER
  35. objectcomputing.com/grails grails-app/domain/example/Vehicle.groovy package example import grails.gorm.MultiTenant class Vehicle implements MultiTenant<Vehicle>

    { String model Integer year static constraints = { model blank:false year min:1980 } }
  36. objectcomputing.com/grails Type DESCRIPTION CurrentTenant Resolves the current tenant and binds

    a Hibernate session for the scope of the method Tenant Resolves a specific tenant and binds a Hibernate session for the scope of the method WithoutTenant Execute some logic within a method without a tenant present MULTI-TENANCY TRANSFORMATION
  37. objectcomputing.com/grails grails-app/services/example/VehicleService.groovy import grails.gorm.multitenancy.CurrentTenant import grails.gorm.services.Join import grails.gorm.services.Service import grails.gorm.transactions.Transactional

    import groovy.transform.CompileStatic @Service(Vehicle) @CurrentTenant @CompileStatic abstract class VehicleService { @Join('engines') abstract List<Vehicle> list(Map args ) abstract Integer count() @Join('engines') abstract Vehicle find(Serializable id) }
  38. objectcomputing.com/grails grails-app/controllers/example/VehicleController.groovy @TestFor(VehicleController) class VehicleControllerSpec extends HibernateSpec { @Override Map

    getConfiguration() { [(Settings.SETTING_MULTI_TENANT_RESOLVER_CLASS): SystemPropertyTenantResolver] } VehicleService vehicleService def setup() { System.setProperty(SystemPropertyTenantResolver.PROPERTY_NAME, 'audi') vehicleService = hibernateDatastore.getService(VehicleService) controller.vehicleService = vehicleService } def cleanup() { System.setProperty(SystemPropertyTenantResolver.PROPERTY_NAME, '') }
  39. objectcomputing.com/grails

  40. ?

  41. OCI 12140 Woodcrest Exec. Dr., Ste. 250 Saint Louis, MO

    63141 USA © 2017, All Rights Reserved. No part of this publication may be photocopied or reproduced in any form without written permission from OCI. Nor shall the OCI logo or copyright information be removed from this publication. No part of this publication may be stored in a retrieval system, transmitted by any means, recorded or otherwise, without written permission from OCI. While every precaution has been taken in preparing this material, including research, development and testing, OCI assumes no responsibility for errors or omissions. No liability is assumed by OCI for any damages resulting from the use of this information.