Grails 1.2.0
23rd of December 2009
SpringSource are pleased to announce the 1.2 release of the Grails web application development framework.
New features in 1.2 are described below.
New Features
Dependency Resolution DSL
Grails 1.2 features a new DSL for configuring JAR dependencies that can be resolved against Maven repositories:
grails.project.dependency.resolution = {
inherits "global" // inherit Grails' default dependencies
repositories {
grailsHome()
mavenCentral()
}
dependencies {
runtime 'com.mysql:mysql-connector-java:5.1.5'
}
}
Built on Apache Ivy, users can now explicitly control how Grails resolves all of its dependencies without needing to use Maven or Apache Ivy directly.
There is also a new command to easily install dependencies into your local cache for use with the DSL:
grails install-dependency mysql:mysql-connector-java:5.1.5
Significant Performance Optimizations
GSP and the Sitemesh rendering layer have been significantly improved to offer 2 to 3 times better throughput. This was achieved by:
- Output buffering has been refactored to a streaming approach: the minimum amount of new objects are created.
- Tag library calls return an instance of org.codehaus.groovy.grails.web.util.StreamCharBuffer class by default.
- Tag libraries can now return object values too. This was changed to reduce object generation.
- Sitemesh doesn't have to parse the output coming from GSP since the head, meta, title and body tags are captured from the beginning. This is done in GSP compilation when the config param "grails.views.gsp.sitemesh.preprocess" is unset or is true.
- Some performance improvements are the result of monitoring for unnecessary Exceptions created in Grails and preventing them from happening.
Thanks to @lhotari for his significant contributions to Grails in this area.
BootStrap environments
The BootStrap.groovy
class now has environment support:
def init = { ServletContext ctx ->
environments {
production {
ctx.setAttribute("env", "prod")
}
development {
ctx.setAttribute("env", "dev")
}
}
ctx.setAttribute("foo", "bar")
}
Spring 3 Upgrade
Grails now also supports Spring annotation prototypes through component scanning such as @Service
, @Component
etc.
In order to do so you need to tell Grails which packages your Spring beans are contained within in Config.groovy. For example:
grails.spring.bean.packages = \['com.foo.stuff','com.foo.otherstuff'\]
Any class can be annotated with @Component
and will automatically become a Spring bean injectable in other classes.
In addition you can annotate classes with @Controller
and these will be able to handle requests just like regular Spring MVC controllers, thus providing support for those wanting to use a combination of Spring MVC and Grails:
@Controller
class SpringController {
@Autowired
SessionFactory sessionFactory
@RequestMapping("/hello.dispatch")
ModelMap handleRequest() {
def session = sessionFactory.getCurrentSession()
return new ModelMap(session.get(Person, 1L))
}
}
In this example, going to /hello.dispatch
will execute the handleRequest()
method and atempt to render either a GSP or JSP view at grails-app/views/hello.(jsp|gsp)
URI Re-writing onto any URI
You can now re-write any request URI onto any other URI using the following syntax in your grails-app/conf/UrlMappings.groovy
file:
"/hello"(uri:"/hello.dispatch")
This feature can be used to provide pretty URIs for Spring MVC controllers (see above) or static resources.
Per-method transactions with @transactional
Building on the component scanning features you can now use the Spring org.springframework.transaction.annotation.Transactional
annotation on Grails service classes to configure transaction properties and have per-method transaction definitions:
import org.springframework.transaction.annotation.*
class BookService {
@Transactional(readOnly = true)
def listBooks() {
Book.list()
}
@Transactional
def updateBook() {
// ...
}
}
{code}
Fine-grained control over DataSource properties
The DataSource.groovy file now provides control over all of the underlying DataSource bean's properties:
dataSource {
pooled = true
dbCreate = "update"
url = "jdbc:mysql://localhost/yourDB"
driverClassName = "com.mysql.jdbc.Driver"
username = "yourUser"
password = "yourPassword"
properties {
maxActive = 50
maxIdle = 25
minIdle = 5
initialSize = 5
minEvictableIdleTimeMillis = 60000
timeBetweenEvictionRunsMillis = 60000
maxWait = 10000
}
}
Global Configuration of GORM defaults
GORMs default mapping settings are now globally configurable inside @grails-app/conf/Config.groovy@:
grails.gorm.default.mapping = {
cache true
id generator:'sequence'
'user-type'( type:org.hibernate.type.YesNoType, class:Boolean )
}
The default values for constraints is also globally configurable:
grails.gorm.default.constraints = {
'*'(nullable:true, blank:false, size:1..20)
}
You can even define named constraint groups:
grails.gorm.default.constraints = {
myConstraints(nullable:true, blank:false, size:1..20)
}
And then reuse them inside your domain classes:
static constraints = {
myProperty shared:"myConstraints"
}
Improved Dynamic Finders for Boolean properties
GORM dynamic finders have been improved with an easier notation for handling boolean properties. For example given a domain class of:
class Book {
String title
String author
Boolean paperback
}
You can do:
def results = Book.findAllPaperbackByAuthor("Douglas Adams")
or
def results = Book.findAllNotPaperbackByAuthor("Douglas Adams")
In this case the boolean value is implicitly inferred from the method signature.
Named Query Support
GORM now supports defining named queries in a domain class. For example, given a domain class like this:
class Publication {
String title
Date datePublished
static namedQueries = {
recentPublications {
def now = new Date()
gt 'datePublished', now - 365
}
publicationsWithBookInTitle {
like 'title', '%Book%'
}
}
}
You can do:
// get all recent publications
def recentPubs = Publication.recentPublications.list()
// get up to 10 recent publications, skip the first 5...
def recentPubs = Publication.recentPublications.list(max: 10, offset: 5)
// get the number of recent publications...
def numberOfRecentPubs = Publication.recentPublications.count()
// get a recent publication with a specific id...
def pub = Publication.recentPublications.get(42)
// dynamic finders are supported
def pubs = Publication.recentPublications.findAllByTitle('Some Title')
Support for SQL restriction in criteria builder
The criteria builder now supports arbitrary SQL expressions:
def c = Person.createCriteria()
def peopleWithShortFirstNames = c.list {
sqlRestriction "char_length( first_name ) <= 4"
}
Support for hasOne mapping
GORM now supports @hasone@ mapping where the foreign key is stored in the child instead of the parent association. For example:
class Person {
String name
static hasOne = [address: Address]
}
class Address {
String street
String postCode
}
In the above case a foreign key column called @person_id@ will be created in the @address@ table rather than the default where an @address_id@ is created in the @person@ table.
Strict Validation Errors
There is a new @failOnError@ argument available on the @save()@ method that will throw an exception if a validation error occurs:
try {
book.save(failOnError:true)
}
catch(ValidationException e) {
// handle
}
Improved support for annotated entities
You can now use annotated entities instead of the GORM syntax inside domains located in grails-app/domain
. These entities can use Grails constraints and events just like normal GORM entities for those who prefer annotations over GORM syntax:
import javax.persistence.*
@Entity
@Table(name = "animal")
class Animal {
@Id @GeneratedValue
int id
String name
@ManyToOne
@JoinColumn
Owner owner
static constraints = {
name blank:false
}
}
Precompilation of Groovy Server Pages in WAR deployment
GSPs are now pre-compiled when producing a WAR file meaning less permgen space is used at deployment time for Grails applications.
Improved handling of i18n class and property names
You can now put entries in your i18n messages.properties file for all class and property names in your application. For example:
book.label = Libro
book.title.label = TÃtulo del libro
These are then picked up by Grails' default error messages and scaffolded views.
Tomcat & Multiple Embedded Containers Supported
Grails now supports multiple embedded containers with Tomcat being the default. Grails will by default install the Tomcat plugin into your application. You can easily switch containers by switching plugins:
grails uninstall-plugin tomcat
grails install-plugin jetty
The Tomcat plugin comes with built in remote deployment commands:
grails tomcat deploy
grails tomcat undeploy
Web Flow as a plugin
Grails' support for Spring Web Flow has been extracted into a plugin. To install simply type:
grails install-plugin webflow
If you are migrating from older versions of Grails and use Web Flow you will need to run the above command. If you don't wish to use Web Flow in your application you can uninstall it:
grails uninstall-plugin webflow
New Web Flow Events
Grails' Web Flow DSL now supports more of the common Web Flow events such as @onEntry@, @OnRender@ and so on:
def searchFlow = {
onStart {
println "started"
}
onEnd {
println "ended"
}
displaySearchForm {
onRender {
println "rendering"
}
onEntry {
println "entered"
}
onExit {
println "exited"
}
on("submit") {
[results: Book.findAllByTitle(params.title)]
}.to "displayResults"
}
displayResults()
}
Named URL Mappings
Grails now supports named URL mappings and associated dynamic tags for view layer URL rewriting. For example:
{code}
name productDetail: "/showProduct/$productName/$flavor?" {
controller = "product"
action = "show"
}
{code}
The above creates a named URL mapping called "productDetail" this can be linked to from the view with:
<link:productDetail productName="licorice" flavor="strawberry">
Strawberry Licorice
</link:productDetail>
JSONBuilder
JSONBuilder has been re-written, better documented and improved in general. Rendering JSON responses is now as simple as:
render(contentType:"text/json") {
categories = [ { a = "A" }, { b = "B" } ]
}
Which will produce:
{"categories":[ {"a":"A"} , {"b":"B"}] }
This is breaking change from previous versions of Grails, if you want backwards compatibility set
grails.json.legacy.builder=true in Config.groovy
Better Date parsing
If you submit a date from a <g:datePicker name="foo" />
tag, obtaining the Date
object is now as simple as looking it up from the params
object by name (eg. params.foo
)
Convenient, null safe converters in params and tag attributes
New convenience methods have been added to the @params@ object and tag @attrs@ objects that allow the easy, exception safe and null safe conversion of parameters to common types:
def total = params.int('total')
There are methods for all the common base types such as @int@, @long@, @boolean@ and so on. There is a special converter called @list@ that always returns a list for cases when dealing with one or many parameters of the same name:
def names = params.list('names')
REST improvements
The <g:form>
tag can now pass other methods beside POST and GET:
<g:form method="DELETE">
This works by creating a hidden field called _method. Now browser clients can also pass the X-HTTP-Method-Override HTTP header
Project Documentation Engine
The same documentation engine that powers the Grails reference documentation is now available in your projects. Simply create your documentation inside the @src/docs/ref@ and src/docs/guide
directories of your project. See the Grails documentation source for an example.
The engine is also usable outside of Grails by adding the grails-docs dependency to your application (example in Gradle syntax):
compile group: 'org.grails', name: 'grails-docs', version:'1.2.0.RC1'
And using the provided Ant task:
ant.taskdef (name: 'docs', classname : 'grails.doc.ant.DocPublisherTask')
ant.docs(src:"src/docs", dest:"build/docs",
properties:"src/docs/doc.properties")
Zip-only plugin releases
If you prefer to use Git/Mercurial/etc. to version control your plugin you can now distribute only the zipped release of the plugin in the central repository and continue to manage the actual sources outside of SVN:
grails release-plugin --zipOnly
Plugin Metadata
Every plugin Groovy source or GSP view is now annotated with a @GrailsPlugin annotation which describes the name and the version of the plugin that the source or view originates from. This adds additional capabilities to Grails in terms of the runtime discovery of plugin sources.
In addition, when releasing a plugin into the Grails central repository metadata is generated about the methods and properties added to Grails classes at runtime.
This metadata is put into the plugin.xml descriptor and can be read by IDEs and documentation engines.
If your plugin doesn't add methods at runtime you can skip the metadata generation process by doing:
grails release-plugin --skipMetadata
formatNumber and formatDate taglib improvements
g:formatNumber and g:formatDate taglibs are now feature compatible with JSTL formatNumber and formatDate tags.
Property Override Configuration Improvements
The Property Override mechanism has been improved to support non-String types and transactional services.
// Config.groovy
beans {
someService {
someProperty = new SomeObject()
}
}
Testing infrastructure improvements
Non JUnit providers
The test running mechanics have been completely overhauled, opening the door to all kinds of testing possibilities for Grails applications. Previously it was very difficult for non JUnit based tests to be deeply integrated into Grails (e.g grails-easyb). Expect to see testing plugins taking advantage of this new infrastructure.
Test phase and type targeting
There is now a more sophisticated mechanism for targeting the exact test you wish to run. Previously, it was only possible to target test phases but it is now also possible to target test types .
You target particular test phases and/or types by using the following syntax:
grails test-app <phase>:<type>
grails test-app unit:spock // run "spock" test type in "unit" test phase
Either side is optional, and its absence implies all
grails test-app unit: // run all types in "unit" phase
grails test-app :spock // run "spock" type in all phases
It can be used in conjunction with test targeting…
grails test-app unit: SomeController // run all test for "SomeController" in the "unit" phase
And is additive:
grails test-app unit: integration: // run all test types in the "unit" and "integration" phases
Legacy phase targeting syntax is supported for backwards compatibility
grails test-app --unit // equivalent to grails test-app unit:
Clean testing
You can can force a clean before testing by passing -clean to test-app:
grails test-app -clean
Echoing System.out and System.err
By default, grails does not show the output from your tests. You can make it do so by passing -echoOut and/or -echoErr to test-app:
grails test-app -echoOut -echoErr