Network Application Framework

1 - Overview

NAF (Network Application Framework) is a Java library that implements an API framework based around the Java JDK's NIO subsystem.
It provides an event-driven environment that lets programmers perform non-blocking (asynchronous) I/O operations without launching multiple threads, and it enables the support of extremely large numbers of concurrent connections and high data throughput levels.

NAF is meant to be easy to use and does not provide gratuitous layers of abstraction (which force you to learn excessively large custom APIs) over JDK methods that already do what most people need. What it does, is turn the raw NIO Selector and Channel interfaces into a functioning multiplexor based on the Reactor comms pattern, that performs the I/O ops for you, calls your code with the received data, and then gets out of the way to let you handle the event.
NAF transparently handles non-blocking writes as you would expect, lets you schedule an unlimited number of arbitrary timed ops (and easily cancel or reschedule them before they trigger) as you would hope and also provides the possibly unique feature of a non-blocking in-thread DNS resolver, which lets you perform hostname lookups, reverse-IP (PTR) lookups and MX (mailserver) lookups.

As you may have gathered from the Reactor reference above, a NAF application (more precisely termed a "NAFlet", or sometimes a "task") is a single-threaded one, consisting of fragments of callback user code which may or may not be related to each other (though not as far as NAF is concerned, or needs to know).
At its heart is an object called a Dispatcher, which as its name suggests is the NIO wrapper, and which invokes registered application code as necessary, in response to I/O, timer and DNS events. Each Dispatcher resides in its own thread, and the terms Dispatcher and Thread are sometimes used synonymously in NAF.
As well as simplifying the code, the single-threaded reactor mode of operation permits many optimisations, not least of which is pre-allocating all the temp objects, that various bits of code might require.

As well as accommodating as much callback code as possible (effectively, as many connections as possible) in one thread, and minimising or eliminating buffer copies, one of the main design principles of NAF is to minimise memory churn by reusing objects wherever possible, thus drastically reducing the rate of garbage generation, and minimising expensive GC (Garbage Collector) runs.


An overview of the main NAF components follows.
To find code-level documentation, you are urged to go to the example programs (see section §10 below), along with the API Reference (which is included in the NAF download), and of course the source code itself, which is freely available.


2 - NAF Config (naf.xml)

NAF applications are config-driven, and are specified in an XML-based configuration file which is generically referred to as the naf.xml file, though it is not necessarily named that.
This config file defines the application's Dispatchers and provides a mechanism for automatically wiring in and launching all your application code without you having to provide the main() method (and without the use of custom annotations).
The top-level structure of a naf.xml file is outlined below (and the binary NAF distribution also contains working naf.xml files for the portfwd sample program and the DNS batch-resolver app embedded in NAF).

<naf>
    <dirpaths>
        <root>.<root>
        <config>%DIRTOP%/conf<config>
        <var>%DIRTOP%/var<var>
        <logs>%DIRVAR%/logs<logs>
        <tmp>%DIRVAR%/tmp<tmp>
    </dirpaths>
    <baseport>12000</baseport>
    <dependjars>app1.jar : app2.jar</dependjars>
    <configcheck>N</configcheck>
    <dnsresolver>
        ...
    </dnsresolver>
    <nafman>
        ...
    </nafman>
    <dispatchers>
        ...
    </dispatchers>
</naf>

The naf.xml header config elements are as follows:

dirpaths
This section defines directory paths that a typical NAF application might use. The entire section is optional, since all the paths have default values that are as explicitly defined above.
For each path, there is an associated system-property that is looked up if the config entry is absent, and the default value kicks in if the system property isn't defined either.
Thereafter, the value of the path is used to substitute token strings to generate a concrete path.

The use of some replacement tokens is illustrated above. These substitutions are automatically performed on all naf.xml paths, and applications can also dynamically perform them on arbitrary strings by calling com.grey.naf.Config.getPath(String path, Class clss) (the clss param is typically null).

The matrix of configuration settings for these paths is specified in the table below:

