GC Enhancements for M3
GC enhancements for M3 are being coordinated under
bug 1881. The changes are in trunk.
Objectives
- Provide a common GC API across programming languages (Java, Groovy, PHP). We're close already. By renaming methods in a few places, we'll be the same.
- Clarify the use of the
GCAccessor, which is the Groovy shortcut for GC access. Use of the GCAccessor is optional.
- Improve usability of the GC APIs. For example:
- Simpler
get(): GlobalContext.zget(uri) will return null if the uri is not found (rather than throwing an IllegalArgumentException).
- Simpler "walking":
GlobalContext.zlist(uri) will return a list of child URIs. Thus, zlist() will be useful for "walking" through the GC.
Summary of changes
Revised GC APIs
The GC APIs now have a "z" prefix as a simple means of reducing namespace collisions.
The following sections describe the new syntax and behavior changes in the GC API. We'll use examples based on the following GC contents:
/config/foo/a = b
/config/foo/x = y
/config/map = ["a" : "b", "x" : "y"]
| Syntax | Comments/examples |
T zget(String uri [, T defaultValue]) | zget("/config/foo/a") == "b" zget("/config/foo") == null (M2: threw an exception) |
List zlist(String uriPrefix [, boolean includePrefix]) | Returns a list of children of uriPrefix (M2: returned all uris that started with uriPrefix).
zlist("/") == ["/config"] zlist("/config/foo") == ["/config/foo/a", "/config/foo/b"] zlist("/config", false) == ["foo", "map"] |
boolean zcontains(String uri) | Was containsKey(uri) in M2 Handles value path (M2: no value path) =zcontains("/config") == true zcontains("/config/map#a") == true |
T zpost(String uri, V value) | Same as M2. |
T zput(String uri, V value) | Same as M2. |
Map zputs(String uriPrefix, Map payload) | Was available in Groovy for M2. map.put("w", "z") zputs("/config/bar", map) zget("/config/bar/w") == "z" |
void zdelete(String uri [, boolean deleteChildren]) | Same as M2. |
String zdump(String uri) | Same as M2. |
Java: Using static imports
Java code can be a clean demonstration of the GC APIs by using static imports, as follows:
Java example
import static zero.core.context.GlobalContext.*;
public class MyHandler {
public void onGET() {
String qvalue = zget("/request/params/qname");
}
}
Groovy: Added method bindings for GC APIs
All .groovy scripts, as well as
classes that implement ZeroObject , receive bindings for the GC APIs. So, there's no need to import
GlobalContext.
Revised exception conditions
In M2,
GlobalContext.get(uri) threw an
IllegalArgumentException if
uri was not found. Most of the other APIs were more friendly, returning default/empty values, even if
uri was ill-formed.
In M3,
zget(uri) returns
null if
uri is not found. This is equivalent to
zget(uri, null).
Another significant change in M3 is that all of the GC APIs throw an
IllegalArgumentException if the
uri is ill-formed. A valid GC
uri has the following characterics:
Groovy: Clarified GCAccessor
GCAccessor is the support behind the GPath-like interactions with the GC. An example from M2:
def id = request.params.userId.get()
request.status = 202
This approach was rather confusing because we tried to use the GCAccessor to represent data, but it didn't always work that way (note the occasional need for
.get()). Also, there were other use cases where we wanted to extract the GC URI or an accessor object from the data (ref. [[http://www.projectzero.org/wiki/bin/view/Community/JasonsBlog/BlogEntry5][.toPath() and .toGPath()]), which stretched the intuition.
For M3,
GCAccessor has been reworked to be just that: An accessor object. It's been integrated into the Groovy syntax, as summarized here:
| GC API | Groovy shortcut |
zget(uri) | Always ends with brackets; empty brackets are valid. def m = request.method[] def id = request.params.userId[2] |
zget(uri, defaultValue) | def id = request.params.userId.zget('anonymous') |
zlist(uriPrefix) | request.headers.in.zlist() request.headers.in.zlist(true) |
zcontains(uri) | request.headers.in.zcontains('X-METHOD-OVERRIDE') |
zpost(uri) | request.headers.out.MyHeader << ['MyValue'] |
zput(uri) | request.method = 204 |
zputs(uri, payload) | request.headers.out.zputs([MyHeader : 'MyValue']) |
zdelete(uri) | user.counter.zdelete() |
zdump(uri) | request.zdump() |
The following property bindings are
GCAccessor objects available by default to all .groovy scripts and class implementations of
ZeroObject:
- app
- config
- event
- headers
- request
- user
Additional bindings may be registered using the same
method as for M2.
A more complete set of examples are
available in the
zero.core.tests unit test cases.
Groovy: Removed _gc binding
Use method bindings (e.g.
zget('/request/method')) or
GCAccessor bindings (e.g.
request.method[]) instead.
Zone handlers
Changes for
ZoneHandler implementations:
- No longer implement
containsKey(); this is now handled in the GlobalContext implementation.
- Must implement
isAvailable(). Currently used to control access; may be removed early next week. Depends on whether the zone handlers can be self-protecting on the normal ZoneHandler APIs without inadvertent auto-activation.
Changes for
ThreadLocalZoneHandler implementations:
- That interface has been removed; replaced with
ActivatableZoneHandler interface.
Porting notes
- Search .groovy and .gt files for instances of
.get(); replace with [] for GC accessors.
- Search .groovy and .gt files for
.condGet(uri, null); replace with [].
While debugging, if you find that a variable comes through with what looks like a path instead of a value (e.g. /request/status instead of 200), then you're probably missing a
[] on the property binding. That is, you probably have
def x = request.status where you should have
def x = request.status[].