Development of NOD Clients

This chapter describes the most central concepts for NOD Client development. The chapter does not cover all details but aims instead to put the NOD Interface Specification in context for a NOD Client developer. The first part describes the "raw" NOD Client communication protocol, the second part discusses how a NOD Client Developer may use the NOD Client Java API to communicate with NOD.

Developing a Client using the NOD REST Communication Protocol

Development of a NOD client is relatively simple. The goal of NOD is to make the NOD Clients as "thin" as possible, so a Client will typically not even know what it is doing. NOD Clients must explicitly support different types of Client Commands, for example <apduframe>,<buzz>,<message> etc. Which commands a given client should support must be carefully planned to suit the usage scenarios planned for the given device. For example: To pick up a Desfire product the only capability strictly needed is support for the <apduFrame> client command, but the user experience will improve by adding support for the <message> command as the NOD Client itself cannot tell the traveler what the received <apduFrame> commands actually do. But not all devices will have a screen large enough to display a message, so message support is not mandatory.

NOD Client communication cycle

The following table describes the steps in a successful execution of an Order Group.

StepActionExample
1POST the appropriate Client <context> to NODPOST /ecard/<mediaSerialNumberID>/nodsession or POST /group/<groupId>/nodsession
---
2Execute all ClientCommands returned by the NOD serverExecute <apduframe>, <buzz>, <message> etc. This list is called a CommandSet
---
3POST the results from the execution to the LOCATION of the previous response. Only post results from commands with expectedResult=truePOST /group/<groupId>/nodsession/<nodsessionId>/cmdset/<cmdsetId>
---
4Repeat step 2-3 until no commands with expectedResult=true are returned from NOD.

All NOD requests must include the HTTP header X-NODClient-Capabilities that declare which functionality the NOD Client supports, this enables the NOD Server to adjust the responses to the NOD Client according to capability. The following rules apply:

  • The Client should never assume that execution will follow any predictable sequence, for example that the <message> command always comes last, or that there is only one message in one pickup.

  • The Client can always assume that only commands that are declared as supported through the NODClientCapabilities are returned.

Implementation of a NOD Client Command

The main bulk of developing a NOD Client is implementing support for the desired NOD Client Commands. The exact expected behavior for each command is described in detail in the specification for the command, but the implementation on the NOD Client side is similar for all commands: The NOD Client should connect the physical hardware to the logical NOD Client commands.

For each ClientCommand returned by NOD

<nod:commands xmlns:nod="URL/nod/client/commands" >
   <command cmdID="1" expectedResult="true">
      <apdu:apduFrame xmlns:apdu="URL/nod/client/commands/desfire/apdu">
         <frame>5A008057</frame>
      </apdu:apduFrame>
   </command>
   <command cmdID="2">
      <msg:message msgID="hb206.200">
         <line>tPurse kreditert 50kr</line>
         <duration min="2000">4000</duration>
      </msg:message>
   </command>
</nod:commands>

Execute the Command

Send the bytes 5A008057 to the Desfire driver on the device and display the message on the screen.

POST the results from the execution

    <nod:commands xmlns:nod="URL/nod/client/commands" >
       <command cmdID="1">
          <apdu:apduFrame xmlns:apdu="URL/nod/client/commands/desfire/apdu">
             <response>006300</response>
          </apdu:apduFrame>
          <result>200</result>
       </command>
    </nod:commands>

Repeat from step 1 until no commands with expectedResult=true are returned from NOD.

The returned <apduFrame> illustrates how "thin" the NOD Client is. <result>200</result> indicates that the NOD Client successfully sent the APDU Frame to the driver, but the response from the driver <response>006300</response> may very well be an APDU errorcode. Interpreting the actual response is handled by NOD. For example, this means that the Client can't know if the APDU commit command was executed successfully. Instead, the response is sent to NOD and NOD returns a new command set that display eventual error messages.

Error situations

The NOD uses standard HTTP errorcodes to signal error situations. See the NOD Client REST APIs for details. All clients must implement support for these codes. The following codes are particularly important to a NOD Client as they may occur as part of a normal scenario (not trigged by a system or client error).

CodeDescription
200OKNOD execution was successful
201CreatedNOD execution is successful, an additional CommandSet is created (see HTTP Location header)
409ConflictNOD Execution was interrupted, the Order Group is still distributed (client may retry).
410GoneNOD Execution was interrupted, the Order Group is removed from distribution (client should not retry).

Note that a NOD Client should always be able to produce a basic flow handling just by checking for the series (2XX, 3XX, 4XX, 5XX). Note that responses with HTTP error codes may still contain Client Commands. For example:

  • If a Desfire card cannot hold more products the NOD server will interrupt the execution with HTTP errorcode 409 Conflict. All NOD Clients should be able to handle this error. However, if the NOD Client supports the <message> Client Command, a <message> command may be returned from NOD that explains the error in more detail. Note that the <message> command uses specific returncodes that may be used by NOD Clients to differentiate the UI flow on specific feedback. See the NOD Client Message Feedback specification for details.

