WARNING: This server provides a static reference view of the NetKernel documentation. Links to dynamic content do not work. For the best experience we recommend you install NetKernel and view the documentation in the live system .

The pluggable overlay mediates and relays requests to a wrapped space. The support for pre-processing of requests, post-processing of responses and handling or exceptions provides developers with a simple framework for building sophisticated customized overlays. The pluggable overlay is transparent - all of the endpoints in the wrapped space are surfaced in the host space. The processing steps are illustrated in this diagram:

2009-12-18 13:59ZCanvas 1Layer 1Host SpaceWrapped SpacePluggableOverlay3a3b5123postProcesspreProcess1a1b4processexceptionProcess4a4b

  • 1 A request is received by the pluggable overlay
    • 1a If a preProcess request is specified, it is issued.
    • 1b The preProcess request returns a new INKFRequest in the response.
  • 2 Pluggable overlay issues either the original or the new INKRequest into space
  • 3 if Pluggable overlay received response
    • 3a If a postProcess request is specified, it is issued.
    • 3b The postProcess request returns a response
  • 4 or Pluggable overlay receives exception
    • 4a If an exceptionProcess request is specified, it is issued.
    • 4b The exceptionProcess request returns a response
  • 5 Pluggable overlay returns the response.

The pluggable overlay wraps a space and supports a pre-process, post-process and an exception-process request that follows the standard declarative request syntax:

<pluggable-overlay>
  <preProcess>
    <!-- request -->
  </preProcess>
  <space />
  <postProcess>
    <!-- request -->
  </postProcess>
  <exceptionProcess>
    <!-- request -->
  </exceptionProcess>
</pluggable-overlay>

preProcess

The preProcess service can either act as a filter stopping further processing of the request if a criteria is not satisfied, or as a request adapter, changing the incoming request before it is passed into the wrapped space.

The preProcess request must return an instance of INKFRequest as the representation.

When the pluggable overlay receives a request (1) it checks to see if a preProcess request is specified. If present, it issues this request (1a) and uses the INKFRequest returned as the representation in response (1b) as the request issued into the wrapped space (2). If preProcess throws an exception then the sub-request (2) is not issued to the wrapped space, post-processing is not executed and the exception is returned as the final response (4).

The inbound request (1) that the pluggable overlay receives should be passed in the preProcess request; it is referred to as arg:request, as illustrated in this example:

<pluggable-overlay>
  <preProcess>
    <identifier>active:audit</identifier>
    <argument name="operand">arg:request</argument>
  </preProcess>
  <space> ... </space>
</pluggable-overlay>

Example

The following pre-processing accessor implements a simple auditing function by sending the request identifier to the System.out console stream. Note that it creates a clone of the initial request (1) and returns this as its representation (1b); the pluggable overlay will use this for the inner request (2).

public class AuditAccessor extends StandardAccessorImpl
  {
  public void onSource(INKFRequestContext context) throws Exception 
    {
    INKFRequestReadOnly initialRequest = context.source("arg:operand", INKFRequestReadOnly.class);
    System.out.println(initialRequest.getIdentifier());
    INKFRequest innerRequest = initialRequest.getIssuableClone();
    context.createResponseFrom(innerRequest);
    }
  }

Example

The following pre-processing accessor provides a template for accessors that need to adapt the initial request (1) before it is issued into the wrapped space (2). This template creates a faithful copy of the initial request; accessors that need to adapt the request can modify this template to create a modified request.

public class PreProcessExplicitRelayAccessor extends StandardAccessorImpl
  {
  public void onSource(INKFRequestContext context) throws Exception 
    {
    INKFRequestReadOnly initialRequest = context.source("arg:operand", INKFRequestReadOnly.class);

    INKFRequest innerRequest = context.createRequest(initialRequest.getIdentifier());
    innerRequest.setVerb(initialRequest.getVerb());
    innerRequest.setRepresentationClass(initialRequest.getRepresentationClass());
    INKFResponseReadOnly primaryResponse = initialRequest.getPrimaryAsResponse();
    if (primaryResponse!=null)
      {
      innerRequest.addPrimaryArgumentFromResponse(primaryResponse);
      }
    for (String key: initialRequest.getHeaderKeys())
      { 
      for (Object value : initialRequest.getHeaderValues(key))
        { 
        innerRequest.setHeader(key, value);
        }
      }
    context.createResponseFrom(innerRequest);
    }
  }

