A key aspect that you must always keep in mind is proper threading. Often times controller actions will be bound in response to an event driven by the UI. Those actions will usually be invoked in the same thread that triggered the event, which would be the UI thread. When that happens you must make sure that the executed code is short and that it quickly returns control to the UI thread. Failure to do so may result in unresponsive applications.The following example is the typical use case that must be avoidedclass BadController {
def badAction = {
def sql = Sql.newInstance(
app.config.datasource.url,
model.username,
model.password,
app.config.datasource.driver
)
model.products.clear()
sql.eachRow("select * from products") { product ->
model.products << [product.id, product.name, product.price]
}
sql.close()
}
}
There are two problems here. First the database connection is established inside the UI thread (which takes precious milliseconds or even longer), then a table (which could be arbitrarily large) is queried and each result sent to a List belonging to the model. Assuming that the list is bound to a Table Model then the UI will be updated constantly by each added row; which happens to be done all inside the UI thread. The application will certainly behave slow and sluggish, and to top it off the user won't be able to click on another button or select a menu item until this actions has been processed entirely.Chapter 9 will discuss with further detail the options that you have at your disposal to make use of proper threading constructs. Here's a quick fix for the previous controller
class GoodController {
def goodAction = {
execOutsideUI {
def sql = null
try {
sql = Sql.newInstance(
app.config.datasource.url,
model.username,
model.password,
app.config.datasource.driver
)
List results = []
sql.eachRow("select * from products") { product ->
results << [product.id, product.name, product.price]
}
execInsideUIAsync {
model.products.clear()
model.addAll(results)
}
} finally {
sql?.close()
}
}
}
}
However starting with Griffon 0.9.2 you're no longer required to surround the action code with execOutsideUI
as the compiler will do it for you. This feature breaks backward compatibility with previous releases so it's possible to disable it altogether. Please refer to section 4.7.5 Disable Threading Injection. This feature can be partially enabled/disabled too. You can specify with absolute precision which actions should have this feature enabled or disabled, by adding the following settings to griffon-app/conf/BuildConfig.groovy
compiler {
threading {
sample {
SampleController {
action1 = false
action2 = true
}
FooController = false
}
bar = false
}
}
The compiler will evaluate these settings as follows:
- the action identified by
sample.SampleController.action1
will not have automatic threading injected into its code, while sample.SampleController.action2
(and any other found in the same controller) will have it.
- all actions belonging to
sample.FooController
will not have automatic threading injected.
- all actions belonging to all controllers in the
bar
package will not have threading injected.
Automatic threading injection only works for Groovy based controllers. You must add appropriate threading code to controller actions that are written in languages other than Groovy.