(Quick Reference)

5.5 Application Events - Reference Documentation

Authors: Andres Almiray

Version: 1.2.0

5.5 Application Events

Applications have the ability to publish events from time to time to communicate that something of interest has happened at runtime. Events will be triggered by the application during each of its life cycle phases, also when MVC groups are created and destroyed.

All application event handlers are guaranteed to be called in the same thread that originated the event.

Any artifact or class can trigger an application event, by routing it through the reference to the current running application instance. All artifacts posses an instance variable that points to that reference. All other classes can use ApplicationHolder to gain access to the current application's instance.

Publishing an event can be done synchronously on the current thread or asynchronously relative to the UI thread. For example, the following snippet will trigger an event that will be handled in the same thread, which could be the UI thread itself

app.event('MyEventName', ['arg0', 'arg1'])

Whereas the following snippet guarantees that all event handlers that are interested in an event of type MyEventName will be called outside of the UI thread

app.eventOutsideUI('MyEventName', ['arg0', 'arg1'])

Finally, if you'd want event notification to be handed in a thread that is not the current one (regardless if the current one is the UI thread or not) then use the following method

app.eventAsync('MyEventName', ['arg0', 'arg1'])

There may be times when event publishing must be stopped for a while. If that's the case then you can instruct the application to stop delivering events by invoking the following code

app.eventPublishingEnabled = false

Any events sent through the application's event bus will be discarded after that call; there's no way to get them back or replay them. When it's time to enable the event bus again simply call

app.eventPublishingEnabled = true

5.5.1 Life Cycle Events

The following events will be triggered by the application during each one of its phases
  • BootstrapStart[app] - after logging configuration has been setup, during the Initialize phase.
  • BootstrapEnd[app] - at the end of the Initialize phase.
  • StartupStart[app] - at the beginning of the Startup phase.
  • StartupEnd[app] - at the end of the Startup phase.
  • ReadyStart[app] - at the beginning of the Startup phase.
  • ReadyEnd[app] - at the end of the Startup phase.
  • ShutdownRequested[app] - before the Shutdown begins.
  • ShutdownAborted[app] - if a Shutdown Handler prevented the application from entering the Shutdown phase.
  • ShutdownStart[app] - at the beginning of the Shutdown phase.

5.5.2 Artifact Events

The following events will be triggered by the application when dealing with artifacts
  • NewInstance[klass, type, instance] - when a new artifact is created.
  • DestroyInstance[klass, type, instance] - when an artifact instance is destroyed.
  • LoadAddonsStart[app] - before any addons are initialized, during the Initialize phase.
  • LoadAddonsEnd[app, addons] - after all addons have been initialized, during the Initialize phase. addons is a Map of <name, instance> pairs.
  • LoadAddonStart[name, addon, app] - before an addon is initialized, during the Initialize phase.
  • LoadAddonEnd[name, addon, app] - after an addon has been initialized, during the Initialize phase.

These events will be triggered when dealing with MVC groups

  • InitializeMVCGroup[configuration, group] - when a new MVC group is initialized. configuration is of type MVCGroupConfiguration; group is of type MVCGroup.
  • CreateMVCGroup[group] - when a new MVC group is created. configuration is of type MVCGroupConfiguration; group is of type MVCGroup.
  • DestroyMVCGroup[group] - when an MVC group is destroyed. group is of type MVCGroup.

5.5.3 Miscellaneous Events

These events will be triggered when a specific condition is reached
  • UncaughtExceptionThrown[exception] - when an uncaught exception bubbles up to GriffonExceptionHandler.
  • WindowShown[window] - triggered by the WindowManager when a Window is shown.
  • WindowHidden[window] - triggered by the WindowManager when a Window is hidden.

5.5.4 Custom Events

Any artifact that holds a reference to the current application may trigger events at its leisure by calling the event() or eventAsync methods on the application instance. The following example demonstrates how a Controller triggers a "Done" event after an action has finished

class MyController {
    def action = { evt = null ->
        // do some work
        app.event('Done')
    }
}

There are two versions of the event() method. The first takes just the name of the event to be published; the second accepts an additional argument which should be a List of parameters to be sent to every event handler. Event handlers notified by this method are guaranteed to process the event in the same thread that published it. However, if what you need is to post a new event and return immediately then use the eventAsync variants. If you want the event to be handled outside of the UI thread then use the eventOutsideUI() variants.

5.5.5 Event Handlers