Stable, robust interfaces

NOD is designed to be backwards compatible so that existing NOD Clients very rarely will need updates when new functionality is made available to new clients. NOD Clients on the other hand must be designed as "forward compatible" as possible. By this we mean that a Client should make as few and as generic assumptions as possible. For example:

  • A client should not specifically check for HTTP errorcode 200/201 to verify that a request was executed OK. It should verify that the returned NOD response is on the 2XX series. Read [Del 22 App B - Document provided on request.] chapter 4.1.

  • A client should not assume more than what is specified in the interface XSDs and REST service descriptions. For example, it is not given that OrderGroup IDs will be numbers/letters in the future even if they are today.

  • A client should not attempt to deduce what the returned NOD Client Commands are attempting to achieve or produce alternative feedback. If a case can be made for more fuctionality on the NOD Client, this should be implemented in NOD Server and Client as new capabilities that may benefit all NOD Clients.

  • A client should always verify that the HTTP content negotiation has been satisfactory.

  • A client should have robust communication with support for resuming lost connections in unpredictable network environments.

  • A client should be very aware of the scale of distribution. It is imperative that all precautions are made to avoid flooding the NOD server with requests from thousands of clients, in particular during system of network failure.

Latency

All clients should aim to reduce latency as much as possible. The following are examples:

  • Use pre-emptive authentication

  • Reuse HTTP connections

  • Stream the parsing, execution and responses of Client Commands

Suggestions are welcome for improvements that involve the NOD server, for example:

  • HTTP Compression

  • JSON support

  • EfficientXML or EXI support

Developing a Client using the NOD Client API

The NODClient developer API for Java is a modular API that handles the low-level REST communication with NOD and lets the NODClient developer concentrate on Client-side functionality.The API is intended to use on several different platforms that have different support APIs. This means that even the HTTP communication layer must be pluggable. An example implementation of an Android APP using this API is available to developers.

Set up the ClientContext containing ClientID, password and supported capabilities.

Note that only the "C16_DESFIRE_APDU" capability is strictly required to support pickup of NOD orders for ecards, however the "C27_MESSAGE" capability enables feedback to the user and should declared in most cases. Other capabilities enable enhanced user feedback and optimizations.

ClientContext ctx = new MyClientContextImpl() {
    @Override
    public String getPassword() {
        return "secret";
    }
    @Override
    public String getClientID() {
        return "ExampleClientID";
    }
    @Override
    public Capabilities getCapabilities() {
        ArrayList<Capability> caps = new ArrayList<Capability>();
        caps.add(Capability.C16_DESFIRE_APDU);
        caps.add(Capability.C22_LED_RED);
        caps.add(Capability.C23_LED_GREEN);
        caps.add(Capability.C24_LED_YELLOW);
        caps.add(Capability.C25_PARALLEL_EXECUTION);
        caps.add(Capability.C26_BUZZER);
        caps.add(Capability.C27_MESSAGE);
        caps.add(Capability.C14_KEEPALIVE);
        caps.add(Capability.C18_SCREEN1);
        Capability[] capsArray = caps.toArray(new Capability[0]);
        return new Capabilities(capsArray);
    }
};

Create a NODClient

A NODClient instance represents one NOD Client that has an identity (NodClientID), a ClientContext and a certain set of Capabilities. Additionally, the URL of the NOD server needs to be specified.

NODClient client = new NODClient(ctx);
client.setNodServerUrl("NOD URL");

Register the listeners for the declared Capabilities

Note that the BuzzerListener and LedListener and MessageListener are only required to be implemented if the corresponding capabilities are declared. In most cases the MessageListener should be implemented to provide user feedback.

client.setAPDUListener(new MyAPDUListenerImpl());
client.setHTTPListener(new MyHTTPListenerImpl());
client.setMessageListener(new MyMessageListenerImpl());
client.setBuzzerListener(new MyBuzzerListenerImpl());
client.setLedListener(new MyLedListenerImpl());

Modify the ClientContext with values only the NOD Client knows

ClientContext ctx = client.getClientContext();
ctx.put(ClientContext.KEY_LOCATION, 5435L);
ctx.put(ClientContext.KEY_DEVICE_ID, new byte[]{(byte)0xAA,(byte)0xBB,(byte)0xCC});
ctx.put(ClientContext.KEY_SERVICEPROVIDER_ID, 3);

Retrieve a list of OrderGroups on a specific Ecard

Ecard ecard = new Ecard(1234567890);
List<OrderGroup> groups = client.getAvailableOrderGroupsOnEcard(ecard);

Pick up all OrderGroups

for (OrderGroup orderGroup : groups) {
   client.pickupWithGroup(orderGroup);
}