dnsjnio is an extension module for dnsjava that uses the NIO library (java.nio) to handle the I/O. The NonblockingResolver class is used instead of SimpleResolver.
Each instance of NonblockingResolver will, where possible, route all queries over a single port. If a query is sent which has the same header ID as a query currently in use on the single port, then a new port will be used for that query.
The existing ResolverListener interface is still available. This interface requires each response to be handled by a new server thread.
However, a new interface, using a ResponseQueue, is also supported. To use this interface, the caller must pass in a ResponseQueue to the sendAsync() call. When the response is available, it will be added to the blocking queue. The caller must simply remove the response from the queue, and process it in its own thread.
This functionality allows DNS queries to be run in a single thread, and be sent over a single port (although the default is to a random port for each new query).
The inner workings of dnsjnio are detailed below.
NB: the test code is intended to exercise dnsjnio. It can take several minutes to run.
The basic idea of dnsjnio is to plug in a non-blocking Resolver implementation to dnsjava. An asynchronous implementation of ExtendedResolver has also been added; this handles querying multiple resolvers in accordance with RFC1035 section 4.2.1.
The org.xbill.DNS.INonblockingResolver extends the dnsjava Resolver implementation to define an alternative to the ResolverListener callback mechanism (which is still supported but not recommended). A ResponseQueue is used to buffer Responses from the I/O thread to the client. The client can either block on the queue or else poll the queue for new Responses.
The DnsController controls the java.nio non-blocking I/O package and is the heart of dnsjnio. It creates a new thread named "DnsSelect" which runs in a continuous loop. Each loop, the input queue is checked for new tasks. These tasks are then run in the DnsSelect thread and consist of calls such as connect(), send() and close(). Once the input queue is empty the DnsSelect thread makes a blocking call to select(), before processing the result. The loop then starts again.
The rest of the dnsjnio code runs in the client thread (unless using the ResolverListener callback interface which must start a new thread for each callback). Connections are handled by instances of AbstractTransactionControllers - if communication over a single port is required then a SinglePortTransactionController is used - otherwise a Transaction per query is instantiated by the NonblockingResolver.
A "DnsTimer" thread is also run to handle the timeouts for DNS requests.
The ExtendedNonblockingResolver handles querying multiple resolvers for the same name. UDP transport is assumed but TCP may also be used. The first nameserver in the list is queried first - if this query times out then the next nameserver is tried and so on. Retries will also be made to the previous nameserver if the retry limit has not been exceeded. No exponential backoff strategy is implemented, nor is any attempt made to track behaviour of the nameservers.
Use of the ExtendedNonblockingResolver creates one additional thread. The "EnbrResolutionThread" handles querying the NonblockingResolvers and communicates with the client thread by an input queue and an output queue.
This code will run against dnsjava-2.0.0 -> dnsjava-2.0.8 inclusive. It is necessary to download and install the correct version of dnsjava before using this library.
dnsjnio-1.0.5.jar is compiled using JDK 7.
There is a demonstration project that can be downloaded from dnsjnio-demo The DemoClient.java loads a list of names from to_resolve.txt, and resolves them all in a single thread over a single port.