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 .

Endpoints issue requests to obtain resources or use logical level services.

Details of how NetKernel mediates requests in the ROC abstraction are provided in the ROC In a Nutshell book.

Physical level code is able to access the logical level through an instance of INKFRequestContext which is provided as a method argument to the endpoint with each call from the kernel. By convention, the local name used in the documentation is context. For example:

public void onSource(INKFRequestContext context)
  {
  // Endpoint may use the context argument to issue requests
  ...
  }

Convenience Methods

The context object provides several methods to succinctly issue common request forms.

source(...)

Instead of using the following code:

request = context.createRequest("res:/resources/readme.txt");
request.setRepresentationClass(String.class);
text = context.issueRequest(request);

one may use the succinct code:

String text = context.source("res:/resources/readme.txt", String.class):

or

Object text = context.source("res:/resources/readme.txt"):

sink(...)

The sink(...) method issues a request with the SINK verb. The two arguments include the resource identifier and a representation object containing the information to be transferred to the resource. The representation is set as the primary argument for the request (as compared to named arguments):

String primaryArgument = "Sink this text";
context.sink("transient:ID", primaryArgument);

transrept(...)

The transrept(...) method issues a request with the TRANSREPT verb. This method is used if an endpoint has a resource representation in one form and needs to work with the information in a different form. For example, the following request is issued to convert an HDS node to a W3C DOM Document object:

IHDSNode node = // ...
Document doc = context.transrept(node, Document.class);

exists(...)

The exists(...) method issues a request with the EXISTS verb. This method is used to get the boolean response about the status of a resource. For example:

if (context.exists("res:/customer/44"))
  {
  ...
  }

requestNew(...)

The method name new() conflicts with a Java reserved word.

The requestNew(...) method issues a request with the NEW verb.

Making General Requests

To fully customize a request, a request object is first created and then its methods are used to provide changes. The first step is creating a request object with the createRequest(...) method using a resource identifier:

INKFRequest request = context.createRequest("res:/resources/readme.txt");

or with the createRequestToEndoint(...) method using an endpoint identifier

INKFRequest request = context.createRequestToEndpoint("widget:box-with-title")

Once created, the request can be issued immediately or arguments and meta data may be added first.

Arguments

In most programming systems there are two approaches to passing information in a request: pass-by-reference and pass-by-value. With pass-by-reference an identifier for the information is provided in the request and the recipient then uses the reference to retrieve the information. With pass-by-value, the information representation is pushed to the recipient within the request. NetKernel and ROC recognizes that pass-by-value is really a special case of the more general and powerful pass-by-reference approach. Please refer to the physical level documentation on pass by value for more details.

Pass-By-Reference

To pass a request argument by reference, add the named argument along with its value (which is a resource identifier) using the addArgument(...) method. For example, the following code will request that the toUpper service process the resource identified by res:/resources/readme.txt:

request = context.createRequest("active:toUpper");
request.addArgument("operand", "res:/resources/readme.txt");

Pass-By-Value

To pass a request argument by value, add the named argument along with the object containing the value using the addArgumentByValue(...) method. For example, the following code will request that the toUpper service process the information passed by value as a String:

String message = "Hi, this is a message";
request = context.createRequest("active:toUpper");
request.addArgumentByValue("operand", message);

Comparison

The NKF API has been designed to hide differences between pass-by-reference and pass-by-value from the resolved endpoint. As discussed in the request analysis documentation, an endpoint issues a SOURCE request using the arg: URI scheme to obtain the representation of the argument; it does not know which approach was used by the client.

Identifier Encoding

If information to be passed in a request can be converted into textual form, it is possible to encode the information within the resource identifier.

REST Path Value Encoding

REST path value encoding has its origins in the http URI scheme as used in the World Wide Web. The scheme specific part of the http URI is a hierarchical, tree-shape structure structure using the slash ("/") character to delimit path parts or tree hierarchy levels. For example, http://mycom.com/customer/44584, is an identifier that can be interpreted as an identifier for the specific customer with the customer "ID" of 44584.

