$ grails create-plugin quartz
3.3 Upgrading from Grails 2.x
Version: 3.2.3
Table of Contents
3.3 Upgrading from Grails 2.x
This guide takes you through the fundamentals of upgrading a Grails 2.x application or plugins to Grails 3.x.
3.3.1 Upgrading Plugins
To upgrade a Grails 2.x plugin to Grails 3.x you need to make a number of different changes. This documentation will outline the steps that were taken to upgrade the Quartz plugin to Grails 3, each individual plugin may differ.
Step 1 - Create a new Grails 3 plugin
The first step is to create a new Grails 3 plugin using the command line:
This will create a Grails 3 plugin in the quartz
directory.
Step 2 - Copy sources from the original Grails 2 plugin
The next step is to copy the sources from the original Grails 2 plugin to the Grails 3 plugin:
# first the sources
cp -rf ../quartz-2.x/src/groovy/ src/main/groovy
cp -rf ../quartz-2.x/src/java/ src/main/groovy
cp -rf ../quartz-2.x/grails-app/ grails-app
cp -rf ../quartz-2.x/QuartzGrailsPlugin.groovy src/main/groovy/grails/plugins/quartz
# then the tests
cp -rf ../quartz-2.x/test/unit/* src/test/groovy
mkdir -p src/integration-test/groovy
cp -rf ../quartz-2.x/test/integration/* src/integration-test/groovy
# then templates / other resources
cp -rf ../quartz-2.x/src/templates/ src/main/templates
Step 3 - Alter the plugin descriptor
You will need to add a package declaration to the plugin descriptor. In this case QuartzGrailsPlugin
is modified as follows:
// add package declaration
package grails.plugins.quartz
...
class QuartzGrailsPlugin extends Plugin {
...
}
In addition you should remove the version
property from the descriptor as this is now defined in build.gradle
.
Step 4 - Update the Gradle build with required dependencies
The repositories and dependencies defined in grails-app/conf/BuildConfig.groovy
of the original Grails 2.x plugin will need to be defined in build.gradle
of the new Grails 3.x plugin:
compile("org.quartz-scheduler:quartz:2.2.1") {
exclude group: 'slf4j-api', module: 'c3p0'
}
It is recommended to use the latest stable, Grails 3+ compatible version of plugins. (Grails 2.x plugin versions will not work.)
Step 5 - Modify Package Imports
In Grails 3.x all internal APIs can be found in the org.grails
package and public facing APIs in the grails
package. The org.codehaus.groovy.grails
package no longer exists.
All package declaration in sources should be modified for the new location of the respective classes. Example org.codehaus.groovy.grails.commons.GrailsApplication
is now grails.core.GrailsApplication
.
Step 5 - Migrate Plugin Specific Config to application.yml
Some plugins define a default configuration file. For example the Quartz plugin defines a file called grails-app/conf/DefaultQuartzConfig.groovy
. In Grails 3.x this default configuration can be migrated to grails-app/conf/application.yml
and it will automatically be loaded by Grails without requiring manual configuration merging.
Step 6 - Update plugin exclusions
Old plugins may have a pluginExcludes
property defined that lists the patterns for any files that should not be included in the plugin package. This is normally used to exclude artifacts such as domain classes that are used in the plugin’s integration tests. You generally don’t want these polluting the target application.
This property is no longer sufficient in Grails 3, and nor can you use source paths. Instead, you must specify patterns that match the paths of the compiled classes. For example, imagine you have some test domain classes in the grails-app/domain/plugin/tests
directory. You should first change the pluginExcludes
value to
def pluginExcludes = ["plugin/test/**"]
and then add this block to the build file:
jar {
exclude "plugin/test/**"
}
The easiest way to ensure these patterns work effectively is to put all your non-packaged class into a distinct Java package so that there is a clean separation between the main plugin classes and the rest.
Step 7 - Register ArtefactHandler Definitions
In Grails 3.x ArtefactHandler definitions written in Java need to be declared in a file called src/main/resources/META-INF/grails.factories
since these need to be known at compile time.
If the ArtefactHandler is written in Groovy this step can be skipped as Grails will automatically create the grails.factories file during compilation.
|
The Quartz plugin requires the following definition to register the ArtrefactHandler
:
grails.core.ArtefactHandler=grails.plugins.quartz.JobArtefactHandler
Step 8 - Migrate Code Generation Scripts
Many plugins previously defined command line scripts in Gant. In Grails 3.x command line scripts have been replaced by two new features: Code generation scripts and Gradle tasks.
If your script is doing simple code generation then for many cases a code generation script can replace an old Gant script.
The create-job
script provided by the Quartz plugin in Grails 2.x was defined in scripts/CreateJob.groovy
as:
includeTargets << grailsScript("_GrailsCreateArtifacts")
target(createJob: "Creates a new Quartz scheduled job") {
depends(checkVersion, parseArguments)
def type = "Job"
promptForName(type: type)
for (name in argsMap.params) {
name = purgeRedundantArtifactSuffix(name, type)
createArtifact(name: name, suffix: type, type: type, path: "grails-app/jobs")
createUnitTest(name: name, suffix: type)
}
}
setDefaultTarget 'createJob'
A replacement Grails 3.x compatible script can be created using the create-script
command:
$ grails create-script create-job
Which creates a new script called src/main/scripts/create-job.groovy
. Using the new code generation API it is simple to implement:
description("Creates a new Quartz scheduled job") {
usage "grails create-job <<JOB NAME>>"
argument name:'Job Name', description:"The name of the job"
}
model = model( args[0] )
render template:"Job.groovy",
destination: file( "grails-app/jobs/$model.packagePath/${model.simpleName}Job.groovy"),
model: model
Please refer to the documentation on Creating Custom Scripts for more information.
Migrating More Complex Scripts Using Gradle Tasks
Using the old Grails 2.x build system it was relatively common to spin up Grails inside the command line. In Grails 3.x it is not possible to load a Grails application within a code generation script created by the create-script command.
Instead a new mechanism specific to plugins exists via the create-command command. The create-command
command will create a new ApplicationCommand, for example the following command will execute a query:
import grails.dev.commands.*
import javax.sql.*
import groovy.sql.*
import org.springframework.beans.factory.annotation.*
class RunQueryCommand implements ApplicationCommand {
@Autowired
DataSource dataSource
boolean handle(ExecutionContext ctx) {
def sql = new Sql(dataSource)
println sql.executeQuery("select * from foo")
return true
}
}
With this command in place once the plugin is installed into your local Maven cache you can add the plugin to both the build classpath and the runtime classpath of the application’s build.gradle
file:
buildscript {
...
dependencies {
classpath "org.grails.plugins:myplugin:0.1-SNAPSHOT"
}
}
...
dependencies {
runtime "org.grails.plugins:myplugin:0.1-SNAPSHOT"
}
Grails will automatically create a Gradle task called runQuery
and a command named run-query
so both the following examples will execute the command:
$ grails run-query
$ gradle runQuery
Step 8 - Delete Files that were migrated or no longer used
You should now delete and cleanup the project of any files no longer required by Grails 3.x (BuildConfig.groovy
, Config.groovy
, DataSource.groovy
etc.)
3.3.2 Upgrading Applications
Upgrading applications to Grails 3.x will require that you upgrade all plugins the application uses first, hence you should follow the steps in the previous section to first upgrade your plugins.
Step 1 - Create a New Application
Once the plugins are Grails 3.x compatible you can upgrade the application. To upgrade an application it is again best to create a new Grails 3 application using the "web" profile:
$ grails create-app myapp
$ cd myapp
Step 2 - Migrate Sources
The next step is to copy the sources from the original Grails 2 application to the Grails 3 application:
# first the sources
cp -rf ../old_app/src/groovy/ src/main/groovy
cp -rf ../old_app/src/java/ src/main/groovy
cp -rf ../old_app/grails-app/ grails-app
# then the tests
cp -rf ../old_app/test/unit/ src/test/groovy
mkdir -p src/integration-test/groovy
cp -rf ../old_app/test/integration/ src/integration-test/groovy
Step 3 - Update the Gradle build with required dependencies
The repositories and dependencies defined in grails-app/conf/BuildConfig.groovy
of the original Grails 2.x application will need to be defined in build.gradle
of the new Grails 3.x application.
Step 4 - Modify Package Imports
In Grails 3.x all internal APIs can be found in the org.grails
package and public facing APIs in the grails
package. The org.codehaus.groovy.grails
package no longer exists.
All package declaration in sources should be modified for the new location of the respective classes. Example org.codehaus.groovy.grails.commons.GrailsApplication
is now grails.core.GrailsApplication
.
Step 5 - Migrate Configuration
The configuration of the application will need to be migrated, this can normally be done by simply renaming grails-app/conf/Config.groovy
to grails-app/conf/application.groovy
and merging the content of grails-app/conf/DataSource.groovy
into grails-app/conf/application.groovy
.
Note however that Log4j has been replaced by grails-app/conf/logback.groovy
for logging, so any logging configuration in grails-app/conf/Config.groovy
should be migrated to logback format.
Step 6 - Migrate web.xml Modifications to Spring
If you have a modified web.xml
template then you will need to migrate this to Spring as Grails 3.x does not use a web.xml (although it is still possible to have on in src/main/webapp/WEB-INF/web.xml
).
New servlets and filters can be registered as Spring beans or with ServletRegistrationBean and FilterRegistrationBean respectively.
Step 7 - Migrate Static Assets not handled by Asset Pipeline
If you have static assets in your web-app
directory of your Grails 2.x application such as HTML files, TLDs etc. these need to be moved. For public assets such as static HTML pages and so on these should go in src/main/resources/public
.
TLD descriptors and non public assets should go in src/main/resources/WEB-INF
.
As noted earlier, src/main/webapp
folder can also be used for this purpose but it is not recommended.
Step 8 - Migrate Tests
Once the package names are corrected unit tests will continue to run, however any tests that extend the deprecated and removed JUnit 3 hierarchy will need to be migrated to Spock or JUnit 4.
Integration tests will need to be annotated with the Integration annotation and should not extend GroovyTestCase or any JUnit 3 super class.
3.3.3 General Changes to be aware of when migrating apps
There are other miscellaneous changes between Grails 2.x and Grails 3.x that it may help to be aware of when migrating your applications and plugins. Minor changes may be required.
Domain classes
The Constraints section of a Domain Class (or other validateable object) looks like this:
static constraints = {
name nullable: true, blank: false
myField nullable: true
another unique: true
}
In Grails 2.x, fields with no constraints could be declared in the Constraints block, as a method call with no arguments. Example (NB. the following syntax is no longer supported):
static constraints = {
name nullable: true, blank: false
mySimpleProperty() // <- A field that has no constraints. This syntax is not supported in Grails 3.
anotherProperty unique: true
}
A different syntax has to be used in Grails 3. Either remove the field declaration from the constraints block (if there are no constraints to specify for it), or to keep the field placeholder, pass an empty map argument: \[:\]
instead of ()
.
Replacement code for Grails 3.x:
static constraints = {
name nullable: true, blank: false
mySimpleProperty [:] // <- Empty map argument instead of ()
anotherProperty unique: true
}
If such declarations have not yet been changed then a log message like this emits on startup:
ORM Mapping Invalid: Specified config option <<mySimpleProperty>> does not exist for class [example.MyDomainClass]
Multi-project builds (Grails 2.x inline plugins)
If your project had inline plugins in Grails 2.x, contains ASTs, or if your project is composed of several modules or related applications then you may decide to restructure your project as a Gradle multi-project build.
Sample multi-project structure:
+ example
+ example-app <-- Main app
+ example-core <-- Shared code plugin
+ example-ast <-- AST transformations plugin
How to configure this is documented in the Plugins section under the heading 'Inline Plugins in Grails 3.0'.
Migrating from Grails 2.x to Grails 3.1+
During the progress of migrating code from Grails 2.4 to Grails 3.1+, your project (and the plugins that your project depends on) will be moving to GORM 5 (or higher) and other newer library versions. You might also wish to familiarise yourself with the differences mentioned in the section Upgrading from Grails 3.0.
AST Transformations
If your application contains AST transformations, please be aware that for these to be applied to your application code, they must now be contained within a plugin. (In Grails 2.x it was possible to pre-compile AST transformations then apply them to your application code by hooking into compile events in _Events.groovy
. This is no longer possible. Move your AST Transformation classes and associated annotations into a plugin for this purpose.)
There are two AST patterns on which you can base migration of your AST transformer code:
-
Groovy way: Use Groovy AST transformation annotations.
-
Grails way: Use Grails AST transformer annotations.
Groovy AST transformations
-
Import
org.codehaus.groovy.transform.GroovyASTTransformation
-
Annotate your transformation class with
GroovyASTTransformation(phase=CompilePhase.CANONICALIZATION)
-
A useful example app can be found here: grails3ast
Grails AST transformations
-
Import
grails.compiler.ast.AstTransformer
-
Annotate your transformation class with
AstTransformer
-
Implement applicable interfaces, particularly if you are transforming Artefacts, e.g.
implements GrailsArtefactClassInjector, AnnotatedClassInjector
-
Your Transformer class must reside in a package under org.grails.compiler, otherwise it will not be detected. Example: org.grails.compiler.myapp
-
Examples can be found in the Grails source code
-
Example reference: ControllerActionTransformer.java
Deployment to containers
Grails uses Spring Boot to embed a Tomcat or Jetty instance by default. To build a war file for deployment to a container you will need to make a simple change to build.gradle
(so that a container is not embedded).
If you deploy to a Tomcat 7 container then there is an additional step. Grails 3 is built against Tomcat 8 APIs by default. You will need to change the target Tomcat version in the build to 7.
There are standalone deployment options available.
Refer to the Deployment guide for further details.
Multiple datasources
If your application uses multiple datasources, then be aware that the way these are declared in application.yml
or application.groovy
(previously DataSources.groovy
) has changed.
If there is more than one DataSource in an application there is now a dataSources { … }
configuration block to contain them all. Previously, multiple dataSource
declarations were used, with an underscore and suffix on the additional datasources, e.g. dataSource_lookup { … }
.
Please refer to the user guide section on Multiple Datasources for examples.
Improvements to dependency injection
In your Grails 2.x app you may have used Spring @Autowired
in a few situations, such as dependency injection into certain base classes, and for typed field dependency injection. For example:
@Autowired
org.quartz.Scheduler quartzScheduler
Grails now has support for dependency injection into typed fields in addition to untyped def
fields, following the usual Grails conventions of field name matching the bean property name. Example:
GrailsApplication grailsApplication
You may find that @Autowired
no longer works as it did previously in your code on artefacts or base classes, in certain scenarios, resulting in NULL for these fields. Changing these to a simple typed Grails dependency following the Grails naming convention and removing @Autowired
should resolve this.