Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Mastering Grails 3
Plugins
Álvaro Sánchez-Mariscal
Álvaro Sánchez-Mariscal
Software Engineer
Grails Development Team
sanchezmariscala@ociweb.com
OCI is the new home of Grails
More at ociweb.com/grails
The Basics
Creating a Grails 3 plugin
$ grails create-plugin myWebPlugin
| Plugin created at /private/tmp/myWebPlugin
$ grails create...
Understanding profiles
• A profile defines:
• Project’s build.gradle.
• Commands: create-domain-class,
run-app, etc.
• Featur...
plugin vs. web-plugin
Trim your plugin!
Keep clean
• Start with the plugin profile whenever
possible.
• Remove empty and/or unwanted files/
folders.
• Otherwise, th...
The burtbeckwith bot
The burtbeckwith bot
• Watches messy plugin repos and sends a PR
to clean them up.
• Dozens of pull requests in the last 3...
The minimal plugin
• Folder containing:
• build.gradle
• src/main/groovy with plugin descriptor.
• grails-app/init with an...
The plugin descriptor
• A class inside src/main/groovy. Extends
grails.plugins.Plugin.
• Can override methods to define beh...
Demo - Creating a
plugin
Plugin features
Plugin configuration
• A plugin can define:
• Configuration values for the host Grails app.
• One of plugin.yml or plugin.gr...
Excluding content
• In the plugin descriptor:
• In build.gradle:
// resources that are excluded from plugin packaging

def...
Command Line extensions
• Use create-script for code generation
commands.
• Runnable with the Grails CLI.
• Use create-com...
Scripts
• Base class:
org.grails.cli.profile.commands.script.GroovyScriptCommand
import org.grails.cli.interactive.complet...
Commands (3.1)
import grails.dev.commands.ApplicationCommand

import grails.dev.commands.ExecutionContext



class MyComma...
Commands (3.2)
import grails.dev.commands.ApplicationCommand

import grails.dev.commands.ExecutionContext



class MyComma...
Enhancing artefacts
import grails.artefact.Enhances

import groovy.transform.CompileStatic



