Grails basics

Uploaded on

Basics about grails, how to have command objects on the grails application, scope in grails, URL Mappings

Basics about grails, how to have command objects on the grails application, scope in grails, URL Mappings

More in: Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads


Total Views
On Slideshare
From Embeds
Number of Embeds



Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

    No notes for slide


  • 1. By: Saurabh Dixit
  • 2.  Grails is a full stack framework and attemptsto solve as many pieces of the webdevelopment puzzle through the coretechnology and its associated plugins. Need not to worry about XML configurationfiles
  • 3.  Creating controller classes:
  • 4.  Creating Services:
  • 5.  Command Objects in grails: In Grails, command objects are like domainclasses, but don’t persist data. They are very useful when you need togroup request parameters in a single object.
  • 6.  First thing you‘ll need to do is describe yourcommand object. It is fine to do in the samefile that contains controller that will use it. If command will be used by more than onecontroller, describe it in controllers or ingroovy source directory.
  • 7.  DeclaringCommand Objects:▪ Command object classes are defined just like any other class.@Validateableclass PasswordChangeCommand {String usernameString actualPasswordString newPasswordString repNewPasswordstatic constraints = {newPassword(nullable: false, blank: false)repNewPassword(nullable: false, blank: false, validator: {StringrepNewPassword, obj ->String newPassword =[newPassword]if( !(newPassword == repNewPassword) ) {return PasswordChangeCommand.newPasswordMismatch //key of the i18 message} else {return true}})}}
  • 8. def checkRegisterCommand = { PasswordChangeCommandcommand->if ( command.validate() ) {println("Command validated")redirect action: index}else {println("Command validation failed")flash.message = command.errors.allErrors.collect { error ->error.field}.join(", ")redirect action: index}}
  • 9. <g:form action="checkRegisterCommand">username: <g:textField name="username"/>actualPassword: <g:textField name="actualPassword"/>newPassword: <g:textField name="newPassword"/>repNewPassword: <g:textField name="repNewPassword"/><g:submitButton name="checkRegisterCommand" /></g:form>
  • 10.  As this example shows, since the commandobject class is marked withValidateable youcan define constraints in command objectsjust like in domain classes. Another way to make a command objectclass validateable is to define it in the samesource file as the controller which is using theclass as a command object.
  • 11.  If a command object class is not defined inthe same source file as a controller whichuses the class as a command object and theclass is not marked withValidateable, theclass will not be made validateable. It is not required that command objectclasses be validateable.
  • 12.  Command objects can participate in dependencyinjection.This is useful if your command object has somecustom validation logic which uses a Grails service:@grails.validation.Validateableclass LoginCommand {def loginServiceString usernameString passwordstatic constraints = {username validator: { val, obj ->obj.loginService.canLogin(obj.username, obj.password)}}}
  • 13.  Sharing Constraints Between Classes: A common pattern in Grails is to use commandobjects for validating user-submitted data andthen copy the properties of the command objectto the relevant domain classes. This often means that your command objects anddomain classes share properties and theirconstraints. You could manually copy and paste theconstraints between the two, but thats a veryerror-prone approach. Instead, make use of Grailsglobal constraints and import mechanism.
  • 14.  Global Constraints: In addition to defining constraints in domainclasses, command objects and other validateableclasses, you can also define them in grails-app/conf/Config.groovy:grails.gorm.default.constraints = {*(nullable: true, size: 1..20)myShared(nullable: false, blank: false)}
  • 15.  These constraints are not attached to any particularclasses, but they can be easily referenced from anyvalidateable class:class User {...static constraints = {login shared: "myShared"}} Note:▪ the use of the shared argument, whose value is the name of one of the constraintsdefined in grails.gorm.default.constraints.▪ Despite the name of the configuration setting, you can reference these sharedconstraints from any validateable class, such as command objects. The * constraintis a special case: it means that the associated constraints (nullable and size in theabove example) will be applied to all properties in all validateable classes. Thesedefaults can be overridden by the constraints declared in a validateable class.
  • 16.  Importing Constraints: Grails 2 introduced an alternative approach tosharing constraints that allows you to import a setof constraints from one class into another. Lets say you have a domain class like so:class User {String firstNameString lastNameString passwordHashstatic constraints = {firstName blank: false, nullable: falselastName blank: false, nullable: falsepasswordHash blank: false, nullable: false}}
  • 17.  You then want to create a command object,UserCommand, that shares some of theproperties of the domain class and thecorresponding constraints.You can do thiswith the importFrom() method:
  • 18. class UserCommand {String firstNameString lastNameString passwordString confirmPasswordstatic constraints = {importFrom Userpassword blank: false, nullable: falseconfirmPassword blank: false, nullable: false}} This will import all the constraints from the Userdomain class and apply them to UserCommand. The import will ignore any constraints in the sourceclass (User) that dont have corresponding propertiesin the importing class (UserCommand).
  • 19.  In the above example, only the firstNameand lastName constraints will be importedinto UserCommand because those are theonly properties shared by the two classes. If you want more control over whichconstraints are imported, use the include andexclude arguments.
  • 20.  So for example, if you only wanted to importthe lastName constraint you would use:…static constraints = {importFrom User, include: ["lastName"]…} or if you wanted all constraints that ended withName:…static constraints = {importFrom User, include: [/.*Name/]…}
  • 21.  Exploring scopes: You’ve seen how controller actions can return amap that gets passed through to the view, andyou’ve seen the flash object also passing datafrom controller to view. This passing of information, and the lifetime ofthe variables that you pass, is known as variablescope.
  • 22.  Grails supports different scopes to storeinformation in, and each of them lasts adifferent length of time.When you passed datain a map from a controller to a view,you were implicitly making use of request scope. But Grails lets you reference the different scopesexplicitly so you can store data for as long as youneed it.
  • 23.  In particular, Grails offers you four specialmap-like storage scopes that you canreference by name in every controller action. Grails supports four storage contexts forpassing content between controllers andforms Scope variable Survival time for entriesin this scope:
  • 24.  request: Survive until the target GSP finishesrendering flash: Survive to the next page, andthe one after that session: Survive until the current user closesthe browser servletContext: Survive until the applicationis restarted (this map is shared by all users)
  • 25.  REQUEST SCOPE:▪ Request scope is great when you want to store data that isshared only between your controller and the view (normally aGSP page). FLASH SCOPE: Basically single redirect▪ Entries in the flash map survive one (and only one) redirect,after which they’re removed. SESSION SCOPE:▪ The next longest lived scope is session scope. Objects thatyou place in session scope remain until the user closes theirbrowser.▪ Internally, servers use a session cookie called JSESSIONID tomap a user to their session, and the cookie expires when theyclose their browser (or when the server times out the sessiondue to inactivity).
  • 26.  Session gotchas:the mystery of thedetached object If you’re storing a domain object in the session(such as the logged-in User object), the object willbecome detached from your Hibernate session.That means you won’t be able to access anyuninitialized lazy-loaded relationships the objectholds.
  • 27.  For example, you won’t be able to callsession.user.following.each {}. To reattach the object (so you can walk itsobject graph of relationships, for instance), use the attach() method that is available onall domain classes.
  • 28.  For our User example, it would look like this:def user = session.userif (!user.isAttached()) {user.attach()}user.following.each { nextFollowing -> /* do stuff */ } SERVLETCONTEXT (APPLICATION) SCOPE: The final scope available is servletContext scope,sometimes called application scope. This map is shared across your whole application, andit’s handy for storing state that’s not dependent onany particular user (such as the number of logins sincethe application was started).
  • 29.  Throughout the documentation so far theconvention used for URLs has been thedefault of /controller/action/id. However, this convention is not hard wiredinto Grails and is in fact controlled by a URLMappings class located at grailsapp/conf/UrlMappings.groovy.
  • 30.  The UrlMappings class contains a singleproperty called mappings that has beenassigned a block of code:class UrlMappings {static mappings = {}}
  • 31.  Mapping to Controllers and Actions:To create a simple mapping simply use arelative URL as the method name and specifynamed parameters for the controller andaction to map to:"/product"(controller: "product", action: "list")
  • 32.  In this case weve mapped the URL /product tothe list action of the ProductController. Omit theaction definition to map to the default action ofthe controller:"/product"(controller: "product") An alternative syntax is to assign the controllerand action to use within a block passed to themethod:"/product" {controller = "product"action = "list"}
  • 33.  Which syntax you use is largely dependent onpersonal preference. To rewrite one URI onto another explicit URI(rather than a controller/action pair) dosomething like this:"/hello"(uri: "/hello.dispatch") Rewriting specific URIs is often useful whenintegrating with other frameworks.
  • 34.  SimpleVariables The previous section demonstrated how to mapsimple URLs with concrete "tokens". In URL mapping speak tokens are the sequence ofcharacters between each slash, /. A concrete token is one which is well defined suchas as /product. However, in many circumstancesyou dont know what the value of a particulartoken will be until runtime.
  • 35.  In this case you can use variable placeholderswithin the URL for example:static mappings = {"/product/$id"(controller: "product")} In this case by embedding a $id variable asthe second token Grails will automaticallymap the second token into a parameter(available via the params object) called id.
  • 36.  For example given the URL /product/MacBook,the following code will render "MacBook" to theresponse:class ProductController {def index() { render }} In this case by embedding a $id variable as thesecond token Grails will automatically map thesecond token into a parameter (available via theparams object) called id.
  • 37.  For example given the URL/product/MacBook,the following code will render "MacBook" tothe response:class ProductController {def index() { render }}
  • 38.  You can of course construct more complexexamplesof mappings. For example the traditional blog URL format couldbe mapped as follows:static mappings = {"/$blog/$year/$month/$day/$id"(controller: "blog", action: "show")} The above mapping would let you do things like:/graemerocher/2007/01/10/my_funky_blog_entry The individual tokens in the URL would again bemapped into the params object with valuesavailable for year, month, day, id and so on.
  • 39.  OptionalVariables: Another characteristic of the default mapping isthe ability to append a ? at the end of a variable tomake it an optional token. In a further example this technique could beapplied to the blog URL mapping to have moreflexible linking:static mappings = {"/$blog/$year?/$month?/$day?/$id?"(controller:"blog",action:"show")}
  • 40.  With this mapping all of these URLs wouldmatch with only the relevant parametersbeing populated in the params object:/graemerocher/2007/01/10/my_funky_blog_entry/graemerocher/2007/01/10/graemerocher/2007/01/graemerocher/2007/graemerocher
  • 41.  ArbitraryVariables:You can also pass arbitrary parameters from the URL mappinginto the controller by just settingthem in the block passed to the mapping:"/holiday/win" {id = "Marrakech"year = 2007} This variables will be available within theparams object passed to the controller.
  • 42.  You can resolve a URL to a view without acontroller or action involved.For example to map the root URL / to a GSPat the location grails-app/views/index.gspyou could use:static mappings = {"/"(view: "/index") // map the root URL}
  • 43.  Alternatively if you need a view that isspecific to a given controller you could use:static mappings = {"/help"(controller: "site", view: "help") // to a view for acontroller}
  • 44.  Grails also lets you map HTTP response codesto controllers, actions or views. Just use amethod name that matches the responsecode you are interested in:static mappings = {"403"(controller: "errors", action: "forbidden")"404"(controller: "errors", action: "notFound")"500"(controller: "errors", action: "serverError")}
  • 45.  Or you can specify custom error pages:static mappings = {"403"(view: "/errors/forbidden")"404"(view: "/errors/notFound")"500"(view: "/errors/serverError")}
  • 46.  Declarative Error Handling: In addition you can configure handlers for individualexceptions:static mappings = {"403"(view: "/errors/forbidden")"404"(view: "/errors/notFound")"500"(controller: "errors", action: "illegalArgument",exception: IllegalArgumentException)"500"(controller: "errors", action: "nullPointer",exception: NullPointerException)"500"(controller: "errors", action: "customException",exception: MyException)"500"(view: "/errors/serverError")}
  • 47.  With this configuration, anIllegalArgumentException will be handled by theillegalArgument action in ErrorsController, a NullPointerException will be handled by thenullPointer action, and a MyException will behandled by the customException action. Other exceptions will be handled by the catch-allrule and use the/errors/serverError view.
  • 48.  You can access the exception from your custom errorhanding view or controller action using the requestsexception attribute like so:class ErrorController {def handleError() {def exception = request.exception// perform desired processing to handle the exception}} If your error-handling controller action throws anexception as well, youll end up with aStackOverflowException.
  • 49.  Grails URL mappings mechanism also supportswildcard mappings. For example consider thefollowing mapping:static mappings = {"/images/*.jpg"(controller: "image")} This mapping will match all paths to imagessuch as /image/logo.jpg. Of course you can achieve the same effect with avariable:static mappings = {"/images/$name.jpg"(controller: "image")}
  • 50.  However, you can also use double wildcardsto match more than one level below:static mappings = {"/images/**.jpg"(controller: "image")} In this cases the mapping will match/image/logo.jpg as well as/image/other/logo.jpg.
  • 51.  Even better you can use a double wildcardvariable:static mappings = {// will match /image/logo.jpg and /image/other/logo.jpg"/images/$name**.jpg"(controller: "image")} In this case it will store the path matched bythe wildcard inside a name parameterobtainable from the params object:def name = params.nameprintln name // prints "logo" or "other/logo"
  • 52.  URL Mappings also support named mappings, that ismappings which have a name associated with them. The name may be used to refer to a specific mappingwhen links are generated. The syntax for defining a named mapping is asfollows:static mappings = {name <mapping name>: <url pattern> {// …}}
  • 53.  For example:static mappings = {name personList: "/showPeople" {controller = personaction = list}name accountDetails: "/details/$acctNumber" {controller = productaction = accountDetails}}
  • 54.  The mapping may be referenced in a link tag in aGSP.<g:link mapping="personList">List People</g:link> That would result in:<a href="/showPeople">List People</a> Parameters may be specified using the paramsattribute.<g:link mapping="accountDetails“params="[acctNumber:8675309]">Show Account</g:link>
  • 55.  That would result in:<a href="/details/8675309">ShowAccount</a> Alternatively you may reference a namedmapping using the link namespace.<link:personList>List People</link:personList> That would result in:<a href="/showPeople">List People</a> The link namespace approach allowsparameters to be specified as attributes.<link:accountDetails acctNumber="8675309">Show Account</link:accountDetails>
  • 56.  That would result in:<a href="/details/8675309">ShowAccount</a> To specify attributes that should be applied tothe generated href, specify a Map value tothe attrs attribute.
  • 57.  These attributes will be applied directly to thehref, not passed through to be used asrequest parameters.<link:accountDetails attrs="[class: fancy]" acctNumber="8675309">ShowAccount</link:accountDetails> That would result in:<a href="/details/8675309" class="fancy">ShowAccount</a>
  • 58.  Customizing URL Formats: The default URL Mapping mechanism supportscamel case names in the URLs.The default URLfor accessing an action named addNumbers in acontroller named MathHelperController would besomething like /mathHelper/addNumbers. Grails allows for the customization of this patternand provides an implementation which replacesthe camel case convention with a hyphenatedconvention that would support URLs like /math-helper/add-numbers.
  • 59.  To enable hyphenated URLs assign a value of"hyphenated" to the grails.web.url.converterproperty in grails-app/conf/Config.groovy.// grails-app/conf/Config.groovygrails.web.url.converter = hyphenated
  • 60.  Namespaced Controllers: An application is not allowed to define multiplecontrollers with the same name, even if they aredefined in separate packages. For example an application may not containcom.accounting.ReportingController andcom.humanresources.ReportingController.
  • 61.  However it is allowed for an application to use aplugin which provides a controller with the samename as a controller provided by the applicationas long as the controllers are in separatepackages. For example, an application may include acontroller namedcom.accounting.ReportingController and the application may use a plugin whichprovides a controller namedcom.humanresources.ReportingController.
  • 62.  The only issue with that is the URL mapping forthe controller provided by the plugin needs to beexplicit in specifying that the mapping applies tothe ReportingController which is provided by theplugin.static mappings = {"/accountingReports" {controller = "reporting"}"/humanResourceReports" {controller = "reporting"plugin = "humanResources"}}
  • 63.  With that mapping in place, a request to/accountingReports will be handled by theReportingController which is defined in theapplication. A request to /humanResourceReports will behandled by the ReportingController which isprovided by the humanResources plugin
  • 64.  There could be any number ofReportingController controllers provided byany number of plugins but no plugin mayprovide more than one ReportingControllereven if they are defined in separate packages. Assigning a value to the plugin variable in themapping is only required if there are multiplecontrollers with the same name available atruntime provided by the application and/orplugins.
  • 65.  If the humanResources plugin provides aReportingController and there is no otherReportingController available at runtime, thefollowing mapping would work.static mappings = {"/humanResourceReports" {controller = "reporting"}