Config Element System Property Default Value Replacement Token
root greynaf.paths.root .
(current directory)
%DIRTOP%
config greynaf.paths.conf ./conf %DIRCONF%
var greynaf.paths.var ./var %DIRVAR%
logs grey.logger.dir ./var/logs %DIRLOG%
tmp grey.paths.tmp ./var/tmp %DIRTMP%

The paths are actually defined in terms of each other, so the full derivation is as shown in the naf.xml listing above, and the defaults in the above table are what would result if no config items or system properties are defined.

If the grey.paths.tmp system property is set, then NAF's temp path will default to the global value specified by com.grey.base.config.SysProps.TMPDIR, else it will diverge. It's up to the application which setting it wishes to abide by.
If the grey.logger.dir system property is not set, then NAF will set it to the resolved value of its dirpaths/logs config item, and this ought to propagate to the underlying SLF4J logger, if it is of type greylog-slf4j.

baseport
This optional setting specifies the base port number above which NAF applications allocate the range of TCP ports they may use - whether purely internally or advertised externally. The actual number of reserved TCP ports depends on the NAF application.
If this config item is absent, the setting is obtained from the greynaf.baseport system property, and if that is also absent, the absolute default is 12000.

dependjars
A list of dependent JAR files to dynamically load.
Some of the list items may be directory paths rather than a JAR pathname, and if so, then:
- If directory spec ends in / (forward slash): NAF loads any .jar files found in that directory.
- Else, the directory itself is placed on the live classpath.
The list separator is colon for Unix and semi-colon for Windows. This may be overridden by the grey.path.separator system property.

Note that the syntax and treatment of the dependjars config value is identical to the value of the greynaf.cp system property, which is evaluated a fraction earlier. It is a matter of personal preference as to whether you would prefer to specify extra JARs in the naf.xml file or via system properties.

configcheck
This is purely for debugging and assurance, and if True then the Java process will exit after loading the naf.xml file and completing the initialisation phase, so that you can examine the config values echoed back in the log files and verify that they are as expected (given the extensive use NAF and its apps make of auto-calculated defaults).
Like all the boolean config settings, you may enter Yes, Y, True, T or 1 to specify True. It is not case-sensitive, and anything else will be taken to mean False.
This setting defaults to False, if absent.


3 - Dispatchers

Dispatchers are the event multiplexers at the heart of the NAF framework, and each dispatcher resides in its own thread. Multiple Dispatchers can exist within one JVM process, and they are completely independent of each other.
Users can simply regard Dispatchers as the abstract vehicles within which their applications execute. Dispatchers monitor all events (eg. I/O) in which their applications have registered an interest, and they also provide built-in services such as DNS resolution, NAFMAN and logging.

An illustrative dispatchers config block is shown below, and as can be seen, each individual dispatcher section consists of nothing more than one or more NAFlets, and a set of attributes controlling the dispatcher's own behaviour.
The top-level naf.xml outline in section 2 above shows where this block fits into the overall config file.

<dispatchers>
    <dispatcher name="dispatcher1">
        <naflets>
            <naflet name="naflet1a" class="com.my.package.Task1a">
                ...
            </naflet>
            <naflet name="naflet1b" class="com.my.package2.Task1b">
                ...
            </naflet>
        </naflets>
    </dispatcher>

    <dispatcher name="dispatcher2">
        <naflets>
            <naflet name="naflet2" class="com.my.package2.Task2">
                <configfile root="xpath">%DIRCONF%naflet2.xml</configfile>
            </naflet>
        </naflets>
    </dispatcher>
</dispatchers>

A description of all the Dispatcher attributes follows.
The config block above contains name attributes, which illustrate where dispatcher attributes should be defined.
The com.grey.naf.DispatcherDef class is where these attributes are parsed, so refer to the source for the full details.

As explained in the Overview section (see EchoBot), Dispatchers can be either be specified in a naf.xml file, in which case the NAF launcher automatically creates and runs them, or they can be programmatically created and started.
The latter are termed dynamic (or programmatically created) Dispatchers, as opposed to the configured ones created via naf.xml. The distinction is in their startup mode, and once launched, there is no difference in the resulting Dispatchers.
Dispatchers are dynamically created by either of these static methods:
com.grey.naf.Dispatcher.create(com.grey.naf.DispatcherDef, com.grey.naf.Config, org.slf4j.Logger)
com.grey.naf.Dispatcher.create(com.grey.naf.DispatcherDef, int baseport, org.slf4j.Logger)
Once they have been created and set up, you simply call their start() method.


