Skip to content

Commit

Permalink
updated documentation on plugins for Grails 3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
graemerocher committed Feb 24, 2015
1 parent 9968541 commit 68be830
Show file tree
Hide file tree
Showing 27 changed files with 118 additions and 808 deletions.
1 change: 1 addition & 0 deletions resources/doc.properties
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ alias.XML=6.1.7 XML and JSON Responses

# javadoc alias used to link to external javadocs in the form [HttpServletRequest|api:javax.servlet.http.HttpServletRequest]
api.org.hibernate=http://docs.jboss.org/hibernate/core/3.6/javadocs
api.org.springframework.boot=http://docs.spring.io/spring-boot/docs/current/api
api.org.springframework=http://docs.spring.io/spring/docs/4.0.x/javadoc-api
api.javax.servlet=http://download.oracle.com/javaee/1.4/api
api.java.=http://docs.oracle.com/javase/6/docs/api
Expand Down
27 changes: 15 additions & 12 deletions src/en/guide/plugins/addingDynamicMethodsAtRuntime.gdoc
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
h4. The Basics

Grails plugins let you register dynamic methods with any Grails-managed or other class at runtime. This work is done in a @doWithDynamicMethods@ closure.
Grails plugins let you register dynamic methods with any Grails-managed or other class at runtime. This work is done in a @doWithDynamicMethods@ method.

For Grails-managed classes like controllers, tag libraries and so forth you can add methods, constructors etc. using the "ExpandoMetaClass":http://groovy.codehaus.org/ExpandoMetaClass mechanism by accessing each controller's [MetaClass|api:groovy.lang.MetaClass]:
{note}
Note that Grails 3.x features newer features such as traits that are usable from code compiled with @CompileStatic@. It is recommended that dynamic behavior is only added for cases that are not possible with traits.
{note}