postProcess

The postProcess service provides an opportunity to implement a wide range of architectural patterns. Essentially the postProcess service can modify the response (3) by changing either the representation or the configuration of the response. This modification can be based on just the response (3) or from information contained in the initial request (1), such as the identifier or the resolved logical endpoint identifier.

The postProcess request must return an instance of INKFResponseReadOnly as the representation.

When the pluggable overlay receives a response (3) to the request (2), it checks to see if a postProcess request is specified. If present, it issues this request (3a) and uses the INKFResponseReadOnly returned as the representation in response (3b) as the final response (4).

If the postProcess request needs to provide the INKFRequestReadOnly issued into the wrapped space (2) or the INKFResponseReadOnly returned from the wrapped space (3) as arguments, this example illustrates how do specify this:

<pluggable-overlay>
  <postProcess>
    <identifier>active:postprocess</identifier>
    <argument name="request">arg:request</argument>
    <argument name="response">arg:response</argument>
  </postProcess>
  <space> ... </space>
</pluggable-overlay>

Example

The following post-processing accessor sets the HTTP expiry to be one day if the initial request specified a GIF resource.

public class ExpiryAccessor extends StandardAccessorImpl
  {
  private static final long DAY=1000*60*60*24;
    
  public void onSource(INKFRequestContext context) throws Exception
    {
    INKFRequestReadOnly initialRequest = context.source("arg:request", INKFRequestReadOnly.class);
    String identifier = initialRequest.getIdentifier();
    INKFResponseReadOnly innerResponse=context.sourceForResponse("arg:response");
    INKFResponse response=context.createResponseFrom(innerResponse);
    if (identifier.endsWith(".gif"))
      {
      response.setMetaData("httpResponse:/header/Expires", System.currentTimeMillis()+DAY);
      }
    }
  }

Example

The following post-processing accessor performs an XSLT transformation of the returned result to transform the returned XML to XHTML.

The following post-processing accessor sets the expiry for a response to be one day.

public class TransformOutputAccessor extends StandardAccessorImpl
  {
  public void onSource(INKFRequestContext context) throws Exception
    {
    Object representation = context.source("arg:response");
    INKFRequest request = context.createRequest("active:xslt");
    request.addArgument("operator", "res:/resources/convertToXHTML.xsl");
    request.addArgumentByValue("operand", representation);
    INKFResponseReadOnly innerResponse = context.issueRequestForResponse(request);
    context.createResponseFrom(innerResponse);
    }
  }

exceptionProcess

The exceptionProcess service provides an opportunity to implement a wide range of exception handling patterns. Essentially the exceptionProcess service can modify the default behaviour or propagating the unhandled exception (4) by either wrapping the exception or performing some remedial action.

When the pluggable overlay receives an exception (4) from the request (2), it checks to see if a exceptionProcess request is specified. If present, it issues this request (4a) and uses the response returned (3b) as the final response (4).

If the exceptionProcess request needs to provide the INKFRequestReadOnly issued into the wrapped space (2) or the NKFException thrown from the wrapped space (4) as arguments, this example illustrates how do specify this:

<pluggable-overlay>
  <exceptionProcess>
    <identifier>active:exceptionprocess</identifier>
    <argument name="request">arg:request</argument>
    <argument name="exception">arg:exception</argument>
  </exceptionProcess>
  <space> ... </space>
</pluggable-overlay>

Example

The following exception-processing accessor wraps the exception with another exception to show it was handled

public class ExceptionHandlerAccessor extends StandardAccessorImpl
  {
    
  public void onSource(INKFRequestContext context) throws Exception
    {
    INKFRequestReadOnly initialRequest = context.source("arg:request", INKFRequestReadOnly.class);
    String identifier = initialRequest.getIdentifier();
    NKFException ex=(NKFException)aContext.source("arg:exception",WrappedThrowable.class).getThrowable();
    
    NKFException ex2 = new NKFException("Handled","Exception thrown by "+identifier, ex);
    INKFResponse response=context.createResponseFrom(ex2);
    }
  }

Unhandled Exceptions

If an exception is thrown while any of the process-requests are being executed that exception is percolated back to the initial requestor (4). It is important to note that the pluggable overlay is not transactional. This means that if the request (2) results in a state change and the processing of request (3a) causes an exception then that exception is percolated back (4) and the state change remains.