Today is history.
Today there is a NetKernel™ book.
There will be many like it, probably better ones, but this one is first and this one is mine. You can check it out (and order it) on the O'Reilly site. The American and European Amazon sites will follow suit in a couple of days.
However excited (or not) you may be about this, I do urge my fellow Europeans to wait for it to appear on European sites. Given the price you'll otherwise pay it three times over (transport and import taxes) ... and of course I'd rather have you buy three copies instead.
They (never found out who they are) say that pride goes before fall and that they are proportional. I figure I'll end up close to the earth's core then.
Seriously, it is a beginner's book, written from the point of view of my own meanderings through ROC™ and NetKernel™. There's many things I would do/do differently today but at the time it made sense and it will make sense to you too.
Is this first then the end of the free community version ? No ! In fact, now that it is published I'm able to do a - long overdue - refresh of the community version. And work towards a 2nd edition of course.
On this day of firsts, here is a screenshot of something I've been working on recently :
We need to have more fun with NetKernel™ and what better way to have fun than a game ? The first nkMUD (unless you beat me to the release of course ... challengers are always welcome) is in the pipeline, watch this space for updates !
Phew, it's hot here at the earth's core. Enjoy your week while I climb back up !
2012/05/18
2012/05/04
the nature of ducks
There is a saying that goes like this ... if it looks like a duck, walks like a duck and quacks like a duck, chances are good that it is a duck. That does of course not keep the animal from being 10 feet tall, but even then it probably is a duck.
Here is a table I want to share with you :
You've probably seen that table before or noticed the similarity, the point I'm trying to make in this post is that ... if it looks like a crud, walks like a crud and quacks like a crud, chances are good that it is a crud.
In the REST world, it is considered bad form to use a GET when you should be using another method. Example
GET http://yourserver/kernelproperty/get/x
GET http://yourserver/kernelproperty/delete/x
is bad and
GET http://yourserver/kernelproperty/x
DELETE http://yourserver/kernelproperty/x
is good.
Yet, from the point of view of most browsers, GET is the only thing you will ever need. And developers follow that adagio in their web applications. Possibly wrong, but hey, it works ...
In the ROC world, all you need is SOURCE, the abstraction requires no more. However, from a code-convenience point of view things look different and the other verbs can be put to good use in creating a model that gives us a handle on the abstraction.
A good place to start is where REST and ROC meet. Now, HTTP is only one entry into ROC, but no method to verb translation is done. Everything comes in as a SOURCE request, you do have access to the httpRequest:/ space though and a very useful resource in there is httpRequest:/method.
An interesting and recent development in NetKernel is the RESTOverlay. I played a bit with that, mixed it with the above thoughts and came up with this :
<rootspace
name="System Admin"
public="true"
uri="urn:org:elbeesee:ext:system:admin">
<fileset>
<regex>res:/etc/system/SimpleDynamicImportHook.xml</regex>
</fileset>
<overlay>
<prototype>RESTOverlay</prototype>
<config>
<basepath>/elbeesee/</basepath>
</config>
<space>
<endpoint>
<meta>
<rest>
<simple>{accessorname}/{propertyname}/{propertyvalue}</simple>
<method>PUT,POST</method>
</rest>
</meta>
<grammar>
<active>
<identifier>active:restMethodToVerb_pp</identifier>
<argument name="accessorname"/>
<varargs/>
</active>
</grammar>
<class>org.elbeesee.ext.system.RESTMethodToVerbAccessor</class>
</endpoint>
<endpoint>
<meta>
<rest>
<simple>{accessorname}/{propertyname}</simple>
<method>GET,DELETE</method>
</rest>
</meta>
<grammar>
<active>
<identifier>active:restMethodToVerb_gd</identifier>
<argument name="accessorname"/>
<varargs/>
</active>
</grammar>
<class>org.elbeesee.ext.system.RESTMethodToVerbAccessor</class>
</endpoint>
<import>
<private/>
<uri>urn:org:elbeesee:ext:system:accessors</uri>
</import>
</space>
</overlay>
<import>
<private/>
<uri>urn:org:netkernel:tpt:http</uri>
</import>
</rootspace>
And the onSource method (everything that comes in over HTTP is a SOURCE) of the org.elbeesee.ext.system.RESTMethodToVerbAccessor class looks like this :
public void onSource(INKFRequestContext aContext) throws Exception {
INKFRequestReadOnly lThisRequest = aContext.getThisRequest();
// One mandatory argument
String aAccessorName = aContext.getThisRequest().getArgumentValue("accessorname");
String aHTTPMethod = (String) aContext.source("httpRequest:/method");
aContext.logRaw(INKFLocale.LEVEL_DEBUG,"SOURCE HTTPMethod = " + aHTTPMethod);
INKFRequest subrequest = aContext.createRequest("active:" + aAccessorName);
for (int i = 0; i < lThisRequest.getArgumentCount(); i++) {
aContext.logRaw(INKFLocale.LEVEL_DEBUG,"SOURCE argument = " + lThisRequest.getArgumentName(i));
if (! "accessorname".equals(lThisRequest.getArgumentName(i))) {
subrequest.addArgument(lThisRequest.getArgumentName(i), lThisRequest.getArgumentValue(i));
}
}
if ("GET".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_SOURCE);
}
else if ("POST".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_NEW);
}
else if ("PUT".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_SINK);
}
else if ("DELETE".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_DELETE);
}
else if ("HEAD".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_EXISTS);
}
else {
subrequest.setVerb(INKFRequestReadOnly.VERB_SOURCE);
}
// One response
aContext.createResponseFrom(aContext.issueRequestForResponse(subrequest));
}
That was not too hard. The RESTMethodToVerbAccessor works as a dispatcher, doing the method-to-verb translation and launching the real request with the correct verb.
There's one thing I'm not too happy about. For the RESTOverlay the inner grammar has to be unique. Hence the active:restMethodToVerb_pp and active:restMethodToVerb_gd identifiers. That is not elegant. For me the inner grammar + method would have to be unique.
Let us start the discussion. I've created a topic on the NetKernelROC forums where we can have a free-for-all (discussion that is).
Here is a table I want to share with you :
REST method | ROC verb | Database dml/ddl | |
---|---|---|---|
create | POST | NEW | INSERT |
read | GET | SOURCE | SELECT |
update | PUT | SINK | UPDATE |
delete | DELETE | DELETE | DELETE |
You've probably seen that table before or noticed the similarity, the point I'm trying to make in this post is that ... if it looks like a crud, walks like a crud and quacks like a crud, chances are good that it is a crud.
In the REST world, it is considered bad form to use a GET when you should be using another method. Example
GET http://yourserver/kernelproperty/get/x
GET http://yourserver/kernelproperty/delete/x
is bad and
GET http://yourserver/kernelproperty/x
DELETE http://yourserver/kernelproperty/x
is good.
Yet, from the point of view of most browsers, GET is the only thing you will ever need. And developers follow that adagio in their web applications. Possibly wrong, but hey, it works ...
In the ROC world, all you need is SOURCE, the abstraction requires no more. However, from a code-convenience point of view things look different and the other verbs can be put to good use in creating a model that gives us a handle on the abstraction.
A good place to start is where REST and ROC meet. Now, HTTP is only one entry into ROC, but no method to verb translation is done. Everything comes in as a SOURCE request, you do have access to the httpRequest:/ space though and a very useful resource in there is httpRequest:/method.
An interesting and recent development in NetKernel is the RESTOverlay. I played a bit with that, mixed it with the above thoughts and came up with this :
<rootspace
name="System Admin"
public="true"
uri="urn:org:elbeesee:ext:system:admin">
<fileset>
<regex>res:/etc/system/SimpleDynamicImportHook.xml</regex>
</fileset>
<overlay>
<prototype>RESTOverlay</prototype>
<config>
<basepath>/elbeesee/</basepath>
</config>
<space>
<endpoint>
<meta>
<rest>
<simple>{accessorname}/{propertyname}/{propertyvalue}</simple>
<method>PUT,POST</method>
</rest>
</meta>
<grammar>
<active>
<identifier>active:restMethodToVerb_pp</identifier>
<argument name="accessorname"/>
<varargs/>
</active>
</grammar>
<class>org.elbeesee.ext.system.RESTMethodToVerbAccessor</class>
</endpoint>
<endpoint>
<meta>
<rest>
<simple>{accessorname}/{propertyname}</simple>
<method>GET,DELETE</method>
</rest>
</meta>
<grammar>
<active>
<identifier>active:restMethodToVerb_gd</identifier>
<argument name="accessorname"/>
<varargs/>
</active>
</grammar>
<class>org.elbeesee.ext.system.RESTMethodToVerbAccessor</class>
</endpoint>
<import>
<private/>
<uri>urn:org:elbeesee:ext:system:accessors</uri>
</import>
</space>
</overlay>
<import>
<private/>
<uri>urn:org:netkernel:tpt:http</uri>
</import>
</rootspace>
And the onSource method (everything that comes in over HTTP is a SOURCE) of the org.elbeesee.ext.system.RESTMethodToVerbAccessor class looks like this :
public void onSource(INKFRequestContext aContext) throws Exception {
INKFRequestReadOnly lThisRequest = aContext.getThisRequest();
// One mandatory argument
String aAccessorName = aContext.getThisRequest().getArgumentValue("accessorname");
String aHTTPMethod = (String) aContext.source("httpRequest:/method");
aContext.logRaw(INKFLocale.LEVEL_DEBUG,"SOURCE HTTPMethod = " + aHTTPMethod);
INKFRequest subrequest = aContext.createRequest("active:" + aAccessorName);
for (int i = 0; i < lThisRequest.getArgumentCount(); i++) {
aContext.logRaw(INKFLocale.LEVEL_DEBUG,"SOURCE argument = " + lThisRequest.getArgumentName(i));
if (! "accessorname".equals(lThisRequest.getArgumentName(i))) {
subrequest.addArgument(lThisRequest.getArgumentName(i), lThisRequest.getArgumentValue(i));
}
}
if ("GET".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_SOURCE);
}
else if ("POST".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_NEW);
}
else if ("PUT".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_SINK);
}
else if ("DELETE".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_DELETE);
}
else if ("HEAD".equals(aHTTPMethod)) {
subrequest.setVerb(INKFRequestReadOnly.VERB_EXISTS);
}
else {
subrequest.setVerb(INKFRequestReadOnly.VERB_SOURCE);
}
// One response
aContext.createResponseFrom(aContext.issueRequestForResponse(subrequest));
}
That was not too hard. The RESTMethodToVerbAccessor works as a dispatcher, doing the method-to-verb translation and launching the real request with the correct verb.
There's one thing I'm not too happy about. For the RESTOverlay the inner grammar has to be unique. Hence the active:restMethodToVerb_pp and active:restMethodToVerb_gd identifiers. That is not elegant. For me the inner grammar + method would have to be unique.
Let us start the discussion. I've created a topic on the NetKernelROC forums where we can have a free-for-all (discussion that is).
Subscribe to:
Posts (Atom)