{code:java}
class ExamplePlugin {
def doWithDynamicMethods = { applicationContext ->
for (controllerClass in application.controllerClasses) {
class ExamplePlugin extends Plugin {
void doWithDynamicMethods() {
for (controllerClass in grailsApplication.controllerClasses) {
controllerClass.metaClass.myNewMethod = {-> println "hello world" }
}
}
Expand All @@ -19,9 +21,10 @@ In this case we use the implicit application object to get a reference to all of
For example we can add a new method @swapCase@ to @java.lang.String@:

{code:java}
class ExamplePlugin {
class ExamplePlugin extends Plugin {

def doWithDynamicMethods = { applicationContext ->
@Override
void doWithDynamicMethods() {
String.metaClass.swapCase = {->
def sb = new StringBuilder()
delegate.each {
Expand All @@ -44,11 +47,11 @@ The @doWithDynamicMethods@ closure gets passed the Spring @ApplicationContext@ i
{code:java}
import org.springframework.orm.hibernate3.HibernateTemplate

class ExampleHibernatePlugin {
class ExampleHibernatePlugin extends Plugin{

def doWithDynamicMethods = { applicationContext ->
void doWithDynamicMethods() {

for (domainClass in application.domainClasses) {
for (domainClass in grailsApplication.domainClasses) {

domainClass.metaClass.static.load = { Long id->
def sf = applicationContext.sessionFactory
Expand All @@ -65,8 +68,8 @@ Also because of the autowiring and dependency injection capability of the Spring
{code:java}
class MyConstructorPlugin {

def doWithDynamicMethods = { applicationContext ->
for (domainClass in application.domainClasses) {
void doWithDynamicMethods()
for (domainClass in grailsApplication.domainClasses) {
domainClass.metaClass.constructor = {->
return applicationContext.getBean(domainClass.name)
}
Expand Down
Empty file.
6 changes: 3 additions & 3 deletions src/en/guide/plugins/evaluatingConventions.gdoc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Before looking at providing runtime configuration based on conventions you first need to understand how to evaluate those conventions from a plugin. Every plugin has an implicit @application@ variable which is an instance of the [GrailsApplication|api:org.codehaus.groovy.grails.commons.GrailsApplication] interface.
Before looking at providing runtime configuration based on conventions you first need to understand how to evaluate those conventions from a plugin. Every plugin has an implicit @application@ variable which is an instance of the [GrailsApplication|api:grails.core.GrailsApplication] interface.

The @GrailsApplication@ interface provides methods to evaluate the conventions within the project and internally stores references to all artifact classes within your application.

Artifacts implement the [GrailsClass|api:org.codehaus.groovy.grails.commons.GrailsClass] interface, which represents a Grails resource such as a controller or a tag library. For example to get all @GrailsClass@ instances you can do:
Artifacts implement the [GrailsClass|api:grails.core.GrailsClass] interface, which represents a Grails resource such as a controller or a tag library. For example to get all @GrailsClass@ instances you can do:

{code:java}
for (grailsClass in application.allClasses) {
Expand Down Expand Up @@ -37,4 +37,4 @@ The @GrailsClass@ interface has a number of useful methods that let you further
* @getNaturalName@ - Returns the name of the property in natural terms (e.g. 'lastName' becomes 'Last Name')
* @getPackageName@ - Returns the package name

For a full reference refer to the [javadoc API|api:org.codehaus.groovy.grails.commons.GrailsClass].
For a full reference refer to the [javadoc API|api:grails.core.GrailsClass].
104 changes: 42 additions & 62 deletions src/en/guide/plugins/hookingIntoRuntimeConfiguration.gdoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,115 +2,95 @@ Grails provides a number of hooks to leverage the different parts of the system

h4. Hooking into the Grails Spring configuration

First, you can hook in Grails runtime configuration by providing a property called @doWithSpring@ which is assigned a block of code. For example the following snippet is from one of the core Grails plugins that provides [i18n|guide:i18n] support:
First, you can hook in Grails runtime configuration overriding the @doWithSpring@ method from the [Plugin|api:grails.plugins.Plugin] class and returning a closure that defines additional beans. For example the following snippet is from one of the core Grails plugins that provides [i18n|guide:i18n] support:

{code:java}
import org.springframework.web.servlet.i18n.CookieLocaleResolver
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor
import org.springframework.context.support.ReloadableResourceBundleMessageSource
import grails.plugins.*

class I18nGrailsPlugin {
class I18nGrailsPlugin extends Plugin {

def version = "0.1"

def doWithSpring = {
Closure doWithSpring() {{->
messageSource(ReloadableResourceBundleMessageSource) {
basename = "WEB-INF/grails-app/i18n/messages"
}
localeChangeInterceptor(LocaleChangeInterceptor) {
paramName = "lang"
}
localeResolver(CookieLocaleResolver)
}
}}
}
{code}

This plugin configures the Grails @messageSource@ bean and a couple of other beans to manage Locale resolution and switching. It using the [Spring Bean Builder|guide:spring] syntax to do so.

h4. Participating in web.xml Generation

Grails generates the @WEB-INF/web.xml@ file at load time, and although plugins cannot change this file directly, they can participate in the generation of the file. A plugin can provide a @doWithWebDescriptor@ property that is assigned a block of code that gets passed the @web.xml@ as an @XmlSlurper@ @GPathResult@.
h4. Customizing the Servlet Environment

h5. Add @servlet@ and @servlet-mapping@
In previous versions of Grails it was possible to dynamically modify the generated @web.xml@. In Grails 3.x there is no @web.xml@ file and it is not possible to programmatically modify the @web.xml@ file anymore.

Consider this example from the @ControllersPlugin@:
However, it is possible to perform the most commons tasks of modifying the Servlet environment in Grails 3.x.

{code:java}
def doWithWebDescriptor = { webXml ->
h4. Adding New Servlets

def mappingElement = webXml.'servlet-mapping'
If you want to add a new Servlet instance the simplest way is simply to define a new Spring bean in the @doWithSpring@ method:

def lastMapping = mappingElement[mappingElement.size() - 1]
lastMapping + {
'servlet-mapping' {
'servlet-name'("grails")
'url-pattern'("*.dispatch")
}
}
}
{code}
Closure doWithSpring() {{->
myServlet(MyServlet)
}}
{code}

Here the plugin gets a reference to the last @<servlet-mapping>@ element and appends Grails' servlet after it using XmlSlurper's ability to programmatically modify XML using closures and blocks.

h5. Add @filter@ and @filter-mapping@

Adding a filter with its mapping works a little differently. The location of the @<filter>@ element doesn't matter since order is not important, so it's simplest to insert your custom filter definition immediately after the last @<context-param>@ element. Order _is_ important for mappings, but the usual approach is to add it immediately after the last @<filter>@ element like so:
If you need to customize the servlet you can use Spring Boot's [ServletRegistrationBean|api:org.springframework.boot.context.embedded.ServletRegistrationBean]:

{code:java}
def doWithWebDescriptor = { webXml ->
{code}
Closure doWithSpring() {{->
myServlet(ServletRegistrationBean, new MyServlet(), "/myServlet/*") {
loadOnStartup = 2
}
}}
{code}

def contextParam = webXml.'context-param'
h4. Adding New Servlet Filters

contextParam[contextParam.size() - 1] + {
'filter' {
'filter-name'('springSecurityFilterChain')
'filter-class'(DelegatingFilterProxy.name)
}
}
Just like Servlets, the simplest way to configure a new filter is to simply define a Spring bean:

def filter = webXml.'filter'
filter[filter.size() - 1] + {
'filter-mapping'{
'filter-name'('springSecurityFilterChain')
'url-pattern'('/*')
}
}
}
{code}
Closure doWithSpring() {{->
myFilter(MyFilter)
}}
{code}

In some cases you need to ensure that your filter comes after one of the standard Grails filters, such as the Spring character encoding filter or the SiteMesh filter. Fortunately you can insert filter mappings immediately after the standard ones (more accurately, any that are in the template web.xml file) like so:

{code:java}
def doWithWebDescriptor = { webXml ->
...

// Insert the Spring Security filter after the Spring
// character encoding filter.
def filter = webXml.'filter-mapping'.find {
it.'filter-name'.text() == "charEncodingFilter"
}
However, if you want to control the order of filter registrations you will need to use Spring Boot's [FilterRegistrationBean|api:org.springframework.boot.context.embedded.FilterRegistrationBean]:

filter + {
'filter-mapping'{
'filter-name'('springSecurityFilterChain')
'url-pattern'('/*')
}
}
{code}
myFilter(FilterRegistrationBean) {
filter = bean(MyFilter)
urlPatterns = ['/*']
order = Ordered.HIGHEST_PRECEDENCE
}
{code}

{note}
Grails' internal registered filters (@GrailsWebRequestFilter@, @HiddenHttpMethodFilter@ etc.) are defined by incrementing @HIGHEST_PRECEDENCE@ by 10 thus allowing several filters to be inserted before or between Grails' filters.
{note}

h4. Doing Post Initialisation Configuration

Sometimes it is useful to be able do some runtime configuration after the Spring [ApplicationContext|api:org.springframework.context.ApplicationContext] has been built. In this case you can define a @doWithApplicationContext@ closure property.

{code:java}
class SimplePlugin {
class SimplePlugin extends Plugin{

def name = "simple"
def version = "1.1"

def doWithApplicationContext = { appCtx ->
def sessionFactory = appCtx.sessionFactory
@Override
void doWithApplicationContext() {
def sessionFactory = applicationContext.sessionFactory
// do something here with session factory
}
}
Expand Down
13 changes: 4 additions & 9 deletions src/en/guide/plugins/participatingInAutoReloadEvents.gdoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@ h4. Monitoring Resources for Changes
Often it is valuable to monitor resources for changes and perform some action when they occur. This is how Grails implements advanced reloading of application state at runtime. For example, consider this simplified snippet from the Grails @ServicesPlugin@:

{code:java}
class ServicesGrailsPlugin {
class ServicesGrailsPlugin extends Plugin {
...
def watchedResources = "file:./grails-app/services/*Service.groovy"

...
def onChange = { event ->
void onChange( Map<String, Object> event) {
if (event.source) {
def serviceClass = application.addServiceClass(event.source)
def serviceClass = grailsApplication.addServiceClass(event.source)
def serviceName = "\${serviceClass.propertyName}"
def beans = beans {
beans {
"$serviceName"(serviceClass.getClazz()) { bean ->
bean.autowire = true
}
}
if (event.ctx) {
event.ctx.registerBeanDefinition(
serviceName,
beans.getBeanDefinition(serviceName))
}
}
}
}
Expand Down
68 changes: 51 additions & 17 deletions src/en/guide/plugins/providingBasicArtefacts.gdoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,58 @@ A plugin can add new commands to the Grails 3.0 interactive shell in one of two
+ etc.
{code}

Code generation scripts can be used to create artefacts within the project tree and automate interactions with Gradle.

If you want to create a new shell command that interacts with a loaded Grails application instance then you should use the @create-command@ command:

{code}
$ grails create-command MyExampleCommand
{code}

This will create a file called @grails-app/commands/PACKAGE_PATH/MyExampleCommand.groovy@ that extends [ApplicationCommand|api:grails.dev.commands.ApplicationCommand]:

{code}
import grails.dev.commands.*

class MyExampleCommand implements ApplicationCommand {

boolean handle(ExecutionContext ctx) {
println "Hello World"
return true
}
}
{code}

An @ApplicationCommand@ has access to the @GrailsApplication@ instance and is subject to autowiring like any other Spring bean.

For each @ApplicationCommand@ present Grails will create a shell command and a Gradle task to invoke the @ApplicationCommand@. In the above example you can invoke the @MyExampleCommand@ class using either:

{code}
$ grails my-example
{code}

Or

{code}
$ gradle myExample
{code}

The Grails version is all lower case hyphen separated and excludes the "Command" suffix.

The main difference between code generation scripts and @ApplicationCommand@ instances is that the latter has full access to the Grails application state and hence can be used to perform tasks that interactive with the database, call into GORM etc.

In Grails 2.x Gant scripts could be used to perform both these tasks, in Grails 3.x code generation and interacting with runtime application state has been cleanly separated.

h4. Adding a new grails-app artifact (Controller, Tag Library, Service, etc.)

A plugin can add new artifacts by creating the relevant file within the @grails-app@ tree. Note that the plugin is loaded from where it is installed and not copied into the main application tree.
A plugin can add new artifacts by creating the relevant file within the @grails-app@ tree.

{code:java}
+ ExamplePlugin.groovy
+ scripts
+ grails-app
+ controllers <-- additional controllers here
+ services <-- additional services here
+ etc. <-- additional XXX here
+ lib
+ grails-app
+ controllers <-- additional controllers here
+ services <-- additional services here
+ etc. <-- additional XXX here

{code}

h4. Providing Views, Templates and View resolution
Expand All @@ -42,18 +82,12 @@ h4. Excluded Artefacts

By default Grails excludes the following files during the packaging process:

* @grails-app/conf/BootStrap.groovy@
* @grails-app/conf/BuildConfig.groovy@ (although it is used to generate @dependencies.groovy@)
* @grails-app/conf/Config.groovy@
* @grails-app/conf/DataSource.groovy@ (and any other @*DataSource.groovy@)
* @grails-app/conf/UrlMappings.groovy@
* @grails-app/conf/logback.groovy@
* @grails-app/conf/application.yml@ (renamed to @plugin.yml@)
* @grails-app/conf/spring/resources.groovy@
* Everything within @/web-app/WEB-INF@
* Everything within @/web-app/plugins/\*\*@
* Everything within @/test/\*\*@
* Everything within @/src/test/\*\*@
* SCM management files within @\*\*/.svn/\*\*@ and @\*\*/CVS/\*\*@

If your plugin requires files under the @web-app/WEB-INF@ directory it is recommended that you modify the plugin's @scripts/_Install.groovy@ Gant script to install these artefacts into the target project's directory tree.

In addition, the default @UrlMappings.groovy@ file is excluded to avoid naming conflicts, however you are free to add a UrlMappings definition under a different name which *will* be included. For example a file called @grails-app/conf/BlogUrlMappings.groovy@ is fine.

Expand Down
31 changes: 0 additions & 31 deletions src/en/guide/plugins/understandingPluginStructure.gdoc

This file was deleted.

5 changes: 0 additions & 5 deletions src/en/guide/theWebLayer/ajax.gdoc

This file was deleted.

Loading

0 comments on commit 68be830

Please sign in to comment.