4 - NAFlets

Each NAF dispatcher executes one or more application entities, known as NAFlets. If dispatchers can be thought of as representing a generic processing mechanism, then NAFlets embody specific functionality.
Naflets are independent processing tasks which are unaware of other Naflets executing within the same dispatcher, and can be freely moved between dispatchers, according to performance-tuning considerations.

When we say that NAF is a single-threaded framework, what we really mean is that individual NAFlets are single-threaded. However, real-world applications may comprise multiple NAFlets, which in turn may or may not be distributed over multiple Dispatchers. The important point is that the Dispatchers are independent of one another (as far as NAF is concerned), and don't incur synchronisation overhead (though that's also ultimately up to the developer, in that it depends how closely coupled the constituent NAFlets are).

The NAFlet config block is illustrated in the Dispatchers section above, and there are 3 top-level attributes.

The inner elements of a naflet block are application-specific and are parsed by the bespoke NAFlet code, but there is one generic setting which is understood and actioned by the NAF core:

There are two ways a NAFlet can be loaded into a NAF process:
One way is to define it in a naf.xml config file as described above, and that will take care of automatically instantiating and run them when NAF starts up.
The other way, is to programmatically create the NAFlet object, and then pass it to a running Dispatcher by calling its loadNaflet(com.grey.naf.Naflet, com.grey.naf.Dispatcher) method.
The Dispatcher will call the NAFlet's start() method from within its own thread (so it must not have been called already) and the NAFlet would then be live. This method can be used to load new NAFlets whether the Dispatcher was originally launched from a naf.xml file or programmatically.
The Dispatcher argument indicates which Dispatcher the calling code is associated with, and would be set to Null if it's not running within a Dispatcher. If you are calling loadNaflet() on a Dispatcher which you have just created programmatically but not yet started, then you should pass in null as the Dispatcher reference, and it will load the NAFlet once it starts up.

There is also an unloadNaflet(String naflet_name, com.grey.naf.Dispatcher) method, which tells the Dispatcher to stop the NAFlet of that name, if it's running.


5 - NAFMAN

NAFMAN is a low-level NAF management agent, through which commands can be issued to a running NAF application. A NAFMAN agent is embedded in every dispatcher by default, and the first NAFMAN-enabled Dispatcher becomes the Primary agent, all the others (if any) becoming its Secondaries.

The nafman config block is typically absent from the naf.xml config file, but that does not mean NAFMAN is disabled, merely that it's operating with its default settings.
The default settings should never need to be modified, and are too arcane to be described in a guide of this level (being more low-level tuning), but see the source of the com.grey.naf.nafman.Primary class to know more.

NAFMAN is one of the subsystems that make use of TCP server ports in the range above baseport and it announces the selected port in the Dispatcher log files during startup.
This is the one NAFMAN-related setting you are likely to need to change (if it clashes with anything else), but you change it via the naf/baseport config item, not from within NAFMAN's own config block.

NAFMAN commands are issued from the command line, using the NAF launcher's -cmd option (see source code for com.grey.naf.Launcher class).
NAF contains about a dozen in-build NAFMAN commands, most notably for stopping the NAF process (stop), listing all running Dispatchers (dsplist) and showing all registered timers and I/O channels (dspshow).
NAFlet developers can define additional NAFMAN commands for their application, and the built-in showcmds command lists all the NAFMAN commands actually registered with a live NAF process.

See the source code of the com.grey.naf.nafman.Registry class for the full set of built-in NAFMAN commands and their calling syntax, eg. some of them take an optional dispatcher name, which tells them to operate only on that dispatcher.


6 - DNS Resolver

The DNS Resolver offers a simple API to NAF components, and supports A (hostname), MX (email domain) and PTR (reverse lookup on IP addresses) lookups.
It issues DNS queries to the nominated DNS servers, and then caches the results so that future lookups on the same domain can be satisfied internally within NAF, without recourse to any more external queries. It also caches negative answers (ie. domain name not found).