Any artifact or class that abides to the following conventions can be registered as an application listener, those conventions are:
  • it is a Script, class, Map, RunnableWithArgs or closure.
  • in the case of scripts or classes, they must define an event handler whose name matches on<EventName>, this handler can be a method, RunnableWithArgs or a closure property.
  • in the case of a Map, each key maps to <EventName>, the value must be a RunnableWithArgs or a closure.
  • scripts, classes and maps can be registered/unregistered by calling addApplicationListener/ removeApplicationListener on the app instance.
  • RunnableWithArgs and closure event handlers must be registered with an overloaded version of addApplicationListener/removeApplicationListener that takes <EventName> as the first parameter, and the runnable/closure itself as the second parameter.

There is a general, per application, script that can provide event handlers. If you want to take advantage of this feature you must define a script named Events.groovy inside griffon-app/conf. Lastly both Controller and Service instances are automatically registered as application event listeners. This is the only way to declare event listeners for the BootstrapStart event.

You can also write a class named Events.java in src/main as an alternative to griffon-app/conf/Events.groovy, but not both!

These are some examples of event handlers:

  • Display a message right before default MVC groups are instantiated

File: griffon-app/conf/Events.groovy

onBootstrapEnd = { app ->
  println """Application configuration has finished loading.
MVC Groups will be initialized now."""
}
  • Quit the application when an uncaught exception is thrown

File: src/main/Events.java

import griffon.util.ApplicationHolder;

public class Events { public void onUncaughtExceptionThrown(Exception e) { ApplicationHolder.getApplication().shutdown(); } }

  • Print the name of the application plus a short message when the application is about to shut down.

File: griffon-app/controller/MyController.groovy

class MyController {
  def onShutdownStart = { app ->
    println "${app.config.application.title} is shutting down"
  }
}
  • Print a message every time the event "Foo" is published

File: griffon-app/controller/MyController.groovy

class MyController {
  void mvcGroupInit(Map args) {
    app.addApplicationListener([
      Foo: {-> println 'got foo!' }
    ])
  }

def fooAction = { evt = null -> // do something app.event('Foo') } }

  • An alternative to the previous example using a closure event handler

File: griffon-app/controller/MyController.groovy

class MyController {
  void mvcGroupInit(Map args) {
    app.addApplicationListener('Foo'){-> println 'got foo!' }
  }

def fooAction = { evt = null -> // do something app.event('Foo') } }

  • Second alternative to the previous example using a RunnableWithArgs event handler

File: griffon-app/controller/MyController.java

import java.util.Map;
import griffon.util.RunnableWithArgs;
import org.codehaus.griffon.runtime.core.AbstractGriffonController;

public class MyController extends AbstractGriffonController { public void mvcGroupInit(Map<String, Object> params) { getApp().addApplicationListener("Foo", new RunnableWithArgs() { public void run(Object[] args) { System.out.println("got foo!"); } }); }

public void fooAction() { // do something getApp().event("Foo"); } }

5.5.6 Custom Event Publishers

As the name implies application events are sent system wide. However there is an option to create localized event publishers. Griffon provides a @griffon.transform.EventPublisher AST transformation that you can apply to any class that wishes to be an event publisher.

This AST transformation will inject the following methods to the annotated classes:

  • addEventListener(Object)
  • addEventListener(String, Closure)
  • addEventListener(String, RunnableWithArgs)
  • removeEventListener(Object)
  • removeEventListener(String, Closure)
  • removeEventListener(String, RunnableWithArgs)
  • publishEvent(String)
  • publishEvent(String,List)
  • publishEventOutsideUI(String)
  • publishEventOutsideUI(String,List)
  • publishEventAsync(String)
  • publishEventAsync(String,List)
  • isEventPublishingEnabled()
  • setEventPublishingEnabled(boolean)

Event listeners registered with these classes should follow the same rules as application event handlers (they can be Scripts, classes, maps or closures, and so on).

The following example shows a trivial usage of this feature

@griffon.transform.EventPublisher
class Publisher {
   void doit(String name) {
      publishEvent('arg', [name])
   }

void doit() { publishEvent('empty') } }

class Consumer { String value

void onArg(String arg) { value = 'arg = ' + arg } void onEmpty() { value = 'empty' } }

p = new Publisher() c = new Consumer() p.addEventListener(c) assert !c.value p.doit() assert c.value == 'empty' p.doit('Groovy') assert c.value == 'arg = Groovy'