| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
| "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> |
| <head> |
| <!-- Matomo --> |
| <script> |
| var _paq = window._paq = window._paq || []; |
| /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ |
| _paq.push(["setDoNotTrack", true]); |
| _paq.push(["disableCookies"]); |
| _paq.push(['trackPageView']); |
| _paq.push(['enableLinkTracking']); |
| (function() { |
| var u="https://analytics.apache.org/"; |
| _paq.push(['setTrackerUrl', u+'matomo.php']); |
| _paq.push(['setSiteId', '79']); |
| var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; |
| g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); |
| })(); |
| </script> |
| <!-- End Matomo Code --> |
| <title>18 Plugins 5.3.0</title> |
| <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> |
| <link rel="stylesheet" href="../css/main.css" type="text/css" media="screen, print" title="Style" charset="utf-8"/> |
| <link rel="stylesheet" href="../css/pdf.css" type="text/css" media="print" title="PDF" charset="utf-8"/> |
| <script type="text/javascript"> |
| function addJsClass() { |
| var classes = document.body.className.split(" "); |
| classes.push("js"); |
| document.body.className = classes.join(" "); |
| } |
| </script> |
| </head> |
| |
| <body class="body" onload="addJsClass();"> |
| <div id="navigation"> |
| <div class="navTitle"> |
| |
| The Grails Framework |
| </div> |
| <div class="navLinks"> |
| <ul> |
| <li> |
| <div id="nav-summary" onmouseover="toggleNavSummary(false)" onmouseout="toggleNavSummary(true)"> |
| <a href="../guide/index.html" class="button">Table of contents</a> |
| |
| <div id="nav-summary-childs" style="display:none;"> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/introduction.html"><strong>1</strong><span>Introduction</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/gettingStarted.html"><strong>2</strong><span>Getting Started</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/upgrading.html"><strong>3</strong><span>Upgrading from the previous versions</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/conf.html"><strong>4</strong><span>Configuration</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/commandLine.html"><strong>5</strong><span>The Command Line</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/profiles.html"><strong>6</strong><span>Application Profiles</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/GORM.html"><strong>7</strong><span>Object Relational Mapping (GORM)</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/theWebLayer.html"><strong>8</strong><span>The Web Layer</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/traits.html"><strong>9</strong><span>Traits</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/REST.html"><strong>10</strong><span>REST</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/async.html"><strong>11</strong><span>Asynchronous Programming</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/validation.html"><strong>12</strong><span>Validation</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/services.html"><strong>13</strong><span>The Service Layer</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/staticTypeCheckingAndCompilation.html"><strong>14</strong><span>Static Type Checking And Compilation</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/testing.html"><strong>15</strong><span>Testing</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/i18n.html"><strong>16</strong><span>Internationalization</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/security.html"><strong>17</strong><span>Security</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/plugins.html"><strong>18</strong><span>Plugins</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/spring.html"><strong>19</strong><span>Grails and Spring</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/scaffolding.html"><strong>20</strong><span>Scaffolding</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/deployment.html"><strong>21</strong><span>Deployment</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0"><a href="../guide/contributing.html"><strong>22</strong><span>Contributing to Grails</span></a> |
| </div> |
| |
| </div> |
| </div> |
| </li> |
| <li class="separator selected"> |
| <a id="ref-button" onclick="localToggle(); return false;" href="#">Quick Reference</a> |
| </li> |
| </ul> |
| </div> |
| |
| |
| </div> |
| |
| <table id="colset" border="0" cellpadding="0" cellspacing="0"> |
| <tr> |
| <td id="col1"> |
| <div id="main" class="corner-all"> |
| |
| |
| <div class="toc-item prev-left"><a href="../guide/security.html"><< <strong>17</strong><span>Security</span></a></div> |
| |
| |
| <span id='toggle-col1' class="toggle">(<a href="#" onclick="localToggle(); return false;">Quick Reference</a>)</span> |
| |
| |
| <div class="toc-item next-right"><a href="../guide/spring.html"><strong>19</strong><span>Grails and Spring</span> >></a></div> |
| |
| |
| |
| <div class="project"> |
| <h1>18 Plugins</h1> |
| |
| <p><strong>Version:</strong> 5.3.0</p> |
| </div> |
| |
| |
| <div id="table-of-content"> |
| <h2>Table of Contents</h2> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#creatingAndInstallingPlugins"><strong>18.1</strong><span>Creating and Installing Plugins</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#repositories"><strong>18.2</strong><span>Plugin Repositories</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#providingBasicArtefacts"><strong>18.3</strong><span>Providing Basic Artefacts</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#evaluatingConventions"><strong>18.4</strong><span>Evaluating Conventions</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#hookingIntoRuntimeConfiguration"><strong>18.5</strong><span>Hooking into Runtime Configuration</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#addingMethodsAtCompileTime"><strong>18.6</strong><span>Adding Methods at Compile Time</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#addingDynamicMethodsAtRuntime"><strong>18.7</strong><span>Adding Dynamic Methods at Runtime</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#participatingInAutoReloadEvents"><strong>18.8</strong><span>Participating in Auto Reload Events</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#understandingPluginLoadOrder"><strong>18.9</strong><span>Understanding Plugin Load Order</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:0px"><a href="#artefactApi"><strong>18.10</strong><span>The Artefact API</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:10px"><a href="#queryingArtefacts"><strong>18.10.1</strong><span>Asking About Available Artefacts</span></a> |
| </div> |
| |
| <div class="toc-item" style="margin-left:10px"><a href="#customArtefacts"><strong>18.10.2</strong><span>Adding Your Own Artefact Types</span></a> |
| </div> |
| |
| </div> |
| |
| |
| |
| <a name="15. Plug-ins"><!-- Legacy link --></a> |
| <h1 id="plugins">18 Plugins</h1> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Grails is first and foremost a web application framework, but it is also a platform. By exposing a number of extension points that let you extend anything from the command line interface to the runtime configuration engine, Grails can be customised to suit almost any needs. To hook into this platform, all you need to do is create a plugin.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Extending the platform may sound complicated, but plugins can range from trivially simple to incredibly powerful. If you know how to build a Grails application, you’ll know how to create a plugin for <a href="#providingBasicArtefacts">sharing a data model</a> or some static resources.</p> |
| </div> |
| |
| <a name="15.1 Creating and Installing Plug-ins"><!-- Legacy link --></a> |
| <h2 id="creatingAndInstallingPlugins">18.1 Creating and Installing Plugins</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/creatingAndInstallingPlugins.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_creating_plugins">Creating Plugins</h4> |
| <div class="paragraph"> |
| <p>Creating a Grails plugin is a simple matter of running the command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails create-plugin <<PLUGIN NAME>></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This will create a web-plugin project for the name you specify. For example running <code>grails create-plugin example</code> would create a new web-plugin project called <code>example</code>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In Grails 3.0 you should consider whether the plugin you create requires a web environment or whether the plugin can be used with other profiles. If your plugin does not require a web environment then use the "plugin" profile instead of the default "web-plugin" profile:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails create-plugin <<PLUGIN NAME>> --profile=plugin</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Make sure the plugin name does not contain more than one capital letter in a row, or it won’t work. Camel case is fine, though.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Being a regular Grails project has a number of benefits in that you can immediately test your plugin by running (if the plugin targets the "web" profile):</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails run-app</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Plugin projects don’t provide an index.gsp by default since most plugins don’t need it. So, if you try to view the plugin running in a browser right after creating it, you will receive a page not found error. You can easily create a <code>grails-app/views/index.gsp</code> for your plugin if you’d like. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>The structure of a Grails plugin is very nearly the same as a Grails application project’s except that in the <code>src/main/groovy</code> directory under the plugin package structure you will find a plugin descriptor class (a class that ends in "GrailsPlugin"). For example:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.plugins.*</span> |
| |
| <span class="type">class</span> <span class="class">ExampleGrailsPlugin</span> <span class="directive">extends</span> Plugin { |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>All plugins must have this class under the <code>src/main/groovy</code> directory, otherwise they are not regarded as a plugin. The plugin class defines metadata about the plugin, and optionally various hooks into plugin extension points (covered shortly).</p> |
| </div> |
| <div class="paragraph"> |
| <p>You can also provide additional information about your plugin using several special properties:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>title</code> - short one-sentence description of your plugin</p> |
| </li> |
| <li> |
| <p><code>grailsVersion</code> - The version range of Grails that the plugin supports. eg. "1.2 > *" (indicating 1.2 or higher)</p> |
| </li> |
| <li> |
| <p><code>author</code> - plugin author’s name</p> |
| </li> |
| <li> |
| <p><code>authorEmail</code> - plugin author’s contact e-mail</p> |
| </li> |
| <li> |
| <p><code>developers</code> - Any additional developers beyond the author specified above.</p> |
| </li> |
| <li> |
| <p><code>description</code> - full multi-line description of plugin’s features</p> |
| </li> |
| <li> |
| <p><code>documentation</code> - URL of the plugin’s documentation</p> |
| </li> |
| <li> |
| <p><code>license</code> - License of the plugin</p> |
| </li> |
| <li> |
| <p><code>issueManagement</code> - Issue Tracker of the plugin</p> |
| </li> |
| <li> |
| <p><code>scm</code> - Source code management location of the plugin</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>Here is a slimmed down example from the <a href="https://github.com/grails-plugins/grails-quartz">Quartz Grails plugin</a>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> quartz |
| |
| <span class="annotation">@Slf4j</span> |
| <span class="type">class</span> <span class="class">QuartzGrailsPlugin</span> <span class="directive">extends</span> Plugin { |
| <span class="comment">// the version or versions of Grails the plugin is designed for</span> |
| <span class="keyword">def</span> grailsVersion = <span class="string"><span class="delimiter">"</span><span class="content">3.0.0.BUILD-SNAPSHOT > *</span><span class="delimiter">"</span></span> |
| <span class="comment">// resources that are excluded from plugin packaging</span> |
| <span class="keyword">def</span> pluginExcludes = [ |
| <span class="string"><span class="delimiter">"</span><span class="content">grails-app/views/error.gsp</span><span class="delimiter">"</span></span> |
| ] |
| <span class="keyword">def</span> title = <span class="string"><span class="delimiter">"</span><span class="content">Quartz</span><span class="delimiter">"</span></span> <span class="comment">// Headline display name of the plugin</span> |
| <span class="keyword">def</span> author = <span class="string"><span class="delimiter">"</span><span class="content">Jeff Brown</span><span class="delimiter">"</span></span> |
| <span class="keyword">def</span> authorEmail = <span class="string"><span class="delimiter">"</span><span class="content">zzz@yyy.com</span><span class="delimiter">"</span></span> |
| <span class="keyword">def</span> description = <span class="string"><span class="delimiter">'''</span><span class="content">\ |
| </span><span class="content">Adds Quartz job scheduling features |
| </span><span class="delimiter">'''</span></span> |
| <span class="keyword">def</span> profiles = [<span class="string"><span class="delimiter">'</span><span class="content">web</span><span class="delimiter">'</span></span>] |
| <span class="predefined-type">List</span> loadAfter = [<span class="string"><span class="delimiter">'</span><span class="content">hibernate3</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">hibernate4</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">hibernate5</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">services</span><span class="delimiter">'</span></span>] |
| <span class="keyword">def</span> documentation = <span class="string"><span class="delimiter">"</span><span class="content">http://grails.org/plugin/quartz</span><span class="delimiter">"</span></span> |
| <span class="keyword">def</span> license = <span class="string"><span class="delimiter">"</span><span class="content">APACHE</span><span class="delimiter">"</span></span> |
| <span class="keyword">def</span> issueManagement = [ <span class="key">system</span>: <span class="string"><span class="delimiter">"</span><span class="content">Github Issues</span><span class="delimiter">"</span></span>, <span class="key">url</span>: <span class="string"><span class="delimiter">"</span><span class="content">http://github.com/grails3-plugins/quartz/issues</span><span class="delimiter">"</span></span> ] |
| <span class="keyword">def</span> developers = [ |
| [ <span class="key">name</span>: <span class="string"><span class="delimiter">"</span><span class="content">Joe Dev</span><span class="delimiter">"</span></span>, <span class="key">email</span>: <span class="string"><span class="delimiter">"</span><span class="content">joedev@gmail.com</span><span class="delimiter">"</span></span> ] |
| ] |
| <span class="keyword">def</span> scm = [ <span class="key">url</span>: <span class="string"><span class="delimiter">"</span><span class="content">https://github.com/grails3-plugins/quartz/</span><span class="delimiter">"</span></span> ] |
| |
| Closure doWithSpring()......</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_plugin_configuration">Plugin Configuration</h4> |
| <div class="paragraph"> |
| <p>Instead of directly accessing Grails configuration as <code>grailsApplication.config.mail.hostName</code>, use a Spring Boot configuration bean (or a POJO) annotated with <a href="https://docs.spring.io/spring-boot/docs/2.5.2/api/org/springframework/boot/context/properties/ConfigurationProperties.html">ConfigurationProperties</a> annotation. Here is an example plugin configuration:</p> |
| </div> |
| <div class="paragraph"> |
| <p><em>./src/main/groovy/example/MailPluginConfiguration.groovy</em></p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code>package example |
| |
| import org.springframework.boot.context.properties.ConfigurationProperties |
| |
| @ConfigurationProperties(prefix = "mail") |
| class MailPluginConfiguration { |
| |
| String hostName |
| int port |
| String from |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You can inject the <code>MailPluginConfiguration</code> bean into your bean like any other bean.</p> |
| </div> |
| <div class="paragraph"> |
| <p><em>./grails-app/services/example/MailService.groovy</em></p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code>package example |
| |
| class MailService { |
| |
| MailPluginConfiguration mailPluginConfiguration |
| |
| void sendMail() { |
| |
| } |
| |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Please read the <a href="https://docs.spring.io/spring-boot/docs/2.5.2/reference/html/features.html#features.external-config">Spring Boot Externalized Configuration</a> section for more information.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_installing_local_plugins">Installing Local Plugins</h4> |
| <div class="paragraph"> |
| <p>In order to install the Grails plugin to your local Maven, you could use Gradle <a href="https://docs.gradle.org/current/userguide/publishing_maven.html">Maven Publish</a> plugin. You may also need to configure the publishing extension as:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">publishing { |
| publications { |
| maven(MavenPublication) { |
| versionMapping { |
| usage(<span class="string"><span class="delimiter">'</span><span class="content">java-api</span><span class="delimiter">'</span></span>) { |
| fromResolutionOf(<span class="string"><span class="delimiter">'</span><span class="content">runtimeClasspath</span><span class="delimiter">'</span></span>) |
| } |
| usage(<span class="string"><span class="delimiter">'</span><span class="content">java-runtime</span><span class="delimiter">'</span></span>) { |
| fromResolutionResult() |
| } |
| } |
| from components.java |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Please refer to the Gradle Maven Publish plugin documentation for up-to-date information. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>To make your plugin available for use in a Grails application run the <code>./gradlew publishToMavenLocal</code> command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="bash">./gradlew publishToMavenLocal</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This will install the plugin into your local Maven cache. Then to use the plugin within an application declare a dependency on the plugin in your <code>build.gradle</code> file and include <code>mavenLocal()</code> in your repositories hash:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">... |
| repositories { |
| ... |
| mavenLocal() |
| } |
| ... |
| implementation <span class="string"><span class="delimiter">"</span><span class="content">org.grails.plugins:quartz:0.1</span><span class="delimiter">"</span></span></code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| In Grails 2.x plugins were packaged as ZIP files, however in Grails 3.x plugins are simple JAR files that can be added to the classpath of the IDE. |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_plugins_and_multi_project_builds">Plugins and Multi-Project Builds</h4> |
| <div class="paragraph"> |
| <p>If you wish to setup a plugin as part of a multi project build then follow these steps.</p> |
| </div> |
| <div class="paragraph"> |
| <p><strong>Step 1: Create the application and the plugin</strong></p> |
| </div> |
| <div class="paragraph"> |
| <p>Using the <code>grails</code> command create an application and a plugin:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> grails create-app myapp |
| <span class="error">$</span> grails create-plugin myplugin</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p><strong>Step 2: Create a settings.gradle file</strong></p> |
| </div> |
| <div class="paragraph"> |
| <p>In the same directory create a <code>settings.gradle</code> file with the following contents:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">include <span class="string"><span class="delimiter">"</span><span class="content">myapp</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">myplugin</span><span class="delimiter">"</span></span></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The directory structure should be as follows:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">PROJECT_DIR |
| - settings.gradle |
| - myapp |
| - build.gradle |
| - myplugin |
| - build.gradle</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p><strong>Step 3: Declare a project dependency on the plugin</strong></p> |
| </div> |
| <div class="paragraph"> |
| <p>Within the <code>build.gradle</code> of the application declare a dependency on the plugin within the <code>plugins</code> block:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails { |
| plugins { |
| implementation project(<span class="string"><span class="delimiter">'</span><span class="content">:myplugin</span><span class="delimiter">'</span></span>) |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| You can also declare the dependency within the <code>dependencies</code> block, however you will not get subproject reloading if you do this! |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p><strong>Step 4: Configure the plugin to enable reloading</strong></p> |
| </div> |
| <div class="paragraph"> |
| <p>In the plugin directory, add or modify the <code>gradle.properties</code> file. A new property <code>exploded=true</code> needs to be set in order for the plugin to add the exploded directories to the classpath.</p> |
| </div> |
| <div class="paragraph"> |
| <p><strong>Step 5: Run the application</strong></p> |
| </div> |
| <div class="paragraph"> |
| <p>Now run the application using the <code>grails run-app</code> command from the root of the application directory, you can use the <code>verbose</code> flag to see the Gradle output:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> cd myapp |
| <span class="error">$</span> grails run-app -verbose</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You will notice from the Gradle output that plugins sources are built and placed on the classpath of your application:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">:<span class="key">myplugin</span>:compileAstJava UP-TO-DATE |
| :<span class="key">myplugin</span>:compileAstGroovy UP-TO-DATE |
| :<span class="key">myplugin</span>:processAstResources UP-TO-DATE |
| :<span class="key">myplugin</span>:astClasses UP-TO-DATE |
| :<span class="key">myplugin</span>:compileJava UP-TO-DATE |
| :<span class="key">myplugin</span>:configScript UP-TO-DATE |
| :<span class="key">myplugin</span>:compileGroovy |
| :<span class="key">myplugin</span>:copyAssets UP-TO-DATE |
| :<span class="key">myplugin</span>:copyCommands UP-TO-DATE |
| :<span class="key">myplugin</span>:copyTemplates UP-TO-DATE |
| :<span class="key">myplugin</span>:processResources |
| :<span class="key">myapp</span>:compileJava UP-TO-DATE |
| :<span class="key">myapp</span>:compileGroovy |
| :<span class="key">myapp</span>:processResources UP-TO-DATE |
| :<span class="key">myapp</span>:classes |
| :<span class="key">myapp</span>:findMainClass |
| :<span class="key">myapp</span>:bootRun |
| Grails application running at <span class="key">http</span>:<span class="comment">//localhost:8080 in environment: development</span></code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_notes_on_excluded_artefacts">Notes on excluded Artefacts</h4> |
| <div class="paragraph"> |
| <p>Although the <a href="../ref/Command%20Line/create-plugin.html">create-plugin</a> command creates certain files for you so that the plugin can be run as a Grails application, not all of these files are included when packaging a plugin. The following is a list of artefacts created, but not included by <a href="../ref/Command%20Line/package-plugin.html">package-plugin</a>:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>grails-app/build.gradle</code> (although it is used to generate <code>dependencies.groovy</code>)</p> |
| </li> |
| <li> |
| <p><code>grails-app/conf/application.yml</code> (renamed to plugin.yml)</p> |
| </li> |
| <li> |
| <p><code>grails-app/conf/spring/resources.groovy</code></p> |
| </li> |
| <li> |
| <p><code>grails-app/conf/logback.groovy</code></p> |
| </li> |
| <li> |
| <p>Everything within <code>/src/test/*\*</code></p> |
| </li> |
| <li> |
| <p>SCM management files within <code>*\*/.svn/*\*</code> and <code>*\*/CVS/*\*</code></p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_customizing_the_plugin_contents">Customizing the plugin contents</h4> |
| <div class="paragraph"> |
| <p>When developing a plugin you may create test classes and sources that are used during the development and testing of the plugin but should not be exported to the application.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To exclude test sources you need to modify the <code>pluginExcludes</code> property of the plugin descriptor AND exclude the resources inside your <code>build.gradle</code> file. For example say you have some classes under the <code>com.demo</code> package that are in your plugin source tree but should not be packaged in the application. In your plugin descriptor you should exclude these:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="comment">// resources that should be loaded by the plugin once installed in the application</span> |
| <span class="keyword">def</span> pluginExcludes = [ |
| <span class="string"><span class="delimiter">'</span><span class="content">**/com/demo/**</span><span class="delimiter">'</span></span> |
| ]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>And in your <code>build.gradle</code> you should exclude the compiled classes from the JAR file:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">jar { |
| exclude <span class="string"><span class="delimiter">"</span><span class="content">com/demo/**/**</span><span class="delimiter">"</span></span> |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_inline_plugins_in_grails_3_0">Inline Plugins in Grails 3.0</h4> |
| <div class="paragraph"> |
| <p>In Grails 2.x it was possible to specify inline plugins in <code>BuildConfig</code>, in Grails 3.x this functionality has been replaced by Gradle’s multi-project build feature.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To set up a multi project build create an appliation and a plugin in a parent directory:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> grails create-app myapp |
| <span class="error">$</span> grails create-plugin myplugin</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Then create a <code>settings.gradle</code> file in the parent directory specifying the location of your application and plugin:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">include <span class="string"><span class="delimiter">'</span><span class="content">myapp</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">myplugin</span><span class="delimiter">'</span></span></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Finally add a dependency in your application’s <code>build.gradle</code> on the plugin:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">implementation project(<span class="string"><span class="delimiter">'</span><span class="content">:myplugin</span><span class="delimiter">'</span></span>)</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Using this technique you have achieved the equivalent of inline plugins from Grails 2.x.</p> |
| </div> |
| </div> |
| |
| <a name="15.2 Plugin Repositories"><!-- Legacy link --></a> |
| <h2 id="repositories">18.2 Plugin Repositories</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/repositories.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_distributing_plugins_in_the_grails_central_plugin_repository">Distributing Plugins in the Grails Central Plugin Repository</h4> |
| <div class="paragraph"> |
| <p>The preferred way to distribute plugin is to publish to the official Grails Central Plugin Repository. This will make your plugin visible to the <a href="../ref/Command%20Line/list-plugins.html">list-plugins</a> command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails list-plugins</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>which lists all plugins that are in the central repository. Your plugin will also be available to the <a href="../ref/Command%20Line/plugin-info.html">plugin-info</a> command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">grails plugin-info [plugin-name]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>which prints extra information about it, such as its description, who wrote, etc.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| If you have created a Grails plugin and want it to be hosted in the central repository, you’ll find instructions for getting an account on the <a href="http://plugins.grails.org/">plugin portal</a> website. |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| |
| <a name="15.4 Providing Basic Artefacts"><!-- Legacy link --></a> |
| <h2 id="providingBasicArtefacts">18.3 Providing Basic Artefacts</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/providingBasicArtefacts.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_add_command_line_commands">Add Command Line Commands</h4> |
| <div class="paragraph"> |
| <p>A plugin can add new commands to the Grails 3.0 interactive shell in one of two ways. First, using the <a href="../ref/Command%20Line/create-script.html">create-script</a> you can create a code generation script which will become available to the application. The <code>create-script</code> command will create the script in the <code>src/main/scripts</code> directory:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">+ src/main/scripts <-- additional scripts here |
| + grails-app |
| + controllers |
| + services |
| + etc.</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Code generation scripts can be used to create artefacts within the project tree and automate interactions with Gradle.</p> |
| </div> |
| <div class="paragraph"> |
| <p>If you want to create a new shell command that interacts with a loaded Grails application instance then you should use the <code>create-command</code> command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> grails create-command MyExampleCommand</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This will create a file called <code>grails-app/commands/PACKAGE_PATH/MyExampleCommand.groovy</code> that extends <a href="https://grails.apache.org/docs/5.3.0/api/grails/dev/commands/ApplicationCommand.html">ApplicationCommand</a>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">grails.dev.commands.*</span> |
| |
| <span class="type">class</span> <span class="class">MyExampleCommand</span> <span class="directive">implements</span> ApplicationCommand { |
| |
| <span class="type">boolean</span> handle(ExecutionContext ctx) { |
| println <span class="string"><span class="delimiter">"</span><span class="content">Hello World</span><span class="delimiter">"</span></span> |
| <span class="keyword">return</span> <span class="predefined-constant">true</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>An <code>ApplicationCommand</code> has access to the <code>GrailsApplication</code> instance and is subject to autowiring like any other Spring bean.</p> |
| </div> |
| <div class="paragraph"> |
| <p>You can also inform Grails to skip the execution of <code>Bootstrap.groovy</code> files with a simple property in your command:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">MyExampleCommand</span> <span class="directive">implements</span> ApplicationCommand { |
| |
| <span class="type">boolean</span> skipBootstrap = <span class="predefined-constant">true</span> |
| |
| <span class="type">boolean</span> handle(ExecutionContext ctx) { |
| ... |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>For each <code>ApplicationCommand</code> present Grails will create a shell command and a Gradle task to invoke the <code>ApplicationCommand</code>. In the above example you can invoke the <code>MyExampleCommand</code> class using either:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> grails my-example</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Or</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="error">$</span> gradle myExample</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The Grails version is all lower case hyphen separated and excludes the "Command" suffix.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The main difference between code generation scripts and <code>ApplicationCommand</code> 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.</p> |
| </div> |
| <div class="paragraph"> |
| <p>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.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_adding_a_new_grails_app_artifact_controller_tag_library_service_etc">Adding a new grails-app artifact (Controller, Tag Library, Service, etc.)</h4> |
| <div class="paragraph"> |
| <p>A plugin can add new artifacts by creating the relevant file within the <code>grails-app</code> tree.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">+ grails-app |
| + controllers <-- additional controllers here |
| + services <-- additional services here |
| + etc. <-- additional XXX here</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_providing_views_templates_and_view_resolution">Providing Views, Templates and View resolution</h4> |
| <div class="paragraph"> |
| <p>When a plugin provides a controller it may also provide default views to be rendered. This is an excellent way to modularize your application through plugins. Grails' view resolution mechanism will first look for the view in the application it is installed into and if that fails will attempt to look for the view within the plugin. This means that you can override views provided by a plugin by creating corresponding GSPs in the application’s <code>grails-app/views</code> directory.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For example, consider a controller called <code>BookController</code> that’s provided by an 'amazon' plugin. If the action being executed is <code>list</code>, Grails will first look for a view called <code>grails-app/views/book/list.gsp</code> then if that fails it will look for the same view relative to the plugin.</p> |
| </div> |
| <div class="paragraph"> |
| <p>However if the view uses templates that are also provided by the plugin then the following syntax may be necessary:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><<span class="key">g</span>:render template=<span class="string"><span class="delimiter">"</span><span class="content">fooTemplate</span><span class="delimiter">"</span></span> plugin=<span class="string"><span class="delimiter">"</span><span class="content">amazon</span><span class="delimiter">"</span></span>/></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Note the usage of the <code>plugin</code> attribute, which contains the name of the plugin where the template resides. If this is not specified then Grails will look for the template relative to the application.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_excluded_artefacts">Excluded Artefacts</h4> |
| <div class="paragraph"> |
| <p>By default Grails excludes the following files during the packaging process:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>grails-app/conf/logback.groovy</code></p> |
| </li> |
| <li> |
| <p><code>grails-app/conf/application.yml</code> (renamed to <code>plugin.yml</code>)</p> |
| </li> |
| <li> |
| <p><code>grails-app/conf/spring/resources.groovy</code></p> |
| </li> |
| <li> |
| <p>Everything within <code>/src/test/*\*</code></p> |
| </li> |
| <li> |
| <p>SCM management files within <code>*\*/.svn/*\*</code> and <code>*\*/CVS/*\*</code></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>The default <code>UrlMappings.groovy</code> file is not excluded, so remove any mappings that are not required for the plugin to work. You are also free to add a UrlMappings definition under a different name which <strong>will</strong> be included. For example a file called <code>grails-app/controllers/BlogUrlMappings.groovy</code> is fine.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The list of excludes is extensible with the <code>pluginExcludes</code> property:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="comment">// resources that are excluded from plugin packaging</span> |
| <span class="keyword">def</span> pluginExcludes = [ |
| <span class="string"><span class="delimiter">"</span><span class="content">grails-app/views/error.gsp</span><span class="delimiter">"</span></span> |
| ]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This is useful for example to include demo or test resources in the plugin repository, but not include them in the final distribution.</p> |
| </div> |
| </div> |
| |
| <a name="15.5 Evaluating Conventions"><!-- Legacy link --></a> |
| <h2 id="evaluatingConventions">18.4 Evaluating Conventions</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/evaluatingConventions.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>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 <code>application</code> variable which is an instance of the <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsApplication.html">GrailsApplication</a> interface.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>GrailsApplication</code> interface provides methods to evaluate the conventions within the project and internally stores references to all artifact classes within your application.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Artifacts implement the <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsClass.html">GrailsClass</a> interface, which represents a Grails resource such as a controller or a tag library. For example to get all <code>GrailsClass</code> instances you can do:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">for</span> (grailsClass <span class="keyword">in</span> application.allClasses) { |
| println grailsClass.name |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p><code>GrailsApplication</code> has a few "magic" properties to narrow the type of artefact you are interested in. For example to access controllers you can use:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">for</span> (controllerClass <span class="keyword">in</span> application.controllerClasses) { |
| println controllerClass.name |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The dynamic method conventions are as follows:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>*Classes</code> - Retrieves all the classes for a particular artefact name. For example <code>application.controllerClasses</code>.</p> |
| </li> |
| <li> |
| <p><code>get*Class</code> - Retrieves a named class for a particular artefact. For example <code>application.getControllerClass("PersonController")</code></p> |
| </li> |
| <li> |
| <p><code>is*Class</code> - Returns <code>true</code> if the given class is of the given artefact type. For example <code>application.isControllerClass(PersonController)</code></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>GrailsClass</code> interface has a number of useful methods that let you further evaluate and work with the conventions. These include:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>getPropertyValue</code> - Gets the initial value of the given property on the class</p> |
| </li> |
| <li> |
| <p><code>hasProperty</code> - Returns <code>true</code> if the class has the specified property</p> |
| </li> |
| <li> |
| <p><code>newInstance</code> - Creates a new instance of this class.</p> |
| </li> |
| <li> |
| <p><code>getName</code> - Returns the logical name of the class in the application without the trailing convention part if applicable</p> |
| </li> |
| <li> |
| <p><code>getShortName</code> - Returns the short name of the class without package prefix</p> |
| </li> |
| <li> |
| <p><code>getFullName</code> - Returns the full name of the class in the application with the trailing convention part and with the package name</p> |
| </li> |
| <li> |
| <p><code>getPropertyName</code> - Returns the name of the class as a property name</p> |
| </li> |
| <li> |
| <p><code>getLogicalPropertyName</code> - Returns the logical property name of the class in the application without the trailing convention part if applicable</p> |
| </li> |
| <li> |
| <p><code>getNaturalName</code> - Returns the name of the property in natural terms (e.g. 'lastName' becomes 'Last Name')</p> |
| </li> |
| <li> |
| <p><code>getPackageName</code> - Returns the package name</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>For a full reference refer to the <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsClass.html">javadoc API</a>.</p> |
| </div> |
| |
| <a name="15.7 Hooking into Runtime Configuration"><!-- Legacy link --></a> |
| <h2 id="hookingIntoRuntimeConfiguration">18.5 Hooking into Runtime Configuration</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/hookingIntoRuntimeConfiguration.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Grails provides a number of hooks to leverage the different parts of the system and perform runtime configuration by convention.</p> |
| </div> |
| <div class="sect3"> |
| <h4 id="_hooking_into_the_grails_spring_configuration">Hooking into the Grails Spring configuration</h4> |
| <div class="paragraph"> |
| <p>First, you can hook in Grails runtime configuration overriding the <code>doWithSpring</code> method from the <a href="https://grails.apache.org/docs/5.3.0/api/grails/plugins/Plugin.html">Plugin</a> class and returning a closure that defines additional beans. For example the following snippet is from one of the core Grails plugins that provides <a href="i18n.html">i18n</a> support:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">org.springframework.web.servlet.i18n.CookieLocaleResolver</span> |
| <span class="keyword">import</span> <span class="include">org.springframework.web.servlet.i18n.LocaleChangeInterceptor</span> |
| <span class="keyword">import</span> <span class="include">org.springframework.context.support.ReloadableResourceBundleMessageSource</span> |
| <span class="keyword">import</span> <span class="include">grails.plugins.*</span> |
| |
| <span class="type">class</span> <span class="class">I18nGrailsPlugin</span> <span class="directive">extends</span> Plugin { |
| |
| <span class="keyword">def</span> version = <span class="string"><span class="delimiter">"</span><span class="content">0.1</span><span class="delimiter">"</span></span> |
| |
| Closure doWithSpring() {{-> |
| messageSource(ReloadableResourceBundleMessageSource) { |
| basename = <span class="string"><span class="delimiter">"</span><span class="content">WEB-INF/grails-app/i18n/messages</span><span class="delimiter">"</span></span> |
| } |
| localeChangeInterceptor(LocaleChangeInterceptor) { |
| paramName = <span class="string"><span class="delimiter">"</span><span class="content">lang</span><span class="delimiter">"</span></span> |
| } |
| localeResolver(CookieLocaleResolver) |
| }} |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>This plugin configures the Grails <code>messageSource</code> bean and a couple of other beans to manage Locale resolution and switching. It using the <a href="spring.html#theBeanBuilderDSLExplained">Spring Bean Builder</a> syntax to do so.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_customizing_the_servlet_environment">Customizing the Servlet Environment</h4> |
| <div class="paragraph"> |
| <p>In previous versions of Grails it was possible to dynamically modify the generated <code>web.xml</code>. In Grails 3.x there is no <code>web.xml</code> file and it is not possible to programmatically modify the <code>web.xml</code> file anymore.</p> |
| </div> |
| <div class="paragraph"> |
| <p>However, it is possible to perform the most commons tasks of modifying the Servlet environment in Grails 3.x.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_adding_new_servlets">Adding New Servlets</h4> |
| <div class="paragraph"> |
| <p>If you want to add a new Servlet instance the simplest way is simply to define a new Spring bean in the <code>doWithSpring</code> method:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">Closure doWithSpring() {{-> |
| myServlet(MyServlet) |
| }}</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>If you need to customize the servlet you can use Spring Boot’s <a href="http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/embedded/ServletRegistrationBean.html">ServletRegistrationBean</a>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">Closure doWithSpring() {{-> |
| myServlet(ServletRegistrationBean, <span class="keyword">new</span> MyServlet(), <span class="string"><span class="delimiter">"</span><span class="content">/myServlet/*</span><span class="delimiter">"</span></span>) { |
| loadOnStartup = <span class="integer">2</span> |
| } |
| }}</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_adding_new_servlet_filters">Adding New Servlet Filters</h4> |
| <div class="paragraph"> |
| <p>Just like Servlets, the simplest way to configure a new filter is to simply define a Spring bean:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">Closure doWithSpring() {{-> |
| myFilter(MyFilter) |
| }}</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>However, if you want to control the order of filter registrations you will need to use Spring Boot’s <a href="http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/web/servlet/FilterRegistrationBean.html">FilterRegistrationBean</a>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy">myFilter(FilterRegistrationBean) { |
| filter = bean(MyFilter) |
| urlPatterns = [<span class="string"><span class="delimiter">'</span><span class="content">/*</span><span class="delimiter">'</span></span>] |
| order = Ordered.HIGHEST_PRECEDENCE |
| }</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Grails' internal registered filters (<code>GrailsWebRequestFilter</code>, <code>HiddenHttpMethodFilter</code> etc.) are defined by incrementing <code>HIGHEST_PRECEDENCE</code> by 10 thus allowing several filters to be inserted before or between Grails' filters. |
| </td> |
| </tr> |
| </table> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_doing_post_initialisation_configuration">Doing Post Initialisation Configuration</h4> |
| <div class="paragraph"> |
| <p>Sometimes it is useful to be able do some runtime configuration after the Spring <a href="https://docs.spring.io/spring/docs/5.3.8/javadoc-api/org/springframework/context/ApplicationContext.html">ApplicationContext</a> has been built. In this case you can define a <code>doWithApplicationContext</code> closure property.</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">SimplePlugin</span> <span class="directive">extends</span> Plugin{ |
| |
| <span class="keyword">def</span> name = <span class="string"><span class="delimiter">"</span><span class="content">simple</span><span class="delimiter">"</span></span> |
| <span class="keyword">def</span> version = <span class="string"><span class="delimiter">"</span><span class="content">1.1</span><span class="delimiter">"</span></span> |
| |
| <span class="annotation">@Override</span> |
| <span class="type">void</span> doWithApplicationContext() { |
| <span class="keyword">def</span> sessionFactory = applicationContext.sessionFactory |
| <span class="comment">// do something here with session factory</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| |
| |
| <h2 id="addingMethodsAtCompileTime">18.6 Adding Methods at Compile Time</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/addingMethodsAtCompileTime.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Grails 3.0 makes it easy to add new traits to existing artefact types from a plugin. For example say you wanted to add methods for manipulating dates to controllers. This can be done by defining a trait in <code>src/main/groovy</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> myplugin |
| |
| <span class="annotation">@Enhances</span>(<span class="string"><span class="delimiter">"</span><span class="content">Controller</span><span class="delimiter">"</span></span>) |
| trait DateTrait { |
| <span class="predefined-type">Date</span> currentDate() { |
| <span class="keyword">return</span> <span class="keyword">new</span> <span class="predefined-type">Date</span>() |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>@Enhances</code> annotation defines the types of artefacts that the trait should be applied to.</p> |
| </div> |
| <div class="paragraph"> |
| <p>As an alternative to using the <code>@Enhances</code> annotation above, you can implement a <a href="https://grails.apache.org/docs/5.3.0/api/grails/compiler/traits/TraitInjector.html">TraitInjector</a> to tell Grails which artefacts you want to inject the trait into at compile time:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> myplugin |
| |
| <span class="annotation">@CompileStatic</span> |
| <span class="type">class</span> <span class="class">ControllerTraitInjector</span> <span class="directive">implements</span> TraitInjector { |
| |
| <span class="annotation">@Override</span> |
| <span class="predefined-type">Class</span> getTrait() { |
| SomeTrait |
| } |
| |
| <span class="annotation">@Override</span> |
| <span class="predefined-type">String</span><span class="type">[]</span> getArtefactTypes() { |
| [<span class="string"><span class="delimiter">'</span><span class="content">Controller</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">String</span><span class="type">[]</span> |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The above <code>TraitInjector</code> will add the <code>SomeTrait</code> to all controllers. The <code>getArtefactTypes</code> method defines the types of artefacts that the trait should be applied to.</p> |
| </div> |
| <div class="sect3"> |
| <h4 id="_applying_traits_conditionally">Applying traits conditionally</h4> |
| <div class="paragraph"> |
| <p>A <code>TraitInjector</code> implementation can also implement the <a href="https://grails.apache.org/docs/5.3.0/api/grails/compiler/ast/SupportsClassNode.html">SupportsClassNode</a> interface to apply traits to only those artefacts which satisfy a custom requirement. |
| For example, if a trait should only be applied if the target artefact class has a specific annotation, it can be done as below</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">package</span> myplugin |
| |
| <span class="annotation">@CompileStatic</span> |
| <span class="type">class</span> <span class="class">AnnotationBasedTraitInjector</span> <span class="directive">implements</span> TraitInjector, SupportsClassNode { |
| |
| <span class="annotation">@Override</span> |
| <span class="predefined-type">Class</span> getTrait() { |
| SomeTrait |
| } |
| |
| <span class="annotation">@Override</span> |
| <span class="predefined-type">String</span><span class="type">[]</span> getArtefactTypes() { |
| [<span class="string"><span class="delimiter">'</span><span class="content">Controller</span><span class="delimiter">'</span></span>] <span class="keyword">as</span> <span class="predefined-type">String</span><span class="type">[]</span> |
| } |
| |
| <span class="type">boolean</span> supports(ClassNode classNode) { |
| <span class="keyword">return</span> GrailsASTUtils.hasAnnotation(classNode, SomeAnnotation) |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Above <code>TraitInjector</code> will add the <code>SomeTrait</code> to only those controllers which has the <code>SomeAnnotation</code> declared.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The framework discovers trait injectors by way of a <code>META-INF/grails.factories</code> descriptor that is in the .jar file. This descriptor is automatically generated. The descriptor generated for the code shown above would look like this:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code>#Grails Factories File |
| grails.compiler.traits.TraitInjector= |
| myplugin.ControllerTraitInjector,myplugin.DateTraitTraitInjector</code></pre> |
| </div> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Due to formatting issues, above code snippet includes a line break after equal sign. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="paragraph"> |
| <p>That file is generated automatically and added to the .jar file at build time. If for any reason the application defines its own <code>grails.factories</code> file at <code>src/main/resources/META-INF/grails.factories</code>, it is important that the trait injectors be explicitly defined in that file. The auto-generated metadata is only reliable if the application does not define its own <code>src/main/resources/META-INF/grails.factores</code> file.</p> |
| </div> |
| </div> |
| |
| <a name="15.8 Adding Dynamic Methods at Runtime"><!-- Legacy link --></a> |
| <h2 id="addingDynamicMethodsAtRuntime">18.7 Adding Dynamic Methods at Runtime</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/addingDynamicMethodsAtRuntime.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_the_basics">The Basics</h4> |
| <div class="paragraph"> |
| <p>Grails plugins let you register dynamic methods with any Grails-managed or other class at runtime. This work is done in a <code>doWithDynamicMethods</code> method.</p> |
| </div> |
| <div class="admonitionblock note"> |
| <table> |
| <tr> |
| <td class="icon"> |
| <i class="fa icon-note" title="Note"></i> |
| </td> |
| <td class="content"> |
| Note that Grails 3.x features newer features such as traits that are usable from code compiled with <code>CompileStatic</code>. It is recommended that dynamic behavior is only added for cases that are not possible with traits. |
| </td> |
| </tr> |
| </table> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">ExamplePlugin</span> <span class="directive">extends</span> Plugin { |
| <span class="type">void</span> doWithDynamicMethods() { |
| <span class="keyword">for</span> (controllerClass <span class="keyword">in</span> grailsApplication.controllerClasses) { |
| controllerClass.metaClass.myNewMethod = {-> println <span class="string"><span class="delimiter">"</span><span class="content">hello world</span><span class="delimiter">"</span></span> } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this case we use the implicit application object to get a reference to all of the controller classes' MetaClass instances and add a new method called <code>myNewMethod</code> to each controller. If you know beforehand the class you wish the add a method to you can simply reference its <code>metaClass</code> property.</p> |
| </div> |
| <div class="paragraph"> |
| <p>For example we can add a new method <code>swapCase</code> to <code>java.lang.String</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">ExamplePlugin</span> <span class="directive">extends</span> Plugin { |
| |
| <span class="annotation">@Override</span> |
| <span class="type">void</span> doWithDynamicMethods() { |
| <span class="predefined-type">String</span>.metaClass.swapCase = {-> |
| <span class="keyword">def</span> sb = <span class="keyword">new</span> <span class="predefined-type">StringBuilder</span>() |
| delegate.each { |
| sb << (<span class="predefined-type">Character</span>.isUpperCase(<span class="local-variable">it</span> <span class="keyword">as</span> <span class="type">char</span>) ? |
| <span class="predefined-type">Character</span>.toLowerCase(<span class="local-variable">it</span> <span class="keyword">as</span> <span class="type">char</span>) : |
| <span class="predefined-type">Character</span>.toUpperCase(<span class="local-variable">it</span> <span class="keyword">as</span> <span class="type">char</span>)) |
| } |
| sb.toString() |
| } |
| |
| <span class="keyword">assert</span> <span class="string"><span class="delimiter">"</span><span class="content">UpAndDown</span><span class="delimiter">"</span></span> == <span class="string"><span class="delimiter">"</span><span class="content">uPaNDdOWN</span><span class="delimiter">"</span></span>.swapCase() |
| } |
| }</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_interacting_with_the_applicationcontext">Interacting with the ApplicationContext</h4> |
| <div class="paragraph"> |
| <p>The <code>doWithDynamicMethods</code> closure gets passed the Spring <code>ApplicationContext</code> instance. This is useful as it lets you interact with objects within it. For example if you were implementing a method to interact with Hibernate you could use the <code>SessionFactory</code> instance in combination with a <code>HibernateTemplate</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">import</span> <span class="include">org.springframework.orm.hibernate3.HibernateTemplate</span> |
| |
| <span class="type">class</span> <span class="class">ExampleHibernatePlugin</span> <span class="directive">extends</span> Plugin{ |
| |
| <span class="type">void</span> doWithDynamicMethods() { |
| |
| <span class="keyword">for</span> (domainClass <span class="keyword">in</span> grailsApplication.domainClasses) { |
| |
| domainClass.metaClass.static.load = { <span class="predefined-type">Long</span> id-> |
| <span class="keyword">def</span> sf = applicationContext.sessionFactory |
| <span class="keyword">def</span> template = <span class="keyword">new</span> HibernateTemplate(sf) |
| template.load(delegate, id) |
| } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Also because of the autowiring and dependency injection capability of the Spring container you can implement more powerful dynamic constructors that use the application context to wire dependencies into your object at runtime:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">MyConstructorPlugin</span> { |
| |
| <span class="type">void</span> doWithDynamicMethods() |
| <span class="keyword">for</span> (domainClass <span class="keyword">in</span> grailsApplication.domainClasses) { |
| domainClass.metaClass.constructor = {-> |
| <span class="keyword">return</span> applicationContext.getBean(domainClass.name) |
| } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here we actually replace the default constructor with one that looks up prototyped Spring beans instead!</p> |
| </div> |
| </div> |
| |
| <a name="15.9 Participating in Auto Reload Events"><!-- Legacy link --></a> |
| <h2 id="participatingInAutoReloadEvents">18.8 Participating in Auto Reload Events</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/participatingInAutoReloadEvents.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_monitoring_resources_for_changes">Monitoring Resources for Changes</h4> |
| <div class="paragraph"> |
| <p>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 <code>ServicesPlugin</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">ServicesGrailsPlugin</span> <span class="directive">extends</span> Plugin { |
| ... |
| def watchedResources = <span class="string"><span class="delimiter">"</span><span class="content">file:./grails-app/services/**/*Service.groovy</span><span class="delimiter">"</span></span> |
| |
| ... |
| void onChange( <span class="predefined-type">Map</span><<span class="predefined-type">String</span>, <span class="predefined-type">Object</span>> event) { |
| <span class="keyword">if</span> (event.source) { |
| <span class="keyword">def</span> serviceClass = grailsApplication.addServiceClass(event.source) |
| <span class="keyword">def</span> serviceName = <span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">${</span>serviceClass.propertyName<span class="inline-delimiter">}</span></span><span class="delimiter">"</span></span> |
| beans { |
| <span class="string"><span class="delimiter">"</span><span class="inline"><span class="inline-delimiter">$</span>serviceName</span><span class="delimiter">"</span></span>(serviceClass.getClazz()) { bean -> |
| bean.autowire = <span class="predefined-constant">true</span> |
| } |
| } |
| } |
| } |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>First it defines <code>watchedResources</code> as either a String or a List of strings that contain either the references or patterns of the resources to watch. If the watched resources specify a Groovy file, when it is changed it will automatically be reloaded and passed into the <code>onChange</code> closure in the <code>event</code> object.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>event</code> object defines a number of useful properties:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>event.source</code> - The source of the event, either the reloaded <code>Class</code> or a Spring <code>Resource</code></p> |
| </li> |
| <li> |
| <p><code>event.ctx</code> - The Spring <code>ApplicationContext</code> instance</p> |
| </li> |
| <li> |
| <p><code>event.plugin</code> - The plugin object that manages the resource (usually <code>this</code>)</p> |
| </li> |
| <li> |
| <p><code>event.application</code> - The <code>GrailsApplication</code> instance</p> |
| </li> |
| <li> |
| <p><code>event.manager</code> - The <code>GrailsPluginManager</code> instance</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>These objects are available to help you apply the appropriate changes based on what changed. In the "Services" example above, a new service bean is re-registered with the <code>ApplicationContext</code> when one of the service classes changes.</p> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_influencing_other_plugins">Influencing Other Plugins</h4> |
| <div class="paragraph"> |
| <p>In addition to reacting to changes, sometimes a plugin needs to "influence" another.</p> |
| </div> |
| <div class="paragraph"> |
| <p>Take for example the Services and Controllers plugins. When a service is reloaded, unless you reload the controllers too, problems will occur when you try to auto-wire the reloaded service into an older controller Class.</p> |
| </div> |
| <div class="paragraph"> |
| <p>To get around this, you can specify which plugins another plugin "influences". This means that when one plugin detects a change, it will reload itself and then reload its influenced plugins. For example consider this snippet from the <code>ServicesGrailsPlugin</code>:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> influences = [<span class="string"><span class="delimiter">'</span><span class="content">controllers</span><span class="delimiter">'</span></span>]</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_observing_other_plugins">Observing other plugins</h4> |
| <div class="paragraph"> |
| <p>If there is a particular plugin that you would like to observe for changes but not necessary watch the resources that it monitors you can use the "observe" property:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> observe = [<span class="string"><span class="delimiter">"</span><span class="content">controllers</span><span class="delimiter">"</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this case when a controller is changed you will also receive the event chained from the controllers plugin.</p> |
| </div> |
| <div class="paragraph"> |
| <p>It is also possible for a plugin to observe all loaded plugins by using a wildcard:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> observe = [<span class="string"><span class="delimiter">"</span><span class="content">*</span><span class="delimiter">"</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The Logging plugin does exactly this so that it can add the <code>log</code> property back to <em>any</em> artefact that changes while the application is running.</p> |
| </div> |
| </div> |
| |
| <a name="15.10 Understanding Plug-in Load Order"><!-- Legacy link --></a> |
| <h2 id="understandingPluginLoadOrder">18.9 Understanding Plugin Load Order</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/understandingPluginLoadOrder.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="sect3"> |
| <h4 id="_controlling_plugin_dependencies">Controlling Plugin Dependencies</h4> |
| <div class="paragraph"> |
| <p>Plugins often depend on the presence of other plugins and can adapt depending on the presence of others. This is implemented with two properties. The first is called <code>dependsOn</code>. For example, take a look at this snippet from the Hibernate plugin:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">HibernateGrailsPlugin</span> { |
| |
| <span class="keyword">def</span> version = <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span> |
| |
| <span class="keyword">def</span> dependsOn = [<span class="key">dataSource</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>, |
| <span class="key">domainClass</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>, |
| <span class="key">i18n</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>, |
| <span class="key">core</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0</span><span class="delimiter">"</span></span>] |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The Hibernate plugin is dependent on the presence of four plugins: the <code>dataSource</code>, <code>domainClass</code>, <code>i18n</code> and <code>core</code> plugins.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The dependencies will be loaded before the Hibernate plugin and if all dependencies do not load, then the plugin will not load.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>dependsOn</code> property also supports a mini expression language for specifying version ranges. A few examples of the syntax can be seen below:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> dependsOn = [<span class="key">foo</span>: <span class="string"><span class="delimiter">"</span><span class="content">* > 1.0</span><span class="delimiter">"</span></span>] |
| <span class="keyword">def</span> dependsOn = [<span class="key">foo</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0 > 1.1</span><span class="delimiter">"</span></span>] |
| <span class="keyword">def</span> dependsOn = [<span class="key">foo</span>: <span class="string"><span class="delimiter">"</span><span class="content">1.0 > *</span><span class="delimiter">"</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>When the wildcard * character is used it denotes "any" version. The expression syntax also excludes any suffixes such as -BETA, -ALPHA etc. so for example the expression "1.0 > 1.1" would match any of the following versions:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>1.1</p> |
| </li> |
| <li> |
| <p>1.0</p> |
| </li> |
| <li> |
| <p>1.0.1</p> |
| </li> |
| <li> |
| <p>1.0.3-SNAPSHOT</p> |
| </li> |
| <li> |
| <p>1.1-BETA2</p> |
| </li> |
| </ul> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_controlling_load_order">Controlling Load Order</h4> |
| <div class="paragraph"> |
| <p>Using <code>dependsOn</code> establishes a "hard" dependency in that if the dependency is not resolved, the plugin will give up and won’t load. It is possible though to have a weaker dependency using the <code>loadAfter</code> and <code>loadBefore</code> properties:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> loadAfter = [<span class="string"><span class="delimiter">'</span><span class="content">controllers</span><span class="delimiter">'</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here the plugin will be loaded after the <code>controllers</code> plugin if it exists, otherwise it will just be loaded. The plugin can then adapt to the presence of the other plugin, for example the Hibernate plugin has this code in its <code>doWithSpring</code> closure:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">if</span> (manager?.hasGrailsPlugin(<span class="string"><span class="delimiter">"</span><span class="content">controllers</span><span class="delimiter">"</span></span>)) { |
| openSessionInViewInterceptor(OpenSessionInViewInterceptor) { |
| flushMode = HibernateAccessor.FLUSH_MANUAL |
| sessionFactory = sessionFactory |
| } |
| grailsUrlHandlerMapping.interceptors << openSessionInViewInterceptor |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>Here the Hibernate plugin will only register an <code>OpenSessionInViewInterceptor</code> if the <code>controllers</code> plugin has been loaded. The <code>manager</code> variable is an instance of the <a href="https://grails.apache.org/docs/5.3.0/api/grails/plugins/GrailsPluginManager.html">GrailsPluginManager</a> interface and it provides methods to interact with other plugins.</p> |
| </div> |
| <div class="paragraph"> |
| <p>You can also use the <code>loadBefore</code> property to specify one or more plugins that your plugin should load before:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> loadBefore = [<span class="string"><span class="delimiter">'</span><span class="content">rabbitmq</span><span class="delimiter">'</span></span>]</code></pre> |
| </div> |
| </div> |
| </div> |
| <div class="sect3"> |
| <h4 id="_scopes_and_environments">Scopes and Environments</h4> |
| <div class="paragraph"> |
| <p>It’s not only plugin load order that you can control. You can also specify which environments your plugin should be loaded in and which scopes (stages of a build). Simply declare one or both of these properties in your plugin descriptor:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> environments = [<span class="string"><span class="delimiter">'</span><span class="content">development</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">test</span><span class="delimiter">'</span></span>, <span class="string"><span class="delimiter">'</span><span class="content">myCustomEnv</span><span class="delimiter">'</span></span>] |
| <span class="keyword">def</span> scopes = [<span class="key">excludes</span>:<span class="string"><span class="delimiter">'</span><span class="content">war</span><span class="delimiter">'</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this example, the plugin will only load in the 'development' and 'test' environments. Nor will it be packaged into the WAR file, because it’s excluded from the 'war' phase. This allows <code>development-only</code> plugins to not be packaged for production use.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The full list of available scopes are defined by the enum <a href="https://grails.apache.org/docs/5.3.0/api/grails/util/BuildScope.html">BuildScope</a>, but here’s a summary:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>test</code> - when running tests</p> |
| </li> |
| <li> |
| <p><code>functional-test</code> - when running functional tests</p> |
| </li> |
| <li> |
| <p><code>run</code> - for run-app and run-war</p> |
| </li> |
| <li> |
| <p><code>war</code> - when packaging the application as a WAR file</p> |
| </li> |
| <li> |
| <p><code>all</code> - plugin applies to all scopes (default)</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>Both properties can be one of:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>a string - a sole inclusion</p> |
| </li> |
| <li> |
| <p>a list - a list of environments or scopes to include</p> |
| </li> |
| <li> |
| <p>a map - for full control, with 'includes' and/or 'excludes' keys that can have string or list values</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>For example,</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> environments = <span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span></code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>will only include the plugin in the test environment, whereas</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> environments = [<span class="string"><span class="delimiter">"</span><span class="content">development</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>will include it in both the development <em>and</em> test environments. Finally,</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">def</span> environments = [<span class="key">includes</span>: [<span class="string"><span class="delimiter">"</span><span class="content">development</span><span class="delimiter">"</span></span>, <span class="string"><span class="delimiter">"</span><span class="content">test</span><span class="delimiter">"</span></span>]]</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>will do the same thing.</p> |
| </div> |
| </div> |
| |
| |
| <h2 id="artefactApi">18.10 The Artefact API</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/artefactApi.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>You should by now understand that Grails has the concept of artefacts: special types of classes that it knows about and can treat differently from normal Groovy and Java classes, for example by enhancing them with extra properties and methods. Examples of artefacts include domain classes and controllers. What you may not be aware of is that Grails allows application and plugin developers access to the underlying infrastructure for artefacts, which means you can find out what artefacts are available and even enhance them yourself. You can even provide your own custom artefact types.</p> |
| </div> |
| |
| |
| <h2 id="queryingArtefacts">18.10.1 Asking About Available Artefacts</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/artefactApi/queryingArtefacts.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>As a plugin developer, it can be important for you to find out about what domain classes, controllers, or other types of artefact are available in an application. For example, the <a href="https://grails.org/plugins.html#plugin/elasticsearch">Elasticsearch plugin</a> needs to know what domain classes exist so it can check them for any <code>searchable</code> properties and index the appropriate ones. So how does it do it? The answer lies with the <code>grailsApplication</code> object, and instance of <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsApplication.html">GrailsApplication</a> that’s available automatically in controllers and GSPs and can be <a href="services.html#dependencyInjectionServices">injected</a> everywhere else.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>grailsApplication</code> object has several important properties and methods for querying artefacts. Probably the most common is the one that gives you all the classes of a particular artefact type:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">for</span> (cls <span class="keyword">in</span> grailsApplication.<artefactType>Classes) { |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>In this case, <code>artefactType</code> is the property name form of the artefact type. With core Grails you have:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>domain</p> |
| </li> |
| <li> |
| <p>controller</p> |
| </li> |
| <li> |
| <p>tagLib</p> |
| </li> |
| <li> |
| <p>service</p> |
| </li> |
| <li> |
| <p>codec</p> |
| </li> |
| <li> |
| <p>bootstrap</p> |
| </li> |
| <li> |
| <p>urlMappings</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>So for example, if you want to iterate over all the domain classes, you use:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">for</span> (cls <span class="keyword">in</span> grailsApplication.domainClasses) { |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>and for URL mappings:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="keyword">for</span> (cls <span class="keyword">in</span> grailsApplication.urlMappingsClasses) { |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>You need to be aware that the objects returned by these properties are not instances of <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html">Class</a>. Instead, they are instances of <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsClass.html">GrailsClass</a> that has some particularly useful properties and methods, including one for the underlying <code>Class</code>:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><code>shortName</code> - the class name of the artefact without the package (equivalent of <code>Class.simpleName</code>).</p> |
| </li> |
| <li> |
| <p><code>logicalPropertyName</code> - the artefact name in property form without the 'type' suffix. So <code>MyGreatController</code> becomes 'myGreat'.</p> |
| </li> |
| <li> |
| <p><code>isAbstract()</code> - a boolean indicating whether the artefact class is abstract or not.</p> |
| </li> |
| <li> |
| <p><code>getPropertyValue(name)</code> - returns the value of the given property, whether it’s a static or an instance one. This works best if the property is initialised on declaration, e.g. <code>static transactional = true</code>.</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>The artefact API also allows you to fetch classes by name and check whether a class is an artefact:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p>get<type>Class(String name)</p> |
| </li> |
| <li> |
| <p>is<type>Class(Class clazz)</p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>The first method will retrieve the <code>GrailsClass</code> instance for the given name, e.g. 'MyGreatController'. The second will check whether a class is a particular type of artefact. For example, you can use <code>grailsApplication.isControllerClass(org.example.MyGreatController)</code> to check whether <code>MyGreatController</code> is in fact a controller.</p> |
| </div> |
| |
| |
| <h2 id="customArtefacts">18.10.2 Adding Your Own Artefact Types</h2> |
| |
| <div class='contribute-btn'> |
| <button type='button' class='btn btn-default' onclick='window.location.href="https://github.com/grails/grails-doc/edit/5.3.x/src/en/guide/plugins/artefactApi/customArtefacts.adoc"'> |
| <i class='fa fa-pencil-square-o'></i> Improve this doc |
| </button> |
| </div> |
| |
| |
| <div class="paragraph"> |
| <p>Plugins can easily provide their own artefacts so that they can easily find out what implementations are available and take part in reloading. All you need to do is create an <code>ArtefactHandler</code> implementation and register it in your main plugin class:</p> |
| </div> |
| <div class="listingblock"> |
| <div class="content"> |
| <pre class="CodeRay highlight"><code data-lang="groovy"><span class="type">class</span> <span class="class">MyGrailsPlugin</span> { |
| <span class="keyword">def</span> artefacts = [ org.somewhere.MyArtefactHandler ] |
| ... |
| }</code></pre> |
| </div> |
| </div> |
| <div class="paragraph"> |
| <p>The <code>artefacts</code> list can contain either handler classes (as above) or instances of handlers.</p> |
| </div> |
| <div class="paragraph"> |
| <p>So, what does an artefact handler look like? Well, put simply it is an implementation of the <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/ArtefactHandler.html">ArtefactHandler</a> interface. To make life a bit easier, there is a skeleton implementation that can readily be extended: <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/ArtefactHandlerAdapter.html">ArtefactHandlerAdapter</a>.</p> |
| </div> |
| <div class="paragraph"> |
| <p>In addition to the handler itself, every new artefact needs a corresponding wrapper class that implements <a href="https://grails.apache.org/docs/5.3.0/api/grails/core/GrailsClass.html">GrailsClass</a>. Again, skeleton implementations are available such as <a href="https://grails.apache.org/docs/5.3.0/api/org/grails/core/AbstractInjectableGrailsClass.html">AbstractInjectableGrailsClass</a>, which is particularly useful as it turns your artefact into a Spring bean that is auto-wired, just like controllers and services.</p> |
| </div> |
| <div class="paragraph"> |
| <p>The best way to understand how both the handler and wrapper classes work is to look at the Quartz plugin:</p> |
| </div> |
| <div class="ulist"> |
| <ul> |
| <li> |
| <p><a href="https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/GrailsJobClass.java">GrailsJobClass</a></p> |
| </li> |
| <li> |
| <p><a href="https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/DefaultGrailsJobClass.java">DefaultGrailsJobClass</a></p> |
| </li> |
| <li> |
| <p><a href="https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/JobArtefactHandler.groovy">JobArtefactHandler</a></p> |
| </li> |
| </ul> |
| </div> |
| <div class="paragraph"> |
| <p>Another example is the <a href="http://github.com/pledbrook/grails-shiro">Shiro plugin</a> which adds a realm artefact.</p> |
| </div> |
| |
| |
| <div style="clear:both;margin-top:15px;"></div> |
| |
| <div class="toc-item prev-left"><a href="../guide/security.html"><< <strong>17</strong><span>Security</span></a></div> |
| |
| <div class="toc-item next-right"><a href="../guide/spring.html"><strong>19</strong><span>Grails and Spring</span> >></a></div> |
| |
| <div style="clear:both"></div> |
| </div> |
| </td> |
| <td id="col2"> |
| <div class="local clearfix"> |
| <div class="local-title"> |
| <a href="../guide/index.html" target="mainFrame">Quick Reference</a> |
| <span class="toggle">(<a href="#" onclick="localToggle(); return false;">hide</a>)</span> |
| </div> |
| <div class="menu"> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Command Line</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/bug-report.html">bug-report</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/clean.html">clean</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/compile.html">compile</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/console.html">console</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-app.html">create-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-command.html">create-command</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-controller.html">create-controller</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-domain-class.html">create-domain-class</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-functional-test.html">create-functional-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-hibernate-cfg-xml.html">create-hibernate-cfg-xml</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-integration-test.html">create-integration-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-interceptor.html">create-interceptor</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-plugin.html">create-plugin</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-profile.html">create-profile</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-script.html">create-script</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-service.html">create-service</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-taglib.html">create-taglib</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/create-unit-test.html">create-unit-test</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/dependency-report.html">dependency-report</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/docs.html">docs</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-all.html">generate-all</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-controller.html">generate-controller</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/generate-views.html">generate-views</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/help.html">help</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/install-templates.html">install-templates</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/list-plugins.html">list-plugins</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/list-profiles.html">list-profiles</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/package-plugin.html">package-plugin</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/package.html">package</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/plugin-info.html">plugin-info</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/profile-info.html">profile-info</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-app.html">run-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-command.html">run-command</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/run-script.html">run-script</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/schema-export.html">schema-export</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/shell.html">shell</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/stats.html">stats</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/stop-app.html">stop-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/test-app.html">test-app</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Command%20Line/war.html">war</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Constraints</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Constraints/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Constraints/attributes.html">attributes</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/bindable.html">bindable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/blank.html">blank</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/creditCard.html">creditCard</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/email.html">email</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/inList.html">inList</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/matches.html">matches</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/max.html">max</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/maxSize.html">maxSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/min.html">min</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/minSize.html">minSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/notEqual.html">notEqual</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/nullable.html">nullable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/range.html">range</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/scale.html">scale</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/size.html">size</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/unique.html">unique</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/url.html">url</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/validator.html">validator</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Constraints/widget.html">widget</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Controllers</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Controllers/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Controllers/actionName.html">actionName</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/allowedMethods.html">allowedMethods</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/bindData.html">bindData</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/chain.html">chain</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/controllerName.html">controllerName</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/defaultAction.html">defaultAction</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/errors.html">errors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/flash.html">flash</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/forward.html">forward</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/grailsApplication.html">grailsApplication</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/hasErrors.html">hasErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/header.html">header</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/namespace.html">namespace</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/params.html">params</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/redirect.html">redirect</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/render.html">render</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/request.html">request</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/respond.html">respond</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/response.html">response</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/responseFormats.html">responseFormats</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/scope.html">scope</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/servletContext.html">servletContext</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/session.html">session</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/withForm.html">withForm</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Controllers/withFormat.html">withFormat</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Database Mapping</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/autoImport.html">autoImport</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/autoTimestamp.html">autoTimestamp</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/batchSize.html">batchSize</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/cache.html">cache</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/cascade.html">cascade</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/column.html">column</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/comment.html">comment</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/discriminator.html">discriminator</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/dynamicInsert.html">dynamicInsert</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/dynamicUpdate.html">dynamicUpdate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/fetch.html">fetch</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/id.html">id</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/ignoreNotFound.html">ignoreNotFound</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/indexColumn.html">indexColumn</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/insertable.html">insertable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/joinTable.html">joinTable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/lazy.html">lazy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/order.html">order</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/sort.html">sort</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/table.html">table</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/type.html">type</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/updateable.html">updateable</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Database%20Mapping/version.html">version</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Domain Classes</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/addTo.html">addTo</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/attach.html">attach</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/belongsTo.html">belongsTo</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/clearErrors.html">clearErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/constraints.html">constraints</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/count.html">count</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/countBy.html">countBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/createCriteria.html">createCriteria</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/delete.html">delete</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/discard.html">discard</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/embedded.html">embedded</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/errors.html">errors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/executeQuery.html">executeQuery</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/executeUpdate.html">executeUpdate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/exists.html">exists</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/fetchMode.html">fetchMode</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/find.html">find</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAll.html">findAll</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAllBy.html">findAllBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findAllWhere.html">findAllWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findBy.html">findBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrCreateBy.html">findOrCreateBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrCreateWhere.html">findOrCreateWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrSaveBy.html">findOrSaveBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findOrSaveWhere.html">findOrSaveWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/findWhere.html">findWhere</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/first.html">first</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/get.html">get</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getAll.html">getAll</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getDirtyPropertyNames.html">getDirtyPropertyNames</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/getPersistentValue.html">getPersistentValue</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasErrors.html">hasErrors</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasMany.html">hasMany</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/hasOne.html">hasOne</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/ident.html">ident</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/instanceOf.html">instanceOf</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/isAttached.html">isAttached</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/isDirty.html">isDirty</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/last.html">last</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/list.html">list</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/listOrderBy.html">listOrderBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/load.html">load</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/lock.html">lock</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mapWith.html">mapWith</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mappedBy.html">mappedBy</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/mapping.html">mapping</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/merge.html">merge</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/namedQueries.html">namedQueries</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/properties.html">properties</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/read.html">read</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/refresh.html">refresh</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/removeFrom.html">removeFrom</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/save.html">save</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/transients.html">transients</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/validate.html">validate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/where.html">where</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/whereAny.html">whereAny</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withCriteria.html">withCriteria</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withNewSession.html">withNewSession</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withSession.html">withSession</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Domain%20Classes/withTransaction.html">withTransaction</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Plug-ins</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/URL%20mappings.html">URL mappings</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/codecs.html">codecs</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/controllers.html">controllers</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/core.html">core</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/dataSource.html">dataSource</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/domainClasses.html">domainClasses</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/hibernate.html">hibernate</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/i18n.html">i18n</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/logging.html">logging</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/scaffolding.html">scaffolding</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/services.html">services</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Plug-ins/servlets.html">servlets</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Services</h1><div class="menu-sub"> |
| |
| <div class="menu-item"><a href="../ref/Services/Usage.html">Usage</a></div> |
| |
| |
| <div class="menu-item"><a href="../ref/Services/scope.html">scope</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Services/transactional.html">transactional</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| <div class="menu-block"><h1 class="menu-title" onclick="toggleRef(this.parentNode.childNodes[1])">Servlet API</h1><div class="menu-sub"> |
| |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/request.html">request</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/response.html">response</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/servletContext.html">servletContext</a> |
| </div> |
| |
| <div class="menu-item"><a href="../ref/Servlet%20API/session.html">session</a> |
| </div> |
| |
| </div> |
| </div> |
| |
| </div> |
| </div> |
| </td> |
| </tr> |
| </table> |
| |
| <div id="footer"> |
| Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically. |
| |
| </div> |
| |
| <script type="text/javascript" src="../js/docs.js"></script> |
| |
| </body> |
| </html> |