WWHRM Endpoint Methods

Background

In a classic OO Java program, all interactions between methods are by direct calls.

graph LR; A(Method1) -->|Calls| B(Method2)

The NetKernel ROC abstraction replaces all interactions between code endpoints in your system with a series of request-response pairs. The two endpoints are entirely separated from each other by exchange of a request via the NetKernel microkernel.

graph LR; A(Endpoint1) -->|Sub-request| B((Kernel)) B -->|Issue| C(Endpoint2)

Physical Endpoint Methods

When you write the implementation class of a physical Accessor endpoint in code, you override one or more of the NKFEndpointImpl request - handling methods. All you get given is a request context object, from which the code has to figure out what to do.

    public void onSource(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onSink(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onExists(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onDelete(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onNew(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onTransrept(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }
    public void onMeta(INKFRequestContext aContext) throws Exception
    {   unsupportedVerb(aContext);
    }

Each endpoint method that you write has to do the same job of interacting with its aContext object to figure out what it should do, then do it, then return a response again via the aContext object.

The NetKernel documentation does a good job in describing this common sequence.

This pattern is so common for accessor endpoint methods, you quickly get used to breaking down the implementation of an endpoint into the same structure every time. Which is where the worm slithers onto the stage. Well, WWHRM to be exact.

WWHRM

When you type out the declaration of every new endpoint method of a StandardAccessorImpl subclass in your code editor of choice, start by sketching out the structure with comments like this:

  public void onSource(INKFRequestContext aContext) throws Exception
    {   
        // What
        
        // With

        // How

        // Respond

        // Meta 
    }

It takes a few seconds to type (or even quicker if you have this set up as a code snippet on a hotkey), and I find it really helps set your focus on the task in hand:

What

Figure out what the endpoint method is being asked to do. In ROC - like terms, the code has to determine what requested resource (identifier including any arguments parsed by the endpoint grammar) it has a responsibility to return a response for. Note that a single physical endpoint class can be used to compute the representation of many logical resources, although the normal usecase is one endpoint class to one resource (or closely - related set of resources).

This is achieved by analysing the request that is accessed via the aContext object. What were the identifier and arguments? I can do no better than to link to the NetKernel documentation that exhaustively describes how an endpoint can analyse its request.

With

With what else does the endpoint need to do its task of computing a representation of the requested ROC resource? Normally, an endpoint will need to obtain additional state it needs to complete its job … remember that NetKernel endpoint classes should mostly be written with no instance state at all. This is very different from the OO approach!

An endpoint obtains the additional state it needs by issuing further requests.

How

OK, now we get to the good bit. We know what we have to do, with what existing data and state. So, we just get on and do it! This is the added value of the endpoint; indeed it is why you are writing it in the first place.

The value-add may be state transfer, resource transformation or something else. – NetKernel documentation Accessors page

Here, you can use the full power of Java or your favourite JVM language. But remember … stay thread safe!

Respond (or “Reply”)

An accessor endpoint may prepare a resource representation as the response to the request. This must be an Java object of any type, which should be immutable (unchangeable). If the endpoint does not explicitly return a response, the kernel ensures it returns a repsonse with a null representation.

Much more on preparing responses can be found here.

Meta

Optionally, the endpoint can set meta data on its response object before completing the method. For example, setting an non-default expiry type or declaring the MIME type of the response.

See Response Configuration for more details.

Notes

  • I am not suggesting that every method should rigidly stick to the WWHRM structure. There are many cases where it is not appropriate or performant. For example, looping over a section of code, issuing sub-requests in each iteration and building the final representation at the same time is quite common. Here’s a nice example of that.

    But, I would argue that that is still a form of WWHRM, just with additional repeations … W(WHR)*M !!

  • Approaching the coding of each endpoint in the same way helps to enforce consistency across endpoint implementations, thus improving maintainability of the codebase. Using the suggested // What - type comments in the code will probably make the structure of each endpoint clearer to other developers, especially for newbie Netkernel-ers (which, in 2018 at least, is most of the world).