Each dispatcher may contain its own asynchronous (non-blocking) DNS resolver, but they're all controlled by one dnsresolver config block at the top level of the naf.xml file.
This config block consists entirely of optional parameters, and is typically absent from the config file, as there should be no need to modify the defaults, but its absence simply means that the defaults will be in effect, not that DNS lookup is disabled. A resolver will still be created in all dispatchers that don't have their dns attribute set to No.

Selected DNS config attributes are shown below.
See the source of the com.grey.naf.dns.ResolverService class for the full list

<dnsresolver class="com.grey.naf.dns.distributedresolver.Client" alwaystcp="N">
    <servers>192.168.1.1 | 192.168.1.2</servers>
    <query_mx fallback_a="N" maxrr="5">
    <cache negttl="1h" exitdump="N">
</dnsresolver>

7 - Listeners

The Listener is a generic NAF component that crops up in many scenarios. It is a functional building block that allows you to listen on a specified TCP port and serve incoming connections with a specified server class.
It can be created either programmatically or by a naf.xml config file. While the programmatic mode allows you to create concurrent or iterative servers, the config mode only supports concurrent.

The Listener config block can be specified in arbitrary locations within the NAFlet config, and takes the form shown below:

<listener name="webserver" port="80" interface="127.0.0.1"
          backlog="100">
    <server class="com.grey.http.Server">
        ...
    </server>
</listener>

The Listener's attributes are as follows:

The contents of the server config block depend on the particular server, but it's top-level attributes are as follows:

Note that the Listener does not instantiate a Server for every incoming connection, as it stores server objects on an ObjectWell (see com.grey.base.utils.ObjectWell for reuse, and will soon reach a steady state where it always has spare servers in reserve. The portfwd example app is a good example of how this works, including the use of the prototype factory.

There is one further variation to consider:
Listener config blocks may exist as a sequence, and you may want Listeners on different interfaces or even ports to share the same Server config.
This can be achieved by the use of an extra Listener attribute called configlink, which points at the config block of another named listener, as illustrated below.

<listeners>
    <listener name="webserver" port="80" interface="127.0.0.1"
              iterative="N" backlog="100">
        <server class="com.grey.http.Server">
            ...
        </server>
    </listener>
    <listener name="webserver_alt" port="8080" interface="127.0.0.1"
              configlink="webserver"/>
</listeners>

The above XML block configures Listener webserver_alt on port 8080 to use the same server config as the first Listener on port 80.


8 - Logging

NAF logging is based on the SLF4J interface, and developers are free to use whatever underlying logger they want, but NAF has a bias towards its own GreyLog logging framework, which it is bound to by default.
The NAF JAR includes a dependency on GreyLog and its SLF4J binding in its Manifest classpath, so if you want to use an alternative logging provider (eg. Log4j), you will need to include it in your classpath before the NAF JAR, or load it before referencing NAF.

Note the warning in the GreyLog Overview about how each SLF4J getLogger() call actually creates a new logfile I/O stream, if it is the underlying SLF4J logging framework.
This is probably not what you want, so you should use the SLF4J logger instance that was created by each Dispatcher, and is exported via the public Dispatcher.logger field.

See the GreyLog Guide for how to use it.
As a developer of NAF applications, you don't have to worry about the GreyLog API, as NAF doesn't invoke it directly. You just need to provide GreyLog's logging.xml runtime config file.


9 - GreyBase

GreyBase is a term that crops up frequently in the NAF code and comments. It is a sub-project within NAF and constitutes one of the JAR files in the NAF distribution.
Broadly speaking however, we consider that NAF consists of all these JARs (including the logging ones - see §9), not just the one that happens to be called greynaf.jar, so GreyBase is simply a physical subdivision within NAF.

The dividing line between the GreyBase and NAF sub-projects is that NAF is the framework library and can only be used by NAF applications (indeed, using it is what defines a NAF application). GreyBase on the other hand is a utility API, which contains classes that are not intrinsically tied to NAF and can be used by any application in any context.

Notwithstanding that, GreyBase has evolved to serve the needs of NAF applications and will remain focussed on NAF's particular concerns and optimisations, such as object reuse (see ObjectWell), interpreting byte streams as 8-bit text (a valid assumption for most Internet protocols, including HTTP, FTP & SMTP headers - see ByteChars) and avoiding incidental garbage generation (see HashedList and friends).


10 - Demo Apps

In addition to the source code and Javadoc-generated API Reference, NAF also provides 3 sample apps (both in pre-built form in the binary download and in source form in the published NAF source tree) that demonstrate its main modes of operation.
They also demonstrate how little Java code you need to write, to build relatively complex NAF apps.


11 - Running NAF

When NAF starts up, it loads system properties from a grey.properties file which it looks for it on the following search path:
• If the grey.properties system property is set, then it specifies the pathname.
./grey.properties (ie. look in current directory)
$HOME/grey.properties
If no grey.properties file is found or the grey.properties system property is set to "-", then it is simply not loaded.
The file itself is a standard Java properties file, consisting of name=value lines.

The portfwd demo app provides an example of how an application that is based on a naf.xml config file might pass its main() method directly onto NAF.
It's com.grey.portfwd.Task.main() method does no more than load its own bespoke NAFMAN commands (which only applies if the -cmd option is used) before calling NAF's main() method.

The EchoBot demo app illustrates the behaviour of an application that doesn't use a naf.xml file, but it still leverages NAF's command-line parsing facilities.
As you can see, com.grey.echobot.App subclasses NAF's com.grey.naf.Launcher class, and contains the EchoBot's main() which instantiates the Launcher subclass and then passes control to it.
The EchoBot's App class defines a subclass of com.grey.base.utils.CommandParser.OptionsHandler to define its own command-line options (on top of those understood by NAF), which it loads in its constructor.
Because the App class overrides the Launcher's appExec() method, NAF passes control back to it after parsing the command-line, and the EchoBot takes over from there.

Run NAF directly with the -h option to see its built-in options.

java -jar greynaf-2.0.0.jar -h

As you can see, in addition to running as an application (ie. starting up the Dispatchers), the NAF Launcher also has two modes for sending NAFMAN commands (with or without a naf.xml file).
When issuing NAFMAN commands, the naf.xml file is only used to look up the NAFMAN agent's server port, so you can specify that with the -remote option instead.
Beware that if you issue a NAFMAN command by executing the NAF JAR, it would not recognise bespoke commands (such as the showconns command defined by the port-forwarder demo app) and would therefore reject it.
To get around this, you can use the -nocheck option (which prevents client-side validation of NAFMAN commands) or send it using the application JAR, as shown below.

java -jar greynaf-2.0.0.jar -cmd showconns -nocheck  

  java -jar portfwd-1.0.0.jar -cmd showconns

NAF would normally be loaded by your own application rather than executed directly, not least of the reasons being that the classpath you require will be set in your application's JAR (or enclosing framework), rather than NAF.
As noted in the naf.xml section above though, you could specify all the JARs your application requires in the dependjars config item or the greynaf.cp system property, and if so, you could launch your application from greynaf.jar.

One case where you would execute NAF from its own JAR (and without having to specify any extra JAR dependencies) is when running the DNS Batch Resolver, NAF's sole built-in NAFlet.
Assuming you have downloaded and extracted the binary NAF distribution (or built your own and extracted the resulting ZIP/Tar file), you would run it as follows:

java -jar lib/greynaf-2.0.0.jar -c conf/batchresolver.xml -logger dnsbatch < infile

The input file should contain one record per line, but the type of the records depends on the batchresolver.xml settings.
mode=MX and mailbox=Y: Simple email addresses, eg. somebody@nowhere.org
mode=MX and mailbox=N: Email domains without the mailbox part, eg. nowhere.org
mode=A: Hostnames in FQDN (Fully Qualified Domain Name) form
mode=PTR: Dotted IP address
The above command also assumes a logging.xml GreyLog config file, which contains a logger entry called dnsbatch.