3 Configuration - Reference Documentation
Authors: Andres Almiray
Version: 1.2.0
Table of Contents
3 Configuration
It may seem odd that in a framework that embraces "convention-over-configuration" that we tackle this topic now, but since what configuration there is typically a one off, it is best to get it out the way.3.1 Basic Configuration
For general configuration Griffon provides a file calledgriffon-app/conf/Config.groovy
. This file uses Groovy's ConfigSlurper which is very similar to Java properties files except it is pure Groovy hence you can re-use variables and use proper Java types!You can add your own configuration in here, for example:
foo.bar.hello = "world"
Then later in your application you can access these settings via the GriffonApplication object, which is available as a variable in mvc members
assert "world" == app.config.foo.bar.hello
3.2 Logging
The Basics
Griffon uses its common configuration mechanism to provide the settings for the underlying Log4j log system, so all you have to do is add alog4j
setting to the file griffon-app/conf/Config.groovy
.So what does this log4j
setting look like? Here's a basic example:log4j = { error 'org.codehaus.griffon' info 'griffon.util', 'griffon.core', 'griffon.swing', 'griffon.app'}
Logging levels
The are several standard logging levels, which are listed here in order of descending priority:- off
- fatal
- error
- warn
- info
- debug
- trace
- all
log.error(msg)
will log a message at the 'error' level. Likewise, log.debug(msg)
will log it at 'debug'. Each of the above levels apart from 'off' and 'all' have a corresponding log method of the same name.The logging system uses that message level combined with the configuration for the logger (see next section) to determine whether the message gets written out. For example, if you have an 'org.example.domain' logger configured like so:warn 'org.example.domain'
Loggers
Loggers are fundamental to the logging system, but they are a source of some confusion. For a start, what are they? Are they shared? How do you configure them?A logger is the object you log messages to, so in the calllog.debug(msg)
, log
is a logger instance (of type Logger). These loggers are uniquely identified by name and if two separate classes use loggers with the same name, those loggers are effectively the same instance.There are two main ways to get hold of a logger:
- use the
log
instance injected into artifacts such as domain classes, controllers and services; - use the Slf4j API directly.
log
property, then the name of the logger is 'griffon.app.<type>.<className>', where type
is the type of the artifact, say 'controller' or 'service, and className
is the fully qualified name of the artifact. For example, let's say you have this service:package org.exampleclass MyService {
…
}
package org.otherimport org.slf4j.Logger import org.slf4j.LoggerFactoryclass MyClass { private static final Logger log = LoggerFactory.getLogger(MyClass) … }
getLog()
method, such as "myLogger", but this is less common because the logging system treats names with dots ('.') in a special way.Configuring loggers
You have already seen how to configure a logger in Griffon:log4j = { error 'org.codehaus.griffon.runtime' }
org.codehaus.griffon.runtime.core.DefaultArtifactManager
class and the org.codehaus.griffon.runtime.util.GriffonApplicationHelper
one.In other words, loggers are effectively hierarchical. This makes configuring them by package much, much simpler than it would otherwise be.The most common things that you will want to capture log output from are your controllers, services, and other artifacts. To do that you'll need to use the convention mentioned earlier: griffon.app.<artifactType>.<className> . In particular the class name must be fully qualifed, i.e. with the package if there is one:log4j = { // Set level for all application artifacts info "griffon.app" // Set for a specific controller debug "griffon.app.controller.YourController" // Set for a specific service class debug "griffon.app.service.org.example.SampleService" // Set for all models info "griffon.app.model" }
model
- For model classescontroller
- For controllersview
- For viewsservice
- For service classes
org.codehaus.griffon.runtime.core
- Core internal information such as MVC group instantiation, etc.griffon.swing
- Swing related initialization and application life cycle.
The Root Logger
All logger objects inherit their configuration from the root logger, so if no explicit configuration is provided for a given logger, then any messages that go to that logger are subject to the rules defined for the root logger. In other words, the root logger provides the default configuration for the logging system.Griffon automatically configures the root logger to only handle messages at 'error' level and above, and all the messages are directed to the console (stdout for those with a C background). You can customise this behaviour by specifying a 'root' section in your logging configuration like so:log4j = { root { info() } … }
log4j = {
appenders {
file name:'file', file:'/var/logs/mylog.log'
}
root {
debug 'stdout', 'file'
}
}
org.apache.log4j.Logger
instance is passed as an argument to the log4j closure. This allows you to work with the logger directly:log4j = { root -> root.level = org.apache.log4j.Level.DEBUG … }
Logger
instance, refer to the Log4j API documentation.Those are the basics of logging pretty well covered and they are sufficient if you're happy to only send log messages to the console. But what if you want to send them to a file? How do you make sure that messages from a particular logger go to a file but not the console? These questions and more will be answered as we look into appenders.Appenders
Loggers are a useful mechanism for filtering messages, but they don't physically write the messages anywhere. That's the job of the appender, of which there are various types. For example, there is the default one that writes messages to the console, another that writes them to a file, and several others. You can even create your own appender implementations!This diagram shows how they fit into the logging pipeline:
log4j = { appenders { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } }
- console (ConsoleAppender) - Logs to the console.
- file (FileAppender) - Logs to a single file.
- rollingFile (RollingFileAppender) - Logs to rolling files, for example a new file each day.
- event (GriffonApplicationEventAppender) - Logs to application events. Event name is "LogEvent"; args are log level (as String), log message and optional throwable.
name
, maxFileSize
and file
properties of the RollingFileAppender
instance.You can have as many appenders as you like - just make sure that they all have unique names. You can even have multiple instances of the same appender type, for example several file appenders that log to different files.If you prefer to create the appender programmatically or if you want to use an appender implementation that's not available via the above syntax, then you can simply declare an appender
entry with an instance of the appender you want:import org.apache.log4j.*log4j = { appenders { appender new RollingFileAppender(name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log") } }
JMSAppender
, SocketAppender
, SMTPAppender
, and more.Once you have declared your extra appenders, you can attach them to specific loggers by passing the name as a key to one of the log level methods from the previous section:error myAppender: "griffon.app.controller.BookController"
error myAppender: "griffon.app.controller.BookController", myFileAppender: ["griffon.app.controller.BookController", "griffon.app.service.BookService"], rollingFile: "griffon.app.controller.BookController"
myFileAppender
) by using a list.Be aware that you can only configure a single level for a logger, so if you tried this code:error myAppender: "griffon.app.controller.BookController" debug myFileAppender: "griffon.app.controller.BookController" fatal rollingFile: "griffon.app.controller.BookController"
log4j = {
appenders {
console name: "stdout", threshold: org.apache.log4j.Level.INFO
}
}
threshold
argument which determines the cut-off for log messages. This argument is available for all appenders, but do note that you currently have to specify a Level
instance - a string such as "info" will not work.Custom Layouts
By default the Log4j DSL assumes that you want to use a PatternLayout. However, there are other layouts available including:xml
- Create an XML log filehtml
- Creates an HTML log filesimple
- A simple textual logpattern
- A Pattern layout
layout
setting:log4j = { appenders { console name: "customAppender", layout: pattern(conversionPattern: "%c{2} %m%n") } }
log4j = { appenders { console name: "stdout", layout: pattern(conversionPattern: "%c{2} %m%n") } }
Environment-specific configuration
Since the logging configuration is insideConfig.groovy
, you can of course put it inside an environment-specific block. However, there is a problem with this approach: you have to provide the full logging configuration each time you define the log4j
setting. In other words, you cannot selectively override parts of the configuration - it's all or nothing.To get round this, the logging DSL provides its own environment blocks that you can put anywhere in the configuration:log4j = { appenders { console name: "stdout", layout: pattern(conversionPattern: "%c{2} %m%n") environments { production { rollingFile name: "myAppender", maxFileSize: 1024, file: "/tmp/logs/myApp.log" } } } root { //… } // other shared config info "griffon.app.controller" environments { production { // Override previous setting for 'griffon.app.controller' error "griffon.app.controller" } } }
root
definition, but you can put the root
definition inside an environment block.Full stacktraces
When exceptions occur, there can be an awful lot of noise in the stacktrace from Java and Groovy internals. Griffon filters these typically irrelevant details and restricts traces to non-core Griffon/Groovy class packages.When this happens, the full trace is always logged to theStackTrace
logger, which by default writes its output to a file called stacktrace.log
. As with other loggers though, you can change its behaviour in the configuration. For example if you prefer full stack traces to go to the console, add this entry:error stdout: "StackTrace"
log4j = { appenders { rollingFile name: "stacktrace", maxFileSize: 1024, file: "/var/tmp/logs/myApp-stacktrace.log" } }
log4j = { appenders { 'null' name: "stacktrace" } }
griffon.full.stacktrace
VM property to true
:griffon -Dgriffon.full.stacktrace=true run-app
Logger inheritance
Earlier, we mentioned that all loggers inherit from the root logger and that loggers are hierarchical based on '.'-separated terms. What this means is that unless you override a parent setting, a logger retains the level and the appenders configured for that parent. So with this configuration:log4j = {
appenders {
file name:'file', file:'/var/logs/mylog.log'
}
root {
debug 'stdout', 'file'
}
}
log4j = { appenders { … } root { … } info additivity: false stdout: ["griffon.app.controller.BookController", "griffon.app.service.BookService"] }
info additivity: false, "griffon.app.controller.BookController", "griffon.app.service.BookService"
3.3 Environments
Per Environment Configuration
Griffon supports the concept of per environment configuration. TheBuildConfig.groovy
file within the griffon-app/conf
directory can take advantage of per environment configuration using the syntax provided by ConfigSlurper . As an example consider the following default packaging definitions provided by Griffon:environments { development { signingkey { params { sigfile = 'GRIFFON' keystore = "${basedir}/griffon-app/conf/keys/devKeystore" alias = 'development' storepass = 'BadStorePassword' keypass = 'BadKeyPassword' lazy = true // only sign when unsigned } } } test { griffon { jars { sign = false pack = false } } } production { signingkey { params { sigfile = 'GRIFFON' keystore = 'CHANGE ME' alias = 'CHANGE ME' lazy = false // sign, regardless of existing signatures } } griffon { jars { sign = true pack = true destDir = "${basedir}/staging" } webstart { codebase = 'CHANGE ME' } } } }griffon { jars { sign = false pack = false destDir = "${basedir}/staging" jarName = "${appName}.jar" } }
environments
block too), the environments
block specifies per environment settings for the jars
property.Packaging and Running for Different Environments
Griffon's command line has built in capabilities to execute any command within the context of a specific environment. The format is:griffon [environment] [command name]
dev
, prod
, and test
for development
, production
and test
. For example to package an application for the development
(avoiding jar signing by default) environment you could do:griffon dev package
griffon.env
variable to any command:griffon -Dgriffon.env=UAT run-app
Programmatic Environment Detection
Within your code, such as in a Gant script or a bootstrap class you can detect the environment using the Environment class:import griffon.util.Environment...switch(Environment.current) { case Environment.DEVELOPMENT: configureForDevelopment() break case Environment.PRODUCTION: configureForProduction() break }
Generic Per Environment Execution
You can use thegriffon.util.Environment
class to execute your own environment specific logic:Environment.executeForCurrentEnvironment { production { // do something in production } development { // do something only in development } }
3.4 Versioning
Versioning Basics
Griffon has built in support for application versioning. When you first create an application with the create-app command the version of the application is set to0.1
. The version is stored in the application meta data file called application.properties
in the root of the project.To change the version of your application you can run the set-version command:griffon set-version 0.2
Detecting Versions at Runtime
You can detect the application version using Griffon' support for application metadata using the app class. For example within controllers there is an implicit app variable that can be used:def version = app.metadata['app.version']
def griffonVersion = app.metadata['app.griffon.version']
3.5 Dependency Resolution
In order to control how JAR dependencies are resolved Griffon features (since version 0.9) a dependency resolution DSL that allows you to control how dependencies for applications and plugins are resolved.Inside thegriffon-app/conf/BuildConfig.groovy
file you can specify a griffon.project.dependency.resolution
property that configures how dependencies are resolved:griffon.project.dependency.resolution = { // config here }
griffon.project.dependency.resolution = { // inherit Griffon' default dependencies inherits("global") { } log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose' repositories { griffonHome() // uncomment the below to enable remote dependency resolution // from public Maven repositories //mavenLocal() //mavenCentral() //mavenRepo "http://snapshots.repository.codehaus.org" //mavenRepo "http://repository.codehaus.org" //mavenRepo "http://download.java.net/maven/2/" //mavenRepo "http://repository.jboss.com/maven2/" } dependencies { // specify dependencies here under either 'build', 'compile', 'runtime' or 'test' scopes eg. // runtime 'mysql:mysql-connector-java:5.1.5' } }
3.5.1 Configurations and Dependencies
Griffon features 5 dependency resolution configurations (or 'scopes') which you can take advantage of:-
build
: Dependencies for the build system only -
compile
: Dependencies for the compile step -
runtime
: Dependencies needed at runtime but not for compilation (see above) -
test
: Dependencies needed for testing but not at runtime (see above)
dependencies
block you can specify a dependency that falls into one of these configurations by calling the equivalent method. For example if your application requires the MySQL driver to function at runtime
you can specify as such:runtime 'com.mysql:mysql-connector-java:5.1.5'
group:name:version
. You can also use a map-based syntax:runtime group:'com.mysql', name:'mysql-connector-java', version:'5.1.5'
runtime 'com.mysql:mysql-connector-java:5.1.5', 'commons-lang:commons-lang:2.6'// Orruntime( [group: 'com.mysql', name: 'mysql-connector-java', version: '5.1.5'], [group: 'commnons-lang', name: 'commons-lang', version: '2.6'] )
runtime 'net.sf.json-lib:json-lib:2.4:jdk15'// Orruntime group: 'net.sf.json-lib' name: 'json-lib', version: '2.4', classifier: 'jdk15'
3.5.2 Dependency Repositories
Remote Repositories
Griffon, when installed, does not use any remote public repositories. There is a defaultgriffonHome()
repository that will locate the JAR files Griffon needs from your Griffon installation. If you want to take advantage of a public repository you need to specify as such inside the repositories
block:repositories { mavenCentral() }
ebr()
method:repositories { ebr() }
repositories {
mavenRepo "http://repository.codehaus.org"
}
Local Resolvers
If you do not wish to use a public Maven repository you can specify a flat file repository:repositories { flatDir name:'myRepo', dirs:'/path/to/repo' }
Custom Resolvers
If all else fails since Griffon builds on Apache Ivy you can specify an Ivy resolver:repositories {
resolver new URLResolver(...)
}
Authentication
If your repository requires some form of authentication you can specify as such using acredentials
block:credentials { realm = ".." host = "localhost" username = "myuser" password = "mypass" }
USER_HOME/.griffon/settings.groovy
file using the griffon.project.ivy.authentication
setting:griffon.project.ivy.authentication = { credentials { realm = ".." host = "localhost" username = "myuser" password = "mypass" } }
3.5.3 Debugging Resolution
If you are having trouble getting a dependency to resolve you can enable more verbose debugging from the underlying engine using thelog
method:// log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
log "warn"
3.5.4 Inherited Dependencies
By default every Griffon application inherits a bunch of framework dependencies. This is done through the line:inherits "global"
BuildConfig.groovy
file. If you wish exclude certain inherited dependencies then you can do so using the excludes
method:inherits("global") { excludes "oscache", "ehcache" }
3.5.5 Dependency Reports
As mentioned in the previous section a Griffon application consists of dependencies inherited from the framework, the plugins installed and the application dependencies itself.To obtain a report of an application's dependencies you can run the dependency-report command:griffon dependency-report
target/dependency-report
directory by default. You can specify which configuration (scope) you want a report for by passing an argument containing the configuration name:griffon dependency-report runtime
3.5.6 Plugin JAR Dependencies
The way in which you specify dependencies for a plugin is identical to how you specify dependencies in an application. When a plugin is installed into an application the application automatically inherits the dependencies of the plugin.If you want to define a dependency that is resolved for use with the plugin but not exported to the application then you can set theexported
property of the dependency:compile('org.hibernate:hibernate-core:3.3.1.GA') {
exported = false
}
hibernate-core
dependency will be available only to the plugin and not resolved as an application dependency.
3.6 Project Documentation
Since Griffon 0.9, the documentation engine that powers the creation of this documentation is available to your Griffon projects.The documentation engine uses a variation on the Textile syntax to automatically create project documentation with smart linking, formatting etc.Creating project documentation
To use the engine you need to follow a few conventions. Firstly you need to create asrc/docs/guide
directory and then have numbered text files using the gdoc
format. For example:+ src/docs/guide/1. Introduction.gdoc + src/docs/guide/2. Getting Started.gdoc
Creating reference items
Reference items appear in the left menu on the documentation and are useful for quick reference documentation. Each reference item belongs to a category and a category is a directory located in thesrc/docs/ref
directory. For example say you defined a new method called renderPDF
, that belongs to a category called Controllers
this can be done by creating a gdoc text file at the following location:+ src/ref/Controllers/renderPDF.gdoc
Configuring Output Properties
There are various properties you can set within yourgriffon-app/conf/BuildConfig.groovy
file that customize the output of the documentation such as:
- griffon.doc.authors - The authors of the documentation
- griffon.doc.license - The license of the software
- griffon.doc.copyright - The copyright message to display
- griffon.doc.footer - The footer to use
Generating Documentation
Once you have created some documentation (refer to the syntax guide in the next chapter) you can generate an HTML version of the documentation using the command:griffon doc
docs/manual/index.html
which can be opened to view your documentation.
Documentation Syntax
As mentioned the syntax is largely similar to Textile or Confluence style wiki markup. The following sections walk you through the syntax basics.Basic Formatting
Monospace:monospace
@monospace@
_italic_
*bold*

!http://dist.codehaus.org/griffon/media/griffon.png!
Linking
There are several ways to create links with the documentation generator. A basic external link can either be defined using confluence or textile style markup:[Griffon|http://griffon-framework.org/] or "Griffon":http://griffon-framework.org/
guide:
prefix:[Intro|guide:1. Introduction]
griffon-app/conf/BuildConfig.groovy
:griffon.doc.alias.intro="1. Introduction"
[Intro|guide:intro]
[controllers|renderPDF]
api:
prefix. For example:[String|api:java.lang.String]
griffon-app/conf/BuildConfig.groovy
. For example:griffon.doc.api.org.hibernate="http://docs.jboss.org/hibernate/stable/core/api"
org.hibernate
package to link to the Hibernate website's API docs.Lists and Headings
Headings can be created by specifying the letter 'h' followed by a number and then a dot:h3.<space>Heading3 h4.<space>Heading4
* item 1 ** subitem 1 ** subitem 2 * item 2
# item 1
table
macro:Name | Number |
---|---|
Albert | 46 |
Wilma | 1348 |
James | 12 |
{table}
*Name* | *Number*
Albert | 46
Wilma | 1348
James | 12
{table}
Code and Notes
You can define code blocks with thecode
macro:class Book {
String title
}
{code}
class Book {
String title
}
{code}
<hello>world</hello>
{code:xml} <hello>world</hello> {code}
This is a note!
{note} This is a note! {note}
This is a warning!
{warning} This is a warning! {warning}
3.7 Compiler
The following sections explain settings that affect how the compiler behavesMost of these settings may specified in eithergriffon-app/conf/BuildConfig.groovy
(local to project) or $USER_HOME/.griffon/settings.groovy
(global to all projects), with the caveat that values specified at the command prompt will have precedence over those specified config files.
3.7.1 Source Encoding
The assumed source encoding isUTF-8
. You can change this setting by specifying the following flaggriffon.source.encoding
griffon -Dgriffon.source.encoding=='ISO-8859-1' compile
3.7.2 Source and Target Levels
Java source and target levels are chosen by the compiler to whatever values it picks by default (this is akin to specifyingsource
and target
in Ant's javac
task). You can instruct the compiler to use a specific value by defining the following flagsgriffon.project.source.level griffon.project.target.level
griffon -Dgriffon.project.source.level=1.6 -Dgriffon.project.target.level=1.6 compile
3.7.3 Debug Information
Indicates whether Java sources should be compiled with debug information; default isyes
. You can change this setting by specifying the following flaggriffon.project.compiler.debug
3.7.4 Additional Sources
There may be times where additional sources should be compiled along with application sources, for example when a custom patch is needed when targetting a particular platform or applying a security fix that should not be included in the application's versioned sources.Additional sources may be defined inBuildConfig.groovy
in the following waygriffon.compiler.additional.sources = [ 'path/to/custom.patch.directory', 'path/to/security.fix.directory' ]
3.7.5 Additional Resources
Sometimes you' want additional resources to be included with the application but their inclusion is conditional, for example when packaging a generic applicaiton that contains customization for clients A and B you'd want an application per client that only includes the client's respective logos and brand recognition resources. Another example would be protected resources that should not be included in the application's sources under SCM.Additional resources may be defined inBuildConfig.groovy
in the following waygriffon.compiler.additional.resources = [
'path/to/protected/resources'
]