|
|
|
Event processing
The following sections of this article provide information about event processing architecture within the Zero platform:
Overview
Events in the Zero platform are ways to orchestrate behavior in the form of loosely-coupled event handlers. Zero "fires" a fixed set of events for HTTP request processing and stages of the application lifecycle; developers may add other event types.
"Firing" an event, which equates to an API invocation, causes Zero to invoke the associated handlers through an EventDispatcher. The set of associated handlers is determined by two mechanisms:
- Explicit registration: Evaluate registration rules
- Implicit registration: Identify scripts as handlers through convention
For example, consider a request for GET /foo/Hello.groovy. An explicitly registered handler might have an entry in a configuration file as follows:
/config/handlers += [{
"events" : "GET",
"handler" : "acme.handler.GetHandler.class",
"conditions" : "/request/path =~ /foo(/.*)?"
}]
By convention, the file foo/Hello.groovy in the public virtualized directory is also associated with the request.
Handlers generally implement methods named to match the corresponding event (e.g. onGET() to handle the GET event). Handlers may be implemented in various languages, including Java, Groovy and PHP.
Finally, EventDispatchers govern the behavior of the core event processing engine, including early termination criteria and error handling. For example, the Dispatcher for the GET event allows no more than one handler to be invoked.
Advanced Implicit registration is performed by resolveHandlers handlers, which are are themselves explicitly registered.
Advanced Event handlers are invoked via registered Interpreters. By default, Interpreters are registered for Java classes (.class), Groovy scripts (.groovy), and Groovy templates (.gt).
Application lifecycle events
Developers may hook into key lifecycle events to implement advanced functionality. The following table summarizes these lifecycle events, as well as the behavior of the associated EventDispatchers:
Request-processing events
The following table summarizes the events, in order, fired by Zero during HTTP request processing:
Error status codes returned for Exceptions on the GET, PUT, POST, and DELETE events depend on the Exception type:
- FileNotFoundException -- reported as status code 404
- NoSuchMethodException -- reported as status code 405
- RuntimeException -- catch-all for all other exceptions; reported as status code 500
Note that all log and requestEnd handlers will run, regardless of Exceptions encountered during the previous events.
Advanced Handler exceptions are caught and categorized by the associated Interpreter.
resolveHandlers event
The resolveHandlers event enables dynamic registration of handlers. This special event may be fired as part of the process of identifying handlers for events.
For example, file-serving functionality in Zero includes serving static files and executing scripts. Static-file serving is handled by the zero.core.fileserver.FileServer handler; script execution is handled by the corresponding Interpreter (e.g. zero.core.interpreter.GroovyInterpreter for .groovy files). Rather than register all combinations of file types/locations and handlers in a configuration file, one resolveHandlers handler (zero.core.fileserver.FileServerResolver) provides the appropriate handler based upon the request.
There are two special values placed into the Global Context for resolveHandlers:
-
/event/resolvingEvent: Contains the name of the underlying event.
-
/event/resolvedHandlers: Contains a List<handlerData> of handlers associated with the event.
resolveHandlers handlers are generally registered as resolvers for specific events using /event/resolvingEvent, such as:
/config/handlers += [{
"events" : "resolveHandlers",
"handler" : "zero.core.fileserver.FileServerResolver.class",
"conditions" : "/event/resolvingEvent =~ (GET|POST|DELETE|PUT)"
}]
For example, the FileServerResolver manipulates the list of associated handlers as follows:
public void onResolveHandlers() {
// path is path from the request
String path = zget(Request.path);
HandlerData handlerData = getResolveHandler("public", path, true);
// ...
zpost("/event/resolvedHandlers", handlerData);
}
public static HandlerData getResolveHandler(String appSubdir, String path, boolean serveDirectories) {
// First match wins; search order: app, then dependencies in ivy.xml
List<String> listOfDependencies = zget("/config/dependencies");
for (int i = 0; i < listOfDependencies.size(); i++) {
String dependency = listOfDependencies.get(i);
String dependencyRoot = zget("/config/dependencies/" + dependency);
HandlerInfo info = getResolveHandler(dependencyRoot, appSubdir,
path, serveDirectories);
if (info != null) {
HandlerData handlerData = new HandlerData(info, null);
return handlerData;
}
}
return null;
}
|