@Enhances(['Controller', 'Se...
Bean definition
• In Grails 2.x, you could only define beans in
doWithSpring.
• With Grails 3.x, you can also leverage from
...
Auto-configuration
package com.acme.myplugin



import org.springframework.context.annotation.Bean

import org.springframew...
Auto-configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=

com.acme.myplugin.MyPluginAutoConfigura...
Auto-configuration
• Place services in src/main/groovy.
• Grails registers automatically grails-app/
services/*.
• Users wi...
Classpath scanning
• The Grails way:
class Application extends GrailsAutoConfiguration {

static void main(String[] args) ...
Classpath scanning
• The Spring Boot way:
@ComponentScan(basePackages = ['com.acme.myplugin'])

class Application extends ...
Modularisation
Modularisation
• If your plugin becomes to grow, you might
end up creating a monolith.
• You can modularise your plugins a...
Modularisation
Monolithic plugin
Multi-module plugin
Modularisation
• Benefits:
• Optional dependencies.
• Smaller JAR files.
• Build logic reuse.
Modularisation setup
• settings.gradle:
include ‘myPlugin-core', ‘myPlugin-domain' //etc
Modularisation setup
• Root build.gradle:
allprojects {

apply plugin:"idea"

}



subprojects { Project project ->

ext {...
Modularisation setup
• Sub-module build.gradle:
dependencyManagement {

imports {

mavenBom "org.grails:grails-bom:$grails...
Publishing
Artifact publication
• Snapshots:
• Using the artifactory Gradle plugin.
• Published in OJO (oss.jfrog.org).
• Releases:
•...
Bintray setup
• For Snapshots:
Build setup
artifactory {

contextUrl = 'http://oss.jfrog.org'

publish {

repository {

repoKey = 'oss-s...
grailsPublish {

user = bintrayUser

key = bintrayKey

portalUser = pluginPortalUser

portalPassword = pluginPortalPasswor...
Build setup
• Define rootProject.name in
settings.gradle.
• Define credentials in
~/.gradle/gradle.properties.
Running it
• Snapshot publishing:
• Release publishing:
$ ./gradlew artifactoryPublish
$ ./gradlew publishPlugin notifyPlu...
Plugin portals
• Once your packages are published in your
Bintray repo, go to https://
bintray.com/grails/plugins and clic...
Testing
Testing with a profile
• You can create a profile and use it as a TCK
for your plugin:
• Create test apps from that profile....
Profile descriptor
description: Creates a test app for Spring Security REST plugin

build:

excludes:

- org.grails.grails-...
Feature descriptor
description: First configuration of GORM

dependencies:

build:

- "org.grails.plugins:hibernate4:5.0.0...
Build setup
task generateProfileConfig << {

copy {

from 'profile.yml.tmpl'

into '.'

rename { String fileName -> fileNa...
Build setup
cd build
for feature in `ls ../spring-security-rest-testapp-profile/features/`; do

grails create-app -profile...
Skeleton
• Put in the skeleton all your test files and
resources.
• You can use features to have different sets of
tests, r...
Demo - The Spring
Security REST plugin
Thank you!
Álvaro Sánchez-Mariscal
Mastering Grails 3 Plugins - G3 Summit 2016
Upcoming SlideShare
Loading in …5
×

Mastering Grails 3 Plugins - G3 Summit 2016

261 views

Published on

With Grails 3, the plugin development experience changes a little bit compared to that of Grails 2. In this talk, Álvaro (member of the Grails team at OCI, Grails committer and author of several plugins) will cover several topics to understand how plugins work in Grails 3, focusing on best practices.

The session is structured as a set of tips and tricks with code samples in the following areas: modularisation, build system, testing and publishing.

Published in: Software
  • Be the first to comment

Mastering Grails 3 Plugins - G3 Summit 2016

  1. 1. Mastering Grails 3 Plugins Álvaro Sánchez-Mariscal
  2. 2. Álvaro Sánchez-Mariscal Software Engineer Grails Development Team sanchezmariscala@ociweb.com
  3. 3. OCI is the new home of Grails More at ociweb.com/grails
  4. 4. The Basics
  5. 5. Creating a Grails 3 plugin $ grails create-plugin myWebPlugin | Plugin created at /private/tmp/myWebPlugin $ grails create-plugin myPlugin -profile plugin | Plugin created at /private/tmp/myPlugin
  6. 6. Understanding profiles • A profile defines: • Project’s build.gradle. • Commands: create-domain-class, run-app, etc. • Features: hibernate, json-views, etc. • Skeleton: files and folders.
  7. 7. plugin vs. web-plugin
  8. 8. Trim your plugin!
  9. 9. Keep clean • Start with the plugin profile whenever possible. • Remove empty and/or unwanted files/ folders. • Otherwise, the burtbeckwith bot will send you a cleanup pull request!
  10. 10. The burtbeckwith bot
  11. 11. The burtbeckwith bot • Watches messy plugin repos and sends a PR to clean them up. • Dozens of pull requests in the last 3 months! • Likely hundreds in the last years!
  12. 12. The minimal plugin • Folder containing: • build.gradle • src/main/groovy with plugin descriptor. • grails-app/init with an Application class. • Everything else can be removed.
  13. 13. The plugin descriptor • A class inside src/main/groovy. Extends grails.plugins.Plugin. • Can override methods to define behaviour in the plugin lifecycle. • Syntax has changed a bit from Grails 2.
  14. 14. Demo - Creating a plugin
  15. 15. Plugin features
  16. 16. Plugin configuration • A plugin can define: • Configuration values for the host Grails app. • One of plugin.yml or plugin.groovy. • Configuration for running the plugin as an application, to test it. • application.yml / application.groovy.
  17. 17. Excluding content • In the plugin descriptor: • In build.gradle: // resources that are excluded from plugin packaging
 def pluginExcludes = [
 '**/com/example/myplugin/tests/**'
 ] jar {
 exclude 'com/example/myplugin/tests/**/**'
 }
  18. 18. Command Line extensions • Use create-script for code generation commands. • Runnable with the Grails CLI. • Use create-command for interacting with a loaded Grails application. • Runnable with the Grails CLI or as a Gradle task.
  19. 19. Scripts • Base class: org.grails.cli.profile.commands.script.GroovyScriptCommand import org.grails.cli.interactive.completers.DomainClassCompleter
 
 description( "Generates a controller that performs REST operations" ) {
 usage "grails generate-resource-controller [DOMAIN CLASS]"
 argument name:'Domain Class', description:"The name of the domain class", required:true
 completer DomainClassCompleter
 flag name:'force', description:"Whether to overwrite existing files"
 }
 
 if(args) {
 generateController(*args)
 generateViews(*args)
 generateUnitTest(*args)
 generateFunctionalTest(*args)
 } else {
 error "No domain class specified"
 }
  20. 20. Commands (3.1) import grails.dev.commands.ApplicationCommand
 import grails.dev.commands.ExecutionContext
 
 class MyCommand implements ApplicationCommand {
 
 @Override
 boolean handle(ExecutionContext ctx) {
 def dataSource = applicationContext.getBean(DataSource)
 //Run some SQL...
 
 return true
 }
 
 }
  21. 21. Commands (3.2) import grails.dev.commands.ApplicationCommand
 import grails.dev.commands.ExecutionContext
 
 class MyCommand implements GrailsApplicationCommand {
 
 @Override
 boolean handle() {
 def dataSource = applicationContext.getBean(DataSource)
 //Run some SQL...
 
 return true
 }
 
 }
  22. 22. Enhancing artefacts import grails.artefact.Enhances
 import groovy.transform.CompileStatic
 
 @Enhances(['Controller', 'Service'])
 @CompileStatic
 trait DateSupport {
 
 Date now() {
 return new Date()
 }
 
 }
  23. 23. Bean definition • In Grails 2.x, you could only define beans in doWithSpring. • With Grails 3.x, you can also leverage from Spring Boot auto-configuration. • Beware of the classpath scanning!
  24. 24. Auto-configuration package com.acme.myplugin
 
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Configuration
 
 @Configuration
 class MyPluginAutoConfiguration {
 
 @Bean
 MyService myService() {
 new MyService()
 }
 
 }
  25. 25. Auto-configuration org.springframework.boot.autoconfigure.EnableAutoConfiguration=
 com.acme.myplugin.MyPluginAutoConfiguration src/main/groovy/resources/META-INF/spring.factories
  26. 26. Auto-configuration • Place services in src/main/groovy. • Grails registers automatically grails-app/ services/*. • Users will have to configure classpath scanning. • By default, scanning is limited to the app, not dependent JARS.
  27. 27. Classpath scanning • The Grails way: class Application extends GrailsAutoConfiguration {
 static void main(String[] args) {
 GrailsApp.run(Application, args)
 }
 
 @Override
 protected boolean limitScanningToApplication() {
 return false
 }
 
 @Override
 Collection<String> packageNames() {
 return ['com.acme.myplugin']
 }
 
 
 }
  28. 28. Classpath scanning • The Spring Boot way: @ComponentScan(basePackages = ['com.acme.myplugin'])
 class Application extends GrailsAutoConfiguration {
 static void main(String[] args) {
 GrailsApp.run(Application, args)
 }
 }
  29. 29. Modularisation
  30. 30. Modularisation • If your plugin becomes to grow, you might end up creating a monolith. • You can modularise your plugins as you would do with your apps.
  31. 31. Modularisation Monolithic plugin Multi-module plugin
  32. 32. Modularisation • Benefits: • Optional dependencies. • Smaller JAR files. • Build logic reuse.
  33. 33. Modularisation setup • settings.gradle: include ‘myPlugin-core', ‘myPlugin-domain' //etc
  34. 34. Modularisation setup • Root build.gradle: allprojects {
 apply plugin:"idea"
 }
 
 subprojects { Project project ->
 ext {
 grailsVersion = project.grailsVersion
 gradleWrapperVersion = project.gradleWrapperVersion
 }
 
 repositories {
 //Common repos
 }
 
 version "1.0.0.M1"
 group "org.grails.plugins"
 
 apply plugin: "org.grails.grails-plugin"
 
 dependencies {
 //Common deps
 }
 }
  35. 35. Modularisation setup • Sub-module build.gradle: dependencyManagement {
 imports {
 mavenBom "org.grails:grails-bom:$grailsVersion"
 }
 applyMavenExclusions false
 }
 
 dependencies {
 compile project(":myPlugin-core")
 
 compile "com.example:library:1.0.0"
 }
  36. 36. Publishing
  37. 37. Artifact publication • Snapshots: • Using the artifactory Gradle plugin. • Published in OJO (oss.jfrog.org). • Releases: • Using the grails-plugin-publish Gradle plugin. • Published in Bintray.
  38. 38. Bintray setup
  39. 39. • For Snapshots: Build setup artifactory {
 contextUrl = 'http://oss.jfrog.org'
 publish {
 repository {
 repoKey = 'oss-snapshot-local'
 username = bintrayUser
 password = bintrayKey
 }
 defaults {
 publications('maven')
 }
 }
 }
 
 artifactoryPublish {
 dependsOn sourcesJar, javadocJar
 }
  40. 40. grailsPublish {
 user = bintrayUser
 key = bintrayKey
 portalUser = pluginPortalUser
 portalPassword = pluginPortalPassword
 
 repo = 'plugins'
 githubSlug = 'alvarosanchez/my-plugin'
 license = 'APACHE 2.0'
 title = "My Plugin"
 desc = "A very cool Grails plugin"
 developers = [
 alvarosanchez: "Alvaro Sanchez-Mariscal"
 ]
 } • For Releases: Build setup
  41. 41. Build setup • Define rootProject.name in settings.gradle. • Define credentials in ~/.gradle/gradle.properties.
  42. 42. Running it • Snapshot publishing: • Release publishing: $ ./gradlew artifactoryPublish $ ./gradlew publishPlugin notifyPluginPortal
  43. 43. Plugin portals • Once your packages are published in your Bintray repo, go to https:// bintray.com/grails/plugins and click on “Include my package”. • Grails 3: http://plugins.grails.org/ • Grails 2: http://grails.org/plugins
  44. 44. Testing
  45. 45. Testing with a profile • You can create a profile and use it as a TCK for your plugin: • Create test apps from that profile. • Apps come with a set of tests. • Use features to test different configurations.
  46. 46. Profile descriptor description: Creates a test app for Spring Security REST plugin
 build:
 excludes:
 - org.grails.grails-core
 dependencies:
 compile:
 - "org.grails.plugins:spring-security-rest:${pluginVersion}"
 - "org.grails:grails-datastore-rest-client:5.0.0.RC3"
 testCompile:
 - "com.codeborne:phantomjsdriver:1.2.1"
 - "org.seleniumhq.selenium:selenium-api:2.47.1"
 - "org.seleniumhq.selenium:selenium-firefox-driver:2.47.1" profile.yml.tmpl
  47. 47. Feature descriptor description: First configuration of GORM
 dependencies:
 build:
 - "org.grails.plugins:hibernate4:5.0.0.RC2"
 compile:
 - "org.grails.plugins:hibernate4"
 - "org.hibernate:hibernate-ehcache"
 - "org.grails.plugins:spring-security-rest-gorm:${pluginVersion}"
 runtime:
 - "com.h2database:h2" features/gorm1/feature.yml.tmpl
  48. 48. Build setup task generateProfileConfig << {
 copy {
 from 'profile.yml.tmpl'
 into '.'
 rename { String fileName -> fileName.replaceAll '.tmpl', '' }
 expand pluginVersion: project.version
 }
 
 file('features').eachDir { feature ->
 copy {
 from "features/${feature.name}/feature.yml.tmpl"
 into "features/${feature.name}/"
 rename { String fileName -> fileName.replaceAll '.tmpl', '' }
 expand pluginVersion: project.version
 }
 }
 }
 
 compileProfile.dependsOn generateProfileConfig
  49. 49. Build setup cd build for feature in `ls ../spring-security-rest-testapp-profile/features/`; do
 grails create-app -profile org.grails.plugins:spring-security-rest-testapp-profile:$pluginVersion -features $feature $feature 
 done generate-test-apps.sh File build = new File('build')
 if (build.exists()) {
 build.eachDir {
 include it.path
 }
 }
 settings.gradle
  50. 50. Skeleton • Put in the skeleton all your test files and resources. • You can use features to have different sets of tests, resources and configuration. • Define global configuration values in profile’s root skeleton folder.
  51. 51. Demo - The Spring Security REST plugin
  52. 52. Thank you! Álvaro Sánchez-Mariscal

×