|
NetKernel News Volume 3 Issue 36
September 7th 2012
What's new this week?
Catch up on last week's news here
Repository Updates
The following updates are available in the NKEE and NKSE repositories...
- kernel 1.29.1
- Enhancement to refine detection of redundant scope in dependency accumulation - notably on dependencies involving META requests or if a space element wrongly responds with an impossible scope.
Resource Oriented Analysis and Design - Part 3
Third in a series of articles taking a well understood domain problem and casting it as an ROC solution. Read Part 1 Here., Read Part 2 Here.
Recap
Last week we started defining the address space and set up logical endpoints (mapper mappings) for atomic and composite resources. Our tests were in place to probe the address space and ensure that it was configured correctly to resolve our requests. We deferred the problem of the representation state by mapping all requests to a dummy <literal> representation.
At the end of the article I closed with this cliff-hanger...
"My tests were green. I hadn't screwed the space up. At this point, I put down my tools and went off to reward myself with a cup of coffee. In thirty minutes I'd broken the back of the problem, in another 5 minutes I'd be done, with a full persistent cache-coherent implementation of the atom resource model and I wouldn't have to write a line of code ..."
So lets find out if I was full of BS or not?
Representation State Persistence
Our challenge is to make the atomic service active:cell (or c:x:y our shorthand alias) actually deliver a useful stateful set of resources.
Constructive Solution
If you took the opportunity to look at Tom's approach you'll see that he solved the problem by constructing (the first C of the 3Cs) an endpoint as a Java class. It primarily consisted of a 2-D array data structure whose values would be set based on the position declared by his x,y arguments.
Aside from going with a more macroscopic solution (his atom is the entire array rather than each cell), he implemented the full range of verbs so that the state of the cells could be retrieved and or modified by SOURCE, EXISTS, SINK or DELETE requests. Nothing at all wrong with this.
He also recognises that the state of active:cell is naturally cacheable but that any state modifying request (SINK, DELETE) must cause the expiration of any previously cached representation. He adeptly uses the golden thread pattern (link is to localhost and assumes you're running the NK docs) to solve this, by associating all his SOURCE responses with the gt:oxo virtual resource like this..
//SOURCE and ATTACH GOLDEN THREAD public void onSource(INKFRequestContext aContext) throws Exception { if (vMatrix == null) { throw new NKFException("there is no game in progress"); } INKFRequest vGTrequest = aContext.createRequest("active:attachGoldenThread"); vGTrequest.addArgument("id", "gt:oxo"); aContext.issueRequest(vGTrequest); aContext.createResponseFrom(justForShow(vMatrix)); } //DELETE and CUT GOLDEN THREAD public void onDelete(INKFRequestContext aContext) throws Exception { if (vMatrix == null) { throw new NKFException("there is no game in progress"); } vMatrix = null; INKFRequest vGTrequest = aContext.createRequest("active:cutGoldenThread"); vGTrequest.addArgument("id", "gt:oxo"); aContext.issueRequest(vGTrequest); aContext.createResponseFrom(true); }
All in all, his first cut at the solution took about 130 lines of code. Not bad.
Here's how to do exactly the same thing using composition (the second C of the 3C's). We'll need two imports, a literal and have to change one mapping...
Compositional Solution
The problem of managing a simple state space of resources is a very common one. It doesn't matter what the problem domain is - it comes down to an identity for a resource which may be requested with SOURCE, SINK, DELETE or EXIST requests. It also, because this is ROC, doesn't matter what the representation of the resource is.
NetKernel provides out of the box a module which provides the Persistent Data Service (pds:) resource model. Its documentation is here, but the essential point is that if you import the pds services your application space then has available the pds:/ address space - which supports SOURCE, SINK, DELETE, EXISTS and which manages all the cache dependency and golden thread consistency for you.
Here's what I did. First I import urn:org:netkernel:mod:pds - this space provides the high-level abstract pds:/ service. I also imported urn:org:netkernel:mod:pds:memory - this space provides a basic in-memory persistence implementation which is called by the pds: service. (There is also a local RDBMS backed implementation and its pretty straight forward to implement your own PDS Persistence service with external RDBMS or NoSQL backends for distributed persistent state).
My mapped space now looked like this...
<literal type="string" uri="res:/dummy">ReplaceMe!</literal>
<fileset>
<regex>res:/org/netkernel/demo/tictactoe/.*</regex>
</fileset>
<!--Use PDS as our persistence service-->
<import>
<uri>urn:org:netkernel:mod:pds</uri>
</import>
<import>
<uri>urn:org:netkernel:mod:pds:memory</uri>
</import>
<!--Literal PDS Config-->
<literal type="xml" uri="res:/etc/pdsConfig.xml">
<config>
<zone>global:TicTacToe</zone>
</config>
</literal>
</space>
In the docs you'll see that the pds:/ service always requires that the implementing space provide a contextual configuration resource called res:/etc/pdsConfig.xml (that is, pds: will always look for a local copy of res:/etc/pdsConfig.xml - just like on Unix, ssh will always request ~/.ssh/AuthorizedKeys - its a well known pattern).
PDS uses the configuration resource to determine a collection name for this set of pds resources (something that is called the "zone"). You can see that in my space I provided a very simple static <literal> implementation of res:/etc/pdsConfig.xml and declared my pds zone to be gobal:TicTacToe (remember this, we'll come back to it one day).
So now my space has available a full, extensible and consistent resource space. Now I just need to switch my endpoints from pointing to the dummy resource - to instead map into the pds: space. So I edited my atomMapperConfig.xml and changed the request from dummy to pds:/ttt/cell/x/y (where x and y are just relayed from the active:cell arguments)...
<verbs>SOURCE,SINK,DELETE,EXISTS</verbs>
<grammar>
<active>
<identifier>active:cell</identifier>
<argument name="x" />
<argument name="y" />
</active>
</grammar>
<request>
<identifier>pds:/ttt/cell/[[arg:x]]/[[arg:y]]</identifier>
</request>
</endpoint>
While I was at it, I also added a <verbs> declaration to tell the mapper that I want this mapping to respond to the full range of SOURCE, SINK, DELETE and EXISTS verbs which will simply be relayed on in the request to the pds: resource.
That's it. I had delivered a robust solution that was functionally equivalent to Tom's in exactly 45 seconds.
Stateful Testing
My next task was to go back to my tests and make sure that my composite solution was actually working. Here you can see that in a couple of minutes I modified my atom tests to do setup, test and teardown. The setup phase SINKs state to a c:0:0, the test then SOURCEs that state (which is what we apply assertions to). The teardown then DELETE's the state - so that subsequent tests will start with a clean resource space...
<test name="c:0:0">
<setup>
<verb>SINK</verb>
<identifier>c:0:0</identifier>
<argument name="primary">
<literal type="string">X</literal>
</argument>
</setup>
<request>
<identifier>c:0:0</identifier>
</request>
<teardown>
<verb>DELETE</verb>
<identifier>c:0:0</identifier>
</teardown>
<assert>
<stringEquals>X</stringEquals>
</assert>
</test>
<test name="active:cell+x@0+y@0">
<setup>
<verb>SINK</verb>
<identifier>c:0:0</identifier>
<argument name="primary">
<literal type="string">O</literal>
</argument>
</setup>
<request>
<identifier>active:cell</identifier>
<argument name="x">0</argument>
<argument name="y">0</argument>
</request>
<teardown>
<verb>DELETE</verb>
<identifier>c:0:0</identifier>
</teardown>
<assert>
<stringEquals>O</stringEquals>
</assert>
</test> ...
</testlist>
Executing the test and looking at the results I now see:
- c:0:0 test now returns "X"
- active:cell+x@0+y@0 test now returns "O"
Initialisation - ROC Fallback and Masking Patterns
OK this is starting to look good. But now the question arose: what about initialisation and setup? Tom has a simple solution, he made his stateful endpoint respond to NEW requests. But I had an elegant ROC-level pattern in mind. Why not imagine that all of the cells are already there in the space? If any given c:x:y cell had not yet received game state with a SINK - why not just fallback to a default empty cell?
Unsurprisingly this is also a common pattern for stateful address spaces. And pds: is already ahead of us. It implements a higher-order address space called fpds:. The semantics of which are: first try to SOURCE the resource in pds: if it is not there then fallback and make a request for the same identifier but in the generic res: space.
So to make my solution space be pre-initialised all I needed to do was change pds to fpds in my mapping like this...
<identifier>fpds:/ttt/cell/[[arg:x]]/[[arg:y]]</identifier>
</request>
But then I also had to add the fallback res:/ttt/cell/x/y to my address space so that there would be a "platonic cell". So I also edited the atomMapperConfig.xml to provide a mapping to a default cell resource res:/ttt/cell/default.
<grammar>
<simple>res:/ttt/cell/{x}/{y}</simple>
</grammar>
<request>
<identifier>res:/ttt/cell/default</identifier>
</request>
</endpoint>
Just one more thing - I had to then make sure this resource would resolve to a representation so I added another literal to my space...
Its a little strange looking (and maybe if I wasn't looking at the clock I'd do it differently) but hey, it returns an empty string "".
Finally I added a test that my expected initialisation default was working (notice there is no setup or teardown)...
<request>
<identifier>active:cell</identifier>
<argument name="x">0</argument>
<argument name="y">0</argument>
</request>
<assert>
<stringEquals />
</assert>
</test>
Finally I also added a sequence of tests to make sure that SINKing was correctly going to pds: and that a subsequent DELETE would then return back to the default.
All in I'd added another two more minutes to my time sheet - but I had beaten the 5 minute target by 15 seconds. I went to reward myself with another cup of coffee...
Next time we'll move up to the composite resources...
Checkpoint
You can download a snapshot of the modules at this point here...
Note: Before you try these please make sure to update mod-pds to the latest version from apposite (shipped last week) - I discovered that the in-memory pds had a bug and did not reliably cut the internal golden thread on DELETE.
NetKernel West 2013
Several people have recently asked if we're planning a conference. To which the answer is: "planning" would be too strong a word - but heck, yeah, why not...
So here's the "plan"...
Date: lets say spring 2013 to give everyone some lead time.
Location: USA. Those who've been to any of the previous three conferences will know we have exacting requirements. It has to be somewhere young, cool and hip (college towns have been good to us so far), within reasonable travel from a decent airport, *not* be a corporate hotel and (purely coincidentally) have a thriving local brew-scene. Any ideas?
Format: Shall we stick with the one or two day pre-conference bootcamp followed by two-day main conference?
Content: Now you could come along and listen to me go on and on about the esoteric nature of reality and the relation between resource spaces and chrono-synclastic infundibulae*... But its time to get real. To share experiences. To establish common practice. To get a shared perspective of how ROC fits in the IT landscape....
What we as a community all need are your presentations describing your experience, your practices, your patterns, your stories...
So get thinking and send me a short proposal. All selected speakers will be rewarded with some shameless give-away (can we do your own weight in beer?)...
Sounds like a plan... see you in 2013...
*Courtesy of Kurt Vonnegut and pointed out by B. Sletten Esq.
Have a great weekend.
Comments
Please feel free to comment on the NetKernel Forum
Follow on Twitter:
@pjr1060 for day-to-day NK/ROC updates
@netkernel for announcements
@tab1060 for the hard-core stuff
To subscribe for news and alerts
Join the NetKernel Portal to get news, announcements and extra features.