Views and controllers
Now that our project is all setup, it's time to dive into groovlets and templates.
Tip: A good practice is to separate your views from your logic (following the usual MVC pattern). Since Gaelyk provides both view templates and Groovlet controllers, it's advised to use the former for the view and the later for the logic.
Gaelyk builds on Groovy's own Groovlets and template servlet to add a shorcuts to the App Engine SDK APIs.
Note: You can learn more about Groovy's Groovlets and templates from this article on IBM developerWorks. Gaelyk's own Groovlets and templates are just an extension of Groovy's ones, and simply decorate Groovy's Groovlets and templates by giving access to App Engine services and add some additional methods to them via Groovy categories.
Variables available in the binding
A special servlet binding gives you direct access to some implicit variables that you can use in your views and controllers:
Eager variables
-
request : the
HttpServletRequest
object -
response : the
HttpServletResponse
object -
context : the
ServletContext
object -
application : same as
context
-
session : the lazy
HttpSession
which is nevernull
- params : map of all form parameters (can be empty)
- headers : map of all request header fields
-
log : a Groovy logger is available for logging messages through
java.util.logging
- logger : a logger accessor can be used to get access to any logger (more on logging)
Note: The params map always contains values of type String (for single parameter) or String[] (for multiple parameters). To cast them to expected type, use as keyword. If single value is expected (e.g. params.limit as int) but multiple parameters are submitted the first one is used. If multiple values are expected, use params.tags as String[] to guarantee you obtain String array instead of single String.
Lazy variables
- out : shorthand for
response.getWriter()
which returns aPrintWriter
- sout : shorthand for
response.getOutputStream()
which returns aServletOutputStream
- html : shorthand for
new MarkupBuilder(response.getWriter())
which returns aMarkupBuilder
Note: The eager variables are pre-populated in the binding of your Groovlets and templates. The lazy variables are instantiated and inserted in the binding only upon the first request.
GAE specific variables
Beyond those standard Servlet variables provided by Groovy's servlet binding, Gaelyk also adds ones of his own by injecting specific elements of the Google App Engine SDK:
- datastore : the Datastore service
- memcache : the Memcache service
- urlFetch : the URL Fetch service
- mail : the Mail service
-
images : the
Images service
(actually a convenient wrapper class combining both the methods of
ImagesService
andImagesServiceFactory
and implementing theImagesService
interface) - users : the User service
-
user : the currently logged in
user
(
null
if no user logged in) - defaultQueue : the default queue
- queues : a map-like object with which you can access the configured queues
- xmpp : the Jabber/XMPP service.
- blobstore : the Blobstore service.
- oauth : the OAuth service.
- namespace : the Namespace manager
- capabilities : the Capabilities service
- channel : the Channel service
- files : the File service
- backends : the Backend service
- lifecycle : the Lifecycle manager
- prospectiveSearch : the Prospective search service
- logService : the Log service
- search : the Search service
-
localMode : a boolean variable which is
true
when the application is running in local development mode, andfalse
when deployed on Google's cloud. -
app : a map variable with the following keys and values:
- id : the application ID (here: gaelyk-hrd)
- version : the application version (here: 220b.385600355263483183)
-
env : a map with the following keys and values:
- name : the environment name (here: Production)
- version : the Google App Engine SDK version (here: Google App Engine/1.9.98)
-
gaelyk : a map with the following keys and values:
- version : the version of the Gaelyk toolkit used (here: 2.0)
-
geo : a map variable with the following keys and values:
- country : country from which the request originated, as an ISO 3166-1 alpha-2 country code determinated from the client's IP address
- region : name of region from which the request originated (e.g. ca for California)
- city : name of city from which the request originated (in lower case e.g. mountain view)
- latitude : latitude of city from which the request originated (e.g. 37.386051)
- longitude : longitude of city from which the request originated (e.g. -122.083851)
Note: Regarding theapp
variable, this means you can access those values with the following syntax in your groovlets and templates:app.id app.version app.env.name app.env.version app.gaelyk.version
Note: You can learn more about the environment and system properties Google App Engine exposes.
Thanks to all these variables and services available, you'll be able to access the Google services and Servlet specific artifacts with a short and concise syntax to further streamline the code of your application.
Injecting services and variables in your classes
All the variables and services listed in the previous sections are automatically injected into the binding
of Groovlets and templates, making their access transparent, as if they were implicit or global variables.
But what about classes? If you want to also inject the services and variables into your classes,
you can annotate them with the @GaelykBindings
annotation.
import groovyx.gaelyk.GaelykBindings // annotate your class with the transformation @GaelykBindings class WeblogService { def numberOfComments(post) { // the datastore service is available datastore.execute { select count from comments where postId == post.id } } }
The annotation instructs the compiler to create properties in your class for each of the services and variables.
Note: Variables likerequest
,response
,session
,context
,params
,headers
,out
,sout
,html
are not bound in your classes.
Note: If your class already has a property of the same name as the variables and services injected by this AST transformation, they won't be overriden.
Templates
Gaelyk templates are very similar to JSPs or PHP: they are pages containing scriptlets of code. You can:
- put blocks of Groovy code inside
<% /* some code */ %>
, - call
print
andprintln
inside those scriptlets for writing to the servlet writer, - use the
<%= variable %>
notation to insert a value in the output, - or also the GString notation
${variable}
to insert some text or value.
Let's have a closer look at an example of what a template may look like:
<html> <body> <p><% def message = "Hello World!" print message %> </p> <p><%= message %></p> <p>${message}</p> <ul> <% 3.times { %> <li>${message}</li> <% } %> </ul> </body> </html>
The resulting HTML produced by the template will look like this:
<html> <body> <p>Hello World!</p> <p>Hello World!</p> <p>Hello World!</p> <ul> <li>Hello World!</li> <li>Hello World!</li> <li>Hello World!</li> </ul> </body> </html>
If you need to import classes, you can also define imports in a scriplet at the top of your template as the following snippet shows:
<% import com.foo.Bar %>
Note: Of course, you can also use Groovy's type aliasing withimport com.foo.ClassWithALongName as CWALN
. Then, later on, you can instanciate such a class withdef cwaln = new CWALN()
.
Note: Also please note that import directives don't look like JSP directives (as of this writing).
As we detailed in the previous section, you can also access the Servlet objects (request, response, session, context), as well as Google App Engine's own services. For instance, the following template will display a different message depending on whether a user is currently logged in or not:
<html> <body> <% if (user) { %> <p>You are currently logged in.</p> <% } else { %> <p>You're not logged in.</p> <% } %> </body> </html>
Includes
Often, you'll need to reuse certain graphical elements across different pages.
For instance, you always have a header, a footer, a navigation menu, etc.
In that case, the include mechanism comes in handy.
As adivsed, you may store templates in WEB-INF/includes
.
In your main page, you may include a template as follows:
<% include '/WEB-INF/includes/header.gtpl' %> <div>My main content here.</div> <% include '/WEB-INF/includes/footer.gtpl' %>
Redirect and forward
When you want to chain templates or Groovlets, you can use the Servlet redirect and forward capabilities. To do a forward, simply do:
<% forward 'index.gtpl' %>
For a redirect, you can do:
<% redirect 'index.gtpl' %>
Precompilation
Since version 1.2 you can precompile your templates using Gradle Gaelyk Plugin.
If you are facing issues with precompiled templates in production you can set preferPrecompiled
init parameter of the
Gaelyk Template Servlet to true
.
Precompiled templates will be served as in production if available.
Groovlets
In contrast to view templates, Groovlets are actually mere Groovy scripts. But they can access the output stream or the writer of the servlet, to write directly into the output. Or they can also use the markup builder to output HTML or XML content to the view.
Let's have a look at an example Groovlet:
println """ <html> <body> """ [1, 2, 3, 4].each { number -> println "${number}
" } def now = new Date() println """ <p> ${now} </p> </body> </html> """
You can use print
and println
to output some HTML or other plain-text content to the view.
Instead of writing to System.out
, print
and println
write to the output of the servlet.
For outputing HTML or XML, for instance, it's better to use a template,
or to send fragments written with a Markup builder as we shall see in the next sessions.
Inside those Groovy scripts, you can use all the features and syntax constructs of Groovy
(lists, maps, control structures, loops, create methods, utility classes, etc.)
Note:Don't forget to declare packages for Groovlets when you use Gradle Gaelyk Plugin to build the application. Otherwise they will be placed in wrong destination folders!
Using MarkupBuilder
to render XML or HTML snippets
Groovy's MarkupBuilder
is a utility class that lets you create markup content (HTML / XML) with a Groovy notation,
instead of having to use ugly println
s.
Our previous Groovlet can be written more cleanly as follows:
html.html { body { [1, 2, 3, 4].each { number -> p number } def now = new Date() p now } }
Note: You may want to learn more about MarkupBuilder
in the
Groovy wiki documentation or on this
article from IBM developerWorks.
Delegating to a view template
As we explained in the section about redirects and forwards, at the end of your Groovlet,
you may simply redirect or forward to a template.
This is particularly interesting if we want to properly decouple the logic from the view.
To continue improving our previous Groovlets, we may, for instance, have a Groovlet compute the data needed by a template to render.
We'll need a Groovlet and a template. The Groovlet WEB-INF/groovy/controller.groovy
would be as follows:
request['list'] = [1, 2, 3, 4] request['date'] = new Date() forward 'display.gtpl'
Note: For accessing the request attributes, the following syntaxes are actually equivalent:request.setAttribute('list', [1, 2, 3, 4]) request.setAttribute 'list', [1, 2, 3, 4] request['list'] = [1, 2, 3, 4] request.list = [1, 2, 3, 4]
The Groovlet uses the request attributes as a means to transfer data to the template.
The last line of the Groovlet then forwards the data back to the template view display.gtpl
:
<html> <body> <% request.list.each { number -> %> <p>${number}</p> <% } %> <p>${request.date}</p> </body> </html>
Logging messages
In your Groovlets and Templates, thanks to the log
variable in the binding,
you can log messages through the java.util.logging
infrastructure.
The log
variable is an instance of groovyx.gaelyk.logging.GroovyLogger
and provides the methods:
severe(String)
, warning(String)
, info(String)
, config(String)
,
fine(String)
, finer(String)
, and finest(String)
.
The default loggers in your groovlets and templates follow a naming convention.
The groovlet loggers' name starts with the gaelyk.groovlet
prefix,
whereas the template loggers' name starts with gaelyk.template
.
The name also contains the internal URI of the groovlet and template but transformed:
the slashes are exchanged with dots, and the extension of the file is removed.
Note: The extension is dropped, as one may have configured a different extension name for groovlets and templates than the usual ones (ie..groovy
and.gtpl
).
A few examples to illustrate this:
URI | Logger name |
---|---|
/myTemplate.gtpl |
gaelyk.template.myTemplate |
/crud/scaffolding.gtpl |
gaelyk.template.crud.scaffolding |
/WEB-INF/templates/aTemplate.gtpl |
gaelyk.template.WEB-INF.templates.aTemplate |
/upload.groovy (ie. /WEB-INF/groovy/upload.groovy ) |
gaelyk.groovlet.upload |
/account/credit.groovy (ie. /WEB-INF/groovy/account/credit.groovy ) |
gaelyk.groovlet.account.credit |
This naming convention is particularly interesting as the java.util.logging
infrastructure
follows a hierarchy of loggers depending on their names, using dot delimiters, where
gaelyk.template.crud.scaffolding
inherits from
gaelyk.template.crud
which inherits in turn from
gaelyk.template
, then from
gaelyk
. You get the idea!
For more information on this hierarchy aspect,
please refer to the Java documentation.
Concretely, it means you'll be able to have a fine grained way of defining your loggers hierarchy
and how they should be configured, as a child inherits from its parent configuration,
and a child is able to override parent's configuration.
So in your logging.properties
file, you can have something like:
# Set default log level to INFO .level = INFO # Configure Gaelyk's log level to WARNING, including groovlet's and template's gaelyk.level = WARNING # Configure groovlet's log level to FINE gaelyk.groovlet.level = FINE # Override a specific groovlet familty to FINER gaelyk.groovlet.crud.level = FINER # Set a specific groovlet level to FINEST gaelyk.groovlet.crud.scaffoldingGroovlet.level = FINEST # Set a specific template level to FINE gaelyk.template.crud.editView.level = FINE
You can also use the GroovyLogger
in your Groovy classes:
import groovyx.gaelyk.logging.GroovyLogger // ... def log = new GroovyLogger("myLogger") log.info "This is a logging message with level INFO"
It is possible to access any logger thanks to the logger accessor,
which is available in the binding under the name logger
.
From a Groovlet or a Template, you can do:
// access a logger by its name, as a property access logger.myNamedLogger.info "logging an info message" // when the logger has a complex name (like a package name with dots), prefer the subscript operator: logger['com.foo.Bar'].info "logging an info message"
Additionally, there are two other loggers for tracing the routes filter and plugins handler, with
gaelyk.routesfilter
and gaelyk.pluginshandler
.
The last two log their messages with the CONFIG
level,
so be sure to adapt the logging level in your logging configuration file
if you wish to troubleshoot how routes and plugins are handled.