Instead of overriding the
onRequest(...) method, most endpoints
will leverage
verb detection and override one of the
methods
onSource(...),
onSink(...).
etc.
Request analysis, the first step in request evaluation, involves
examining the request to determine what is being asked of
the endpoint.
The kernel provides access to the request context through
an argument pass to the endpoint's
onRequest(...) method.
Request analysis can be simple or sophisticated and can include
examining the
identifier,
verb,
arguments,
and the requested
representation class.
Information specific to the request is provided in an instance of
INKFRequestReadOnly
which can be retrieved from the context with a call the context's
getThisRequest() method:
INFKRequestReadOnly thisRequest = context.getThisRequest();
Identifier
The method getIdentifier() returns the
resource identifier used in the request.
String identifier = thisRequest.getIdentifier();
This method provides the complete opaque identifier string token of the request.
Usually an endpoint uses a grammar
to define how the opaque identifier is to be parsed into useful constituent parts.
Those
parts have assigned group names which maps to arguments;
INFKRequestReadOnly provides methods to obtain and reference arguments directly (see below).
Endpoint Identifier
When implementing overlays and other endpoints which may receive requests
for many logical endpoints it is often useful to identify which of those logical
endpoints the received request was resolved to.
The INKFRequestReadOnly.getResolvedElementId() method
returns this.
String logicalEndpointId = thisRequest.getResolvedElementId();
Example
An accessor endpoint is declared in a space and uses the
endpoint identifier "customer:list" as show in part below:
<accessor>
<id>customer:list</id>
<!---->
</accessor>
When the endpoint processes a SOURCE request, it is able
to find the identifier is has been declared with as shown
in the following code:
public void onSource(INKFRequestContext context)
{ String elementId = context.getThisRequest().getResolvedElementId();
...
}
Verb
Automatic
The base classes detect if any on... methods
(such as onSource(...))
are used and will automatically set meta information for the endpoint
indicating which verbs the endpoint handles.
The recommend way to detect the request verb is to delegate the task
to the endpoint's base classes.
To use automatic verb detection, simply override one or more of these methods.
- onSource(...)
- onSink(...)
- onNew(...)
- onDelete(...)
- onExists(...)
- onTransrept(...)
- onMeta(...)
Example
public void onSource(INKFRequestContext context)
{
// Process the request when the SOURCE verb is used
}
public void onSink(INKFRequestContext context)
{
// Process the request when the SINK verb is used
}
Manual
When the majority of code in an endpoint is common to all
supported verbs, it is often easier to use manual verb detection.
To use manual detection, add endpoint meta data listing
the supported verbs with the declareSupportedVerbs(...) method
and override the
onRequest(...) method.
For example, the following no-argument constructor sets endpoint meta data
indicating that the SOURCE and SINK verbs are handled by
the endpoint:
public MyEndpoint()
{
declareSupportedVerbs(INKFRequestReadOnly.VERB_SOURCE | INKFRequestReadOnly.VERB_SINK);
}
The request verb is available as an integer value.
Constants defining the verb values are included in the interface
INKFRequestReadOnly as:
INKFRequestReadOnly.VERB_SOURCE
INKFRequestReadOnly.VERB_SINK
INKFRequestReadOnly.VERB_NEW
INKFRequestReadOnly.VERB_EXISTS
INKFRequestReadOnly.VERB_DELETE
INKFRequestReadOnly.VERB_TRANSREPT
INKFRequestReadOnly.VERB_META
For example, the following code tests to see if the request uses the SOURCE verb:
onRequest(INKFRequestContext context)
{
if (context.getThisRequest.getVerb() == INKFRequestReadOnly.VERB_SOURCE)
{
...
}
}Argument Processing
Operand / Operator Convention
A convention you will encounter is the use of the generic argument
names operand and operator.
In this convention, operand is the resource to be transformed
and the operator is the resource that specifies the transformation.
This pattern occurs
so frequently that it becomes very convenient to use these names to
implicitly hint at the semantics of the endpoint.
For example, the service active:xslt
uses operand for the document to be transformed and
operator for the XSLT transform.
Information is passed in requests using named arguments.
Usually the value of a named argument is a reference,
a resource identifier referencing information the
endpoint needs to perform its work.
In other cases the argument value is the actual value being passed.
Argument Value as Reference
When the value associated with a named argument is an identifier,
the endpoint may treat the value in two ways.
If it needs the referenced resource to perform its work, then it will
issue a SOURCE request to obtain a representation of the information.
The "arg" URI scheme can be used to refer, indirectly, to the identifier
associated with a named argument.
For example, "arg:operand", will be automatically dereferenced by NKF
,
and is understood to be the identifier associated with the named
argument "operand".
A specific example will make this clear.
Within the endpoint that resolves the identifier active:toUpper+operand@res:/readme.txt,
the following code:
Object representation = context.source("arg:operand");results in a SOURCE request being issued for the resource res:/readme.txt.
If instead the endpoint needs to work with the resource reference itself,
(potentially modifying it and then using it for a request),
then it will obtain the value of the argument directly as shown in
the following code:
String resourceIdentifier = thisRquest.getArgumentValue("operand");in which case the variable resourceIdentifier will be set to
res:/readme.txt.
Argument Value as Value
In some situations the endpoint will be passed a value directly
as the value of a named argument.
The value can be requested as a
String
with the getArgumentValue(...) method.
For example, if the request identifier is
active:random+lower@0+upper@100
and the endpoint needs the values of the arguments ("0" and "100")
the following code obtains the literal values:
String lower = thisRequest.getArgumentValue("lower");
String upper = thisRequest.getArgumetnValue("upper");Setting the variable lower to "0"
and the variable upper to "100".
Optional Arguments
And endpoint may check to see if a specific named argument is present
in the current request by using the
argumentExists(...)
method.
For example, the following code checks to see if the argument operand is included in
the request:
if (thisRequest.argumentExists("operand")) {
{
//...
}As another example, consider our endpoint which resolves
active:random+lower@0+upper@100.
The following code will see if the lower argument is provided
and set an internal variable to its value, otherwise, it will
use a default value:
private static final int DEFAULT_LOWER_RANDOM_VALUE = 0;
...
// Get lower limit to random number
int lowerRandomValue = DEFAULT_LOWER_RANDOM_VALUE;
if (thisRequest.argumentExists("lower"))
{
try
{
int lowerRandomValue = Integer.parseInt(thisRequest.getArgumentValue("lower"));
}
catch(NumberFormatException nfe)
{
throw new NetKernelException("Invalid value provided for lower argument", "Details", nfe);
}
}Variable Number of Arguments
It is possible to create an endpoint binding with a grammar that uses variable arguments:
<grammar>
<active>
<identifier>active:myService</identifier>
<varargs />
</active>
</grammar>
Such a grammar will match active:myService with zero or more
argument with any name and values.
To assess a request that resolves with this grammar the endpoint code must
iterate through the arguments and analyze them individually.
The following example illustrates one such approach:
int numberOfArguments = thisRequest.getArgumentCount();
With this count a iteration loop can be written:
int numberOfArguments = thisRequest.getArgumentCount();
for (i=0; i < numberOfArguments; i++)
{
String argumentName = thisRequest.getArgumentName(i);
String argumentValue = thisRequest.getArgumentValue(i);
// Process argument...
}
Primary Argument
Some requests need to supply a single implicit argument.
A request with the SINK verb needs to supply a representation to sink
to the resource.
A request with the TRANSREPT verb needs to supply a representation to be
transformed into a different representation.
A single, implicit argument is called a primary argument.
Direct
An endpoint can obtain the primary argument information in two ways.
The first retrieves the same representation, as it was set
in the request, using the getPrimary() method:.
Object primary = thisRequest.getPrimary();
Example
The requestor creates a SINK request and supplies
information in a
String
representation.
request = context.createRequest("transient:ID");
request.addPrimaryArgument("RT44938");
request.setVerb(INKFRequestReadOnly.VERB_SINK);
context.issueRequest(request);
The resolved endpoint can retrieve the representation with the
following code:
public void onSink(INKFRequestContext context)
{
Object primaryRepresentation = context.getPrimary();
...
}
In this example the variable primaryRepresentation refers to exactly the
same object that was set as the primary argument by the requestor.
Request
The second way an endpoint can retrieve the information passed as
a primary argument is to issue a SOURCE request using the
sourcePrimary(...)
method.
This is the preferred method when the resolved endpoint expects
representation in a particular form.
Example
An endpoint resolves SINK requests and stores information in binary stream form.
It will retrieve the primary argument as a IBinaryStreamRepresentation:
public void onSink(INKFRequestContext context)
{
IBinaryStreamRepresentation primary = context.sourcePrimary(IBinaryStreamRepresentation.class);
...
}
Representation Class
The specific representation class requested by the requesting client can be
determined with the following code:
Class requestedRepresentationClass = thisRequest.getRepresentationClass();
Generally,
accessors
can safely ignore the request representation class and rely
on transreptors to transform the representation returned to the one requested.
Transreptors do need the specific requested representation class;
this is discussed in detail in the documentation on
transreptor development.