<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-3635351606950321511</atom:id><lastBuildDate>Wed, 21 Oct 2026 21:40:20 +0000</lastBuildDate><category>GRAILS</category><category>DOMAIN CLASS</category><category>CONTROLLER</category><category>PLUGIN</category><category>GROOVY</category><category>ERROR</category><category>SERVICE</category><category>WS</category><category>GSP</category><category>MIGRATION</category><category>SPRING</category><category>SRC</category><title>Groovy &amp;amp; Grails</title><description>Groovy on Grails blog where new entries will be posted to help developers to understand environment</description><link>http://beginninggroovyandgrails.blogspot.com/</link><managingEditor>noreply@blogger.com (EduSanz)</managingEditor><generator>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-6033608806619121614</guid><pubDate>Wed, 24 May 2017 10:01:00 +0000</pubDate><atom:updated>2017-06-06T16:51:51.478+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">GROOVY</category><category domain="http://www.blogger.com/atom/ns#">MIGRATION</category><category domain="http://www.blogger.com/atom/ns#">PLUGIN</category><title>Migration to Grails 3</title><description>After many years with no posting I dare creating my first entry in English. A few months ago I was playing around Grails 3 migration from a Grails 2.3.11 project and these were my steps to finish the process:&lt;br /&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Create a new project via Intellij with Grails 3.2.9 and Java 8&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;ddae91f5-941e-418a-9dc0-a831e3047b36&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;grails create-app education&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;7d25d4d1-be59-49c0-b62e-e032f607fc44&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;When executing create-app command, by default the &quot;web&quot; profile is used.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Place all files from Grails 2 project to Grails 3 project respecting packages&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;confluenceTable&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th class=&quot;confluenceTh&quot;&gt;&lt;b&gt;Old Location&lt;/b&gt;&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;&lt;b&gt;New Location&lt;/b&gt;&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/conf/BuildConfig.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Build time configuration is now defined in a Gradle build file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/conf/Config.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/conf/application.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Renamed for consistency with Spring Boot&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/conf/UrlMappings.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/controllers/UrlMappings.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved since grails-app/conf is not a source directory anymore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/conf/BootStrap.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;grails-app/init/BootStrap.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved since grails-app/conf is not a source directory anymore&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;scripts&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/main/scripts&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/main/groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/java&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/main/groovy&lt;/code&gt; (yes, groovy!)&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;test/unit&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/test/groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;test/integration&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/integration-test/groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;web-app&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;div class=&quot;tableblock&quot;&gt;
&lt;code&gt;src/main/webapp&lt;/code&gt; or &lt;code&gt;src/main/resources/&lt;/code&gt;&lt;/div&gt;
&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;Moved for consistency with Gradle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;\*GrailsPlugin.groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;src/main/groovy&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;The plugin descriptor moved to a source directory&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;2253d949-4170-42f1-b2cd-40da012725f8&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;div class=&quot;paragraph&quot;&gt;
&lt;code&gt;src/main/resources/public&lt;/code&gt; is recommended as &lt;code&gt;src/main/webapp&lt;/code&gt; only gets included in WAR packaging but not in JAR packaging.&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
It is recommended to merge Java source files from &lt;code&gt;src/java&lt;/code&gt; into &lt;code&gt;src/main/groovy&lt;/code&gt;. You can create a &lt;code&gt;src/main/java&lt;/code&gt;
 directory if you want to and it will be used but it is generally better
 to combine the folders. (The Groovy and Java sources compile together.)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Our BuildConfig.groovy in Grails 2 project will be our &lt;b&gt;build.gradle&lt;/b&gt; in Grails 3 project&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;4640c173-03ef-4d34-b34b-facbdc10bd74&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;buildscript {
&amp;nbsp;&amp;nbsp; repositories {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mavenLocal()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; maven { url &quot;https://repo.grails.org/grails/core&quot; }
&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp; dependencies {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; classpath &quot;org.grails:grails-gradle-plugin:$grailsVersion&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; classpath &quot;com.bertramlabs.plugins:asset-pipeline-gradle:2.14.1&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; classpath &quot;org.grails.plugins:hibernate5:${gormVersion-&quot;.RELEASE&quot;}&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; classpath &quot;org.grails.plugins:database-migration:3.0.0&quot;
&amp;nbsp;&amp;nbsp; }
}

version &quot;1.1&quot;
group &quot;education&quot;

apply plugin:&quot;eclipse&quot;
apply plugin:&quot;idea&quot;
apply plugin:&quot;war&quot;
apply plugin:&quot;org.grails.grails-web&quot;
apply plugin:&quot;org.grails.grails-gsp&quot;
apply plugin:&quot;asset-pipeline&quot;

repositories {
&amp;nbsp;&amp;nbsp; mavenLocal()
&amp;nbsp;&amp;nbsp; maven { url &quot;https://repo.grails.org/grails/core&quot; }
}

dependencies {
&amp;nbsp;&amp;nbsp; compile &quot;org.springframework.boot:spring-boot-starter-logging&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.springframework.boot:spring-boot-autoconfigure&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails:grails-core&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.springframework.boot:spring-boot-starter-actuator&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.springframework.boot:spring-boot-starter-tomcat&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails:grails-dependencies&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails:grails-web-boot&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:cache&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:scaffolding&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:hibernate5&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.hibernate:hibernate-core:5.1.3.Final&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.hibernate:hibernate-ehcache:5.1.3.Final&quot;
&amp;nbsp;&amp;nbsp; console &quot;org.grails:grails-console&quot;
&amp;nbsp;&amp;nbsp; profile &quot;org.grails.profiles:web&quot;
&amp;nbsp;&amp;nbsp; runtime &quot;com.bertramlabs.plugins:asset-pipeline-grails:2.14.1&quot;
&amp;nbsp;&amp;nbsp; runtime &quot;com.h2database:h2&quot;
&amp;nbsp;&amp;nbsp; testCompile &quot;org.grails:grails-plugin-testing&quot;
&amp;nbsp;&amp;nbsp; testCompile &quot;org.grails.plugins:geb&quot;
&amp;nbsp;&amp;nbsp; testRuntime &quot;org.seleniumhq.selenium:selenium-htmlunit-driver:2.47.1&quot;
&amp;nbsp;&amp;nbsp; testRuntime &quot;net.sourceforge.htmlunit:htmlunit:2.18&quot;

&amp;nbsp;&amp;nbsp; // MySQL 5 Driver
&amp;nbsp;&amp;nbsp; runtime &quot;mysql:mysql-connector-java:5.1.36&quot;

&amp;nbsp;&amp;nbsp; // Ajax Tags
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:ajax-tags:1.0.1.BUILD-SNAPSHOT&quot;

&amp;nbsp;&amp;nbsp; // Joda Time
&amp;nbsp;&amp;nbsp; compile &quot;joda-time:joda-time:2.9.6&quot;

&amp;nbsp;&amp;nbsp; // Rest Client Builder
&amp;nbsp;&amp;nbsp; compile &quot;org.grails:grails-datastore-rest-client&quot;

&amp;nbsp;&amp;nbsp; // Database Migration
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:database-migration:3.0.0&quot;
&amp;nbsp;&amp;nbsp; compile &quot;org.liquibase:liquibase-core:3.5.3&quot;

&amp;nbsp;&amp;nbsp; // Spring Security Core
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:spring-security-core:3.1.1&quot;

&amp;nbsp;&amp;nbsp; // Spring Security ACL
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:spring-security-acl:3.1.0&quot;

&amp;nbsp;&amp;nbsp; // Spring Security Rest
&amp;nbsp;&amp;nbsp; //compile &quot;org.grails.plugins:spring-security-rest:2.0.0.M2&quot;

&amp;nbsp;&amp;nbsp; // Email
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:mail:2.0.0.RC6&quot;

&amp;nbsp;&amp;nbsp; // Export
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:export:2.0.0&quot;

&amp;nbsp;&amp;nbsp; // Google Visualization
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:grails-google-visualization:2.2&quot;

&amp;nbsp;&amp;nbsp; // CKEditor
&amp;nbsp;&amp;nbsp; compile &quot;org.grails.plugins:ckeditor:4.5.9.0&quot;
}

bootRun {
&amp;nbsp;&amp;nbsp; jvmArgs(&#39;-Dspring.output.ansi.enabled=always&#39;)
&amp;nbsp;&amp;nbsp; addResources = true
}

sourceSets {
&amp;nbsp;&amp;nbsp; main {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; resources {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; srcDir &#39;grails-app/migrations&#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
&amp;nbsp;&amp;nbsp; }
}

assets {
&amp;nbsp;&amp;nbsp; minifyJs = true
&amp;nbsp;&amp;nbsp; minifyCss = true
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;37ab96f8-23d5-4c7f-b4b1-c07aa0fe7557&quot; data-macro-name=&quot;warning&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e3dhcm5pbmd9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3dhcm5pbmd9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;There will be plugins already created for Grails 3 distribution but others don&#39;t. For these ones we must look for a workaround:&lt;br /&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Convert them by ourselves to Grails 3&lt;/li&gt;
&lt;li&gt;Get rid of them and implement them by code &lt;/li&gt;
&lt;/ol&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;/ul&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Our Datasource.groovy in Grails 2 project will be in our &lt;b&gt;application.yml&lt;/b&gt; in Grails 3 project&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;e9ab9666-213a-4948-95a8-1452dcdc8060&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;---
grails:
    profile: web
    codegen:
        defaultPackage: net.rdcstudios.core
    spring:
        transactionManagement:
            proxies: false
    gorm:
        # Whether to autowire entities. 
        # Disabled by default for performance reasons.
        autowire: true # Changed to true to be able to inject services into domain classes         
        reactor:
            # Whether to translate GORM events into Reactor events
            # Disabled by default for performance reasons
            events: false
info:
    app:
        name: &#39;@info.app.name@&#39;
        version: &#39;@info.app.version@&#39;
        grailsVersion: &#39;@info.app.grailsVersion@&#39;
spring:
    main:
        banner-mode: &quot;off&quot;
    groovy:
        template:
            check-template-location: false

# Spring Actuator Endpoints are Disabled by Default
endpoints:
    enabled: false
    jmx:
        enabled: true

---
grails:
    mime:
        disable:
            accept:
                header:
                    userAgents:
                        - Gecko
                        - WebKit
                        - Presto
                        - Trident
        types:
            all: &#39;*/*&#39;
            atom: application/atom+xml
            css: text/css
            csv: text/csv
            form: application/x-www-form-urlencoded
            html:
              - text/html
              - application/xhtml+xml
            js: text/javascript
            json:
              - application/json
              - text/json
            multipartForm: multipart/form-data
            pdf: application/pdf
            rss: application/rss+xml
            text: text/plain
            hal:
              - application/hal+json
              - application/hal+xml
            xml:
              - text/xml
              - application/xml
    urlmapping:
        cache:
            maxsize: 1000
    controllers:
        defaultScope: singleton
    converters:
        encoding: UTF-8
    views:
        default:
            codec: html
        gsp:
            encoding: UTF-8
            htmlcodec: xml
            codecs:
                expression: html
                scriptlets: html
                taglib: none
                staticparts: none
endpoints:
    jmx:
        unique-names: true

---
hibernate:
    cache:
        queries: false
        use_second_level_cache: true
        use_query_cache: false
        region.factory_class: org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory

dataSource:
    pooled: true
    jmxExport: true
    driverClassName: com.mysql.jdbc.Driver
    dialect: org.hibernate.dialect.MySQL5InnoDBDialect
    # dialect: net.rdcstudios.mysql.dialect.MySQLUTF8MB4InnoDBDialect
    username: core
    password: core

environments:
    development:
            dataSource:
                dbCreate: none
    internaldemo:
            dataSource:
                dbCreate: none
    demo:
            dataSource:
                dbCreate: none
    client:
            dataSource:
                dbCreate: none
    test:
            dataSource:
                dbCreate: create-drop
    uat:
            dataSource:
                dbCreate: none
    production:
        dataSource:
            dbCreate: none
            properties:
                jmxEnabled: true
                initialSize: 5
                maxActive: 50
                minIdle: 5
                maxIdle: 25
                maxWait: 10000
                maxAge: 600000
                timeBetweenEvictionRunsMillis: 5000
                minEvictableIdleTimeMillis: 60000
                validationQuery: SELECT 1
                validationQueryTimeout: 3
                validationInterval: 15000
                testOnBorrow: true
                testWhileIdle: true
                testOnReturn: false
                jdbcInterceptors: ConnectionState
                defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED

&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Our log4j which is inside Config.groovy in Grails 2 project will be our &lt;b&gt;logback.groovy&lt;/b&gt; in Grails 3 project&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;02322169-63de-4395-82da-290a65e94365&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;import grails.util.BuildSettings
import grails.util.Environment
import org.springframework.boot.logging.logback.ColorConverter
import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter

conversionRule &#39;clr&#39;, ColorConverter
conversionRule &#39;wex&#39;, WhitespaceThrowableProxyConverter

// See http://logback.qos.ch/manual/groovy.html for details on configuration
//statusListener(OnConsoleStatusListener)

def appenderList = [&quot;ROLLING&quot;]
def LOG_DIR = &quot;.&quot;
def consoleAppender = true

def targetDir = BuildSettings.TARGET_DIR

if (targetDir != null) {
    appenderList.add(&quot;CONSOLE&quot;)
    LOG_DIR = &quot;/var/log/education/${Environment.currentEnvironment.name}&quot;
} else {
    LOG_DIR = &quot;/var/log/education/${Environment.currentEnvironment.name}&quot;
    consoleAppender = false
}

if (consoleAppender) {
    appender(&quot;CONSOLE&quot;, ConsoleAppender) {
        encoder(PatternLayoutEncoder) {
            pattern = &quot;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&quot;
        }
    }
}

appender(&quot;ROLLING&quot;, RollingFileAppender) {
    encoder(PatternLayoutEncoder) {
        Pattern = &quot;%d %level %thread %mdc %logger - %m%n&quot;
    }
    rollingPolicy(SizeAndTimeBasedRollingPolicy) {
        MaxFileSize = &quot;5MB&quot;
        FileNamePattern = &quot;${LOG_DIR}/log.%d{yyyy-MM-dd}.%i&quot;
    }
}

logger &#39;grails.app.services&#39;, INFO, appenderList
logger &#39;grails.app.controllers&#39;, INFO, appenderList
logger &#39;net.rdcstudios.core&#39;, INFO, appenderList

//root(INFO, appenderList)

// Spring Security
//logger &#39;grails.plugin.springsecurity.web.filter.DebugFilter&#39;, INFO, [&#39;STDOUT&#39;]

// Hibernate Logs
//logger &#39;org.hibernate.SQL&#39;, DEBUG, [&#39;STDOUT&#39;]
//logger &#39;org.hibernate.type.descriptor.sql.BasicBinder&#39;, TRACE, [&#39;STDOUT&#39;]&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;&lt;div class=&quot;paragraph&quot;&gt;
In Grails 3.x all internal APIs can be found in the &lt;b&gt;&lt;code&gt;org.grails&lt;/code&gt;&lt;/b&gt; package and public facing APIs in the &lt;code&gt;grails&lt;/code&gt; package. The &lt;b&gt;&lt;code&gt;org.codehaus.groovy.grails&lt;/code&gt;&lt;/b&gt;
 package no longer exists. All package declaration in sources should be 
modified for the new location of the respective classes. Example&lt;code&gt;s&lt;/code&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;bca61199-3ec2-4552-8ac9-06fc984273a1&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;org.codehaus.groovy.grails.commons.GrailsApplication --&amp;gt; grails.core.GrailsApplication
org.codehaus.groovy.grails.core.io.ResourceLocator --&amp;gt; org.grails.core.io.ResourceLocator
org.codehaus.groovy.grails.web.mapping.LinkGenerator --&amp;gt; grails.web.mapping.LinkGenerator
org.codehaus.groovy.grails.web.binding.DataBindingUtils --&amp;gt; grails.web.databinding.DataBindingUtils
org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap --&amp;gt; grails.web.servlet.mvc.GrailsParameterMap

In unit or integration tests...
org.codehaus.groovy.grails.plugins.testing.GrailsMockMultipartFile --&amp;gt; org.grails.plugins.testing.GrailsMockMultipartFile&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;As
 of now Grails 3 uses Pipeline Plugin to work with assets instead of 
Resource Plugin in Grails 2. Therefore, we have to move all assets:&lt;ol&gt;
&lt;li&gt;web-app/css --&amp;gt; grails-app/assets/stylesheets&lt;/li&gt;
&lt;li&gt;web-app/images --&amp;gt; grails-app/assets/images&lt;/li&gt;
&lt;li&gt;web-app/js --&amp;gt; grails-app/assets/javascripts&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Create &lt;b&gt;application.js&lt;/b&gt; as an index of all js files&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;4afe18a7-2e7a-44c0-894d-92bbe1261e73&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// This is a manifest file that&#39;ll be compiled into application.js.
//
// Any JavaScript file within this directory can be referenced here using a relative path.
//
// You&#39;re free to add application-wide JavaScript to this file, but it&#39;s generally better
// to create separate JavaScript files as needed.
//
//= require jquery-2.2.0.min
//= require ie10-viewport-bug-workaround
//= require rs-plugin/js/jquery.themepunch.tools.min
//= require rs-plugin/js/jquery.themepunch.revolution.min
//= require custom
//= require menu/jquery.mmenu.min.all
//= require bootstrap/bootstrap.min
//= require bootstrap/moment
//= require bootstrap/bootstrap-datetimepicker
//= require bootstrap/bootstrap-switch.min
//= require bootstrap/bootstrap-wizard
//= require bootstrap/bootstrap-combobox
//= require fullcalendar/fullcalendar.min
//= require fullcalendar/lang-all
//= require_self

if (typeof jQuery !== &#39;undefined&#39;) {
    (function($) {
        $(document).ajaxStart(function() {
            $(&#39;#spinner&#39;).fadeIn();
        }).ajaxStop(function() {
            $(&#39;#spinner&#39;).fadeOut();
        });
    })(jQuery);
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Create &lt;b&gt;application.css&lt;/b&gt; as an index of all css files&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;5d13e2a2-a75b-4941-b316-598858b9adc4&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;/*
* This is a manifest file that&#39;ll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS file within this directory can be referenced here using a relative path.
*
* You&#39;re free to add application-wide styles to this file and they&#39;ll appear at the top of the
* compiled file, but it&#39;s generally better to create a new file per style scope.
*
*= encoding UTF-8
*= require bootstrap/bootstrap-responsive
*= require pure-drawer
*= require style
*= require font-awesome/css/font-awesome.min
*= require menu/jquery.mmenu.all
*= require rs-plugin/css/extralayers
*= require rs-plugin/css/settings
*= require bootstrap/bootstrap-datetimepicker
*= require bootstrap/bootstrap-switch.min
*= require bootstrap/bootstrap-wizard
*= require bootstrap/bootstrap-combobox
*= require fullcalendar/fullcalendar.min
*= require_self
*/&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Add application.js and application.css to main.gsp&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;ba303055-429d-444b-b72a-aaf4a9096c7b&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;&amp;lt;asset:stylesheet src=&quot;application.css&quot;/&amp;gt;
&amp;lt;asset:javascript src=&quot;application.js&quot;/&amp;gt;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Change all &amp;lt;img&amp;gt; and &amp;lt;g:img&amp;gt; to &amp;lt;asset:image src=&quot;&quot; /&amp;gt;&lt;/li&gt;
&lt;li&gt;Change all &amp;lt;script&amp;gt;, &amp;lt;g:script&amp;gt; and &amp;lt;r:script&amp;gt; to &amp;lt;&lt;span data-mce-style=&quot;color: #000080;&quot; style=&quot;color: navy;&quot;&gt;asset:script &lt;/span&gt;&lt;span data-mce-style=&quot;color: #0000ff;&quot; style=&quot;color: blue;&quot;&gt;type&lt;/span&gt;&lt;span data-mce-style=&quot;color: #000000;&quot; style=&quot;color: black;&quot;&gt;=&quot;&lt;/span&gt;&lt;span data-mce-style=&quot;color: #008000;&quot; style=&quot;color: green;&quot;&gt;text/javascript&lt;/span&gt;&lt;span data-mce-style=&quot;color: #000000;&quot; style=&quot;color: black;&quot;&gt;&quot;&lt;/span&gt;&amp;gt;&lt;/li&gt;
&lt;li&gt;I had to add this line just before&amp;nbsp;&lt;code&gt;&amp;lt;/body&amp;gt;&lt;/code&gt; in the outer main template to make javascript code embedded in views or templates work&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;e1cfa181-0752-428f-aee1-8258b644e152&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;&amp;nbsp;&amp;lt;asset:deferredScripts/&amp;gt;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Change all ${resource()} methods to ${assetPath(src: &#39;&#39;}&lt;/li&gt;
&lt;li&gt;Change all &amp;lt;link&amp;gt; related to css to &amp;lt;asset:link&amp;gt;&lt;/li&gt;
&lt;li&gt;Change all path in templates from ../ to /&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;20b09fec-3572-4bdd-b97b-a36e9c409c91&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;&amp;lt;g:render template=&quot;../layouts/bottomBar&quot;/&amp;gt;
to
&amp;lt;g:render template=&quot;/layouts/bottomBar&quot;/&amp;gt;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Remove all grailsApplication beans from Controllers as Grails 3 imports them automatically&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;309ed7c2-d8c9-4c9f-a78e-3badb18d465f&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;def grailsApplication&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Install
 new plugin called ajax-tags as Grails 3 removes all taglibs related to 
remote and to keep using them we will need to have it installed&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;afd8aff9-9509-4cb2-b1ad-95a05dd7a9b3&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Ajax Tags plugin
compile &quot;org.grails.plugins:ajax-tags:1.0.1.BUILD-SNAPSHOT&quot;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Change all prefixes about respond method convention&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;confluenceTable&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th class=&quot;confluenceTh&quot;&gt;Example&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;Argument Type&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;Calculated Model Variable&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;respond Book.list()&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;java.util.List&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;bookList&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;respond Book.get(1)&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;example.Book&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;book&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;respond( [1,2] )&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;java.util.List&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;integerList&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;respond( [1,2] as Set )&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;java.util.Set&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;integerSet&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;respond( [1,2] as Integer[] )&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;Integer[]&lt;/code&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;&lt;code&gt;integerArray&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;987244aa-0de8-4fe3-86f8-f19049b2522e&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// course/_grid
&amp;lt;g:each in=&quot;${courseInstanceList}&quot; status=&quot;i&quot; var=&quot;courseInstance&quot;&amp;gt;
to
&amp;lt;g:each in=&quot;${courseList}&quot; status=&quot;i&quot; var=&quot;courseInstance&quot;&amp;gt;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Change all references to config&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;2c981d86-fd01-465d-a39f-545a326d5dcd&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;grailsApplication.config.net.rdcstudios.pagination.size
to
grailsApplication.config.getProperty(&#39;net.rdcstudios.pagination.size&#39;)&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Change all references to grailsResourceLocator&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;12f7b6ed-d53b-4e14-bd55-4edcc87f88c1&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;grailsResourceLocator.findResourceForURI()
to
assetResourceLocator.findAssetForURI()&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;Change all references to domainClass&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;6f3bdc8c-f496-49d4-b58d-aea453baffe2&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;instance.domainClass.propertyName
to
grails.util.GrailsNameUtils.getPropertyName(instance.class)&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;To make external config properties work, we will need to modify the class &lt;b&gt;Application.groovy&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;1987687c-0508-4d49-a3f4-fcaf0569ba36&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;import grails.boot.GrailsApp
import grails.boot.config.GrailsAutoConfiguration
import org.springframework.context.EnvironmentAware
import org.springframework.core.env.Environment
import org.springframework.core.env.MapPropertySource

class Application extends GrailsAutoConfiguration implements EnvironmentAware {
    static void main(String[] args) {
        GrailsApp.run(Application, args)
    }

    @Override
    void setEnvironment(Environment environment) {
        def file = new File(&quot;/etc/core/core-${grails.util.Environment.getCurrent().name}-config.properties&quot;)
        if(file.exists()) {
            def config = new ConfigSlurper().parse(file.text)
            environment.propertySources.addFirst(new MapPropertySource(grails.util.Environment.getCurrent().name, config))
        }
    }
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Plus
 to escape all values from external config properties we will need to 
add quotes &quot;...&quot; as YAML Parser launches :, . and / values as yml values&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;2361c1a2-f616-4345-9ace-3daeffdbb100&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;dataSource.url = jdbc:mysql://localhost/rdcstudios_dev
to
dataSource.url = &quot;jdbc:mysql://localhost/rdcstudios_dev&quot;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Move all global constraints from Config.groovy to application.groovy&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;7f68e4b0-5a58-41f3-a46a-b3f32bb5b144&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Shared constraints
grails.gorm.default.constraints = {
    codeConstraint(blank: false, nullable: true, size: 1..4, unique: &#39;country&#39;)
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Don&#39;t forget to change empty constraints from () to [:]&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;54bbc85c-b120-4439-b8b9-6d85a5b00e56&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;static constraints = {
  name nullable: true, blank: false
  mySimpleProperty()                  // &amp;lt;- A field that has no constraints. This syntax is not supported in Grails 3.
  anotherProperty unique: true
}
to
static constraints = {
  name nullable: true, blank: false
  mySimpleProperty [:]                // &amp;lt;- Empty map argument instead of ()
  anotherProperty unique: true
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;All
 services injected into domain classes don&#39;t work. Why? In Grails 2 
projects they were working property. The reason is that in Grails 3 
there is a property called &lt;b&gt;grails.gorm.autowired&lt;/b&gt; in application.yml set to false by default. This property is disabled by default for performance reasons.&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;3ccfe481-2039-4a15-9418-5b975f194105&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;Further info&lt;br /&gt;
&lt;a data-mce-href=&quot;http://stackoverflow.com/questions/43059313/grails-3-2-8-dependency-injection-in-domain-classes&quot; href=&quot;http://stackoverflow.com/questions/43059313/grails-3-2-8-dependency-injection-in-domain-classes&quot;&gt;http://stackoverflow.com/questions/43059313/grails-3-2-8-dependency-injection-in-domain-classes&lt;/a&gt;&lt;br /&gt;
&lt;a data-mce-href=&quot;https://gist.github.com/erichelgeson/be2f9f62ab63d989f2ec962ae7001f21&quot; href=&quot;https://gist.github.com/erichelgeson/be2f9f62ab63d989f2ec962ae7001f21&quot;&gt;https://gist.github.com/erichelgeson/be2f9f62ab63d989f2ec962ae7001f21&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Every time app throws an exception is displayed the following:&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;a91525ea-3b36-4095-8fe2-a457f3e5a82e&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;Caused by: groovy.lang.MissingMethodException: No signature of method: ch.qos.logback.classic.Logger.error() is applicable for argument types: (org.springframework.dao.DataIntegrityViolationException)&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Why?
 This is because the default logger that is added to Grails artefacts is
 now an instance of SLF4J, and not JCL. Unlike JCL, none of the SLF4J 
logging methods take a single &lt;code&gt;Object&lt;/code&gt; argument. Therefore we need to do some changes:&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;70a1f0f9-4525-4a7b-9d60-5890c92e5f5d&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;catch (DataIntegrityViolationException dive) {
    log.error(dive)
    flash.error = message(code: &#39;default.not.deleted.message&#39;, args: [&quot;User&quot;, id], default: &quot;User could not be deleted&quot;)
    redirect(action: &#39;show&#39;, id: userInstance?.id)
}

to

catch (DataIntegrityViolationException dive) {
    log.error(dive.toString())
    flash.error = message(code: &#39;default.not.deleted.message&#39;, args: [&quot;User&quot;, id], default: &quot;User could not be deleted&quot;)
    redirect(action: &#39;show&#39;, id: userInstance?.id)
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;30fbb3bb-8e5b-429e-8c4e-2af3d1ad5380&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;Further info&lt;br /&gt;
&lt;a data-mce-href=&quot;https://github.com/grails/grails-core/issues/9971&quot; href=&quot;https://github.com/grails/grails-core/issues/9971&quot;&gt;https://github.com/grails/grails-core/issues/9971&lt;/a&gt;&lt;br /&gt;
&lt;a data-mce-href=&quot;http://docs.grails.org/3.2.8/guide/single.html#upgrading31x&quot; href=&quot;http://docs.grails.org/3.2.8/guide/single.html#upgrading31x&quot;&gt;http://docs.grails.org/3.2.8/guide/single.html#upgrading31x&lt;/a&gt; (See Slf4j now default section)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
UNIT TESTS&lt;/h3&gt;
No changes&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
INTEGRATION TESTS&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;I changed extends IntegrationSpec to &lt;b&gt;Specification&lt;/b&gt; from all tests to keep rules as per Grails 3 doc.&lt;/li&gt;
&lt;li&gt;I added &lt;b&gt;@Integration&lt;/b&gt; and &lt;b&gt;@Rollback&lt;/b&gt; annotation at class level.&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;869b65f3-83bc-4f86-bd87-e53e80b4f38d&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;@Integration
@Rollback
class ActivityServiceIntegrationSpec extends Specification {
...
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/li&gt;
&lt;li&gt;I had to change some load methods to get method as in Grails 3 data binding is more stricted.&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;6b9e657b-ec2f-48d3-836b-e7f441b4f600&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;def setupData() {
    def user = User.load(1)
    def course = new Course(shortName: &#39;Test shortname&#39;, summary: &#39;Test summary&#39;, owner: user).save() -&amp;gt; LazyInitializationException
}

to 

def setupData() {
    def user = User.get(1)
    def course = new Course(shortName: &#39;Test shortname&#39;, summary: &#39;Test summary&#39;, owner: user).save() -&amp;gt; Works fine
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;As per Grails 3 it&#39;s not allowed to save any instance in setup method when &lt;b&gt;@Rollback&lt;/b&gt; annotation is not added as app will throw &lt;i&gt;HibernateException: No Session found&lt;/i&gt;. To solve this it&#39;s recommended to added &lt;b&gt;@Rollback&lt;/b&gt; annotation and separate &lt;b&gt;setup&lt;/b&gt; method (for non transactional operations) from &lt;b&gt;setupData&lt;/b&gt; method (for transactional operations). Don&#39;t forget calling setupData method from given block&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;3543f709-3e20-4765-b790-7d46ffdbc206&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;def setup() {
    alfrescoService.metaClass.uploadFile = { Object resource, MultipartFile resourceFile -&amp;gt; &quot;1&quot; }
    alfrescoService.metaClass.deleteFileByNodeRef = { String nodeRef -&amp;gt; true }
}

def setupData() {
    def user = User.get(1)
    def course = new Course(shortName: &#39;Test shortname&#39;, summary: &#39;Test summary&#39;, owner: user).save()
}

...

void &quot;test save method&quot;() {
    given:
    setupData()
...
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;ec65e706-960c-450a-8a88-2db22d33ace8&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;You have two possibilities:&lt;br /&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Adding
 @Rollback you will have Hibernate session in setup method but once 
finished test there will be no rollback. If you split it in setup method 
for non transactional operations and setupData method for transactional 
operations, you will get rollback.&lt;/li&gt;
&lt;li&gt;Without adding&amp;nbsp;@Rollback you will get no Hibernate session in setup method.&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
DEPLOYMENT ON JENKINS&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Below I&#39;ll paste changes I had to do in order to migrate deployment to Grails 3 on Jenkins job&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;confluenceTable&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;th class=&quot;confluenceTh&quot;&gt;&lt;br /&gt;&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;Grails 2 job&lt;/th&gt;&lt;th class=&quot;confluenceTh&quot;&gt;Grails 3 job&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;GENERATE WAR FILE&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails war education.war&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails war&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;RUN TESTS&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails test-app -coverage -xml&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails test-app&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;GENERATE DOC&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails doc&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;grails docs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot; colspan=&quot;1&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot; colspan=&quot;1&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot; colspan=&quot;1&quot;&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;PUBLISH COVERAGE REPORTS&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;target/test-reports/cobertura/coverage.xml&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;build/reports/cobertura/coverage.xml&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;DEPLOY TO TOMCAT&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;(no changes)&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;(no changes)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;confluenceTd&quot;&gt;EMAIL&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;(no changes)&lt;/td&gt;&lt;td class=&quot;confluenceTd&quot;&gt;(no changes)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;On Grails 2 war file was generated under project root path but on Grails 3 war file is generated in &lt;b&gt;build / libs&lt;/b&gt; path so I had to add some lines to change path in build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;09b6cced-890a-45a2-828d-3a7ff4a6d835&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// build.gradle file
war {
    archiveName &quot;${baseName}.war&quot; // This line for not adding version to war file name
    destinationDir project.projectDir // This line for setting path to project root path and not in build / libs
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;When deploying to Tomcat Jenkins threw this weird error&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;40ddcdd9-8dc2-4e5d-bc90-2914adcc326a&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;java.lang.NoSuchMethodError: org.apache.tomcat.util.res.StringManager.getManager(Ljava/lang/Class;)Lorg/apache/tomcat/util/res/StringManager;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
After
 googling a little bit, I figured out it was due to use different Tomcat
 versions among embed Tomcat used by Spring Boot (version 8) and Tomcat 
used by Jenkins (version 7). As a workaround I changed this&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;a54a075d-2725-4b10-8f14-976065932c3c&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// build.gradle file
compile &quot;org.springframework.boot:spring-boot-starter-tomcat&quot;
to
provided &quot;org.springframework.boot:spring-boot-starter-tomcat&quot;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;465d3443-3e99-476e-b97c-d64a70369782&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;Further info -&amp;gt; &lt;a data-mce-href=&quot;http://www.tothenew.com/blog/grails-3-and-deployment-to-tomcat-container/&quot; href=&quot;http://www.tothenew.com/blog/grails-3-and-deployment-to-tomcat-container/&quot;&gt;http://www.tothenew.com/blog/grails-3-and-deployment-to-tomcat-container/&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;To keep using coverage plugin as of now you can use Gradle plugin. For it the only thing you need to do is to add some lines to &lt;b&gt;build.gradle&lt;/b&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;44cbf60d-60ea-452d-b24c-a289dcbc6c42&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;buildscript {
  repositories {
    maven {
      url &quot;https://plugins.gradle.org/m2/&quot;
    }
  }
  dependencies {
    classpath &quot;net.saliman:gradle-cobertura-plugin:2.4.0&quot;
  }
}

apply plugin: &quot;net.saliman.cobertura&quot;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;373b1b64-6ff2-4f3e-ab16-376aad7ffa83&quot; data-macro-name=&quot;info&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2luZm99&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;Further info -&amp;gt; &lt;a data-mce-href=&quot;https://plugins.gradle.org/plugin/net.saliman.cobertura&quot; href=&quot;https://plugins.gradle.org/plugin/net.saliman.cobertura&quot;&gt;https://plugins.gradle.org/plugin/net.saliman.cobertura&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Also you will need to add cobertura after running tests&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;9612a391-3b3f-470a-a9dc-ae26a48d274f&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Cobertura configuration
cobertura {
    coverageFormats = [&#39;xml&#39;]

    coverageExcludes = [&#39;.*Application.*&#39;,
                        &#39;.*BootStrap.*&#39;,
                        &#39;.*UrlMappings.*&#39;]
}&lt;/pre&gt;
&lt;pre&gt;&lt;span style=&quot;background-color: #e4e4ff;&quot;&gt;test&lt;/span&gt;.finalizedBy(project.tasks.&lt;span style=&quot;color: #660e7a; font-weight: bold;&quot;&gt;cobertura&lt;/span&gt;)&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
INTELLIJ IDEA 2017&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Execute all tests under JUnit not under Grails environment, otherwise Intellij IDEA will execute all tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;img class=&quot;confluence-embedded-image&quot; data-base-url=&quot;https://wiki.rdcstudiosonline.net&quot; data-image-height=&quot;95&quot; data-image-src=&quot;/download/attachments/2228248/intellij%20idea.png?version=1&amp;amp;modificationDate=1495121469576&amp;amp;api=v2&quot; data-image-width=&quot;610&quot; data-linked-resource-container-id=&quot;2228248&quot; data-linked-resource-container-version=&quot;60&quot; data-linked-resource-content-type=&quot;image/png&quot; data-linked-resource-default-alias=&quot;intellij idea.png&quot; data-linked-resource-id=&quot;2490425&quot; data-linked-resource-type=&quot;attachment&quot; data-linked-resource-version=&quot;1&quot; data-location=&quot;RDC Core &amp;gt; Migration to Grails 3 &amp;gt; intellij idea.png&quot; data-mce-src=&quot;https://wiki.rdcstudiosonline.net/download/attachments/2228248/intellij%20idea.png?version=1&amp;amp;modificationDate=1495121469576&amp;amp;api=v2&quot; data-unresolved-comment-count=&quot;0&quot; src=&quot;https://wiki.rdcstudiosonline.net/download/attachments/2228248/intellij%20idea.png?version=1&amp;amp;modificationDate=1495121469576&amp;amp;api=v2&quot; title=&quot;RDC Core &amp;gt; Migration to Grails 3 &amp;gt; intellij idea.png&quot; /&gt;&lt;br /&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Sometimes
 Intellij IDEA executes the test under production or development env 
instead of test env. To amend it you just have to add &lt;code&gt;-Dgrails.env=production &lt;b&gt;to command line from Run / Debug configuration&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;2558f9da-52f0-4745-9ba7-cdb31fabdf7a&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Test log trace
The following profiles are active: production&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Sometimes test throws an error related to &lt;b&gt;Cannot add Domain Class [class ...] It is not a Domain!. &lt;/b&gt;To fix it,&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;dc132f22-3abf-46f8-a68c-f8759b1d7a57&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;grails clean&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
SPRING SECURITY CORE&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Our
 Config.groovy in Grails 2 project contained all configuration 
properties related to plugin. As of now we will have to create a new 
file called &lt;b&gt;application.groovy&lt;/b&gt; to put them in&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;0bd44d3a-0b9e-49a1-88f1-d07d3b06790e&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.debug.useFilter = true
grails.plugin.springsecurity.securityConfigType = &quot;Annotation&quot;
grails.plugin.springsecurity.logout.postOnly = false
grails.plugin.springsecurity.userLookup.userDomainClassName = &quot;net.rdcstudios.core.security.User&quot;
grails.plugin.springsecurity.userLookup.authorityJoinClassName = &quot;net.rdcstudios.core.security.UserRole&quot;
grails.plugin.springsecurity.authority.className = &quot;net.rdcstudios.core.security.Role&quot;
grails.plugin.springsecurity.adh.errorPage = null
grails.plugin.springsecurity.rejectIfNoRule = false
grails.plugin.springsecurity.fii.rejectPublicInvocations = false
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
        [pattern: &#39;/&#39;,                              access: [&#39;permitAll&#39;]],
        [pattern: &#39;/index&#39;,                         access: [&#39;permitAll&#39;]],
        [pattern: &#39;/index.gsp&#39;,                     access: [&#39;permitAll&#39;]],
        [pattern: &#39;/assets/**&#39;,                     access: [&#39;permitAll&#39;]]
]

grails.plugin.springsecurity.useSecurityEventListener = true&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Plus as we need to load an event related to plugin at runtime, we will create other file called &lt;b&gt;runtime.groovy&lt;/b&gt; to define it&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;b87fc070-9dc8-408d-b1d2-58aeba782733&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;grails.plugin.springsecurity.onInteractiveAuthenticationSuccessEvent = { e, appCtx -&amp;gt;
    // Setting language by country and language fields from user domain class
    net.rdcstudios.core.security.User.withSession {
        net.rdcstudios.core.security.User user = net.rdcstudios.core.security.User.get(e.source.principal.id)
        def languageCode = user?.language?.code
        def countryCode = user?.country?.code
        def request = grails.plugin.springsecurity.web.SecurityRequestHolder.getRequest()
        if (languageCode &amp;amp;&amp;amp; countryCode &amp;amp;&amp;amp; request) {
            def locale = new Locale(languageCode, countryCode)
            if (locale) {
                org.grails.web.util.WebUtils.setSessionAttribute(request, org.springframework.web.servlet.i18n.SessionLocaleResolver.LOCALE_SESSION_ATTRIBUTE_NAME, locale)
            }
        }
    }
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
DATABASE MIGRATION&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;We could not install the new plugin for Grails 3 as we were working with Java 7 and app threw this error,&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;d0d7e442-154e-4e75-8573-8d9381739671&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;Unsupported major.minor version 52.0

INFO
J2SE 8 = 52
J2SE 7 = 51 
J2SE 6.0 = 50  
J2SE 5.0 = 49
JDK 1.4 = 48
JDK 1.3 = 47
JDK 1.2 = 46
JDK 1.1 = 45&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
When upgrading to Java 8 error disappeared.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
EXPORT&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;I found an error to display export buttons. In Grails 2 projects you set it by adding Resource plugin tag&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;f297efb7-8070-45aa-a9b6-d0e322ad24d1&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;&amp;lt;r:require module=&quot;export&quot;/&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
But
 in Grails 3 projects we use Asset Pipeline so no way to reference 
resources belonging to plugins. I realized manifest file from Export 
plugin was called application.css (the same name as my app one). So as a
 workaround I had to rename my app manifest to app.css and leave 
application.css for Export plugin one.&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;48c43753-1b88-4467-86c0-e9f921789f58&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;&amp;lt;asset:stylesheet src=&quot;application.css&quot;/&amp;gt;&lt;/pre&gt;
&lt;pre&gt;&amp;nbsp;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
I had to add this line just on views where it was used.&lt;br /&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;Plus I had to add the following line to build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;156b5c71-7a03-45de-b56d-28f50bdb740a&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;assets {
    minifyJs = true
    minifyCss = true
    packagePlugin = true 
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;RICH_TEXT&quot; data-macro-id=&quot;1d98d689-950a-4c4f-af5f-232e5db0d1f2&quot; data-macro-name=&quot;warning&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e3dhcm5pbmd9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3dhcm5pbmd9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;div class=&quot;paragraph&quot;&gt;
ASSET PIPELINE DOCUMENTATION&lt;br /&gt;
Plugins
 also can have the same &quot;grails-app/assets&quot; folder and their URL mapping
 is also the same. This means it can be more important to ensure unique 
naming / path mapping between plugins. This is also powerful in the 
sense that a plugin can add helper manifests to be used within your apps
 like jquery, bootstrap, font-awesome, and more.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;paragraph&quot;&gt;
Plugins should make sure that the &lt;code&gt;assets { packagePlugin }&lt;/code&gt; property is set to true in their &lt;code&gt;build.gradle&lt;/code&gt; file otherwise assets will not properly be packaged into the plugin for use by the application.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
JASPER&lt;/h3&gt;
&lt;ul data-mce-style=&quot;list-style-type: square;&quot; style=&quot;list-style-type: square;&quot;&gt;
&lt;li&gt;I found no plugin compatible for Grails 3 so I had to do the following:&lt;ol&gt;
&lt;li&gt;Import JasperController to my controllers folder&lt;/li&gt;
&lt;li&gt;Import JasperService to my services folder&lt;/li&gt;
&lt;li&gt;Import JasperTagLib to my taglib folder (changing all g.resource references to asset.assetPath)&lt;/li&gt;
&lt;li&gt;Import all Jasper images to my assets/images folder&lt;/li&gt;
&lt;li&gt;Import JasperExportFormat and JasperReportDef to src/main/groovy&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Plus I had to add the following lines to build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;9eb39c8b-0caa-462e-9325-c18c3a94d8e7&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;// Jasper
compile &quot;com.lowagie:itext:2.1.7&quot;

compile(&quot;net.sf.jasperreports:jasperreports:5.6.1&quot;) {
    exclude group: &#39;antlr&#39;, module: &#39;antlr&#39;
    exclude group: &#39;commons-logging&#39;, module: &#39;commons-logging&#39;
    exclude group: &#39;org.apache.ant&#39;, module: &#39;ant&#39;
    exclude group: &#39;mondrian&#39;, module: &#39;mondrian&#39;
    exclude group: &#39;commons-javaflow&#39;, module: &#39;commons-javaflow&#39;
    exclude group: &#39;net.sourceforge.barbecue&#39;, module: &#39;barbecue&#39;
    exclude group: &#39;xml-apis&#39;, module: &#39;xml-apis&#39;
    exclude group: &#39;xalan&#39;, module: &#39;xalan&#39;
    exclude group: &#39;org.codehaus.groovy&#39;, module: &#39;groovy-all&#39;
    exclude group: &#39;org.hibernate &#39;, module: &#39;hibernate&#39;
    exclude group: &#39;javax.xml.soap&#39;, module: &#39;saaj-api&#39;
    exclude group: &#39;javax.servlet&#39;, module: &#39;servlet-api&#39;
    exclude group: &#39;org.springframework&#39;, module: &#39;spring-core&#39;
    exclude group: &#39;org.beanshell&#39;, module: &#39;bsh&#39;
    exclude group: &#39;org.springframework&#39;, module: &#39;spring-beans&#39;
    exclude group: &#39;jaxen&#39;, module: &#39;jaxen&#39;
    exclude group: &#39;net.sf.barcode4j &#39;, module: &#39;barcode4j&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-svg-dom&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-xml&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-awt-util&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-dom&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-css&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-gvt&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-script&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-svggen&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-util&#39;
    exclude group: &#39;org.apache.xmlgraphics&#39;, module: &#39;batik-bridge&#39;
    exclude group: &#39;javax.persistence&#39;, module: &#39;persistence-api&#39;
    exclude group: &#39;eclipse&#39;, module: &#39;jdtcore&#39;
    exclude group: &#39;org.olap4j&#39;, module: &#39;olap4j&#39;
}

compile &quot;org.apache.poi:poi:3.10-FINAL&quot;
compile &quot;commons-io:commons-io:2.5&quot;&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;
BULK DATA IMPORTS&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I found no plugin compatible for Grails 3 so I had to do the following:&lt;ol&gt;
&lt;li&gt;Import ImportsController to my controllers folder&lt;/li&gt;
&lt;li&gt;Import ImportsService to my services folder&lt;/li&gt;
&lt;li&gt;Import DefaultImporter and ImportsException to src/main/groovy. Plus logging folder (with all Logguers) to src/main/groovy too.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Plus
 I had to copy all code from doWithSpring closure of 
BulkDataImportsGrailsPlugin in Grails 2 to doWithSpring closure of 
Application in Grails 3&lt;/li&gt;
&lt;/ul&gt;
&lt;table class=&quot;wysiwyg-macro&quot; data-macro-body-type=&quot;PLAIN_TEXT&quot; data-macro-id=&quot;b803394a-9380-4131-989c-9657474a9ee9&quot; data-macro-name=&quot;code&quot; data-macro-schema-version=&quot;1&quot; data-mce-style=&quot;background-image: url(&#39;https://wiki.rdcstudiosonline.net/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2&#39;); background-repeat: no-repeat;&quot; style=&quot;background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGV9&amp;amp;locale=en_GB&amp;amp;version=2); background-repeat: no-repeat;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td class=&quot;wysiwyg-macro-body&quot;&gt;&lt;pre&gt;class Application extends GrailsAutoConfiguration implements EnvironmentAware {
    static void main(String[] args) {
        GrailsApp.run(Application, args)
    }

    @Override
    void setEnvironment(Environment environment) {
        def file = new File(&quot;/etc/core/core-${grails.util.Environment.getCurrent().name}-config.properties&quot;)
        if(file.exists()) {
            def config = new ConfigSlurper().parse(file.text)
            environment.propertySources.addFirst(new MapPropertySource(grails.util.Environment.getCurrent().name, config))
        }
    }

    @Override
    Closure doWithSpring() {
        def loggingProvider = Holders.config.grails.plugins.imports.containsKey(&#39;loggingProvider&#39;) ? Holders.config.grails.plugins.imports.loggingProvider : &#39;default&#39;

        def loggingProviderClass = null
        if (loggingProvider == &#39;mongo&#39;) {
            loggingProviderClass = MongoLogger
        } else if (loggingProvider == &#39;mem&#39;) {
            loggingProviderClass = InMemoryLogger
        } else if (loggingProvider == &#39;default&#39;) {
            loggingProviderClass = DefaultLogger
        } else {
            Class clazz = Class.forName(loggingProvider, true, Thread.currentThread().contextClassLoader)
            loggingProviderClass = clazz
        }

        def beans = {
            importsLogger(loggingProviderClass)
        }

        for(service in Holders.grailsApplication.serviceClasses) {
            if (service.hasProperty(&#39;imports&#39;)) {
                def entityName,
                    imports = service.getPropertyValue(&#39;imports&#39;)
                if (imports instanceof Class || imports instanceof String) {
                    entityName = GrailsNameUtils.getPropertyName(imports)
                }
                if (entityName) {
                    def found = Holders.grailsApplication.domainClasses?.find { GrailsNameUtils.getPropertyName(it.name) == entityName} != null
                    if (!found  &amp;amp;&amp;amp; !service.hasMetaMethod(&#39;processRow&#39;, getArgs(5)) ) {
                        log.warn(&#39;\n    BulkDataImports: could not configure importer &#39;+service.shortName+&#39;... no domain class found and missing processRow method&#39;)
                    } else {
                        DefaultImporter.SERVICE_METHODS.each { k, v-&amp;gt;
                            if (!service.hasMetaMethod(k, getArgs(v))) service.metaClass.&quot;${k}&quot; = DefaultImporter.&quot;${k}&quot;
                        }
                        def props = DefaultImporter.SERVICE_PROPERTIES.clone()
                        props.entityName = entityName
                        props.each {k, v-&amp;gt;
                            if (!service.hasMetaMethod(k)) service.metaClass.&quot;${k}&quot; = { -&amp;gt;  v }
                        }
                        ImporterService.IMPORT_CONFIGURATIONS[entityName] = GrailsNameUtils.getPropertyNameRepresentation(service.shortName)
                    }
                } else {
                    log.warn(&#39;\n    BulkDataImports: invalid imports configuration for &#39;+service.shortName+&#39; :&#39;+imports)
                }
            }

        }

        ImporterService.IMPORT_CONFIGURATIONS.each { k, v-&amp;gt; log.info(&#39;\n    BulkDataImports:&#39;+ k + &#39; imported by &#39;+v) }

        return beans
    }

    private getArgs(ct) {
        (1..ct).collect { Object.class }.toArray()
    }
}&lt;/pre&gt;
&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
Hope this helps.</description><link>http://beginninggroovyandgrails.blogspot.com/2017/05/migration-to-grails-3.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-8678409840614813446</guid><pubDate>Fri, 01 Nov 2013 12:37:00 +0000</pubDate><atom:updated>2013-11-01T13:37:02.460+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ERROR</category><category domain="http://www.blogger.com/atom/ns#">SERVICE</category><category domain="http://www.blogger.com/atom/ns#">SPRING</category><title>Buenas practicas en Servicios</title><description>Muchas veces no hemos encontrado en la situacion de hacer un servicio que guarde las buenas practicas en Grails, en definitiva que guarde la transaccionalidad si ocurre una excepcion dentro de el.
&lt;br&gt;
A continuacion vamos a hacer una serie de reglas para seguir haciendo buen codigo Grails:
&lt;br&gt;
- Grails solo hace rollback cuando se lanza una unchecked (Runtime) exception. Es decir, la mejor manera de controlar esto es crearse una exception en tu proyecto que extienda de Runtime Exception.

&lt;pre class=&quot;brush:java&quot;&gt;
class BaseException extends RuntimeException {
...
}
&lt;/pre&gt;

&lt;pre class=&quot;brush:java&quot;&gt;
class HolidayService {
    static transactional = true
    def saveHoliday(holidayInstance){
        ...intermediate saves before saving holiday... 
        if (holidayInstance.save()) {
            return holidayInstance
        } else {
            throw new BaseException()
        }
    }
}
&lt;/pre&gt;


&lt;pre class=&quot;brush:java&quot;&gt;
class HolidayController {    
    def holidayService
    def save(){
        def holidayInstance = new Holiday(params)
        try{
            def holidaySaved = holidayService.saveHoliday(holidayInstance)
            if(holidaySaved){
                flash.message = message(code: &#39;default.created.message&#39;, args: [message(code: &#39;holiday.label&#39;, default: &#39;Holiday&#39;), holidayInstance?.id])
                redirect(action: &quot;list&quot;)
            }else{
                flash.errors = message(code: &#39;default.not.created.message&#39;, args: [message(code: &#39;holiday.label&#39;, default: &#39;Holiday&#39;), holidayInstance?.id])
                render(view: &quot;create&quot;, model: [holidayInstance: holidayInstance])
            }
        }catch(BaseException e){
            flash.errors = e.getErrors()
            return render(view: &quot;create&quot;, model: [holidayInstance: holidayInstance])
        }
    }
}
&lt;/pre&gt;

- Si os veis mejor con las anotaciones de Spring, podeis usar @Transactional(rollbackFor=Exception.class), siempre seteando antes tu servicio con static transactional = false. Si necesitas tambien que alguna exception NO haga el rollback puedes utilizar @Transactional(noRollbackFor=AnotherException.class)

&lt;pre class=&quot;brush:java&quot;&gt;
class HolidayService {
    static transactional = false
    @Transactional(rollbackFor=BaseException.class,noRollbackFor=AnotherException.class)
    def saveHoliday(holidayInstance){
        ...intermediate saves before saving holiday... 
        if (holidayInstance.save()) {
            return holidayInstance
        } else {
            throw new BaseException()
        }
    }
}
&lt;/pre&gt;

- Si quereis por algun caso especial poner una checked exception en vuestro codigo del Servicio, teneis que hacer lo siguiente ya que Spring por defecto no hace el rollback para las checked exception.&lt;br&gt;

&lt;pre class=&quot;brush:java&quot;&gt;
class HolidayService {
    static transactional = false
    @Transactional(rollbackFor=Exception.class)
    def saveHoliday(holidayInstance){
        try{
          ...intermediate saves before saving holiday... 
          if (holidayInstance.save()) {
              return holidayInstance
          } else {
              return null
          }
       }catch(Exception e){
          e.printStackTrace()
       }
    }
}
&lt;/pre&gt;

- Por defecto la anotacion @Transactional tiene los siguientes parametros:&lt;br&gt;
1) propagation -&gt; por defecto es PROPAGATION_REQUIRED.&lt;br&gt;

2) isolation -&gt; por defecto es ISOLATION_DEFAULT.&lt;br&gt;

3) readOnly -&gt; por defecto es false, es decir read/write.&lt;br&gt;

4) timeout&lt;br&gt;

&lt;br&gt;
NOTA: Mucho cuidado si usais el plugin de Audit Logging ya que con este plugin la transaccionalidad en los servicios fallara si teneis en el Config esta linea.
&lt;pre class=&quot;brush:xml&quot;&gt;
      auditLog.transactional=true
&lt;/pre&gt;
La solucion es ponerlo a false.
&lt;br&gt;
































</description><link>http://beginninggroovyandgrails.blogspot.com/2013/11/buenas-practicas-en-servicios.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-5905900471780905087</guid><pubDate>Fri, 23 Nov 2012 09:29:00 +0000</pubDate><atom:updated>2012-12-03T13:16:09.474+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">GROOVY</category><title>Greach 2.0 - Groovy Spanish Conference</title><description>Ya podeis comprar los tickets para la &lt;a href=&quot;http://greach.es/&quot; target=&quot;_blank&quot;&gt;Greach&lt;/a&gt; o lo que es lo mismo la Groovy Spanish Conference que se celebrará en Madrid los días 25 y 26 de Enero del 2013.&lt;br/&gt; 
Ya están confirmados algunos de los speakers que darán charlas como Graeme Rocher, Cedric Champeau, Andres Almiray, Sebastian Blanch, Corinne Krych, Russel Winder, Burt Beckwith, Marcin Erdmann...&lt;br/&gt;
El precio de los tickets varía ya que si lo comprais &lt;b&gt;antes del 12 de Diciembre&lt;/b&gt; os costará 90 euros y sino el precio es de 120 euros.&lt;br/&gt;</description><link>http://beginninggroovyandgrails.blogspot.com/2012/11/greach-20-groovy-spanish-conference.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-5529239116617913692</guid><pubDate>Fri, 23 Nov 2012 09:19:00 +0000</pubDate><atom:updated>2012-11-23T10:29:27.385+01:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Cambio de look en grails.org</title><description>Ya podeis echarle un vistazo a la nueva web que nos han dejado los miembros de &lt;a href=&quot;http://grails.org&quot; target=&quot;_blank&quot;&gt;grails.org&lt;/a&gt;</description><link>http://beginninggroovyandgrails.blogspot.com/2012/11/nueva-web-grailsorg.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-2945774431510622641</guid><pubDate>Fri, 26 Oct 2012 15:26:00 +0000</pubDate><atom:updated>2012-10-26T17:26:36.042+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GROOVY</category><title>Lo nuevo de Groovy 2.0</title><description>Recordaremos primero lo que llevaba Groovy 1.8:&lt;br/&gt;&lt;br/&gt;
– Mejores DSLs con command chains&lt;br/&gt;
&amp;emsp;Una sintaxis mejorada en donde puedes eliminar puntos y paréntesis cuando encadenas varias llamadas a métodos.
&lt;br/&gt; 
– Mejoras en el rendimiento en tiempo de ejecución&lt;br/&gt;
– GPars está ya agrupado dentro de la distribución de Groovy&lt;br/&gt;
– Mejoras en las Closures&lt;br/&gt;
&amp;emsp;Anotaciones con closures como parámetros y método memoize() en closures para cachear un número de invocaciones.
&lt;br/&gt;
– Soporte mejorado para la producción, construcción e impresión de JSON&lt;br/&gt; 
– Nuevas transformaciones AST&lt;br/&gt;
&amp;emsp;4 loggers diferentes pueden ser inyectados (@Log, @Commons, @Log4j y @Slf4j) o posiblidad de implementar tu propia estrategia.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import groovy.util.logging.*

@Log
class Car{
  Car() {
     log.info &#39;Car constructed&#39;
  }
}

def car = new Car()
&lt;/pre&gt;
@Field&lt;br/&gt;
@AutoClone y @AutoExternalizable&lt;br/&gt;
@Canonical&lt;br/&gt; 
&amp;emsp;@toString: para crear un método toString() a tus tipos.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import groovy.transform.ToString

@ToString
class Person {
  String name
  int age
}

println new Person(name:&#39;Edu&#39;, age:31)
// =&gt; Person(Edu,31)
&lt;/pre&gt; 
&amp;emsp;@EqualsAndHashCode: para crear una implemetación por defecto de equals() y hashCode()
&lt;pre class=&quot;brush:java&quot;&gt;
import groovy.transform.EqualsAndHashCode

@EqualsAndHashCode
class Coord {
  int x, y
}

def c1 = new Coord(x:20,y:10)
def c2 = new Coord(x:20,y:10)

assert c1==c2
assert c1.hashCode()==c2.hashCode()
&lt;/pre&gt; 
&amp;emsp;@TupleConstructor: crea un clásico constructor con todas las propiedades&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import groovy.transform.TupleConstructor

class Person {
  String name
  int age
}

def person = new Person(name:&#39;Edu&#39;, age:31)
assert person.name==&#39;Edu&#39;
assert person.age==31
&lt;/pre&gt;
Controlar la ejecución del código para cuando el código entra en un bucle infinito o consume muchos recursos. Se hace con las 3 nuevas anotaciones: @ThreadInterrupt, @TimeInterrupt y @ConditionalInterrupt.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import groovy.transform.ThreadInterrupt

@ThreadInterrupt
void main(){
  while(true){
    if(Thread.currentThread().isInterrupted())
      throw new InterruptedException()
    //eat lots of CPU
  }
}
&lt;/pre&gt;
@InheritConstructor: para que se pinten todos los constructores heredados&lt;br/&gt;
@WithReadLock y @WithWriteLock&lt;br/&gt;
@ListenerList&lt;br/&gt;
&lt;br/&gt;

Lo nuevo de Groovy 2.0 (NO INCLUIDO EN GRAILS 2):&lt;br/&gt;&lt;br/&gt;
– Alineaciones con JDK 7: Proyecto Coin (Binary literals, Underscore en literales y Multicatch) y InvokeDynamic ()&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
//Ejemplo de Multicatch
  try{
      ...
  }catch(IOException | NullPointerException e){
    //Un bloque para tratar 2 excepciones
  }
&lt;/pre&gt;
– Continuan las mejoras de rendimiento en tiempo de ejecución&lt;br/&gt;  
– Modularidad (todos los jars de Groovy pesan 6 Mb, con un pequeño core (3 Mb) y un pequeño jar por feature)&lt;br/&gt;
&amp;emsp;console – jsr-223 – test – docgenerator – jmx – testng – groovydoc – sql – json – groovysh – swing – xml – ant – servlet – bsf – templates.
&lt;br/&gt;
Dejaremos las dos más importantes features para el final: static type checking y static compilation.&lt;br/&gt;
&lt;br/&gt;
@TypeChecked&lt;br/&gt;
Puede estar a nivel de clase o de método. Ayuda a comprobar errores de compilación (no en tiempo de ejecución). No todo el tiempo necesitas las features dinámicas. Comprueba tipos en métodos y variables, asignaciones erroneas, tipos devueltos erroneos y tipos heredados.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import  groovy.transform.TypeChecked

void  method() { ... }   

@TypeChecked  
test() {
  // No se puede encontrar el método  metthhoood()        
  metthhoood()          
  def  name  =  &quot;Eduuuuuu&quot;        
  // Variable  naamme  no está declarada       
  println  naamme 
}
&lt;/pre&gt; 
La anotación @TypeChecked funciona en tiempo de compilación y no cambia el comportamiento del código. La mayoría de las features dinámicas no pueden ser @TypeChecked. Sin embargo, la metaprogramación funciona en tiempo de compilación.&lt;br/&gt;&lt;br/&gt;
@CompileStatic&lt;br/&gt;
Esto quiere decir que todo el código se compila estáticamente, es decir genera el mismo byte code que si lo compilásemos con javac.&lt;br/&gt; 
Ventajas que consigues:&lt;br/&gt;
- Tipado seguro&lt;br/&gt;
- Código más rápido (casi a la altura de Java)&lt;br/&gt;
- Código inmune&lt;br/&gt;
- Byte code generado más pequeño&lt;br/&gt;
Desventajas&lt;br/&gt;
- Pierdes las features dinámicas&lt;br/&gt;
- Pierdes el método dispatch&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import  groovy.transform.CompileStatic  

@CompileStatic
test() { ... }
&lt;/pre&gt;










</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/lo-nuevo-de-groovy-20.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-4302219507068450502</guid><pubDate>Thu, 25 Oct 2012 14:50:00 +0000</pubDate><atom:updated>2012-10-25T16:50:58.553+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Diferencia entre el método addTo y el operador &lt;&lt; (leftShift)</title><description>Sabemos que cuando creas una clase de dominio en Grails con una relación hasMany, automáticamente te crearán dos métodos para añadir o borrar instancias a la relación (addTo... y removeFrom...)&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class Book {
  static hasMany = [authors: Author]

}
&lt;/pre&gt;
Para añadir una instancia author dentro de Book habría dos formas de hacerlo,
&lt;pre class=&quot;brush:java&quot;&gt;
def author = Author.get(1)
def book = Book.get(3)

book.addToAuthors(author) //La colección authors ya está inicializada
ó
book.authors &lt;&lt; author //La colección tiene que estar inicializada antes con book.authors = []
&lt;/pre&gt;
</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/diferencia-entre-el-metodo-addto-y-el.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-2140017920665820102</guid><pubDate>Wed, 24 Oct 2012 14:56:00 +0000</pubDate><atom:updated>2012-10-24T16:56:01.684+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Múltiples datasources</title><description>En Grails 2 uno de los plugins que ya vienen instalados es el Multiple Datasources Plugin. Muy interesante si tienes más de una BBDD a la que atacar.&lt;br/&gt;
Por defecto todas las clases de dominio atacan a la misma BBDD configurada en tu Config.groovy,&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
dataSource {
    pooled = true
    driverClassName = &quot;org.h2.Driver&quot;
    username = &quot;sa&quot;
    password = &quot;&quot;
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = &#39;net.sf.ehcache.hibernate.EhCacheProvider&#39;
}

environments {
  development {
    dataSource {
      dbCreate = &quot;create-drop&quot; 
      url = &quot;jdbc:h2:mem:devDb&quot; 
    } 
  } 
  test {
    dataSource {
      dbCreate = &quot;update&quot;
      url = &quot;jdbc:h2:mem:testDb&quot;
    }
  }
  production {
    dataSource {
      dbCreate = &quot;update&quot;
      url = &quot;jdbc:h2:prodDb&quot; 
    }
  } 
}
&lt;/pre&gt;
&lt;br/&gt;
Si quieres configurar otra BBDD, la tendrás que definir al mismo nivel que dataSource, sólo que cambiándole el nombre, en este caso dataSource_lookup,&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
dataSource {
    pooled = true
    driverClassName = &quot;org.h2.Driver&quot;
    username = &quot;sa&quot;
    password = &quot;&quot;
}
hibernate {
    cache.use_second_level_cache = true
    cache.use_query_cache = true
    cache.provider_class = &#39;net.sf.ehcache.hibernate.EhCacheProvider&#39;
}

environments {
  development {
    dataSource {
      dbCreate = &quot;create-drop&quot; 
      url = &quot;jdbc:h2:mem:devDb&quot; 
    }
    dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = &#39;com.mysql.jdbc.Driver&#39;
            username = &#39;lookup&#39;
            password = &#39;secret&#39;
            url = &#39;jdbc:mysql://localhost/lookup&#39;
            dbCreate = &#39;update&#39;
    } 
  } 
  test {
    dataSource {
      dbCreate = &quot;update&quot;
      url = &quot;jdbc:h2:mem:testDb&quot;
    }
  }
  production {
    dataSource {
      dbCreate = &quot;update&quot;
      url = &quot;jdbc:h2:prodDb&quot; 
    }
  } 
}
&lt;/pre&gt;
&lt;br/&gt;
El siguiente paso sería configurar las clases de dominio. Habría varias posibilidades:&lt;br/&gt;
 - Que ataque a la nueva BBDD&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ZipCode {

  String code

  static mapping = { datasource &#39;lookup&#39; } 
}
&lt;/pre&gt;
 - Que ataque a varias BBDD&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ZipCode {

  String code

  static mapping = { datasources([&#39;lookup&#39;, &#39;auditing&#39;]) } 
}
&lt;/pre&gt;
 - Que ataque a varias BBDD, entre ellas la de por defecto&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ZipCode {

  String code

  static mapping = { datasources([&#39;lookup&#39;, &#39;DEFAULT&#39;]) } 
}
&lt;/pre&gt;
 - Que ataque a todas las BBDD&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ZipCode {

  String code

  static mapping = { datasource &#39;ALL&#39; } 
}
&lt;/pre&gt;
&lt;br/&gt;
Luego a la hora de hacer save, delete o cualquier método GORM, tendremos que construirlo con un namespace para decirle a qué BBDD tiene que ir.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
def zipCode = ZipCode.auditing.get(42)
…
zipCode.auditing.save()
&lt;/pre&gt;
&lt;br/&gt;
En el ejemplo anterior, le estaremos diciendo que la instancia de la clase ZipCode tiene que hacer el get y el save en la BBDD &quot;auditing&quot;.
&lt;br/&gt;






</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/multiples-datasources.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-7197931912630131345</guid><pubDate>Tue, 23 Oct 2012 14:49:00 +0000</pubDate><atom:updated>2012-10-23T16:49:05.648+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>El argumento &quot;failOnError&quot;</title><description>A veces cuando hacemos un save de una clase de dominio, no sabemos por qué no se han persistido los datos en la BBDD. La mayoría de las veces es porque se nos olvida que todas las clases de dominio tienen una constraint implícita con un nullable: false.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
def book = new Book(params)
if (!book.save()) {
    // Save falla!
    ...
}
&lt;/pre&gt;
Para solucionarlo y ver más claro donde ha fallado podemos utilizar el argumento &quot;failOnError&quot;. De esta manera cuando haya un error en la validación, nos lanzará una excepción para verlo más claramente.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
book.save(failOnError: true)
&lt;/pre&gt;
También se puede definir de forma general para que lo haga con todos los &quot;save&quot; lanzados en la aplicación, configurándolo en nuestro grails-app/conf/Config.groovy,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
grails.gorm.failOnError = true
&lt;/pre&gt;
También podemos presentar los errores de la siguiente manera,
&lt;pre class=&quot;brush:java&quot;&gt;
def book = new Book(params)
if (!book.save() &amp;&amp; book.hasErrors()) {
    // Save falla! Presenta los errores.
    book.errors.allErrors.each{ println it }
    ...
}
&lt;/pre&gt;</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/el-argumento-failonerror.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-5676210848275284768</guid><pubDate>Mon, 22 Oct 2012 14:30:00 +0000</pubDate><atom:updated>2012-10-22T16:30:14.427+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">SRC</category><title>Inyectando beans adicionales</title><description>Por convención de Grails, todos los servicios metidos en nuestra carpeta &quot;services&quot; serán inyectados automáticamente por el framework ayudándose de las bondades de Spring.&lt;br/&gt;
Pero habrá otros casos en donde definamos un bean en src/groovy o en src/java que vayamos a tener que utilizar en cualquiera de nuestros artefactos más adelante.&lt;br/&gt;
Se puede inyectar de dos maneras: usando Spring Bean DSL o por XML.&lt;br/&gt;
  - Por Spring Bean DSL: modificando el archivo grails-app/conf/spring/resources.groovy y añadiendo el bean dentro de la clousure beans.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import my.company.MyBeanImpl

beans = {
 myBean(MyBeanImpl) { 
   someProperty = 42 
   otherProperty = &quot;blue&quot; 
 } 
}
&lt;/pre&gt;

  - Usando XML: modificando el archivo grails-app/conf/spring/resources.xml (lo tienes que crear tu a mano primero) y añadiendo el bean como lo definirías en el applicationContext.xml con Spring.
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;bean id=&quot;myBean&quot; class=&quot;my.company.MyBeanImpl&quot;&amp;gt;
    &amp;lt;property name=&quot;someProperty&quot; value=&quot;42&quot; /&amp;gt;
    &amp;lt;property name=&quot;otherProperty&quot; value=&quot;blue&quot; /&amp;gt;
&amp;lt;/bean&amp;gt;
&lt;/pre&gt;

Una vez configurado, el bean estará disponible para ser inyectado en cualquiera de los artefactos de Grails, así como en clases que soportan la inyección de depedencias (como puede ser Bootstrap o test de integración)&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ExampleController {

  def myBean … 

}
&lt;/pre&gt;
NOTA: Si defines un bean en el fichero resources.groovy con el mismo nombre que uno que se haya configurado previamente por Grails o por un plugin instalado, tu bean reemplazará al bean previamente registrado. Esto es una forma conveniente de customizar el comportamiento sin recurrir a editar el código del plugin u otra clase que pudiera afectar a la mantenibilidad de la aplicación.</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/inyectando-beans-adicionales.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-3875546934217210299</guid><pubDate>Mon, 08 Oct 2012 15:18:00 +0000</pubDate><atom:updated>2012-10-08T17:32:02.748+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">PLUGIN</category><category domain="http://www.blogger.com/atom/ns#">WS</category><title>Consumir servicios web con el Plugin WS-Client</title><description>En la entrada anterior veíamos cómo exponer servicios web a través del Plugin CXF de Grails de una manera rápida y sencilla. Ahora nos toca ver cómo consumir un servicio web de un tercero.&lt;br/&gt; 
Para consumir servicios web, usaremos el Plugin WS-Client basado en GroovyWS.&lt;br/&gt;
Lo primero de todo instalaremos el plugin añadiendo esta linea en tu BuildConfig,
&lt;pre class=&quot;brush:java&quot;&gt;
compile &quot;:ws-client:1.0&quot;
&lt;/pre&gt;
&lt;br/&gt;
Ahora sólo tendremos que crear un controlador para consumir ese servicio web con la url del WSDL donde esté publicado.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = &quot;http://ejemplo.com/services/pruebas?wsdl&quot;
        def proxy = webService.getClient(wsdlUrl)
        
        String resul = proxy.saluda(&quot;Eylen&quot;)
        ...
    }
}
&lt;/pre&gt;
En este ejemplo sólo se ejecuta el método &quot;saluda&quot; del WSDL donde se construye un XML de entrada con un sólo nivel de tipo String y nos devuelve un XML de salida con un sólo nivel también de tipo String.&lt;br/&gt;
Habrá XML de salida muchos más complicados que éste, donde habrá que ir navegando por los diferentes niveles consultando la información.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = &quot;http://ejemplo.com/services/pruebas?wsdl&quot;
        def proxy = webService.getClient(wsdlUrl)
        
        def libros = proxy.getLibros()
        libros.libro.each{
           println it.campo
        }
        ...
    }
}
&lt;/pre&gt;
También se puede dar la situación en la que tenemos que construir un XML de entrada con más niveles. Esto se hace construyendo proxies. Tened cuidado con los namespace de los WSDL que puede dar más de un dolor de cabeza. Y también prestad atención si la entrada del WSDL es de tipo String o es una lista.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import org.grails.plugins.wsclient.service.WebService

class ClienteController {
    WebService webService

    def index = {
        def wsdlUrl = &quot;http://ejemplo.com/services/pruebas?wsdl&quot;
        def proxy = webService.getClient(wsdlUrl)

        def libro = proxy.create(&quot;defaultnamespace.Libro&quot;)

        libro.titulo= &quot;Groovy in Action&quot;
        libro.autor = &quot;Dierk&quot;
        libro.isbn = &quot;123&quot;

        //Detalles es una lista!!!
        def detalle1 = proxy.create(&quot;defaultnamespace.Detalles&quot;)       
        detalle1.paginas = &quot;150&quot;
        detalle1.indice = &quot;S&quot; 
        libro.detalles &lt;&lt; detalle1

        proxy.addLibro(libro)
        ...
    }
}
&lt;/pre&gt;
Para poder debuguear en todos estas construcciones, es mejor que tengais puesto en el Config,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
log4j = {
    …
    debug  &#39;org.apache.cxf&#39;
}
&lt;/pre&gt;
Como nota final decir que si teneis instalados en el mismo proyecto los plugins ws-client y cxf puede haber conflictos con la librería cxf. Se recomienda instalar primero el CXF y luego el WS-CLIENT y eliminar de ésta última el jar cxf.
</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/consumir-servicios-web-con-el-plugin-ws.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-1045712812146863038</guid><pubDate>Mon, 08 Oct 2012 09:50:00 +0000</pubDate><atom:updated>2012-10-08T16:28:15.779+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">PLUGIN</category><category domain="http://www.blogger.com/atom/ns#">WS</category><title>Exponer servicios Grails a través de SOAP con CXF</title><description>Una de las facilidades de Grails es que tiene plugins para casi todo. Y uno de los mejores a la hora de exponer servicios web de tipo SOAP es el CXF plug-in for Grails.
La única dependencia que teneis que añadir a vuestro BuildConfig es,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
compile &quot;:cxf:0.9.0&quot;
&lt;/pre&gt;
Con esta línea, cuando arranqueis vuestra aplicación otra vez, te instalará el plugin. La instalación ya te meterá las librerías necesarias de CXF. La última versión que han sacado ya es compatible para Grails 2.&lt;br/&gt;
Para configurarlo se puede hacer de dos formas diferentes:
&lt;br/&gt;&lt;br/&gt;
1. Con la propiedad estática &quot;expose&quot; igual a &quot;cxf&quot;&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class TestService {

  static expose=[&#39;cxf&#39;] 
  static exclude=[&quot;ignoredMethod&quot;]

  boolean serviceMethod(YourDomainClass dc){ return true; }

  boolean ignoredMethod() { return &quot;you shouldn&#39;t see me&quot; }

}
&lt;/pre&gt;
Si queremos que algún método del servicio sea ignorado y no se exponga lo declararemos con la propiedad estática &quot;exclude&quot; y el nombre del método.
&lt;br/&gt;
Todos los métodos que no estén en el exclude serán expuestos y serán accesibles para terceros a través de su url &quot;http://127.0.0.1:8080/nombreApp/services/miServicio?wsdl&quot;
&lt;br/&gt;
Todas las clases de dominio que sean pasadas o devueltas como parámetros en los servicios web deberán llevar la anotación JAXB @XmlAccessorType(XmlAccessType.FIELD). Esto hará que todos los campos públicos y privados sean serializables. Más info de las anotaciones JAXB &lt;a href=&quot;http://www.caucho.com/resin-3.1/doc/jaxb-annotations.xtp&quot; target=&quot;_blank&quot;&gt;aquí&lt;/a&gt;.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
@XmlAccessorType(XmlAccessType.FIELD)
class YourDomainClass {
  ...
}
&lt;/pre&gt;
&lt;br/&gt;
2. Con la propiedad estática &quot;expose&quot; igual a &quot;cxfjax&quot;. Configurado esto, ya podemos utilizar las anotaciones JaxWS para tener más control sobre tu WSDL.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
import javax.jws.*

class TestService { static expose=[&#39;cxfjax&#39;]

  @WebResult(name=&quot;addResult&quot;) 
  @WebMethod(operationName=&quot;add&quot;) 
  int add(@WebParam(name=&quot;a&quot;)int a, @WebParam(name=&quot;b&quot;)int b) { 
    return a + b 
  } 
}
&lt;/pre&gt;
Con cxfjax, tienes que poner la anotación @WebMethod en el método del servicio que quieres exponer. Con la anotación @WebResult, le decimos el nombre del parámetro que devuelve el servicio en el WSDL y con @WebParam definimos el nombre del parámetro de entrada que está definido en el WSDL.&lt;br/&gt;&lt;br/&gt;
Anotaciones de JAXWS:&lt;br/&gt;
  - @WebService indica que una clase Java está implementando un servicio Web o indica que una SEI (Service Endpoint Interface) está implementando una interfaz de servicio Web. Destino de anotación: Type. Parámetros que permite:&lt;br/&gt;&lt;br/&gt; 
    name=&quot;&quot; -&gt; El nombre de wsdl:portType. El valor por omisión es el nombre sin calificar de la interfaz o clase Java. (String)&lt;br/&gt;&lt;br/&gt;
    targetNamespace=&quot;&quot; -&gt; Especifica el espacio de nombres XML o los elementos WSDL y XML generados a partir del servicio Web. El valor por omisión es el espacio de nombres correlacionado a partir del nombre del paquete que contiene el servicio Web. (String)&lt;br/&gt;&lt;br/&gt;
    serviceName=&quot;&quot; -&gt; Especifica el nombre de servicio del servicio Web: wsdl:service. El valor por omisión es el nombre sencillo de la clase java más Service. (String)&lt;br/&gt;&lt;br/&gt;
    endpointInterface=&quot;&quot; -&gt; Especifica el nombre calificado de la interfaz de punto final de servicio que define el contrato de servicio web abstracto de los servicios. Si se especifica, se utiliza la interfaz de punto final de servicio para determinar el contrato WSDL abstracto. (String)&lt;br/&gt;&lt;br/&gt;
    portName=&quot;&quot; -&gt; wsdl:portName. El valor por omisión es WebService.name más Port. (String)&lt;br/&gt;&lt;br/&gt;
    wsdlLocation=&quot;&quot; -&gt; Especifica la dirección Web del documento WSDL que define el servicio Web. La dirección Web puede ser relativa o absoluta. (String)&lt;br/&gt;
&lt;br/&gt;
  - @WebMethod indica un método que es una operación de servicio Web. Destino de anotación: Method. Parámetros que permite:&lt;br/&gt;&lt;br/&gt;
    operationName=&quot;&quot; -&gt; Especifica el nombre de wsdl:operation que coincide con este método. El valor por omisión es el nombre del método Java. (String)&lt;br/&gt;&lt;br/&gt;
    action=&quot;&quot; -&gt; Define la acción de esta operación. En los enlaces SOAP, este valor determina el valor de la cabecera SOAPAction. El valor por omisión es el nombre del método Java. (String)&lt;br/&gt;&lt;br/&gt;
    exclude=&quot;&quot; -&gt; Especifica si se ha de excluir un método del servicio Web. El valor por omisión es false.(Boolean)&lt;br/&gt;&lt;br/&gt;
  - @Oneway indica un método como una operación unidireccional de servicio Web que sólo tiene un mensaje de entrada y ningún mensaje de salida. Destino de anotación: Method. No hay parámetros.&lt;br/&gt;&lt;br/&gt;
  - @WebParam personaliza la correlación de un parámetro individual con una parte de mensaje de servicio Web y un elemento XML. Destino de anotación: Parameter. Parámetros que permite:&lt;br/&gt;&lt;br/&gt;
    name=&quot;&quot; -&gt; El nombre del parámetro. Si la operación es una RPC (Remote Procedure Call) y no se especifica el atributo partName, este es el nombre del atributo wsdl:part que representa el parámetro. Si la operación es de estilo documento o si el parámetro se correlaciona con una cabecera, -name es el nombre local del elemento XML que representa el parámetro. Este atributo es necesario si la operación es de estilo documento, el estilo de parámetro es BARE, y la modalidad OUT o INOUT. (String)&lt;br/&gt;&lt;br/&gt;
    partName=&quot;&quot; -&gt; Define el nombre del atributo wsdl:part que representa este parámetro. Sólo se utiliza si la operación es de estilo RPC o si el estilo de documento y el estilo de parámetro es BARE. (String)&lt;br/&gt;&lt;br/&gt;
    targetNamespace=&quot;&quot; -&gt; Especifica el espacio de nombres XML del elemento XML del parámetro. Se aplica únicamente a los enlaces de documentos cuando el atributo se correlaciona con un elemento XML. El valor por omisión es el targetNamespace del servicio Web. (String)&lt;br/&gt;&lt;br/&gt;
    mode=&quot;&quot; -&gt; El valor representa la dirección en que fluye el parámetro para este método. Los valores válidos son IN, INOUT y OUT. (String)&lt;br/&gt;&lt;br/&gt;
    header=&quot;&quot; -&gt; Especifica si el parámetro es una cabecera de mensaje y no el cuerpo de un mensaje. El valor por omisión es false. (Boolean)&lt;br/&gt;
&lt;br/&gt; 
  - @WebResult personaliza la correlación de un valor de retorno con una parte WSDL o elemento XML. Destino de anotación: Method. Parámetros que permite:&lt;br/&gt;&lt;br/&gt;        
    name=&quot;&quot; -&gt; Especifica el nombre del valor de retorno como figura en el archivo WSDL y aparece en los mensajes de la comunicación. Para los enlaces RPC, este es el nombre del atributo wsdl:part que representa el valor de retorno. Para los enlaces de documentos, el parámetro -name es el nombre local del elemento XML que representa el valor de retorno. El valor por omisión es return para enlaces RPC y DOCUMENT/WRAPPED. El valor por omisión es el nombre de método más Response para enlaces DOCUMENT/BARE. (String)&lt;br/&gt;&lt;br/&gt; 
    targetNamespace=&quot;&quot; -&gt; Especifica el espacio de nombres XML para el valor de retorno. Este parámetro sólo se utiliza si la operación es de estilo RPC o si la operación es de estilo DOCUMENT y el estilo de parámetro es BARE. (String)&lt;br/&gt;&lt;br/&gt; 
    header=&quot;&quot; -&gt; Especifica si el resultado se lleva a cabo en una cabecera. El valor por omisión es false. (Boolean)&lt;br/&gt;&lt;br/&gt; 
    partName=&quot;&quot; -&gt; Especifica el nombre de la parte para el resultado con las operaciones RPC o DOCUMENT/BARE. El valor por omisión es @WebResult.name. (String)&lt;br/&gt;
&lt;br/&gt;
  - @HandlerChain asocia el servicio Web con una cadena de manejadores definida externamente. Destino de anotación: Type. Parámetros que permite:&lt;br/&gt;&lt;br/&gt;
    file=&quot;&quot; -&gt; Especifica la ubicación del archivo de la cadena de manejadores. La ubicación del archivo es un java.net.URL absoluto con forrmato externo o una vía de acceso relativa al archivo de clases. (String)&lt;br/&gt;&lt;br/&gt;
    name=&quot;&quot; -&gt; Especifica el nombre de la cadena de manejadores en el archivo de configuración. (String)&lt;br/&gt;
&lt;br/&gt;
  - @SOAPBinding especifica la correlación del servicio Web en el protocolo de mensajes SOAP. Destino de anotación: Type o Method. Parámetros que permite:&lt;br/&gt;&lt;br/&gt;
    style=&quot;&quot; -&gt; Define el estilo de codificación para los mensajes enviados a y desde el servicio Web. Los valores válidos son DOCUMENT y RPC. El valor por omisión es DOCUMENT. (String)&lt;br/&gt;&lt;br/&gt;
    use=&quot;&quot; -&gt; Define el formato utilizado para los mensajes enviados a y desde el servicio Web. El valor por omisión es LITERAL. ENCODED no está soportado en Feature Pack for Web Services. (String)&lt;br/&gt;&lt;br/&gt;
    parameterStyle=&quot;&quot; -&gt; Determina si los parámetros del método representan todo el cuerpo del mensaje o si los parámetros son elementos envueltos en un elemento de nivel superior con el nombre de la operación. Los valores válidos son WRAPPED o BARE. Sólo puede utilizar el valor BARE con los enlaces de estilo DOCUMENT. El valor por omisión es WRAPPED.(String)&lt;br/&gt;
&lt;br/&gt;

Mas info en &lt;a href=&quot;http://pic.dhe.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=%2Fcom.ibm.websphere.wsfep.multiplatform.doc%2Finfo%2Fae%2Fae%2Frwbs_jaxwsannotations.html&quot; target=&quot;_blank&quot;&gt;Web Services Metadata Annotations (JSR 181)&lt;/a&gt;
&lt;br/&gt;






</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/exponer-servicios-grails-traves-de-soap.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-4609233661063582426</guid><pubDate>Fri, 05 Oct 2012 11:15:00 +0000</pubDate><atom:updated>2012-10-05T13:15:34.741+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ERROR</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Error de sincronización Grails 2.1.1 con Intellij IDEA 11.1.1</title><description>En uno de mis proyectos personales, teniendo instalado la version de Grails 2.1.1 me salía el siguiente error cuando añadía una dependencia en el BuildConfig.groovy,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
IDEA hook: Grails not found!
| Error java.lang.reflect.InvocationTargetException
| Error
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
| Error
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
| Error
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
| Error
 at java.lang.reflect.Method.invoke(Method.java:601)
| Error
 at org.jetbrains.groovy.grails.rt.Agent$2.run(Agent.java:73)
| Error at java.lang.Thread.run(Thread.java:722)
| Error Caused by: java.lang.NullPointerException
| Error at org.jetbrains.groovy.grails.rt.GrailsSettingsGetter.printSettings(GrailsSettingsGetter.java:62)
&lt;/pre&gt;

Después de volverme loco durante una hora, encontré la solución. Y es que es un bug en la versión 11.1.1 del Intellij IDEA. La solución hacer un upgrade a las versión 11.1.3 y todo solucionado. </description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/error-de-sincronizacion-grails-211-con.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-3918630294752793532</guid><pubDate>Thu, 04 Oct 2012 11:31:00 +0000</pubDate><atom:updated>2012-10-15T14:05:00.775+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Todos las opciones del database mapping</title><description>En esta entrada trataremos de explicar todas las features que tiene Grails 2 para customizar el mapeo con la base de datos.&lt;br /&gt;
Primero de todo diremos que todo esto se puede definir en dos sitios:&lt;br /&gt;
- De una manera específica en una clase de dominio&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt; 
 (static mapping = { ... })
&lt;/pre&gt;
&lt;br /&gt;
-De forma general definiendolo en el Config.groovy con&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt; 
grails.gorm.default.mapping = { ... }
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
1) AUTOIMPORT&lt;br /&gt;
Permite importar o no la clase de dominio en las queries HQL. Por defecto está a true, es decir las clases de dominio por defecto son auto-importadas en las queries HQL, así que sólo con poner el nombre ya es suficiente. Si ponemos esta característica a false, para formar una query habrá que incluir su paquete también para poder identificar la clase de dominio. Esto es una buena solución para cuando tienes dos clases de dominio con el mismo nombre en distintos paquetes y te da un org.hibernate.DuplicateMappingException.
&lt;br/&gt; 
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        autoImport false
    }
&lt;/pre&gt;
&lt;br/&gt;

2) AUTO TIMESTAMP&lt;br /&gt;
Habilitar o no el auto Timestamping, es decir, cuando queremos guardar su dateCreated y su lastUpdated de una clase de dominio automáticamente deberemos tenerlo a true. Si lo quieres deshabilitado para guardar tus propias fechas ponerlo a false.
&lt;br /&gt;   
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        autoTimestamp false
    }
&lt;/pre&gt;
&lt;br/&gt;

3) BATCH SIZE&lt;br /&gt;
Customizar cuántos resultados queremos cuando capturamos datos de la BBDD en una carga lazy.
&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        batchSize 10
    }
&lt;/pre&gt;
&lt;br/&gt;

4) CACHE&lt;br /&gt;
Habilitar la cache de segundo nivel de Hibernate.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        cache true
    }
&lt;/pre&gt;
&lt;br/&gt;

5) CACHE&lt;br /&gt;
Configurar el comportamiento en cascada de la asociación.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static hasMany = [books: Book]

static mapping = { books cascade: &#39;all-delete-orphan&#39; }
&lt;/pre&gt;
&lt;br/&gt;
Aquí podemos definir uno o varios comportamientos separados por coma. Hay varios tipos: all, merge, save-update, delete, lock, refresh, evict, replicate or all-delete-orphan (sólo para relaciones one-to-many)
&lt;br/&gt;
GORM por defecto configura la política de cascada save-update.
&lt;br/&gt;
&lt;br/&gt;
6) COLUMN&lt;br /&gt;
Customiza la definición de una columna.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        currency column: &quot;currency&quot;, sqlType: &quot;char&quot;, length: 3
    }
&lt;/pre&gt;
&lt;br/&gt;
Primero se define el nombre del atributo seguido de los siguientes parámetros a cambiar:&lt;br/&gt;
  - column - nombre de la columna como String&lt;br/&gt;
  - sqlType (optional) - tipo SQL&lt;br/&gt;
  - enumType (optional) - tipo de enumerado para las propiedades Enum. (ordinal o string)&lt;br/&gt;
  - index (optional) - nombre del índice&lt;br/&gt;
  - unique (optional) - si es único&lt;br/&gt;
  - length (optional) - longitud de la columna&lt;br/&gt;
  - precision (optional) - nº de posiciones de la parte entera de la columna&lt;br/&gt;
  - scale (optional) - nº de decimales de la columna
&lt;br /&gt;
&lt;br/&gt;

7) DATASOURCE&lt;br /&gt;
Configura qué datasource está relacionada la clase de dominio.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
      datasource &#39;lookup&#39;
   }
&lt;/pre&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
environments {
    development {
        dataSource {
            dbCreate = &quot;create-drop&quot;
            url = &quot;jdbc:h2:mem:devDb&quot;
        }
        dataSource_lookup {
            dialect = org.hibernate.dialect.MySQLInnoDBDialect
            driverClassName = &#39;com.mysql.jdbc.Driver&#39;
            username = &#39;lookup&#39;
            password = &#39;secret&#39;
            url = &#39;jdbc:mysql://localhost/lookup&#39;
            dbCreate = &#39;update&#39;
        }
    }
}
&lt;/pre&gt;
Si una clase de dominio no tiene una configuración de DataSource, tirará por defecto del DataSource configurado en el Config.groovy. 
&lt;br/&gt;
&lt;br/&gt;

8) DISCRIMINATOR&lt;br /&gt;
Customiza el discriminador de columna cuando se mapea una herencia tipo &quot;table-per-subclass&quot;. 
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class PodCast extends Content {
    …
    static mapping = {
        discriminator &quot;audio&quot;
    }
}
&lt;/pre&gt;
&lt;br/&gt;

9) DYNAMIC INSERT&lt;br /&gt;
Para construir dinamicamente queries INSERT.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        dynamicInsert true
    }
&lt;/pre&gt;
&lt;br/&gt;
Por defecto Hibernate genera todas las queries al principio y las cachea. Esto ayuda a que el rendimiento para los insert, update y delete queries no tengan que ser generadas dinámicamente en tiempo de ejecución. Sin embargo, hay ciertas circunstancias donde las queries dinámicas son útiles.
&lt;br/&gt;
&lt;br/&gt;
10) DYNAMIC UPDATE&lt;br /&gt;
Para construir dinamicamente queries UPDATE.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        dynamicUpdate true
    }
&lt;/pre&gt;
&lt;br/&gt;

11) FETCH&lt;br /&gt;
Configura el comportamiento de carga de datos de una asociación.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static hasMany = [books: Book]

static mapping = { books fetch: &#39;select&#39; }
&lt;/pre&gt;
&lt;br/&gt;
Se define con el nombre de las asociación seguida del parámetro:&lt;br/&gt;
  - fetch - Estrategia a usar. (join o select) 
&lt;br/&gt;
Por defecto GORM asume que la carga de asociaciones está hecha con una SELECT cuando se accede a la asociación. Si prefieres que la asociación sea cargada de modo eager, puedes cambiarlo sobreescribiendo:
&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static hasMany = [books: Book]

static mapping = { books fetch: &#39;join&#39; }
&lt;/pre&gt;
&lt;br/&gt;

12) ID&lt;br /&gt;
Customizar la forma en que el identificador es generado para un dominio.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        id generator: &#39;hilo&#39;,
           params: [table: &#39;hi_value&#39;, column: &#39;next_value&#39;, max_lo: 100]
    }
&lt;/pre&gt;
&lt;br/&gt;
Se define poniendo el id seguido de los siguientes parámetros:&lt;br/&gt;
  - generator (optional) - nombre del generador a usar. Puede ser increment, identity, sequence, hilo, seqhilo, uuid, guid, native, assigned, select, foreign o sequence-identity.&lt;br/&gt;
  - composite (optional) - coge una lista de propiedades a usar para formar la clave compuesta.&lt;br/&gt;
  - name (optional) - nombre de la propiedad a usar como identificador.&lt;br/&gt;
  - params (optional) - algún parámetro para pasar a un generador de identificadores definido.&lt;br/&gt;
  - column (optional) - nombre de la columna para mapear el identificador. El resto de propiedades también está disponible.
&lt;br/&gt;
&lt;br/&gt;
13) IGNORE NOT FOUND &lt;br /&gt;
Especifica cómo las FKs que referencian a filas no encontradas son manejadas en las relaciones many-to-one.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
String title 
Thumbnail thumbnail

static mapping = { thumbnail ignoreNotFound: true }
&lt;/pre&gt;
&lt;br/&gt;
Por defecto el valor es false, es decir si no encuentra la fila Hibernate lanzará una excepción &quot;org.hibernate.ObjectNotFoundException&quot;. Si lo tienes a true, aunque no lo encuentre, lo ignorará. Esto viene bien cuando tienes BBDD corruptas que le van a faltar datos. 
&lt;br/&gt;
&lt;br/&gt;
14) INDEX COLUMN&lt;br /&gt;
Customiza el índice de una colección de tipo Lista o un Mapa.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static hasMany = [matrix: Integer]

static mapping = { matrix indexColumn: [name: &quot;the_matrix&quot;, type: Integer] }
&lt;/pre&gt;
&lt;br/&gt;
Esto se define con el nombre de la asociación seguido de los siguientes parámetros:&lt;br/&gt;
  - name - nombre de columna como un String&lt;br/&gt;
  - type (optional) - tipo Hibernate&lt;br/&gt;
  - sqlType (optional) - tipo SQL&lt;br/&gt;
  - enumType (optional) - tipo de enumerado para las propiedades Enum. (ordinal o string)&lt;br/&gt;
  - index (optional) - nombre del índice&lt;br/&gt;
  - unique (optional) - si es único&lt;br/&gt;
  - length (optional) - longitud de la columna&lt;br/&gt;
  - precision (optional) - nº de posiciones de la parte entera de la columna&lt;br/&gt;
  - scale (optional) - nº de decimales de la columna
&lt;br/&gt;
&lt;br/&gt;
15) INSERTABLE / UPDATEABLE&lt;br /&gt;
Determina si una propiedad de la BBDD está configurada como insertable/updateable cuando una instancia es persistida.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
String title

static belongsTo = [author: Author]

static mapping = { 
              author insertable: false 
              author updateable: false 
}
&lt;/pre&gt;
&lt;br/&gt;
Se define con el nombre del atributo seguido de insertable/updateable: boolean. Por defecto están a true.
&lt;br/&gt;
&lt;br/&gt;
16) JOIN TABLE&lt;br /&gt;
Customiza el tipo de join table que se usa para las relaciones unidireccionales de tipo one-to-many, many-to-many y tipos de colección primitivos. 
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static hasMany = [projects: Project]

static mapping = { 
projects joinTable: [name: &#39;EMP_PROJ&#39;, column: &#39;PROJECT_ID&#39;, key: &#39;EMPLOYEE_ID&#39;] 
}
&lt;/pre&gt;
&lt;br/&gt;
Se define con el nombre de la asociación seguido de joinTable y de los siguientes parámetros:&lt;br/&gt;
  - name - nombre de table&lt;br/&gt;
  - key (optional) - clave foránea&lt;br/&gt;
  - column (optional) - columna inversa
&lt;br/&gt;
&lt;br/&gt;
17) LAZY&lt;br /&gt;
Configura si se usa proxies y carga de datos lazy para las relaciones one-to-one y many-to-one.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static belongsTo = [author: Author]

static mapping = { author lazy: false }
&lt;/pre&gt;
&lt;br/&gt;
Por defecto, en GORM las asociaciones single-ended son lazy: true es decir cuando se carga una instancia de un dominio, la asociación de ese dominio no es cargada hasta que se accede a ella.&lt;br/&gt;
Hibernate crea un proxy dinámico para subclasificar la clase de dominio y sus métodos y propiedades.
&lt;br/&gt;
&lt;br/&gt;
18) ORDER&lt;br /&gt;
Customizar el orden por defecto de los resultado de las queries.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        order &quot;desc&quot;
    }
&lt;/pre&gt;
&lt;br/&gt;

19) SORT&lt;br /&gt;
Customizar el atributo de la clase dominio para ordenar los resultados de las queries.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        sort &quot;releaseDate&quot;
    }
&lt;/pre&gt;
&lt;br/&gt;

20) TABLE&lt;br /&gt;
Customizar el nombre de la BBDD asociada de la clase de dominio.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class Book {
    static mapping = {
        table &quot;book_catalog&quot;
    }
}
&lt;/pre&gt;
&lt;br/&gt;
También se le puede especificar el schema y el catálogo definiéndolo así,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
    table name: &quot;book_catalog&quot;, schema: &quot;dbo&quot;, catalog: &quot;CRM&quot;
}
&lt;/pre&gt;
&lt;br/&gt;

21) TYPE&lt;br /&gt;
Para configurar el tipo de Hibernate para una propiedad particular.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
String title

static mapping = { title type: &quot;text&quot; }
&lt;/pre&gt;
&lt;br/&gt;
Esta propiedad viene bien para definir tipos CLOB o TEXT dependiendo del dialecto de la BBDD.
&lt;br/&gt;
&lt;br/&gt;
22) VERSION&lt;br /&gt;
Se usa para deshabilitar el bloqueo optimista o cambiar la columna que mapea la versión.
&lt;br /&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        version false
    }
&lt;/pre&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static mapping = {
        version &#39;revision_number&#39;
    }
&lt;/pre&gt;
&lt;br/&gt;















</description><link>http://beginninggroovyandgrails.blogspot.com/2012/10/todos-las-opciones-del-database-mapping.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-3039677656952090452</guid><pubDate>Wed, 26 Sep 2012 15:10:00 +0000</pubDate><atom:updated>2012-09-28T10:49:38.042+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><category domain="http://www.blogger.com/atom/ns#">PLUGIN</category><title>Entendiendo el Plugin Resources</title><description>Este plugin representa una nueva forma de declarar tus recursos estáticos (css, js e img) mediante módulos y lincarlos a tus GSP por medio de tags específicas. 
&lt;br/&gt;
Primero, instala el plugin resources. Si tienes instalado Grails 2 ya viene por defecto instalado. Otros plugins muy interesantes para optimizar el rendimiento de tu aplicación serían: zipped-resources, cached-resources, lesscss-resources, gsp-resources, yui-minify-resources, handlebars-resources y page-resources.
&lt;br/&gt;
Empezaremos declarando los módulos en el fichero &quot;AppResources.groovy&quot; que está en el directorio conf
&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
modules = {
 common {
  resource url:&quot;css/main.css&quot;
  resource url:&quot;js/jquery.js&quot;
 }
}
&lt;/pre&gt;
&lt;br/&gt;
Aquí le estamos diciendo que declare un módulo llamado &quot;common&quot; con dos recursos: main.css y jquery.js en donde por defecto los css los pondrá en el head y el js al final de la GSP (“defer”) que le definamos. Si queremos que los js los ponga en el head de la GSP, le añadiremos a la linea del resource disposition:&quot;head&quot; 
&lt;br/&gt;
Ahora necesitas meterlo dentro de una GSP. Para hacer esto, necesitas las tags propias del Plugin. Antes de nada, definamos el layout Sitemesh de nuestra aplicación.
&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &lt;g:layoutHead/&gt;
        &lt;r:layoutResources/&gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &lt;div id=&quot;content&quot;&gt;
            &lt;g:layoutBody/&gt;
        &lt;/div&gt;
        &lt;r:layoutResources/&gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;br/&gt;
El primer r:layoutResources servirá para lincar todos los css, js, iconos, etc que tengas en tu &amp;lt;head&amp;gt; y el segundo para renderizar algún resource “defer”, que normalmente es sólo código JavaScript.
&lt;br/&gt;

&lt;br/&gt;
Para utilizar un módulo de recursos en una GSP:&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &lt;meta name=&quot;layout&quot; content=&quot;main&quot;/&gt;
        &lt;r:require module=&quot;common&quot;/&gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    …
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;br/&gt;
A veces tienes que controlar el orden de tus recursos ya que dependen de otros para cargarse bien. La regla general de este plugin es que se empiecen a cargar los recursos en orden de definición (es decir de arriba a abajo). También se puede hacer de otra manera, con &quot;dependsOn&quot;. Por ejemplo,
&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
modules = {
    customize{
        dependsOn &quot;common&quot;
        resource url:&#39;css/customize.css&#39;
    }
}
&lt;/pre&gt;
&lt;br/&gt;
Con esto le estamos diciendo que antes de cargar el módulo customize, debe estar cargado el módulo common.
&lt;br/&gt;
Otra de las tags que pueden ser útiles es la r:script. Esta tag se utiliza para introducir algo de código JS en la GSP.
&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &lt;meta name=&quot;layout&quot; content=&quot;main&quot;/&gt;
        &lt;r:require module=&quot;common&quot;/&gt;
        &lt;r:script&gt;window.alert(&#39;Boo!&#39;)&lt;/r:script&gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    …
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/pre&gt;
&lt;br/&gt;
Este alert que hemos definido se cargará con disposition &quot;defer&quot;, es decir se cargará cuando se haya cargado toda la página, aunque esté definido en el head.
&lt;br/&gt;
Si quisiéramos forzar a que este trozo de JS se cargara en el head le tendríamos que modificar la tag con,
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;r:script disposition=&quot;head&quot; &amp;gt;
&lt;/pre&gt;
&lt;br/&gt;
Otro tipo de recurso que se puede utilizar en este plugin son las imágenes. Se puede hacer de dos formas: llamándole desde la GSP,
&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
&amp;lt;r:img uri=&quot;images/logo.png&quot;/&amp;gt;
&lt;/pre&gt;
&lt;br/&gt;
o creando un módulo desde AppResources,
&lt;pre class=&quot;brush:java&quot;&gt;
modules = {
    images {
        resource url:&#39;images/logo.png&#39;,
            attrs:[width:400, height:180, alt:&#39;ACME Corp.&#39;],
            disposition: &#39;image&#39;
    }
}
&lt;/pre&gt;









</description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/entendiendo-el-plugin-resources.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-1030886073387977943</guid><pubDate>Mon, 24 Sep 2012 10:46:00 +0000</pubDate><atom:updated>2012-10-04T13:19:06.414+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Chain = encadenar</title><description>Las acciones pueden ser encadenadas. El encadenamiento permite guardar el model de una acción a otra. Por ejemplo llamamos a sucesivas acciones en este código:&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ExampleChainController {

def first() { chain(action: second, model: [one: 1]) }

def second () { chain(action: third, model: [two: 2]) }

def third() { [three: 3]) } 
}
&lt;/pre&gt;&lt;br/&gt;
Al final de la tercera accion tu model sería,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
[one: 1, two: 2, three: 3]
&lt;/pre&gt;&lt;br/&gt;
El modelo puede ser accedido en el siguiente controller al chain usando el mapa chainModel. Esta propiedad dinámica existe sólo en acciones siguientes a la llamada de algún método chain.&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
class ChainController {

def nextInChain() { 
def model = chainModel.myModel
 … 
} 
}
&lt;/pre&gt;&lt;br/&gt;
Como en el método redirect, puedes pasar también parámetros a un método chain:&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
chain(action: &quot;action1&quot;, model: [one: 1], params: [myparam: &quot;param1&quot;])
&lt;/pre&gt;
</description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/chain-encadenar.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-2126798911703512626</guid><pubDate>Mon, 24 Sep 2012 10:36:00 +0000</pubDate><atom:updated>2012-10-04T13:19:12.461+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Peticiones asíncronas</title><description>Grails soporta el procesamiento de peticiones asícronas como característica de  la especificación de Servlet 3.0. Para activar la característica necesitas añadir la siguiente líena de código en tu BuildConfig.groovy:&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
grails.servlet.version = &quot;3.0&quot;
&lt;/pre&gt;
&lt;br/&gt;
Con esto te aseguras de que tienes la caracterísitica activada en tiempo de compilación.&lt;br/&gt;
NOTA: Con el Servlet 3.0 activado, podrás solamente hacer deploy en contenedores que lo soporten como Tomcat 7, etc.&lt;br/&gt;
- Renderizar asíncronamente&lt;br/&gt;
Puedes renderizar contenido (templates, binary data, etc) de una forma asíncrona por medio de una llamada al método startAsync que devuelve una instancia del AsyncContext de Servlet 3.0. Una vez que tienes una referencia al AsyncContext, puedes usar el método render:&lt;br/&gt; 
&lt;pre class=&quot;brush:java&quot;&gt;
def index() {
    def ctx = startAsync()
    ctx.start {
        new Book(title:&quot;The Stand&quot;).save()
        render template:&quot;books&quot;, model:[books:Book.list()]
        ctx.complete()
    }
}
&lt;/pre&gt;&lt;br/&gt;
Deberías llamar al método complete() para terminar la conexión.&lt;br/&gt;&lt;br/&gt;
- Reanudar una petición asíncrona&lt;br/&gt;
Para reanudar el procesamiento de una petición asíncrona (por ejemplo para delegar a una vista renderizada), se usa el método dispatch de la clase AsyncContext:&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
def index() {
    def ctx = startAsync()
    ctx.start {
        // do working
        …
        // render view
        ctx.dispatch()
    }
}
&lt;/pre&gt;


 


</description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/peticiones-asincronas.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-247428391063109324</guid><pubDate>Fri, 21 Sep 2012 15:41:00 +0000</pubDate><atom:updated>2012-10-04T13:19:19.280+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Interceptores en controladores</title><description>A menudo es útil para interceptar procesamientos de cualquier petición, sesión o estado de aplicación. Esto puedes lograrlo con la acción de los interceptores. Actualmente hay dos tipos de interceptores: before and after.&lt;br/&gt;&lt;br/&gt;
NOTA: Si tu interceptor se aplica a más de un controlador, deberías definir mejor un Filtro. Los filtros pueden aplicarse a múltiples controladores o a URIs sin la necesidad de cambiar la lógica de cada controlador.&lt;br/&gt;&lt;br/&gt;
- Before Interception&lt;br/&gt;
Intercepta procesamientos antes de que la acción se ejecute. Si devuelve false, la acción interceptada no será ejecutada. El interceptor podrá ser definido en todas las acciones de un controlador como lo siguiente:&lt;br/&gt; 
&lt;pre class=&quot;brush:java&quot;&gt;
def beforeInterceptor = {
    println &quot;Tracing action ${actionUri}&quot;
}
&lt;/pre&gt;&lt;br/&gt;
El código es declarado en el cuerpo del controlador. Será ejecutado antes que todas las acciones y no interferirá en el procesamiento. Un caso común es una simple autenticación:&lt;br/&gt; 
&lt;pre class=&quot;brush:java&quot;&gt;
def beforeInterceptor = [action: this.&amp;auth, except: &#39;login&#39;]

// defined with private scope, so it&#39;s not considered an action 
private auth() { 
if (!session.user) { redirect(action: &#39;login&#39;) return false } }
def login() { // display login page }
&lt;/pre&gt;&lt;br/&gt;
El código define un método llamado auth. Un método privado es usado para no exponerlo como acción fuera de todo. El beforeInterceptor define un interceptor que es usado para todas las acciones excepto en la acción login y que ejecuta el método auth. El método auth se referencia usando la sintaxis de puntero de un método Groovy.
&lt;br/&gt;
Dentro del método detecta si hay un usuario en sesión, si no es así redirige a la acción de login y devuelve un false, causando que la acción interceptada no sea procesada.&lt;br/&gt;&lt;br/&gt;
- After Interception&lt;br/&gt;
Usa la propiedad afterInterceptor para definir un interceptor que es ejecutado después de una acción:&lt;br/&gt; 
&lt;pre class=&quot;brush:java&quot;&gt;
def afterInterceptor = { model -&gt;
    println &quot;Tracing action ${actionUri}&quot;
}
&lt;/pre&gt;&lt;br/&gt;
El afterInterceptor toma el resultado del model como argumento y por lo tanto puede manipular el modelo o la respuesta.&lt;br/&gt;
Un afterInterceptor puede también modificar el objeto ModelAndView de Spring MVC antes de renderizarlo. E este caso, sería:&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
def afterInterceptor = { model, modelAndView -&gt;
    println &quot;Current view is ${modelAndView.viewName}&quot;
    if (model.someVar) modelAndView.viewName = &quot;/mycontroller/someotherview&quot;
    println &quot;View is now ${modelAndView.viewName}&quot;
}
&lt;/pre&gt;&lt;br/&gt;
Esto permite que la vista sea cambiada basándose en el modelo devuelto por la actual acción. Anotar que el modelAndView puede ser null si la acción que ha sido interceptada llama a un redirect o un render.&lt;br/&gt;

</description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/interceptores-en-controladores.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-1320062966978138628</guid><pubDate>Fri, 21 Sep 2012 15:08:00 +0000</pubDate><atom:updated>2012-10-04T13:19:26.628+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Ámbito en los controladores</title><description>Al igual que los servicios, nosotros podemos fijar el scope también en los controladores. Por defecto, una instancia del controlador es creada por cada petición. De hecho, todos los controladores por defecto son de ámbito &quot;prototype&quot;, es thread-safe que quiere decir que cada petición ocurre en su propio thread.&lt;br/&gt;&lt;br/&gt;
Puedes cambiar este comportamiento de los controladores sustituyendo por uno de los ámbitos soportados:&lt;br/&gt;
- prototype (default): un nuevo controlador será creado por cada petición (recomendado para acciones como propiedades de closure)&lt;br/&gt;
- session: un controlador es creado por cada ámbito de sesión&lt;br/&gt; 
- singleton: solamente una instancia del controlador existe (recomendado para acciones como los métodos)&lt;br/&gt;&lt;br/&gt;
Para activar uno de los ámbitos, se añade una propiedad estática llamada scope en tu controlador con uno de los ámbitos válidos, cmo por ejemplo,&lt;br/&gt;
&lt;pre class=&quot;brush:java&quot;&gt;
static scope = &quot;singleton&quot;
&lt;/pre&gt;&lt;br/&gt;
Puedes definir la estrategia por defecto en el fichero Config.groovy con la clave grails.controllers.defaultScope, por ejemplo:&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
grails.controllers.defaultScope = &quot;singleton&quot;
&lt;/pre&gt;&lt;br/&gt;
NOTA: Usar los ámbitos en los controladores sabiamente. Por instancia, no recomendamos tener ninguna propiedad en un controlador singleton-scoped ya que serán compartidos por todas las peticiones. Poniendo el ámbito por defecto que no sea prototype también puede dar lugar a comportamientos inesperados si tienes controladores suministrados por plugins instalados ya que se esperan que el ámbito sea prototype. </description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/ambito-en-los-controladores.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-7717862056070998350</guid><pubDate>Fri, 21 Sep 2012 14:47:00 +0000</pubDate><atom:updated>2012-10-04T13:19:26.631+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>¿Por qué métodos en lugar de closures?</title><description>En versiones anteriores de Grails, las acciones en los controladores eran implementadas con closures. Esto en Grails 2 todavía está soportado, pero la opción preferida es usar métodos.&lt;br/&gt;&lt;br/&gt;
Elegir métodos en vez de closures tiene algunas ventajas:&lt;br/&gt;
 - Memoria eficiente&lt;br/&gt;
 - Permite uso de de controladores en singleton scope&lt;br/&gt;
 - Puedes sobrescribir acciones de subclases y llamar al método de la superclase con super.actionName()&lt;br/&gt; 
- Los métodos pueden ser interceptados con mecanismos estandar de proxying, algo que es complicado de hacer con closures ya que tienen campos&lt;br/&gt;
&lt;br/&gt;
Si prefieres la sintaxis con closures o tienes un controlador antiguo creado en una versión anterior de Grails y quieres las ventajas de usar métodos, puedes poner en el BuildConfig.groovy la siguiente linea,&lt;br/&gt;
&lt;br/&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;
grails.compile.artefacts.closures.convert=true
&lt;/pre&gt;
y en tiempo de compilación las transformaciones AST convertirán tus closures en métodos en el bytecode generado.&lt;br/&gt;&lt;br/&gt;
NOTA: Si un controlador extiende de otra clase que no está definida dentro del directorio app/controllers/, lo métodos heredados de esa clase no serán conertidos en acciones. Si la intención es exponer esos métodos heredados como acciones de un controlador, los métodos pueden ser sobreescritos en la subclase y el método de la subclase puede invocar al método en la super clase.</description><link>http://beginninggroovyandgrails.blogspot.com/2012/09/por-que-metodos-en-lugar-de-closures.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-3252555337232653575</guid><pubDate>Thu, 30 Aug 2012 12:16:00 +0000</pubDate><atom:updated>2012-10-04T13:19:41.492+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Command objects</title><description>Los controladores de Grails y las clases dentro de src/groovy soportan el concepto de los command objects. Un command object es similar a un bean. También se le puede definir validación.&lt;br/&gt;
- Declarando command objects
&lt;pre class=&quot;brush:java&quot;&gt;
import grails.validation.Validateable

class SampleController {
  ...
}
@Validateable
class LoginCommand {
    String username
    String password

static constraints = { username(blank: false, minSize: 6) password(blank: false, minSize: 6) } 
}
&lt;/pre&gt;

* Como se muestra en el ejemplo, la clase del command object está marcada por la anotación Validateable, donde con esto podrás definir constraints del mismo modo que en las clases de dominio. Otra forma de hacer un command object validable es definirlo sin la anotación y meter una la siguiente línea en tu Config.groovy
&lt;pre class=&quot;brush:java&quot;&gt;
grails.validateable.classes = [com.example.LoginCommand]
&lt;/pre&gt;
- Uso del command object&lt;br/&gt;
Para usar los command objects en las acciones de los controladores que se quieran utilizar, se le pasará como parámetro dicho command object.
&lt;pre class=&quot;brush:java&quot;&gt;
class LoginController {

def login(LoginCommand cmd) { if (cmd.hasErrors()) { redirect(action: &#39;loginForm&#39;) return }

// work with the command object data } }
&lt;/pre&gt;
Antes de que la acción del controlador se ejecute, Grails creará automáticamente una instancia del command object y rellenará sus propiedades con los parámetros de la petición. Si el command object está marcado con @Validateable, entonces se validará con sus contraints.&lt;br/&gt; 
- Inyección de dependencias dentro de los command objects
&lt;pre class=&quot;brush:java&quot;&gt;
@Validateable
class LoginCommand {

def loginService

String username String password

static constraints = { username validator: { val, obj -&gt; obj.loginService.canLogin(obj.username, obj.password) } } }
&lt;/pre&gt;
* Para quien le interese, también se puede crear command objects dentro de servicios. Aquí os dejo el enlace donde lo explican
&lt;a href=&quot;http://jts-blog.com/?p=252&quot;&gt;http://jts-blog.com/?p=252&lt;/a&gt;

</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/command-objects.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-802886872327473047</guid><pubDate>Thu, 09 Aug 2012 12:24:00 +0000</pubDate><atom:updated>2012-10-04T13:19:41.494+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Diferencia entre get / read / load</title><description>A todos se les pasa el ID como identificador. &lt;br /&gt;
- El método get carga el objeto de base de datos. &lt;br /&gt;
- El método read carga el objeto en modo lectura. En este caso Hibernate no hará dirty checking y el objeto no será persistido.&lt;br /&gt;
- El método load carga un proxy para una instancia. Esto no incurre el acceso a base de datos.</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/diferencia-entre-get-read-load.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-8989623277279983542</guid><pubDate>Thu, 09 Aug 2012 12:17:00 +0000</pubDate><atom:updated>2012-10-04T13:20:03.803+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Herencia en GORM</title><description>GORM soporta la herencia tanto de clases abstractas como de entidades persistentes.
&lt;pre class=&quot;brush:java&quot;&gt;
class Content {
 String author
}

class BlogEntry extends Content {
 URL url
}
&lt;/pre&gt;
A nivel de base de datos, Grails por defecto usa la estrategia de mapeo &quot;table-per-hierarchy&quot; con una columna discriminandora llamada &quot;class&quot;, así que la clase padre Content y su hijo/s (BlogEntry, etc) compartirán la misma tabla.&lt;br /&gt;
&lt;br /&gt;
La estrategia de mapeo &quot;table-per-hierarchy&quot; tiene un punto débil que es que no puede tener propiedades non-nullable. Una solución podría ser cambiar la estrategia y ponerla a &quot;table-per-subclass&quot;.
&lt;pre class=&quot;brush:java&quot;&gt;
class Payment {
Integer amount
static mapping = [ tablePerHierarchy : false ]
}
class CreditCardPayment extends Payment {
String cardNumber
}
&lt;/pre&gt;
Sin embargo, el excesivo uso de la herencia y de la estrategia &quot;table-per-subclass&quot; puede penalizar en el rendimiento por el uso de outer join queries. El consejo es usar la herencia pero no abusar de ella sobre todo si es muy profunda.</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/herencia-en-gorm.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-3513240699368504441</guid><pubDate>Wed, 08 Aug 2012 12:07:00 +0000</pubDate><atom:updated>2012-10-04T13:20:10.942+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Data binding en los argumentos de la acción</title><description>En Grails 2 una de las caraterísticas era el databinding en los argumentos de la acción de un Controlador.
&lt;pre class=&quot;brush:java&quot;&gt;
def save (float width, float height) {
...
}
&lt;/pre&gt;
Pero ahora podemos cambiar el nombre de los argumentos para poder utilizarlos en la acción con la anotación @RequestParameter de Spring 3 (ej. para conectar con una API externa).
&lt;pre class=&quot;brush:java&quot;&gt;
def save (@RequestParameter(&#39;w&#39;) float width,&amp;nbsp; @RequestParameter(&#39;h&#39;) float height){
...
}
&lt;/pre&gt;
* Ahora el campo width se inicializá con params.w y height con params.h</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/data-binding-en-los-argumentos-de-la.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-1205138555228187269</guid><pubDate>Wed, 08 Aug 2012 11:56:00 +0000</pubDate><atom:updated>2012-10-04T13:20:03.801+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DOMAIN CLASS</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Entendiendo ignoreNotFound</title><description>Sirve para subsanar el error de una referencia perdida de una FK (foreign key borrada) en una base de datos en una relación many-to-one.&lt;br /&gt;
Cuando los datos en una base de datos están corruptos y una FK no existe, Hibernate nos dará un mensaje de &quot;No row with the given identifier exists&quot;.&lt;br /&gt;
Una forma de solucionarlo es poner ignoreNotFound a true para que Hibernate trate la columna perdida como una asociación nula.&lt;br /&gt;
&lt;br /&gt;
*Hay que tener cuidado porque cuando hagamos el save() nos puede dar una excepción por &quot;Missing reference&quot;.</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/entendiendo-ignorenotfound.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-3635351606950321511.post-4092325862488959448</guid><pubDate>Wed, 08 Aug 2012 11:31:00 +0000</pubDate><atom:updated>2012-10-04T13:20:32.351+02:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CONTROLLER</category><category domain="http://www.blogger.com/atom/ns#">GRAILS</category><title>Nuevo método params.date()</title><description>Hay un nuevo método en Grails 2 para resolver el problema de parsear fechas cuando vienen en el params.
&lt;pre class=&quot;brush:java&quot;&gt;
def date = params.date(&#39;myDate&#39;)
ó
def date = params.date(&#39;myDate&#39;,&#39;dd/MM/yyyy&#39;)
&lt;/pre&gt;</description><link>http://beginninggroovyandgrails.blogspot.com/2012/08/nuevo-metodo-paramsdate.html</link><author>noreply@blogger.com (EduSanz)</author><thr:total>0</thr:total></item></channel></rss>