Within NetKernel, the res URI scheme used to identify general resources, uses the same hierarchical path structure as the http URI scheme. Using grammars, it is easy to construct a REST path that encodes information. For example, the following grammar:

<accessor>
  <id>customer:endpoint</id>
  <grammar>res:/customer/
    <group name="customer-id">
      <regex type="integer" />
    </group>
  </grammar>
</accessor>

associates the trailing part of an identifier with the argument named "customer-id". If the grammar is associated with the logical endpoint identifier "customer:endpoint", the following code will create a properly encoded REST identifier:

INKFRequest request = context.createRequestToEndpoint("customer:endpoint");
request.addArgument("customer-id", "42");
request.setRepresentationClass(String.class);
String identifier = (String)context.issueRequest(request);

Identifier Values

Named argument values can provide a computational value instead of a reference to a resource. For example, a fibonacci number computing service can use the following grammar:

<grammar>
  <active>
    <identifier>active:fib</identifier>
    <argument name="n" min="1" max="1" />
  </active>
</grammar>

which means that identifiers such as active:fib+n@5 and active:fib+n@8 identify resources that are based on the result of computing the fibonacci number for 5'' and ''8.

To get the value of the named argument, use the getArgumentValue(...) method. This will return a String with the value of the argument. For example, to get the value of the argument "n" using the grammar (above):

String operand = context.getThisRequest().getArgumentValue("n");

Representation Class

When the resource representation returned in a response must be an instance of a particular class, that class is specified using the setRepresentationClass(...) method. The requesting endpoint will either receive the information in this form or be thrown an exception. If the kernel detects a mismatch between the required representation class and the representation class provided by the resolved endpoint, it transparently requests a transreption from one form to the other.

For example, the following code sets the requested representation class to be a String:

request.setRepresentationClass(String.class);

Issue Request

The endpoint has a choice of issuing a request synchronously or asynchronously. If it issues the request synchronously, its progress is blocked until the response arrives. If it issues the request asynchronously, the endpoint will continue to execute until it elects to explicitly wait for the response.

Synchronous

An endpoint issues a request synchronously with the issueRequest(...) method, which blocks until a response is returned. This method returns the representation contained in the response.

INKFRequest request = context.createRequest(...);
Object representation = context.issue(request);

Some requests, such as a SINK request, are not expected to return a representation. In such a case simply issue the request and ignore any returned object:

INKFRequest request = context.createRequest(...);
request.setVerb(INKFRequestReadOnly.VERB_SINK);
context.issueRequest(request);

Asynchronous

An endpoint may issue a request asynchronously with the issueAsyncRequest(...) method, which immediately returns a INKFAsyncRequestHandle object. The endpoint may then continue processing while the sub-request is being processed (potentially on a different CPU core). When the endpoint is ready to accept the response to the sub-request it calls the join() method on the returned handle. The join method ensures that the endpoint code will not proceed without the response to the asynchronous sub-request, blocking progress if the sub-request has not yet returned its response. (An alternate method join(...) allows a timeout to be specified)

INKFRequest request = context.createRequest("active:longProcess");
INKFAsyncRequestHandle requestHandle = context.issueAsyncRequest(request);

//... perform other work

// Now wait for the sub-request to return
Object representation = requestHandle.join();

Fire-And-Forget

There is no harm done ignoring the response from an asynchronous sub-request. If a service is needed but it does not matter when the work completes and the results can be ignored, then the fire-and-forget pattern may be used. For example, sending a message to a log sub-system can be done asynchronously and without the response:

INKFRequest request = context.createRequest("active:logSystem");
request.addArgument("message", "Just started our work!");
context.issueAsyncRequest(request);

Asynchronous Response Listener

Instead of using the join() method to rendezvous with the response from an asynchronous sub-request, a response listener can created and set on the handle. The INKFAsyncRequestListener interface has two methods: receiveResponse(...) and receiveException(...), one of which is called when the sub-request returns with a normal representation or an exception representation in the response.

For example, the following class implements the listener interface:

class MyRequestListener implements INKFAsyncRequestListener
    {
    public void receiveException(NKFException aException, 
    INKFRequest aRequest, INKFRequestContext aContext)
    throws Exception
      {
      // Process an exception from the sub-request
      }

    public void receiveResponse(INKFResponseReadOnly aResponse, 
    INKFRequestContext aContext) 
    throws Exception
      {
      // Process a response from the sub-request
      }
    }

and is set as the listener to a sub-request:

INKFRequest request = context.createRequest("active:longProcess");
INKFAsyncRequestHandle requestHandle = context.issueAsyncRequest(request);
handle.setListener(new MyRequestListener());
context.setResponse(null); //inhibits the return of a response from this code

Notice that the context associated with the evaluated request must be set to null to inhibit the response being returned from this code (it must be returned by the listener code).

Advanced Features

Priority

It is possible to set the priority of a request using the setPriority(...) method. There are three priority levels set by integer constants in the interface INKFRequestReadOnly:

INKFRequestReadOnly.PRIORITY_MIN;
INKFRequestReadOnly.PRIORITY_STD;
INKFRequestReadOnly.PRIORITY_MAX;

By default, requests are set to the standard priority and may be set to a lower priority:

request.setPriority(INKFRequestReadOnly.PRIORITY_MIN);

or a higher priority:

request.setPriority(INKFRequestReadOnly.PRIORITY_MAX);

Changing the priority of a request impacts how the kernel selects which request to schedule when a NetKernel application is under heavy load. It is recommended that requests use the default standard priority and for requests that can wait for servicing then use the minimum priority. The maximum priority should not be used as it will potentially conflict with NetKernel system administration tools and services.

Headers

Request headers associate meta information with a request.

Headers may be made "sticky" - once they are set, they stay associated with the request and are transferred to any sub-requests spawned through the processing of the request. Headers are managed as a key-value map with a String used as the key and any object stored as the value. Header entries are set on a INKFRequest and can be retrieved from a INKFRequestReadOnly instance.

Kernel Headers

The kernel interprets headers as hints about how it should processes requests. The headers recognized by the kernel are provided on the Kernel Request Headers page.

Application Headers

Although information can be associated with a request by setting a request header, these must not be used to store or transfer state affecting logical processing of a resource. Their sole function is to capture and transfer meta-level information.

Application designs may benefit from associating meta information with a request. For example, an external message ID could be associated with a request and, if made sticky, will appear in all subsequently generated requests.

This example associates an external message identifier with a request and all subsequent sub-requests:

INKFRequest request = context.createRequest(...);
Integer messageID = // code that retrieves the external message ID
request.setHeader("JMS_MESSAGE_ID", messageID, true);

The header information can be read and used in log messages, etc:

public void onSource(INKFRequestContext context) 
  {
  Integer messageID = context.getThisRequest().getHeaderValue("JMS_MESSAGE_ID");
  ...
  }

If application request header information should only live for the duration of the request then do not set the "sticky bit" to true:

INKFRequest request = context.createRequest(...);
Integer messageID = // code that retrieves the external message ID
request.setHeader("JMS_MESSAGE_ID", messageID);

Working with the request response

There are some situations where it is important to have access to more than just the representation returned in a response. The method issueRequestForResponse(...) issues a request and returns a INKFResponseReadOnly object which contains additional information.

INKRequest request = context.createRequest(...);
INKFResponseReadOnly response = context.issueRequestForResponse(request);

The following table lists the methods available on the response object:

MethodReturnsUse
isExpired()booleanIndicates if the representation is no longer valid
getRepresentation()ObjectRepresentation returned in the response
getRequest()INKFRequestRequest corresponding to this response
getMimeType()StringThe MIME type of the returned representation
hasMetaData()booleanindicates if the response has meta data
getMetaDataKeys()Iterator<String>An iterator of meta data key values (as Strings)
getMetaData(String key)ObjectA object associated with the supplied meta date key value
getMetaData()IRequestResponseFieldsA object with response fields