• Better know a Grails Plugin



  • A journey around the Grails 3 ecosystem

  • Plugins are what makes Grails great

    So many good plugins out there to quickly setup sane defaults for your app and get going quickly

  • Plugins are why we use Grails

    • We don’t want to spend time wiring & configuring beans.
    • Build on what others have built upon.
  • Grails 3 changed (for the better) how plugins work

  • Little less magic and a lot more standard

    • Few things not as apparent to newcomers or seasoned Grails 2 devs.

    • Magic is still there and that’s what I’m going to talk about.

  • Early Days

    • jars instead of zips
    • No portal
      • Hosted on Bintray
      • Users lost
    • Spring-security-core
    • Some plugins left behind
      • and in some cases thats a good thing.
  • Today

  • But new issues

    • Binary incompatibility an issues
    • Spring Boot package name changes
  • @Scheduled

    SpringBoot already comes with one

    • Benefit from being based directly on spring-boot!
  • Quartz Plugin

  • Schwartz

  • Quartz Pro Tips

    • Can run in cluster
    • No way to stop schedulers cluster wide
      • have to do node by node - eg rolling deploy
    • Ensure each node has a unique id.
    • Can be an easy way to do some async work is to take a request and schedule a job
  • Jesque

  • quartz-admin & jesque-admin

  • Spring Security

  • Spring-Session

    • Multiple stores - redis, jdbc, hazlecast, etc
    • Session is immutable by default for performance
    • Security API differences
  • Demo!

    session/springsession

    • Stores on client, not server
      • Can add bulk to request
  • Database Migration

  • FlyWay

  • DB Logging

    • Simple, slightly useful
      datasource { logSql = true }
    • HibernateStats (Interceptor)
    • p6spy
  • dialects

    • Hibernate stores information nativity
    • PostgreSql Extensions
      • Native sequences
      • Native types (JSONB)
    • java.time.*
  • MultiTenant

    • Segregate data by Tenant
      • Schema, Identifier, DB, etc
    • Built into recent GORM
    • Quickly create SaaS solutions
  • Honorable mentions

  • grails-mail

    • Simple, easy to use.
    • Useful async feature to not tie up thread
    • Was in RC6 for 2 years without a release.
      • Until Yesterday :woot:
  • spring-boot-starter-mail

  • spring-boot-email-tools

  • API email services

    • Allow business/marketing to control templates
    • grails-mailgun
    • grails-sendgrid
  • Email Testing

    • Greenmail to catch emails.
    • Wizer
      • Easy to use in memory map of emails
  • Demo!

    mail/emailtools

    • Demo!

      container/undertow
      container/tc

      • Demo!

        info/info

      • list-profiles

        ➜  source grails list-profiles
        | Available Profiles
        --------------------
        * angular - A profile for creating Grails applications with Angular 2
        * rest-api - Profile for REST API applications
        * base - The base profile extended by other profiles
        * angularjs - A profile for creating applications using AngularJS
        * plugin - Profile for plugins designed to work across all profiles
        * profile - A profile for creating new Grails profiles
        * react - A profile for creating Grails applications with a React frontend
        * rest-api-plugin - Profile for REST API plugins
        * web - Profile for Web applications
        * web-jboss7 - A Profile for Creating a JBoss 7.1 EAP Project
        * web-plugin - Profile for Plugins designed for Web applications
        * webpack - A profile for creating applications with node-based frontends using webpack
        • Mostly front end, but a few for custom deployments.
        • Just Jars, distribute the same way.
        • Create your own
        • NEW: Listed on docs site
      • Sometimes unnecessary even for a plugin

      • Grails provides a lot of great tools to build the functionality you ended quickly

        • Interceptors
          • jsonp
          • rate limit
            • Bucket4J
          • pagination
          • CORS (Now in core)
      • Settings plugin (older 2.x plugin)

        • (1 domain class, 1 service & a cache)
        • So simple, just implement it.
      • What to look for:

        • Release & tests automated?
        • When was last release?
        • Active maintainer?
        • Documentation up to date?
        • Review code you pull into your app!
        • The Current Echosystem

          • good
          • bad
          • ugly
        • Thanks

        • My preferred way AWS elastic beanstalk

        • Docker

        • sshoogar

        • War (gradle deploy plugin)

        • Deploy info

        • Logging slf4j
          remote api

        • GSP (Yes it’s a plugin now)

        • Gson (json views)

        • Markup

        • grails-vaadin-plugin

        • asciidoctorj

        • spring-restdocs

        • swagger

        • Cookie

          • Cookie spec, creating Secure (new)
            WebSockets(grails-spring-websocket)
          • rabbitmq
            gorm-graphql
        • Not really something I use, anyways right after building

        • scaffolding

        • angular-scaffolding

            • Jars

              • Grails 1 & 2 plugins were zip’d source, now jar’s
              • Easier distribution
            • Build system is now gradle

              • opens up a lot more functionality
              • Standard build tool instead of custom
            • Grails is now built on top of Spring Boot

              • Opens up a lot more functionality
              • Built on top of solid foundation.
              • Great for simple scheduling

                • Use it on a service method
                • Simple health check
                • Clear or refresh a cache
                • System jobs running at set interval
                  @Scheduled
              • Demo!

                scheduling/scheduled

              • Quartz Plugin

                • Defacto Java Scheduler lib Quarts (of course)
                • Uses custom artifact class
                  • create Job classes to schedule/execute.
                • Set custom, cron, or one off job executions easily.
              • Few rough edges

                • Default started, then didn’t, then did
                • Transaction propagation
                • IDE Mark grails-app/jobs as src
              • Demo!

                scheduling/quartz

                • Similar to Ruby resque

                  • Uses redis to queue background jobs.
                  • Uses similar Job structure as quartz
                  • Process work as it comes in.
                • spring-security-core

                  • All the power of Spring Security with sane defaults
                • spring-security-rest

                  • Defaults Stateless JWT (Java Web Tokens)
                    • Encodes || Encrypts user in token
                    • Other storage (db, redis, memecached)
                • spring-security-ui

                  • While does run on Grails 3, not a lot of new development
                  • Provides
                    • Self Registration
                    • Password Reset tokens
                    • Admin UI for Mappings, Roles, etc
                • Others

                  • spring-security-ldap
                  • spring-security-cas
                  • Supports all the major “enterprise” systems.
                • Demo!

                  security/spring-security

                • Plugin or not?

                  • spring-session works out of box
                  • Easily add in mutable sessions yourself
                  • Generates DB Schema

                    • Inspects domain classes
                    • Takes an educated guess at what to do.
                      • Always review the changes.
                      • Doesn’t know your data access paths.
                    • Underlying technology liqubase.
                    • Uses versioned SQL scripts.
                    • Run outside your application
                      • depending on your deployment needs.
                    • Not directly generated to your Domain Classes
                  • Demo!

                    db/dbm
                    db/flyway

                  • we all know sqlLogging= true, helpfull, but not always clear.

                  • p6spy sits between your app and jdbc driver and intercepts & logs exact sql queries (with params!) unlike hibernate sqlLogging

                  • hibernateStats is available, easily added to an dev-time interceptor

                  • Good to have on in development to quickly see if you’re making 1 or 1000 sql queries, or flushing the session 100’s of times.

                  • Demo!

                    db/logging

                  • Demo!

                    db/dialects

                                    • Demo

                                      testing/testing

                                    • good

                                    • bad

                                    • ugly

                                    • AWS gradle plugins make this trivial

                                    • What to know

                                      • Port 5000 SERVER_PORT
                                      • cant be executable jar - just regular jar
                                        • it explodes the jar before running it, so unzip wont work with exec
                                      • .ebextensions included in jar for cache, etc
                                    • docker gradle plugins

                                    • Easily build your jar into a container, and define your other infra containers as well

                                    • Sometimes you just need to push a file somewhere and restart a service

                                    • sshoogar - dsl for doing just that

                                    • (git commit,)

                                    • grailsinfocontributor

                                    • Log Accutator API, change logging on the fly

                                    • Ship logs off system!
                                      Splunk, ELK, Papertrail, anywhere!

                                    • Static type, speed, inheritance, etc

                                    • hal

                                    • json-api

                                    • Easy & powerful writing tool
                                      Gradle integration

                                    • Keep documentation with your code!

                                      • Changelogs
                                      • Docs
                                    • Publish to static site (s3/etc)

                                    • s2-quickstart

                                      $ grails s2-quickstart com.yourapp User Role
                                      
                                      CONFIGURE SUCCESSFUL in 7s
                                      | Creating User class 'User' and Role class 'Role' in package 'com.yourapp'
                                      | Rendered template PersonWithoutInjection.groovy.template to destination grails-app/domain/com/yourapp/User.groovy
                                      | Rendered template PersonPasswordEncoderListener.groovy.template to destination src/main/groovy/com/yourapp/UserPasswordEncoderListener.groovy
                                      | Rendered template Authority.groovy.template to destination grails-app/domain/com/yourapp/Role.groovy
                                      | Rendered template PersonAuthority.groovy.template to destination grails-app/domain/com/yourapp/UserRole.groovy
                                      | 
                                      ************************************************************
                                      * Created security-related domain classes. Your            *
                                      * grails-app/conf/application.groovy has been updated with *
                                      * the class names of the configured domain classes;        *
                                      * please verify that the values are correct.               *
                                      ************************************************************
                                    • Recent improvements

                                      • bcrypt rounds lowered in tests
                                      • Ignore Case for username prop
                                        • Lowers need for custom UserDetails service
                                      • Password encoder now event listener
                                    • Extras

                                      • <sec:*> taglibs
                                      • User switching
                                      • Easily override defaults
                                    • Everything you need to build a website quickly is there.

                                    • Discover-ability
                                      You need to know where to look, or come to this talk to find everything.

                                    • 2 years between releases
                                      broken links

                                    • Gorm versions break plugins more often

                                    {"cards":[{"_id":"7c85f150d14f825f2700008a","treeId":"7c85f159d14f825f27000088","seq":11750451,"position":1,"parentId":null,"content":"<center>\n<h1> Better know a Grails Plugin </h1>\n![](https://www.filepicker.io/api/file/ajtibQhQrWKj1HLX3rPg)\n</center>"},{"_id":"7c85f0d3d14f825f2700008b","treeId":"7c85f159d14f825f27000088","seq":11732516,"position":1,"parentId":"7c85f150d14f825f2700008a","content":"### Welcome"},{"_id":"7c85f05dd14f825f2700008c","treeId":"7c85f159d14f825f27000088","seq":11720872,"position":2,"parentId":"7c85f150d14f825f2700008a","content":"### Who am I?"},{"_id":"7b843e17206694d4cb00003f","treeId":"7c85f159d14f825f27000088","seq":11666958,"position":1,"parentId":"7c85f05dd14f825f2700008c","content":"### Grails Consulting at Agile Orbit \n\nhttp://www.agileorbit.com\n"},{"_id":"7b4194531a365f8a990000e7","treeId":"7c85f159d14f825f27000088","seq":11666957,"position":3,"parentId":"7c85f05dd14f825f2700008c","content":"### Practical Grails 3\n![](https://www.filepicker.io/api/file/oyIbKAfTbKRRQNkHizOg)\nhttps://www.grails3book.com"},{"_id":"7abc20c5e57bccf9910000f8","treeId":"7c85f159d14f825f27000088","seq":11750283,"position":3.5,"parentId":"7c85f05dd14f825f2700008c","content":"### Sproutary.com\nOnline software for Pre-Schools and Day Cares.\nhttps://sproutary.com"},{"_id":"7b843c22206694d4cb000040","treeId":"7c85f159d14f825f27000088","seq":11667213,"position":4,"parentId":"7c85f05dd14f825f2700008c","content":"### New Plugin Portal\n* Matt Sheehan (mostly), Bobby Warner, & myself.\n* Bintray was not cutting it for usability.\n* OCI Recently revamped in light orange\n\nhttps://github.com/grails/grails3-plugins"},{"_id":"7c85effbd14f825f2700008d","treeId":"7c85f159d14f825f27000088","seq":11720874,"position":2.25,"parentId":"7c85f150d14f825f2700008a","content":"### What is this talk about?"},{"_id":"7ad6bd3bf15c8ae0ec0000ef","treeId":"7c85f159d14f825f27000088","seq":11732539,"position":0.5,"parentId":"7c85effbd14f825f2700008d","content":"### A journey around the Grails 3 ecosystem"},{"_id":"7b841710206694d4cb000043","treeId":"7c85f159d14f825f27000088","seq":11721158,"position":1,"parentId":"7c85effbd14f825f2700008d","content":"### Plugins are what makes Grails great\n\nSo many good plugins out there to quickly setup sane defaults for your app and get going quickly"},{"_id":"7b80a482206694d4cb00007b","treeId":"7c85f159d14f825f27000088","seq":11721191,"position":1.25,"parentId":"7c85effbd14f825f2700008d","content":"### Plugins are why we use Grails\n\n* We don't want to spend time wiring & configuring beans.\n* Build on what others have built upon."},{"_id":"7b80ca3e206694d4cb000072","treeId":"7c85f159d14f825f27000088","seq":11721160,"position":1.5,"parentId":"7c85effbd14f825f2700008d","content":"### Grails 3 changed (for the better) how plugins work"},{"_id":"7b80c8fe206694d4cb000073","treeId":"7c85f159d14f825f27000088","seq":11721171,"position":1,"parentId":"7b80ca3e206694d4cb000072","content":"### Jars\n* Grails 1 & 2 plugins were zip'd source, now jar's\n* Easier distribution"},{"_id":"7b80c4ae206694d4cb000074","treeId":"7c85f159d14f825f27000088","seq":11721169,"position":2,"parentId":"7b80ca3e206694d4cb000072","content":"### Build system is now gradle\n* opens up a lot more functionality\n* Standard build tool instead of custom"},{"_id":"7b80bca9206694d4cb000078","treeId":"7c85f159d14f825f27000088","seq":11721173,"position":3,"parentId":"7b80ca3e206694d4cb000072","content":"### Grails is now built on top of Spring Boot\n* Opens up a lot more functionality\n* Built on top of solid foundation."},{"_id":"7b80bf0f206694d4cb000076","treeId":"7c85f159d14f825f27000088","seq":11721179,"position":3,"parentId":"7c85effbd14f825f2700008d","content":"### Little less magic and a lot more standard\n\n* Few things not as apparent to newcomers or seasoned Grails 2 devs.\n\n* Magic is still there and that's what I'm going to talk about."},{"_id":"7b8419a8206694d4cb000041","treeId":"7c85f159d14f825f27000088","seq":11721266,"position":2.5,"parentId":"7c85f150d14f825f2700008a","content":"### Brief history of Grails 3 plugins"},{"_id":"7b84191b206694d4cb000042","treeId":"7c85f159d14f825f27000088","seq":11721278,"position":1,"parentId":"7b8419a8206694d4cb000041","content":"### Early Days\n* jars instead of zips\n* No portal\n - Hosted on Bintray \n - Users lost\n* Spring-security-core\n* Some plugins left behind \n - and in some cases thats a good thing."},{"_id":"7b840499206694d4cb000044","treeId":"7c85f159d14f825f27000088","seq":11732543,"position":2,"parentId":"7b8419a8206694d4cb000041","content":"### Today \n* Plugin Portal hosted by OCI\n - Pulls data from Bintray, GitHub\n* Open source\n - https://github.com/grails/grails3-plugins\n"},{"_id":"7b83b0ee206694d4cb000045","treeId":"7c85f159d14f825f27000088","seq":11721831,"position":3,"parentId":"7b8419a8206694d4cb000041","content":"### But new issues\n* Binary incompatibility an issues\n* Spring Boot package name changes"},{"_id":"7c85efb4d14f825f2700008e","treeId":"7c85f159d14f825f27000088","seq":11720896,"position":4,"parentId":"7c85f150d14f825f2700008a","content":"### Scheduling"},{"_id":"7c85ef60d14f825f2700008f","treeId":"7c85f159d14f825f27000088","seq":11724379,"position":1,"parentId":"7c85efb4d14f825f2700008e","content":"### @Scheduled\nSpringBoot already comes with one\n- Benefit from being based directly on spring-boot!"},{"_id":"7b80aaa9206694d4cb00007a","treeId":"7c85f159d14f825f27000088","seq":11732544,"position":1,"parentId":"7c85ef60d14f825f2700008f","content":"### Great for simple scheduling\n* Use it on a service method\n* Simple health check\n* Clear or refresh a cache\n* System jobs running at set interval\n`@Scheduled`\n"},{"_id":"7b809db1206694d4cb00007c","treeId":"7c85f159d14f825f27000088","seq":11724380,"position":2,"parentId":"7c85ef60d14f825f2700008f","content":"### Demo!\n\n`scheduling/scheduled`"},{"_id":"7c85ee82d14f825f27000091","treeId":"7c85f159d14f825f27000088","seq":11721018,"position":1.5,"parentId":"7c85efb4d14f825f2700008e","content":"### Quartz Plugin"},{"_id":"7b809c1a206694d4cb00007d","treeId":"7c85f159d14f825f27000088","seq":11724396,"position":1,"parentId":"7c85ee82d14f825f27000091","content":"### Quartz Plugin\n* Defacto Java Scheduler lib Quarts (of course)\n* Uses custom artifact class \n - create Job classes to schedule/execute.\n* Set custom, cron, or one off job executions easily."},{"_id":"7b8097b2206694d4cb00007f","treeId":"7c85f159d14f825f27000088","seq":11725133,"position":1.5,"parentId":"7c85ee82d14f825f27000091","content":"### Few rough edges\n* Default started, then didn't, then did\n* Transaction propagation\n* IDE Mark `grails-app/jobs` as src"},{"_id":"7ae40b93f15c8ae0ec0000e8","treeId":"7c85f159d14f825f27000088","seq":11724404,"position":4,"parentId":"7c85ee82d14f825f27000091","content":"### Demo!\n`scheduling/quartz`"},{"_id":"7c85ee26d14f825f27000092","treeId":"7c85f159d14f825f27000088","seq":11725312,"position":1.75,"parentId":"7c85efb4d14f825f2700008e","content":"### Schwartz\n![](https://www.filepicker.io/api/file/yvQTFgYqQL6wZSvz71Bw)"},{"_id":"7b8091c8206694d4cb000081","treeId":"7c85f159d14f825f27000088","seq":11740721,"position":1,"parentId":"7c85ee26d14f825f27000092","content":"### Also built on Quartz\n#### Different approach\n* Use traits on Grails Service to implement job instead of custom artifact.\n* Provide builder for easily building complex schedules.\n* Very well documented.\n* Generates db-migration changelog for you.\n* Helps you utilize Quartz without getting in the way.\nhttp://blog.agileorbit.com/grails-schwartz/latest/index.html#comparisonWithQuartzPlugin "},{"_id":"7adf4420f15c8ae0ec0000ed","treeId":"7c85f159d14f825f27000088","seq":11725145,"position":2,"parentId":"7c85ee26d14f825f27000092","content":"### Demo!\n`scheduling/schwartz`"},{"_id":"7b80846d206694d4cb000082","treeId":"7c85f159d14f825f27000088","seq":11725263,"position":2.375,"parentId":"7c85efb4d14f825f2700008e","content":"### Quartz Pro Tips\n* Can run in cluster\n* No way to stop schedulers cluster wide \n - have to do node by node - eg rolling deploy\n* Ensure each node has a unique id.\n* Can be an easy way to do some async work is to take a request and schedule a job"},{"_id":"7b817f10206694d4cb000069","treeId":"7c85f159d14f825f27000088","seq":11721076,"position":3,"parentId":"7c85efb4d14f825f2700008e","content":"### Jesque"},{"_id":"7b807cdd206694d4cb000084","treeId":"7c85f159d14f825f27000088","seq":11721056,"position":1,"parentId":"7b817f10206694d4cb000069","content":"### Similar to Ruby `resque`\n* Uses redis to queue background jobs.\n* Uses similar Job structure as quartz\n* Process work as it comes in."},{"_id":"7ae3ff33f15c8ae0ec0000e9","treeId":"7c85f159d14f825f27000088","seq":11721075,"position":4,"parentId":"7c85efb4d14f825f2700008e","content":"### quartz-admin & jesque-admin"},{"_id":"7b81b783206694d4cb000058","treeId":"7c85f159d14f825f27000088","seq":11721081,"position":4.5,"parentId":"7c85f150d14f825f2700008a","content":"### Security"},{"_id":"7b81b75f206694d4cb000059","treeId":"7c85f159d14f825f27000088","seq":11733512,"position":1,"parentId":"7b81b783206694d4cb000058","content":"### Spring Security"},{"_id":"7b806f658fb5a652f1000077","treeId":"7c85f159d14f825f27000088","seq":11732751,"position":0.5,"parentId":"7b81b75f206694d4cb000059","content":"### spring-security-core\n* All the power of Spring Security with sane defaults"},{"_id":"7ad67562f15c8ae0ec0000f1","treeId":"7c85f159d14f825f27000088","seq":11732711,"position":1,"parentId":"7b806f658fb5a652f1000077","content":"### s2-quickstart\n```\n$ grails s2-quickstart com.yourapp User Role\n\nCONFIGURE SUCCESSFUL in 7s\n| Creating User class 'User' and Role class 'Role' in package 'com.yourapp'\n| Rendered template PersonWithoutInjection.groovy.template to destination grails-app/domain/com/yourapp/User.groovy\n| Rendered template PersonPasswordEncoderListener.groovy.template to destination src/main/groovy/com/yourapp/UserPasswordEncoderListener.groovy\n| Rendered template Authority.groovy.template to destination grails-app/domain/com/yourapp/Role.groovy\n| Rendered template PersonAuthority.groovy.template to destination grails-app/domain/com/yourapp/UserRole.groovy\n| \n************************************************************\n* Created security-related domain classes. Your *\n* grails-app/conf/application.groovy has been updated with *\n* the class names of the configured domain classes; *\n* please verify that the values are correct. *\n************************************************************\n```"},{"_id":"7ad66a9df15c8ae0ec0000f2","treeId":"7c85f159d14f825f27000088","seq":11732728,"position":2,"parentId":"7b806f658fb5a652f1000077","content":"### Recent improvements\n* bcrypt rounds lowered in tests\n* Ignore Case for username prop\n - Lowers need for custom UserDetails service\n* Password encoder now event listener"},{"_id":"7ad657ddf15c8ae0ec0000f5","treeId":"7c85f159d14f825f27000088","seq":11732752,"position":3,"parentId":"7b806f658fb5a652f1000077","content":"### Extras\n* `<sec:*>` taglibs\n* User switching\n* Easily override defaults"},{"_id":"7b807649206694d4cb000085","treeId":"7c85f159d14f825f27000088","seq":11732916,"position":1,"parentId":"7b81b75f206694d4cb000059","content":"### spring-security-rest\n* Defaults Stateless JWT (Java Web Tokens)\n - Encodes || Encrypts user in token\n - Other storage (db, redis, memecached)\n"},{"_id":"7b806ef78fb5a652f1000078","treeId":"7c85f159d14f825f27000088","seq":11732743,"position":2,"parentId":"7b81b75f206694d4cb000059","content":"### spring-security-ui\n* While does run on Grails 3, not a lot of new development \n* Provides\n - Self Registration \n - Password Reset tokens\n - Admin UI for Mappings, Roles, etc"},{"_id":"7b8075cf206694d4cb000087","treeId":"7c85f159d14f825f27000088","seq":11732741,"position":3,"parentId":"7b81b75f206694d4cb000059","content":"### Others\n* spring-security-ldap\n* spring-security-cas\n* Supports all the major \"enterprise\" systems."},{"_id":"7ad61c63f15c8ae0ec0000f6","treeId":"7c85f159d14f825f27000088","seq":11732925,"position":3.5,"parentId":"7b81b75f206694d4cb000059","content":"### Demo!\n`security/spring-security`"},{"_id":"7b80758c206694d4cb000088","treeId":"7c85f159d14f825f27000088","seq":11733509,"position":4,"parentId":"7b81b75f206694d4cb000059","content":"### Honorable Mentions\n* Shiro\n - Simple ACL \n - http://grails-plugins.github.io/grails-spring-security-shiro\n* Enforcer\n - Simplify Spring-Security ACL\n - http://plugins.grails.org/plugin/virtualdogbert/org.grails.plugins:enforcer"},{"_id":"7c85e799d14f825f2700009a","treeId":"7c85f159d14f825f27000088","seq":11734038,"position":4.75,"parentId":"7c85f150d14f825f2700008a","content":"### Session Management\n* Move session out of container\n* Blue-green deploys\n* No more sticky sessions"},{"_id":"7c85e74dd14f825f2700009b","treeId":"7c85f159d14f825f27000088","seq":11734049,"position":1,"parentId":"7c85e799d14f825f2700009a","content":"### Spring-Session\n* Multiple stores - redis, jdbc, hazlecast, etc\n* Session is immutable by default for performance\n* Security API differences\n"},{"_id":"7b772c366b80baed9d0000a6","treeId":"7c85f159d14f825f27000088","seq":11734053,"position":1,"parentId":"7c85e74dd14f825f2700009b","content":"### Plugin or not?\n* `spring-session` works out of box\n* Easily add in mutable sessions yourself"},{"_id":"7ad52542f15c8ae0ec0000fb","treeId":"7c85f159d14f825f27000088","seq":11734106,"position":3,"parentId":"7c85e799d14f825f2700009a","content":"### Demo!\n`session/springsession`"},{"_id":"7c85e71cd14f825f2700009c","treeId":"7c85f159d14f825f27000088","seq":11734108,"position":4,"parentId":"7c85e799d14f825f2700009a","content":"### Cookie session Plugin\n* Stores on client, not server\n - Can add bulk to request"},{"_id":"7c85ed28d14f825f27000093","treeId":"7c85f159d14f825f27000088","seq":11721108,"position":5,"parentId":"7c85f150d14f825f2700008a","content":"### Database"},{"_id":"7c85ece6d14f825f27000094","treeId":"7c85f159d14f825f27000088","seq":11721111,"position":1,"parentId":"7c85ed28d14f825f27000093","content":"### Database Migration"},{"_id":"7b806e158fb5a652f100007a","treeId":"7c85f159d14f825f27000088","seq":11733767,"position":1,"parentId":"7c85ece6d14f825f27000094","content":"### Generates DB Schema\n* Inspects domain classes\n* Takes an educated guess at what to do.\n - Always review the changes.\n - Doesn't know your data access paths.\n* Underlying technology liqubase."},{"_id":"7c85ecacd14f825f27000095","treeId":"7c85f159d14f825f27000088","seq":11733756,"position":2,"parentId":"7c85ed28d14f825f27000093","content":"### FlyWay"},{"_id":"7b80699d8fb5a652f100007c","treeId":"7c85f159d14f825f27000088","seq":11733771,"position":1,"parentId":"7c85ecacd14f825f27000095","content":"* Uses versioned SQL scripts.\n* Run outside your application \n - depending on your deployment needs.\n* Not directly generated to your Domain Classes"},{"_id":"7ad5ba9ff15c8ae0ec0000f7","treeId":"7c85f159d14f825f27000088","seq":11733773,"position":2,"parentId":"7c85ecacd14f825f27000095","content":"### Demo!\n`db/dbm`\n`db/flyway`"},{"_id":"7c85e842d14f825f27000099","treeId":"7c85f159d14f825f27000088","seq":11733842,"position":3,"parentId":"7c85ed28d14f825f27000093","content":"### DB Logging\n* Simple, slightly useful\n```\ndatasource { logSql = true }\n```\n* HibernateStats (Interceptor)\n* p6spy"},{"_id":"7b8066908fb5a652f100007d","treeId":"7c85f159d14f825f27000088","seq":11733775,"position":1,"parentId":"7c85e842d14f825f27000099","content":"we all know sqlLogging= true, helpfull, but not always clear.\n"},{"_id":"7b8065198fb5a652f100007e","treeId":"7c85f159d14f825f27000088","seq":11638944,"position":2,"parentId":"7c85e842d14f825f27000099","content":"p6spy sits between your app and jdbc driver and intercepts & logs exact sql queries (with params!) unlike hibernate sqlLogging"},{"_id":"7b805f178fb5a652f100007f","treeId":"7c85f159d14f825f27000088","seq":11638945,"position":3,"parentId":"7c85e842d14f825f27000099","content":"hibernateStats is available, easily added to an dev-time interceptor"},{"_id":"7b805e608fb5a652f1000080","treeId":"7c85f159d14f825f27000088","seq":11638951,"position":4,"parentId":"7c85e842d14f825f27000099","content":"Good to have on in development to quickly see if you're making 1 or 1000 sql queries, or flushing the session 100's of times."},{"_id":"7ad5a745f15c8ae0ec0000f8","treeId":"7c85f159d14f825f27000088","seq":11733843,"position":5,"parentId":"7c85e842d14f825f27000099","content":"### Demo!\n`db/logging`"},{"_id":"7c7c144b24187af9d60000c8","treeId":"7c85f159d14f825f27000088","seq":11734013,"position":4,"parentId":"7c85ed28d14f825f27000093","content":"### dialects\n* Hibernate stores information nativity \n* PostgreSql Extensions\n - Native sequences\n - Native types (JSONB)\n* java.time.*\n\n"},{"_id":"7ad57a4bf15c8ae0ec0000f9","treeId":"7c85f159d14f825f27000088","seq":11733917,"position":3,"parentId":"7c7c144b24187af9d60000c8","content":"### Demo!\n`db/dialects`"},{"_id":"7b817c5f206694d4cb00006a","treeId":"7c85f159d14f825f27000088","seq":11738913,"position":4.5,"parentId":"7c85ed28d14f825f27000093","content":"### MultiTenant \n* Segregate data by Tenant\n - Schema, Identifier, DB, etc\n* Built into recent GORM\n* Quickly create SaaS solutions"},{"_id":"7ad4ecb0f15c8ae0ec0000fc","treeId":"7c85f159d14f825f27000088","seq":11734110,"position":1,"parentId":"7b817c5f206694d4cb00006a","content":"### Great Guides\n* Custom Tenant Resolver by JWT\n- http://guides.grails.org/grails-custom-security-tenant-resolver/guide/index.html\n* Database per Tenant Multi-Tenancy\n - http://guides.grails.org/database-per-tenant/guide/index.html\n* Configure Datasources dynamically while using DATABASE Multi-tenancy\n - http://guides.grails.org/grails-dynamic-multiple-datasources/guide/index.html"},{"_id":"7acb3122f15c8ae0ec0000fd","treeId":"7c85f159d14f825f27000088","seq":11738915,"position":2,"parentId":"7b817c5f206694d4cb00006a","content":"### Demo!\n`db/multitenant`"},{"_id":"7b824b50206694d4cb00004d","treeId":"7c85f159d14f825f27000088","seq":11739010,"position":5,"parentId":"7c85ed28d14f825f27000093","content":"### Honorable mentions\n* Audit logging\n* [Cache](http://grails-plugins.github.io/grails-cache/snapshot/guide/index.html) 4.0 / [EhCache](http://grails-plugins.github.io/grails-cache-ehcache/latest/) 3.x\n - Requires GORM 6.1.x+\n* [soft-delete](http://plugins.grails.org/plugin/danlobo/soft-delete)\n* [hibernate-search](http://plugins.grails.org/plugin/lgrignon/hibernate-search)\n* [hibernate-search](http://plugins.grails.org/plugin/lgrignon/hibernate-search)\n"},{"_id":"7c7d227224187af9d60000c4","treeId":"7c85f159d14f825f27000088","seq":11739387,"position":5.5,"parentId":"7c85f150d14f825f2700008a","content":"### Email"},{"_id":"7c7d225d24187af9d60000c5","treeId":"7c85f159d14f825f27000088","seq":11739015,"position":1,"parentId":"7c7d227224187af9d60000c4","content":"### grails-mail\n* Simple, easy to use.\n* Useful async feature to not tie up thread\n* Was in RC6 for 2 years without a release. \n - Until Yesterday :woot:"},{"_id":"7c7d220c24187af9d60000c6","treeId":"7c85f159d14f825f27000088","seq":11739022,"position":2,"parentId":"7c7d227224187af9d60000c4","content":"### spring-boot-starter-mail\n* Quite low level, lots of setup, etc\n* Example Usage\n - http://www.opencodez.com/java/java-mail-framework-using-spring-boot.htm"},{"_id":"7c7d21e424187af9d60000c7","treeId":"7c85f159d14f825f27000088","seq":11739319,"position":3,"parentId":"7c7d227224187af9d60000c4","content":"### spring-boot-email-tools \n* Multiple template engines\n* Scheduling/Priority\n* Redis queue\n* Builder interface\nhttps://github.com/ozimov/spring-boot-email-tools"},{"_id":"7b818abb206694d4cb000063","treeId":"7c85f159d14f825f27000088","seq":11739330,"position":4,"parentId":"7c7d227224187af9d60000c4","content":"### API email services\n* Allow business/marketing to control templates\n* grails-mailgun\n* grails-sendgrid"},{"_id":"7b81894b206694d4cb000064","treeId":"7c85f159d14f825f27000088","seq":11739380,"position":5,"parentId":"7c7d227224187af9d60000c4","content":"### Email Testing\n* Greenmail to catch emails.\n* [Wizer](https://github.com/voodoodyne/subethasmtp/blob/master/Wiser.md)\n - Easy to use in memory map of emails\n\n"},{"_id":"7aca92eef15c8ae0ec0000fe","treeId":"7c85f159d14f825f27000088","seq":11739381,"position":6,"parentId":"7c7d227224187af9d60000c4","content":"### Demo!\n`mail/emailtools`"},{"_id":"7c85e950d14f825f27000098","treeId":"7c85f159d14f825f27000088","seq":11739616,"position":7,"parentId":"7c85f150d14f825f2700008a","content":"### Front End"},{"_id":"7b824a7a206694d4cb00004e","treeId":"7c85f159d14f825f27000088","seq":11739397,"position":1,"parentId":"7c85e950d14f825f27000098","content":"### Asset Pipeline\n* Process & bundle all your js/css\n* Has own plugin ecosystem\n - http://www.asset-pipeline.com/plugins\n - Handlebars, less, sass, etc\n* Not only Grails\n - Ratpack, Spring Boot, etc"},{"_id":"7b775b8c6b80baed9d00008f","treeId":"7c85f159d14f825f27000088","seq":11739424,"position":1.5,"parentId":"7c85e950d14f825f27000098","content":"### Client Dependencies (gradle)\n* Resolves front end dependencies\n* Stores in `grails-app/assets/vendor`\n* Supports yarn, npm, and bower\nhttps://github.com/craigburke/client-dependencies-gradle"},{"_id":"7b818eb5206694d4cb000061","treeId":"7c85f159d14f825f27000088","seq":11739619,"position":2,"parentId":"7c85e950d14f825f27000098","content":"### node npm gradle plugin\n* Use the same tools as FE devs\n* Allows you to run npm command via gradle tasks\n - Sometimes best to use npm nativity for tasks\n* Isolate for easy CI process\nhttps://github.com/srs/gradle-node-plugin"},{"_id":"7aca8638e57bccf9910000f0","treeId":"7c85f159d14f825f27000088","seq":11739418,"position":3,"parentId":"7c85e950d14f825f27000098","content":"### Demo!\n`frontend/assets`"},{"_id":"7b816f9d206694d4cb00006c","treeId":"7c85f159d14f825f27000088","seq":11740020,"position":7.09375,"parentId":"7c85f150d14f825f2700008a","content":"### HTTP Clients\n* [http-builder-helper](https://plugins.grails.org/plugin/grails/http-builder-helper)\n - Only keeping updated for client\n\n#### Consider newer libs\n* [http-builder-ng](https://github.com/http-builder-ng/http-builder-ng)\n* [http-requests](https://github.com/budjb/http-requests)\n\n"},{"_id":"7c85d41cd14f825f270000a0","treeId":"7c85f159d14f825f27000088","seq":11740307,"position":7.140625,"parentId":"7c85f150d14f825f2700008a","content":"### Servlet Containers\n* Simple to switch\n* Chose which fits your needs\n* Easily update Tomcat\n\nhttps://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto-embedded-web-servers.html "},{"_id":"7ac7bd0be57bccf9910000f2","treeId":"7c85f159d14f825f27000088","seq":11740695,"position":2.5,"parentId":"7c85d41cd14f825f270000a0","content":""},{"_id":"7ac7bce2e57bccf9910000f3","treeId":"7c85f159d14f825f27000088","seq":11740696,"position":2.75,"parentId":"7c85d41cd14f825f270000a0","content":""},{"_id":"7ac8f649e57bccf9910000f1","treeId":"7c85f159d14f825f27000088","seq":11740313,"position":3,"parentId":"7c85d41cd14f825f270000a0","content":"### Demo!\n`container/undertow`\n`container/tc`"},{"_id":"7b818d7e206694d4cb000062","treeId":"7c85f159d14f825f27000088","seq":11740648,"position":7.1953125,"parentId":"7c85f150d14f825f2700008a","content":"### Configuration"},{"_id":"5a0db4e0aab34df40d8251cc","treeId":"7c85f159d14f825f27000088","seq":11739992,"position":0.5,"parentId":"7b818d7e206694d4cb000062","content":"### external-config\n* Defined similar to Grails 2\n```\ngrails.config.locations = [\n \"classpath:myconfig.groovy\",\n \"file:///etc/app/myconfig.yml\",\n \"~/.grails/myconfig.properties\",\n 'file:${catalina.base}/myconfig.groovy',\n]\n```\n* Includes yml<->groovy scripts\n```\ngrails yml-to-groovy-config [ymlFile] [optional outputFile]\ngrails groovy-to-yml-config [ymlFile] [optional outputFile]\n```\n\nhttps://plugins.grails.org/plugin/grails/external-config"},{"_id":"7b76ef6d6b80baed9d00016f","treeId":"7c85f159d14f825f27000088","seq":11739991,"position":1,"parentId":"7b818d7e206694d4cb000062","content":"### Spring Boot Externalized Conf\n\n* spring.config.location\n```\n--spring.config.location=classpath:/some.properties\n```\n* ENV_VARS\n```\nSERVER_PORT == server.port\n```\n* Many options\nhttps://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html"},{"_id":"7c85e4f6d14f825f2700009e","treeId":"7c85f159d14f825f27000088","seq":11750265,"position":8,"parentId":"7c85f150d14f825f2700008a","content":"### Spring Boot Starters\n* 80+ starters available from Spring Boot\n* Many work with grails out of box\n* Some don't YMMV\n - Autoconfig makes assumptions\n\nhttps://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-starters"},{"_id":"7abbf17ee57bccf9910000f9","treeId":"7c85f159d14f825f27000088","seq":11750322,"position":8.125,"parentId":"7c85f150d14f825f2700008a","content":"### Spring Boot Endpoints\n* Allows you to monitor & interact with your application. \n\nhttps://docs.spring.io/spring-boot/docs/1.4.x/reference/html/production-ready-endpoints.html"},{"_id":"7abbef58e57bccf9910000fa","treeId":"7c85f159d14f825f27000088","seq":11750324,"position":1,"parentId":"7abbf17ee57bccf9910000f9","content":"### Demo!\n`info/info`"},{"_id":"7c85d3d6d14f825f270000a1","treeId":"7c85f159d14f825f27000088","seq":11740802,"position":8.25,"parentId":"7c85f150d14f825f2700008a","content":"### Profiles\n* Brings configuration, dependencies together to start apps."},{"_id":"7b7690c16b80baed9d000233","treeId":"7c85f159d14f825f27000088","seq":11740809,"position":1.5,"parentId":"7c85d3d6d14f825f270000a1","content":"### list-profiles\n```\n➜ source grails list-profiles\n| Available Profiles\n--------------------\n* angular - A profile for creating Grails applications with Angular 2\n* rest-api - Profile for REST API applications\n* base - The base profile extended by other profiles\n* angularjs - A profile for creating applications using AngularJS\n* plugin - Profile for plugins designed to work across all profiles\n* profile - A profile for creating new Grails profiles\n* react - A profile for creating Grails applications with a React frontend\n* rest-api-plugin - Profile for REST API plugins\n* web - Profile for Web applications\n* web-jboss7 - A Profile for Creating a JBoss 7.1 EAP Project\n* web-plugin - Profile for Plugins designed for Web applications\n* webpack - A profile for creating applications with node-based frontends using webpack\n```"},{"_id":"7b7669e36b80baed9d000234","treeId":"7c85f159d14f825f27000088","seq":11750264,"position":1.75,"parentId":"7c85d3d6d14f825f270000a1","content":"* Mostly front end, but a few for custom deployments.\n* Just Jars, distribute the same way.\n* Create your own\n* NEW: Listed on docs site"},{"_id":"7c85cf4bd14f825f270000a4","treeId":"7c85f159d14f825f27000088","seq":11740945,"position":8.5,"parentId":"7c85f150d14f825f2700008a","content":"### Testing "},{"_id":"7b819a20206694d4cb00005f","treeId":"7c85f159d14f825f27000088","seq":11740935,"position":0.5,"parentId":"7c85cf4bd14f825f270000a4","content":"### Grails Testing\n* Based on Spock\n* Can run JUnit Rules as well!\n - https://stefanbirkner.github.io/system-rules/\n* [Spock up and Running](http://shop.oreilly.com/product/0636920038597.do)"},{"_id":"7ac7a40ae57bccf9910000f4","treeId":"7c85f159d14f825f27000088","seq":11740941,"position":0.75,"parentId":"7c85cf4bd14f825f270000a4","content":"### New Trait based framework (3.3+)\n* Make your own traits\n - eg: api testing/login\n\nOlder Mixin framework still works in 3.3+\n"},{"_id":"7c85cf26d14f825f270000a5","treeId":"7c85f159d14f825f27000088","seq":11750229,"position":1,"parentId":"7c85cf4bd14f825f270000a4","content":"### Test Data\n* [Dru](https://github.com/agorapulse/dru) \n - Data Reconstruction Utility\n* [seed-me](http://plugins.grails.org/plugin/bertramlabs/seed-me)\n* Build-test-data\n - Grails 3.3+ Support in progress"},{"_id":"7b77102a6b80baed9d0000b1","treeId":"7c85f159d14f825f27000088","seq":11750241,"position":2,"parentId":"7c85cf4bd14f825f270000a4","content":"### CodeNarc\n* Static code analysis\n* Just Gradle plugin now"},{"_id":"7b770e2c6b80baed9d0000b3","treeId":"7c85f159d14f825f27000088","seq":11747639,"position":3,"parentId":"7c85cf4bd14f825f270000a4","content":"### Coverage\n* [Clover](http://guides.grails.org/grails-code-coverage/guide/index.html)\n* [Cobertura](http://cobertura.github.io/cobertura/)\n* [Jacoco](https://docs.gradle.org/current/userguide/jacoco_plugin.html)\n\nMore info\nhttp://mariogarcia.github.io/blog/blog/2017/04/grails_coverage.html"},{"_id":"7abe1bede57bccf9910000f5","treeId":"7c85f159d14f825f27000088","seq":11747640,"position":3,"parentId":"7b770e2c6b80baed9d0000b3","content":"### Demo\n`testing/testing`"},{"_id":"7bd3acfdb13d5752b600002e","treeId":"7c85f159d14f825f27000088","seq":11740707,"position":12.75,"parentId":"7c85f150d14f825f2700008a","content":"### Sometimes a plugin is overkill."},{"_id":"7bd3acd6b13d5752b600002f","treeId":"7c85f159d14f825f27000088","seq":11750236,"position":1,"parentId":"7bd3acfdb13d5752b600002e","content":"Sometimes unnecessary even for a plugin\n* Stripe\n* [Excel](http://jameskleeh.com/groovy-excel-builder/)"},{"_id":"7b818438206694d4cb000066","treeId":"7c85f159d14f825f27000088","seq":11740718,"position":13.375,"parentId":"7c85f150d14f825f2700008a","content":"### Using the tools you have, build your own!"},{"_id":"7b8183e2206694d4cb000067","treeId":"7c85f159d14f825f27000088","seq":11750239,"position":1,"parentId":"7b818438206694d4cb000066","content":"Grails provides a lot of great tools to build the functionality you ended quickly \n- Interceptors\n * jsonp\n * rate limit\n - Bucket4J\n * pagination\n * CORS (Now in core)\n\n\n"},{"_id":"7b421ebcf2c89f9ffa0000e7","treeId":"7c85f159d14f825f27000088","seq":11666383,"position":2,"parentId":"7b818438206694d4cb000066","content":"Settings plugin (older 2.x plugin)\n* (1 domain class, 1 service & a cache)\n* So simple, just implement it."},{"_id":"7c85ead6d14f825f27000096","treeId":"7c85f159d14f825f27000088","seq":11750285,"position":13.6875,"parentId":"7c85f150d14f825f2700008a","content":"### Evaluating Plugins"},{"_id":"7b80b4d6206694d4cb000079","treeId":"7c85f159d14f825f27000088","seq":11720883,"position":1,"parentId":"7c85ead6d14f825f27000096","content":"### What to look for:\n* Release & tests automated?\n* When was last release?\n* Active maintainer?\n* Documentation up to date?\n* Review code you pull into your app!"},{"_id":"7c3c59004a66ca61d800006a","treeId":"7c85f159d14f825f27000088","seq":11746909,"position":14,"parentId":"7c85f150d14f825f2700008a","content":"### Where'd they go?\n* buildInfo -> actuator\n* Tomcat & Standalone -> starters \n* jquery & UI? -> client Dependencies or npm (via gradle)\n* Webflow -> x\n"},{"_id":"7b816733206694d4cb00006e","treeId":"7c85f159d14f825f27000088","seq":11740649,"position":14.125,"parentId":"7c85f150d14f825f2700008a","content":"### Conclusion"},{"_id":"7c85ea61d14f825f27000097","treeId":"7c85f159d14f825f27000088","seq":11666451,"position":2,"parentId":"7b816733206694d4cb00006e","content":"The Current Echosystem\n* good\n* bad\n* ugly"},{"_id":"7b81be61206694d4cb000052","treeId":"7c85f159d14f825f27000088","seq":11637202,"position":1,"parentId":"7c85ea61d14f825f27000097","content":"good"},{"_id":"7b81af1c206694d4cb00005b","treeId":"7c85f159d14f825f27000088","seq":11637252,"position":1,"parentId":"7b81be61206694d4cb000052","content":"Everything you need to build a website quickly is there."},{"_id":"7b81be42206694d4cb000053","treeId":"7c85f159d14f825f27000088","seq":11637203,"position":2,"parentId":"7c85ea61d14f825f27000097","content":"bad"},{"_id":"7b421b37f2c89f9ffa0000e8","treeId":"7c85f159d14f825f27000088","seq":11666392,"position":2,"parentId":"7b81be42206694d4cb000053","content":"Discover-ability\nYou need to know where to look, or come to this talk to find everything. "},{"_id":"7b81be32206694d4cb000054","treeId":"7c85f159d14f825f27000088","seq":11637204,"position":3,"parentId":"7c85ea61d14f825f27000097","content":"ugly"},{"_id":"7b81b106206694d4cb00005a","treeId":"7c85f159d14f825f27000088","seq":11637248,"position":1,"parentId":"7b81be32206694d4cb000054","content":"2 years between releases\nbroken links"},{"_id":"7b819e86206694d4cb00005d","treeId":"7c85f159d14f825f27000088","seq":11637317,"position":2,"parentId":"7b81be32206694d4cb000054","content":"Gorm versions break plugins more often"},{"_id":"7b41f7acf2c89f9ffa0000ea","treeId":"7c85f159d14f825f27000088","seq":11666456,"position":3,"parentId":"7b816733206694d4cb00006e","content":"Thanks"},{"_id":"7c85e62bd14f825f2700009d","treeId":"7c85f159d14f825f27000088","seq":11740704,"position":14.15234375,"parentId":"7c85f150d14f825f2700008a","content":"Deployment\n\n"},{"_id":"7b8363a1206694d4cb000047","treeId":"7c85f159d14f825f27000088","seq":11635821,"position":1,"parentId":"7c85e62bd14f825f2700009d","content":"My preferred way AWS elastic beanstalk"},{"_id":"7b423c8df2c89f9ffa0000db","treeId":"7c85f159d14f825f27000088","seq":11666277,"position":1,"parentId":"7b8363a1206694d4cb000047","content":"AWS gradle plugins make this trivial"},{"_id":"7b423c1ff2c89f9ffa0000dc","treeId":"7c85f159d14f825f27000088","seq":11666282,"position":2,"parentId":"7b8363a1206694d4cb000047","content":"What to know \n* Port 5000 SERVER_PORT\n* cant be executable jar - just regular jar\n - it explodes the jar before running it, so unzip wont work with exec\n* .ebextensions included in jar for cache, etc"},{"_id":"7b8362d3206694d4cb000048","treeId":"7c85f159d14f825f27000088","seq":11635827,"position":2,"parentId":"7c85e62bd14f825f2700009d","content":"Docker"},{"_id":"7b76edfe6b80baed9d000223","treeId":"7c85f159d14f825f27000088","seq":11641733,"position":1,"parentId":"7b8362d3206694d4cb000048","content":"docker gradle plugins"},{"_id":"7b4238a9f2c89f9ffa0000dd","treeId":"7c85f159d14f825f27000088","seq":11666284,"position":2,"parentId":"7b8362d3206694d4cb000048","content":"Easily build your jar into a container, and define your other infra containers as well"},{"_id":"7b8361a6206694d4cb000049","treeId":"7c85f159d14f825f27000088","seq":11641734,"position":3,"parentId":"7c85e62bd14f825f2700009d","content":"sshoogar"},{"_id":"7b76ed716b80baed9d000224","treeId":"7c85f159d14f825f27000088","seq":11641735,"position":1,"parentId":"7b8361a6206694d4cb000049","content":"Sometimes you just need to push a file somewhere and restart a service"},{"_id":"7b76ec986b80baed9d000225","treeId":"7c85f159d14f825f27000088","seq":11641736,"position":2,"parentId":"7b8361a6206694d4cb000049","content":"sshoogar - dsl for doing just that"},{"_id":"7b83615a206694d4cb00004a","treeId":"7c85f159d14f825f27000088","seq":11635830,"position":4,"parentId":"7c85e62bd14f825f2700009d","content":"War (gradle deploy plugin)"},{"_id":"7b76e59a6b80baed9d000226","treeId":"7c85f159d14f825f27000088","seq":11641743,"position":1,"parentId":"7b83615a206694d4cb00004a","content":"https://github.com/bmuschko/gradle-cargo-plugin"},{"_id":"7b423514f2c89f9ffa0000df","treeId":"7c85f159d14f825f27000088","seq":11666659,"position":2,"parentId":"7b83615a206694d4cb00004a","content":"Deploy war to Tomcat api"},{"_id":"7b816946206694d4cb00006d","treeId":"7c85f159d14f825f27000088","seq":11641744,"position":5,"parentId":"7c85e62bd14f825f2700009d","content":"Deploy info"},{"_id":"7b76e51c6b80baed9d000227","treeId":"7c85f159d14f825f27000088","seq":11641745,"position":1,"parentId":"7b816946206694d4cb00006d","content":" (git commit,)"},{"_id":"7b76e4fa6b80baed9d000228","treeId":"7c85f159d14f825f27000088","seq":11641746,"position":2,"parentId":"7b816946206694d4cb00006d","content":"grailsinfocontributor"},{"_id":"7b815c8b206694d4cb00006f","treeId":"7c85f159d14f825f27000088","seq":11637455,"position":6,"parentId":"7c85e62bd14f825f2700009d","content":"Logging slf4j\nremote api"},{"_id":"7b4233d6f2c89f9ffa0000e0","treeId":"7c85f159d14f825f27000088","seq":11666300,"position":1,"parentId":"7b815c8b206694d4cb00006f","content":"Log Accutator API, change logging on the fly"},{"_id":"7b42312df2c89f9ffa0000e1","treeId":"7c85f159d14f825f27000088","seq":11666303,"position":2,"parentId":"7b815c8b206694d4cb00006f","content":"Ship logs off system!\nSplunk, ELK, Papertrail, anywhere!"},{"_id":"7b81fa20206694d4cb000051","treeId":"7c85f159d14f825f27000088","seq":11740701,"position":14.1796875,"parentId":"7c85f150d14f825f2700008a","content":"Views"},{"_id":"7b7738446b80baed9d000099","treeId":"7c85f159d14f825f27000088","seq":11641600,"position":1,"parentId":"7b81fa20206694d4cb000051","content":"GSP (Yes it's a plugin now)"},{"_id":"7b7737ff6b80baed9d00009a","treeId":"7c85f159d14f825f27000088","seq":11641601,"position":2,"parentId":"7b81fa20206694d4cb000051","content":"Gson (json views)"},{"_id":"7b7737636b80baed9d00009c","treeId":"7c85f159d14f825f27000088","seq":11641603,"position":1,"parentId":"7b7737ff6b80baed9d00009a","content":"Static type, speed, inheritance, etc"},{"_id":"7b74ebc66b80baed9d000241","treeId":"7c85f159d14f825f27000088","seq":11644442,"position":2,"parentId":"7b7737ff6b80baed9d00009a","content":"hal"},{"_id":"7b74eba36b80baed9d000242","treeId":"7c85f159d14f825f27000088","seq":11644443,"position":3,"parentId":"7b7737ff6b80baed9d00009a","content":"json-api"},{"_id":"7b7737896b80baed9d00009b","treeId":"7c85f159d14f825f27000088","seq":11641605,"position":3,"parentId":"7b81fa20206694d4cb000051","content":"Markup"},{"_id":"7b77368a6b80baed9d00009d","treeId":"7c85f159d14f825f27000088","seq":11641606,"position":4,"parentId":"7b81fa20206694d4cb000051","content":"grails-vaadin-plugin"},{"_id":"7b81857d206694d4cb000065","treeId":"7c85f159d14f825f27000088","seq":11740317,"position":14.234375,"parentId":"7c85f150d14f825f2700008a","content":"Documentation\n"},{"_id":"7b7759e56b80baed9d000090","treeId":"7c85f159d14f825f27000088","seq":11740303,"position":1,"parentId":"7b81857d206694d4cb000065","content":"asciidoctorj\n"},{"_id":"7b77573c6b80baed9d000093","treeId":"7c85f159d14f825f27000088","seq":11650626,"position":1,"parentId":"7b7759e56b80baed9d000090","content":"Easy & powerful writing tool\nGradle integration"},{"_id":"7b7756c86b80baed9d000094","treeId":"7c85f159d14f825f27000088","seq":11641549,"position":2,"parentId":"7b7759e56b80baed9d000090","content":"Keep documentation with your code!\n* Changelogs\n* Docs"},{"_id":"7b7755c36b80baed9d000095","treeId":"7c85f159d14f825f27000088","seq":11641551,"position":3,"parentId":"7b7759e56b80baed9d000090","content":"Publish to static site (s3/etc)"},{"_id":"7b7759b46b80baed9d000091","treeId":"7c85f159d14f825f27000088","seq":11641544,"position":2,"parentId":"7b81857d206694d4cb000065","content":"spring-restdocs\n"},{"_id":"7b77472b6b80baed9d000096","treeId":"7c85f159d14f825f27000088","seq":11641583,"position":1,"parentId":"7b7759b46b80baed9d000091","content":"https://github.com/jlstrater/grails-spring-restdocs-example"},{"_id":"7b7759946b80baed9d000092","treeId":"7c85f159d14f825f27000088","seq":11641594,"position":3,"parentId":"7b81857d206694d4cb000065","content":"swagger"},{"_id":"7b773ae16b80baed9d000097","treeId":"7c85f159d14f825f27000088","seq":11641595,"position":1,"parentId":"7b7759946b80baed9d000092","content":"https://github.com/ajay-kmr/swagger"},{"_id":"7b773ac86b80baed9d000098","treeId":"7c85f159d14f825f27000088","seq":11641596,"position":2,"parentId":"7b7759946b80baed9d000092","content":"Or just use the library itself"},{"_id":"7c85e37cd14f825f2700009f","treeId":"7c85f159d14f825f27000088","seq":11739970,"position":14.34375,"parentId":"7c85f150d14f825f2700008a","content":"Metrics"},{"_id":"7b824c5e206694d4cb00004b","treeId":"7c85f159d14f825f27000088","seq":11636419,"position":1,"parentId":"7c85e37cd14f825f2700009f","content":"http://plugins.grails.org/plugin/grails/dropwizard-metrics"},{"_id":"7b824c41206694d4cb00004c","treeId":"7c85f159d14f825f27000088","seq":11636422,"position":2,"parentId":"7c85e37cd14f825f2700009f","content":"http://plugins.grails.org/plugin/sergiomichels/grails-melody-plugin"},{"_id":"7b81926a206694d4cb000060","treeId":"7c85f159d14f825f27000088","seq":11637342,"position":3,"parentId":"7c85e37cd14f825f2700009f","content":"statsd metrics"},{"_id":"7b76d76f6b80baed9d00022c","treeId":"7c85f159d14f825f27000088","seq":11641753,"position":3.5,"parentId":"7c85e37cd14f825f2700009f","content":"Sentry"},{"_id":"7b76d87f6b80baed9d00022a","treeId":"7c85f159d14f825f27000088","seq":11641750,"position":4,"parentId":"7c85e37cd14f825f2700009f","content":"Datadog"},{"_id":"7b76d84f6b80baed9d00022b","treeId":"7c85f159d14f825f27000088","seq":11641751,"position":5,"parentId":"7c85e37cd14f825f2700009f","content":"Accutaotrs monitoring "},{"_id":"7b76d7056b80baed9d00022d","treeId":"7c85f159d14f825f27000088","seq":11641755,"position":1,"parentId":"7b76d84f6b80baed9d00022b","content":"Health"},{"_id":"7b76d6d86b80baed9d00022e","treeId":"7c85f159d14f825f27000088","seq":11641756,"position":2,"parentId":"7b76d84f6b80baed9d00022b","content":"Build your own"},{"_id":"7b76d6b86b80baed9d00022f","treeId":"7c85f159d14f825f27000088","seq":11641765,"position":3,"parentId":"7b76d84f6b80baed9d00022b","content":"(careful, up/down one) - https://tedvinke.wordpress.com/2017/10/25/why-is-springs-health-down-down-up-up-up-and-down-again/"},{"_id":"7b81b85b206694d4cb000056","treeId":"7c85f159d14f825f27000088","seq":11739951,"position":14.5625,"parentId":"7c85f150d14f825f2700008a","content":"Web (Other)"},{"_id":"7b81b845206694d4cb000057","treeId":"7c85f159d14f825f27000088","seq":11650625,"position":1,"parentId":"7b81b85b206694d4cb000056","content":"Cookie\n- Cookie spec, creating Secure (new)\nWebSockets(grails-spring-websocket)\n- rabbitmq\ngorm-graphql"},{"_id":"7b820ee3206694d4cb00004f","treeId":"7c85f159d14f825f27000088","seq":11739953,"position":14.78125,"parentId":"7c85f150d14f825f2700008a","content":"scaffolding"},{"_id":"7b76c3ce6b80baed9d000231","treeId":"7c85f159d14f825f27000088","seq":11666304,"position":0.25,"parentId":"7b820ee3206694d4cb00004f","content":"Not really something I use, anyways right after building"},{"_id":"7b76c4526b80baed9d000230","treeId":"7c85f159d14f825f27000088","seq":11641766,"position":0.5,"parentId":"7b820ee3206694d4cb00004f","content":"scaffolding"},{"_id":"7b820e4a206694d4cb000050","treeId":"7c85f159d14f825f27000088","seq":11637129,"position":1,"parentId":"7b820ee3206694d4cb00004f","content":"angular-scaffolding\n"}],"tree":{"_id":"7c85f159d14f825f27000088","name":"Better know a Grails plugin","publicUrl":"better-know-a-grails-plugin"}}