Global Context Enhancements

The current GC is based upon a hierarchical data store, meaning a request with a GC key requires a "walk" through a hierarchy of SubContexts down to the specified leaf node (value).

For M2, we'll move to a "flat GC", meaning no more hierarchical storage. Rather, a GC zone can be thought of as a Map. This change simplifies the concepts and improves performance.

Porting notes

  • GC support in trunk is functionally complete. Problems should be reported as bugs.
    • The /user zone uses in-memory persistence for now.

  • All GC URIs must start with a slash (/request/method is valid; request/method is not).

  • GC URIs have changed. *The old GC supported transparent value pathing, meaning /app/foo/bar might refer to any of the following:
      • A value stored at /app/foo/bar
      • The value at bar in a Map stored at /app/foo
    • The new GC is explicit about value pathing. /app/foo/bar refers to a valued stored at that key. /app/foo#bar refers to the value at bar in a Map stored at /app/foo.

  • Config files
    • Syntax is unchanged.
    • "config" data is now stored in the /config zone. For example, [/app/handlers] should be changed to [/config/handlers].
    • Maps are now flattened when placed into the new GC. For example, the following used to result in a Map stored at /app/foo:
[/app/foo]
bar = x

Now, that results in the value x stored at /app/foo/bar.

  • No more SubContext. Check out list() as an alternative. For example, replace the following based on the old GC:
      SubContext headers = GlobalContext.condGet(Request.Headers.out, null);
      if (headers != null) {

         for (String header : headers.keys()) {
            Object header_value = GlobalContext.get(headers, header);

with

      List<String> headerPaths = GlobalContext.list(Request.Headers.out);
      for (String headerPath : headerPaths) {
         String header_name = headerPath.substring(headerPath.lastIndexOf('/'));
         Object header_value = GlobalContext.get(headerPath);

  • GC contents can be viewed with String GlobalContext.dump(String uri).
    • The GlobalContext viewer (Eclipse) doesn't yet work with the new GC. In the meantime, you can setup an "expression" in the debugger to invoke the dump() API.

Key elements

The key elements include the GC APIs and extensible zone and type handlers. The GC APIs delegate storage to the appropriate zone handler; value manipulation is delegated to the appropriate type handler.

GC path syntax

GC path syntax: /<zone>/<key>

GC APIs

GlobalContext APIs Description
V get(String path) Returns the value stored at path (null is a valid value), if found; throws an IllegalArgumentException otherwise.
V condGet(String path, V default) Returns the value stored at path (null is a valid value), if found; returns default otherwise.
boolean containsKey(String path) Returns true if the path is found; false otherwise.
List<String> list(String prefix) Returns a list of defined paths that start with the specified pathPrefix.
List<String> list(String prefix, boolean includePrefix) Returns a list of paths that start with the specified pathPrefix. If includePrefix == false, then the paths are relative to the prefix (e.g. list("/request", true) would return paths like /request/foo/bar; list("/request", false) would return foo/bar ).
T put(String path, V o) Replaces a value, if present; else creates an entry. Returns previous value associated with the path, or null if the path was not found. Throws an IllegalArgumentException if no zone handler is found.
T post(String path, V o) Appends o to the value, if present; else creates an entry. Returns previous value associated with the path, or null if the path was not found. Support for "append" is a function of the zone handler. Throws an IllegalArgumentException if no zone handler is found.
void delete(String path) Removes the entry. Equivalent to delete(path, false).
void delete(String path, boolean deleteChildren) If deleteChildren == true, then removes all entries whose path starts with path; otherwise, removes just the entry at path.
String dump(String uri) Returns a formatted String listing the GC content at and below the specified uri. Throws an IllegalArgumentException if no zone handler is found.

Value manipulation

"Value manipulation" refers to the ability to specify sub-elements within a value by appending a fragment identifier to the path. Details about the fragment (syntax and meaning) are dependent on the type handler associated with the value.

In general, put and post support emulates that of the corresponding HTTP methods:

  • put() means create (if not defined) or replace (otherwise)
  • post() means create (if not defined) or append (otherwise)

Maps

Java API Groovy API Description Can create entry
GlobalContext.put("/app/myMap", map) app.myMap = map Create/replace existing map DONE
GlobalContext.put("/app/myMap#foo", "bar") app.myMap['foo'] = 'bar' Add/replace entry; map must exist  
       
GlobalContext.post("/app/myMap", map) app.myMap << map Create/merge into existing map; replaces existing keys DONE
       
GlobalContext.get("/app/myMap") x = app.myMap.get() Returns full map  
GlobalContext.get("/app/myMap#foo") x = app.myMap['foo'] Returns entry at key foo  
       
GlobalContext.delete("/app/myMap") _gc.delete('/app/myMap') Deletes map  
GlobalContext.delete("/app/myMap#foo") _gc.delete('/app/myMap#foo') Removes entry  

Note that GlobalContext.post("/app/myMap#foo", "bar") is not valid.

Fragments supported by the Map type handler:

Fragment Description
#<key> Refers to the value mapped to <key>.

Lists

Java API Groovy API Description Can create entry
GlobalContext.put("/app/myList", list) app.myList = list Create/replace existing list DONE
GlobalContext.put("/app/myList#0", "bar") app.myList[0] Set/replace list element; list must exist  
       
GlobalContext.post("/app/myList", list) app.myList << list Create/append to list DONE
GlobalContext.post("/app/myList", "bar") app.myList << 'bar' Append to list; list must exist  
       
GlobalContext.get("/app/myList") x = app.myList.get() Returns full list  
GlobalContext.get("/app/myList#0") x = app.myList[0] Returns 0th element  
       
GlobalContext.delete("/app/myList") _gc.delete("/app/myList") Deletes list  
GlobalContext.delete("/app/myList#0") _gc.delete("/app/myList[0]") Removes first entry  

Fragments supported by the List type handler:

Fragment Description
#<N> Refers to the N-th element in the list.

FirstElementList

FirstElementList is a variant of List that supports access to the first element as a simple object (without needing to specify the index). This support was added as a means to simplify handling of values like query parameters, which are properly stored as lists but are most commonly retrieved as single values.

Within the default zones, only /request/params/<parmName> and /request/headers/in/<headerName> are instances of FirstElementList.

Java API Groovy API Description Can create entry
GlobalContext.put("/app/myList", list) app.myList = list Create list; list does not exist DONE
GlobalContext.put("/app/myList#*", list) app.myList['*'] = list Replace list; list must exist DONE
GlobalContext.put("/app/myList", "bar") app.myList = "bar" Set/replace first element; list must exist  
GlobalContext.put("/app/myList#0", "bar") app.myList[0] Set/replace list element; list must exist  
       
GlobalContext.post("/app/myList", list) app.myList << list Create list; list does not exist DONE
GlobalContext.post("/app/myList#*", "bar") app.myList['*'] << "bar" Append to list; list must exist  
       
GlobalContext.get("/app/myList") x = app.myList.get() Returns first element  
GlobalContext.get("/app/myList#0") x = app.myList[0] Returns first element  
GlobalContext.get("/app/myList#*") x = app.myList['*'] Returns full list  
       
GlobalContext.delete("/app/myList#*") _gc.delete("/app/myList#*") Deletes list  
GlobalContext.delete("/app/myList#0") _gc.delete("/app/myList[0]") Removes first entry  

Fragments supported by the List type handler:

Fragment Description
#<N> Refers to the N-th element in the list.

Samples

app.aList = new FirstElementArrayList<String>()
assert app.aList == null
// app.aList[0] would throw an IllegalArgumentException

Objects (default)

Java API Groovy API Description Can create entry
GlobalContext.put("/app/myObj", o) app.myObj = o Create/replace object DONE
       
GlobalContext.get("/app/myObj") x = app.myObj.get() Returns object  
       
GlobalContext.delete("/app/myObj") _gc.delete('/app/myObj') Deletes object  

post is not a valid operation on Objects.

Nested

Value manipulation may be nested for get() and condGet(); all other operations support up to one level of value manipulation. Parameters for value manipulation are delimited by a slash (/).

For example:

Map<String, String> innerMap0 = new HashMap<String, String>();
innerMap0.put("im0a", "a");
innerMap0.put("im0b", "b");
      
Map<String, String> innerMap1 = new HashMap<String, String>();
innerMap1.put("im1c", "c");
innerMap1.put("im1d", "d");

List<Map<String, String>> list = new ArrayList<Map<String, String>>();
list.add(innerMap0);
list.add(innerMap1);
      
Map<String, List<Map<String, String>>> map
    = new HashMap<String, List<Map<String, String>>>();
map.put("x", list);
      
GlobalContext.put("/config/nested", map);

      
assertEquals("b", GlobalContext.get("/config/nested#x/0/im0b"));
assertEquals("c", GlobalContext.get("/config/nested#x/1/im1c"));

Other type handlers (pending)

XML

// document represents:
// <customers>
//   <customer><name>Abe</name></customer>
//   <customer><name>Beth</name></customer>
// </customers>
GlobalContext.put("/request/myXml", document);

assert GlobalContext.get("/request/myXml#/customers/customer[0]/name/text()").equals("Abe");

Fragments supported by the XML type handler:

Fragment Description
#<xpath> XPath expression.

JavaBean

Customer customer = new Customer();
customer.setName("Cathy");
GlobalContext.put("/request/myBean", customer);

assert GlobalContext.get("/request/myBean#name").equals("Cathy");

Fragments supported by the JavaBean type handler:

Fragment Description
#<propertyName> Property name. Handled by getter/setter or field. Supports get() and put().

Zone handler interface

Zone handlers are responsible for basic storage operations. Note that post() implies an append operation, which is a function of the type handler -- not the zone handler.

public interface ZoneHandler {
   public String getName();


   public <T> T get(String path);
   
   public boolean containsKey(String path);
   
   public List<String> list(String prefix);

   public <T,V> T put(String path, V value);
   
   public void delete(String path, boolean deleteChildren);
}

Zone handlers are registered by putting the fully-qualified class name in the GC at /config/zoneHandlers/<zoneName>.

Type handler interface

Type handlers are responsible for value manipulation. In order to maintain support for nested value paths, implementations must "consume" their corresponding parameter from the value path and advance the valuePathOffset in parsedUri. See the source for zero.core.context.types.MapTypeHandler or zero.core.context.types.ListTypeHandler for examples.

public interface TypeHandler {
   public <T, V> T get(ParsedURI parsedUri, V storedValue);
   
   public <T,V,X> T put(ParsedURI parsedUri, V newValue, X storedValue);
   
   public <T,V,X> T post(ParsedURI parsedUri, V newValue, X storedValue);

   public <T, X> T delete(ParsedURI parsedUri, X storedValue);
}

r22 - 06 Nov 2007 - 15:11:02 - dma
Syndicate this site RSS ATOM
Copyright 2007 © IBM Corporation | Privacy | Terms of Use | About this site