Sunday, May 27, 2012

Netty Tutorial Part 1: Introduction to Netty

Netty Tutorial, Part 1: Introduction to Netty



Update:  Part 1.5 Has Been Published: Netty Tutorial Part 1.5: On Channel Handlers and Channel Options

From the Netty web site:

"Netty is a NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP and UDP socket server."




Netty is a Java library and API primarilly aimed at writing highly concurrent networked and networking applications and services. One aspect of Netty you may find different from standard Java APIs is that it is predominantly an asynchronous API. That term implies differnet things to different people and may overlap with the terms non-blocking and event-driven. Regardless, if you have never used an asynchronous API before, it takes a little bit of a mind shift to implement Netty if you are accustomed to writing linear software. Here's how I would boil it down.
You build a Netty stack and start it. Issuing requests is easy and much the same as it is in any Java API. The mind shift comes in processing responses because there are none. Almost every single method invocation of substance is asynchronous, which means that there is no return value and invocation is usually instantaneous. The results (if there are any) will be delivered back in another thread. This is the fundamental difference between a standard API and an asynchronous one. Consider a client API that supplies a method to acquire the number of widgets from the server.

A Standard API

   public int getWidgetCount();
When a thread calls getWidgetCount(), some period of time will elapse and an int will be returned.

Asynchronous API

   public WidgetCountListener myListener = new WidgetCountListener() {
        public void onWidgetCount(int widgetCount) {
             ...... do your thing with the widget count

        }
   };

In my fabricated asynchronous version of the same API, the getWidgetCount call does not return anything and could conceivably execute instantly. However, it accepts a response handler as an argument and that listener will be called back when the widget count has been acquired and the listener can then do whatever is usefully defined with that result.
It might seem the additional layer[s] of complexity are unwarranted, but this is a crucial aspect of high performance applications and services written with Netty and the like. Your client need not waste resources having threads stuck in waiting or timed waiting mode, waiting for the server to respond. They can be notified when the result is available. For one thread, issuing one call, this may seem overkill, but consider hundreds of threads executing this method millions of times. Moreover, a fundamental benefit of NIO is that Selectors can be used to delegate the notification of events we are interested in to the underlying operating system and at the root of it all, to the hardware we're running on. Being informed through callbacks from the OS that 16 bytes have been successfully written out across the network to a server, or that 14 bytes were just read in from the same is obviously a very low level and granular way to work, but through a chain of abstractions, a developer can implement the Java NIO and Netty APIs to enable handling things at much less granular and abstracted level.
In this introduction, I want to start with some basic concepts and name some of the core building blocks and then slide into some actual code examples.

How Does All That Work ?


 The basic currency of Netty is a ChannelBuffer. From the Netty JavaDocs:
A random and sequential accessible sequence of zero or more bytes (octets). This interface provides an abstract view for one or more primitive byte arrays (byte[]) and NIO buffers.
 That's how data is passed around in Netty. If your application only deals with byte arrays and byte buffers, you're in luck ! However, if you need to take a higher order data representation, such as a Java object,  and send it to a remote server, and then receive another object back again, those byte collections need to be converted. Or, if you need to issue a request to an HTTP server, your request might start life as a simple string in the form of a URL, but then it needs to be wrapped into a request that the HTTP server will understand and then decomposed into some raw bytes and sent off across the network.  The HTTP server needs to accept the bytes that are sent and compose them back into an HTTP request that it can interpret and fulfil. Once fulfilled, the reverse must occur where the response (perhaps the response is a JPEG or a JavaScript file) must be wrapped in an HTTP response, converted into bytes and sent back to the calling client.
The basic abstraction of the network through which these bytes are transported is the Netty Channel. Once more to the Netty JavaDocs:
A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind.
Nexus is an apt name to describe a channel:  (from dictionary.com)
1.  a means of connection; tie; link
2.  a connected series or group
3.  the core or center, as of a matter or situation
 A channel is the externally exposed API through which you interact with Netty. A more grounded way of saying it, a Channel is an abstracted representation of a socket. But.... it's not necessarily a socket, it could be a file, or something even more abstract, so Nexus is a good way to describe it. Suffice it so say, a Channel provides the interface to connect and write to the destination represented by the Channel. No read ? you might ask ? Nope. Remember, it's like the asynchronous getWidgetCount method mentioned above. There's no return value.
All methods on a Channel fall into two categories:
  1. A simple attribute method that (synchronously) provides information about the channel itself.
  2. I/O operations like bind, disconnect, or write.
All methods in category #2 are asynchronous* and they all return a ChannelFuture which is a deferred result, meaning that it is a container for a result that is not known yet, but through which the result will be delivered when it is available. It's a bit like our contrived WidgetCountListener, except that you register your listener with the ChannelFuture instead of passing the listener with the original invocation. (Makes for a cleaner API, no ?) The interface that describes the listener you can implement is the ChannelFutureListener which has one method, called when the operation is complete: public void operationComplete(ChannelFuture future). When this method is called, the operation is complete, but it may not have succeeded, so the passed future can be interrogated to determine the outcome of the operation.
* Not completely true. Netty is predominantly used for NIO, but it also has channel implementations for OIO which refers to the Old IO which is completely synchronous. OIO has some benefits and the Netty implementation is consistent with NIO implementation so components can be interchangeable and reusable.

Creating a Connected Channel


 Channels are not created directly. They are created by a ChannelFactory. There are two variations for ChannelFactories, one for client channels and one for server channels. For each of those two variations, there are several implementations to account for the I/O type as well as the transport protocol:
 UDP based channel factories are the same for clients and servers as they are considered connectionless. There are two additional types:
I will be focusing on the TCP NIO channels in this blog, but be aware there are slight differences in creating different types of channel factories.
The constructors for the TCP NIO ChannelFactories have the same signatures and while there are a few overloads, the basics are that the factory needs two thread pools, or Executors for:
  • The Boss Threads: Threads provided by this pool are boss threads. Boss threads create and connect/bind sockets and then pass them off to the worker threads. In the server, there is one boss thread allocated per listening socket. In the client, there is only one boss thread*
  • The Worker Threads: Worker threads perform all the asynchronous I/O. They are not general purpose threads and developers should take precautions not to assign unrelated tasks to threads in this pool which may cause the threads to block, be rendered unable to perform their real work which in turn may cause deadlocks and an untold number of performance issues.
* So if there is only one boss thread in the client, why does the NioClientSocketChannelFactory require an Executor ?
  1. The boss thread can be released when there is no work to do and is created lazily, but it may be more efficient to pool a small number of threads than create a new one when required and destroying it when idle. 
  2. It is possible that one might want to create several different channel factories and rather than giving each one their own boss pool, they can all share one.
NIO channel factories are the only type that use a boss pool since they are the only ones that can asynchronously connect to sockets or bind to server sockets. The others either have virtual connections (Local), only synchronously connect (OIO) or are connectionless (UDP). The HttpTunelingClientSocketChannelFactory is simply a wrapper for another client socket channel factory, so it may or may not be using a boss thread but it not configured with one.

Keep this in mind about ChannelFactories:  in the course of conducting business with Netty, the factories will allocate resources, including the thread pools. Once you're done with a ChannelFactory, be sure to call releaseExternalResources() on the factory. This will ensure that all its resources are released.

In a nutshell, to send something to a listening server:
  1. Create a channel
  2. Connect the channel to the remote listening socket
  3. Call write(Object message) on the channel.
 Passing Object to the channel seems fairly flexible, no ?  So what happens if I do this ?

   channel.write(new Date());

Netty will issue this exception:

java.lang.IllegalArgumentException: unsupported message type: class java.util.Date

So what is supported ? ChannelBuffers. That's it. However, Channels have a construct called Pipelines (or more specifically, ChannelPipelines). A pipeline is a stack of interceptors that can manipulate or transform the values that are passed to them. Then, when they're done, the pass the value on to the next interceptor. These interceptors are referred to as ChannelHandlers.  The pipeline maintains strict ordering of the ChannelHandler instances that it contains, and typically, the first channel handler will accept a raw ChannelBuffer and the last chanel handler (referred to as the Sink) will discard whatever payload has been passed to it. Somewhere in the pipeline, you want to implement a handler that does something useful. The ChannelHandler itself is only a marker interface with no methods, so handlers have a lot of flexibility, but for a handler to do anything useful, it must be able to respond and/or forward ChannelEvents.  (There's a lot of terminology here.....)
What the heck is a ChannelEvent ? For the purposes of these two paragraphs, consider a ChannelEvent to be a package containing a ChannelBuffer, which as we already know, is the singular currency of Channels. Skip the next paragraphs if you're satisfied with this simplification.

Channel Events -For Real ?

Ok, ChannelEvents are not just packets containing ChannelBuffers.Since almost everything in Netty occurs asynchronously, the driving code patterns consist of bits of code that generate events (like Connect, Disconnect and Write) and bits of code that handle those events once they've been executed by one of the ChannelFactory's thread pools. All these events in the Netty API are instances of the interface ChannelEvent. The javadoc for ChannelEvent has a comprehensive list of the different types of events and explanations of each. For a good example of what an event handler looks like, see the javadoc for the class SimpleChannelHandler. This class implements handler methods for almost every type of event. There is even an event called IdleStateEvent which can be fired when ....... nothing. It fires when a channel has been idle, which can be useful.
In order to implement anything other than a simple ChannelBuffer sender or receiver, we need to add ChannelHandlers to the pipeline. Most of the time, ChannelHandlers in use are probably encoders and decoders, meaning:
  •     Encoder: Converts a non ChannelBuffer object into a ChannelBuffer, suitable for transmission to somewhere else. It might not encode directly to a ChannelBuffer, rather, it might do a partial conversion, implicitly relying on another handler[s] in the pipeline to complete the conversion. One way or the other, if you're not sending straight ChannelBuffers, one or more of the handlers in concert in the pipeline must convert the payload to a ChannelBuffer before it goes out the door. An example of a encoder is ObjectEncoder which converts regular [serializable] java objects into ChannelBuffers containing the byte representation of the written object.
  •     Decoder: The reverse of an encoder where a ChannelBuffer's contents are converted into something more useful. The counterpart of the ObjectEncoder mentioned above, is the ObjectDecoder and it does exactly this.
The Netty SDK supplies various codecs (contraction of Coder/Decoder[s]) such as Google ProtoBufs, Compression, HTTP Request/Response and Base64.
Not all ChannelHandlers are Encoders/Decoders (although the majority in the core API are). A ChannelHandler can be put to work doing all sorts of useful things. For example:
  •     ExecutionHandler: Forwards channel events to another thread pool. Intended to push events out of the Worker (I/O processing) thread pool in order to make sure it stays lively.
  •     BlockingReadHandler: Allows your code to treat a Channel as though it was not asynchronous for the purposes of reading incoming data.
  •     LoggingHandler:Simply logs what events are passing through the handler. A highly useful debugging tool......
So how do I get these ChannelHandlers to help me with my java.util.Date problem ? The answer is that you need to add an ObjectEncoder* to the channel's pipeline so it can translate the Date to a ChannelBuffer. Here's a visual, and I'll get to the code in a minute:



ObjectEncoder is actually an optimized Netty special. So long as you have a Netty ObjectDecoder on the other end, you're good to go. Otherwise, you need to use a CompatibleObjectEncoder which uses standard Java serialization.


Alright, checkpoint:  We need a Channel, which we get from a ChannelFactory, but we also need to define a ChannelPipeline. There's a few ways you can do this, but Netty provides a construct called a Bootstrap that wraps all these things together quite nicely, so here's how all this stuff fits together in order to create a Netty client that can send a Date. This example will implement the NioClientSocketChannelFactory which will create an instance of an NioClientSocketChannel. It bears mentioning, though, that for the most part, the Netty public API simply returns a SocketChannel, an abstract parent class, and you can just consider this to be a plain Channel as the implementations themselves exhibit identical behavior and exposed functionality. This code sample is a little more simplified that what you might see in other examples, but I doeth it for clarity.

   Executor bossPool = Executors.newCachedThreadPool();
   Executor workerPool = Executors.newCachedThreadPool();
   ChannelFactory channelFactory = new NioClientSocketChannelFactory(bossPool, workerPool);
   ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
     public ChannelPipeline getPipeline() throws Exception {
       return Channels.pipeline(
         new ObjectEncoder()
       }
   };
   Bootstrap boostrap = new ClientBootstrap(channelFactory);
   boostrap.setPipelineFactory(pipelineFactory);
   // Phew. Ok. We built all that. Now what ?
   InetSocketAddress addressToConnectTo = new InetSocketAddress(remoteHost, remotePort);
   ChannelFuture cf = bootstrap.connect(addressToConnectTo);

That's how you get a Channel.....  wait up !  That's not a Channel. It's a ChannelFuture. What's that ? Remember that [almost] everything is asynchronous in Netty, so when you request a connection, the actual process of connecting is asynchronous. For that reason, the bootstrap returns a ChannelFuture which is a "handle" to a forthcoming completion event. The ChannelFuture provides the state of the requested operation, and assuming the connect completes successfully, will also provide the Channel. There are (as always....) several options for exactly how the calling thread can wait for the connection to complete, and the following outline of these options applies equally to all asynchronous invocations against channels (disconnects, writes etc.) since all these operations return ChannelFutures.

[A]Waiting on Godot (where Godot is a Channel Invocation)


Regardless of the strategy implemented to wait for a Channel operation to complete, completion itself does not necessarilly indicate success, simply that the operation concluded. The result of completion could be one of the following:
  • Success !: The operation completed successfully.
  • Failed: The operation failed and the reason (a Throwable) for the failure is available from ChannelFuture.getCause().
  • Timed Out: Also a failure as far as we're concerned.
  • Cancelled: A channel invocation can be cancelled by calling ChannelFuture.cancel().
Having said that, here's how you can wait for completion:
  • Await: Await is a fancy way of saying wait, (perhaps used to avoid confusing with Object.wait()  ?) The ChannelFuture provides a number of methods that will cause the connecting thread to wait until the connect operation is complete. Note that this does not mean the thread will wait until it connects, rather, it waits until the final conclusion of the call which might be a successful connect, a failed connect, a connect timeout or the connect request could be cancelled. If there are too many options here to keep you attention, skip to the next paragraph, but the asynchronous nature of these operations requires a few different ways of awaiting, but basically stated:
    • Interruptibility: The waiting thread may be interrupted. This is a fact of life with Threads and the core Java thread API always throws a checked exception (InterruptedException) whenever a thread is directed to wait (sleep, join etc.). However, the ChannelFuture gives you the option of supressing calls to interrupt() which basically means you don't need to put a try/catch block around the call which is awaitUninterruptibly. Otherwise, the interruptible option can be used which is await.
    • Timeout: For various reasons, operations may be indefinitely blocked so it may be wise to apply a timeout to channel operations which limits the time that a thread will wait before getting a timeout exception.
  • Don't Wait: The connecting thread can optionally fire-and-forget by registering a listener on the ChannelFuture which will be fired when the operation completes.This listener is an implementation of a ChannelFutureListener and it receives a callback when the operation completes, passing a reference of the ChannelFuture in question.
In summary, this code outlines examples of the completion waiting options, using a connect operation as the asynchronous event we're waiting for, but applicable for most operations:
   // Waiting on a connect. (Pick one)
   ChannelFuture cf = bootstrap.connect(addressToConnectTo);
   // A. wait interruptibly
   cf.await();
   // B. wait interruptibly with a timeout of 2000 ms.
   cf.await(2000, TimeUnit.MILLISECONDS);
   // C. wait uninterruptibly
   cf.awaitUninterruptibly();
   // D. wait uninterruptibly with a timeout of 2000 ms.
   cf.awaitUninterruptibly(2000, TimeUnit.MILLISECONDS);
   // E. add a ChannelFutureListener that writes the Date when the connect is complete
   cf.addListener(new ChannelFutureListener(){
    public void operationComplete(ChannelFuture future) throws Exception {
     // chek to see if we succeeded
     if(future.isSuccess()) {
      Channel channel = future.getChannel();
      channel.write(new Date());
      // remember, the write is asynchronous too !
     }
    }
   });
   // if a wait option was selected and the connect did not fail,
   // the Date can now be sent.
   Channel channel = cf.getChannel();
   channel.write(new Date());
Typically, in a client side application, the awaitXXX would be appropriate, since your client thread may not have much to do while it waits anyways. In a server code stack (perhaps some sort of proxy server), the connect event callback might be a better choice since there is more likely to be a backlog of work to be done and threads should not be sitting around waiting on I/O events to complete.
The Netty documentation makes this fairly clear, but it is worth emphasising here, you want your Worker threads working, not waiting, so avoid calling any awaits in a worker thread.
One last item on connects, or more to the point, triggering some useful activity when a connect completes. In the example above, one of the options registered a ChannelFutureEvent and the write was executed in the body of the listener when the connection completed successfully. What may not be obvious here is that the operationComplete method is called-back on as a result of a broadcast ChannelStateEvent. This is an instance of a ChannelEvent which is broadcast when a Channel changes state. The ChannelFuture handled this event by invoking the defined callback, but ChannelEvents are passed to all handlers in the pipeline, so you might implement your post-connection action in one of the handlers. I point this out because much of the Netty sample code implements this slightly more non-linear style. For example, see ObjectEchoClient where the client class does all the work of establishing a connected channel, but never actually writes anything. The write of the Object to be echoed actually occurs in the last handler in the pipeline (the ObjectEchoClientHandler) and it is executed when it receives the ChannelStateEvent indicating the Channel connected.

What about the server ?


Following up on the DateSender, we only got as far as the server socket which is listening for data being sent by client sockets. The server side is much like the reverse of the client side.


The server socket sends ChannelEvents loaded with the byte stream it is receiving from the client into the pipeline by passing them to the sink. The pipeline needs to be configured to do the reverse so the next ChannelHandler is an ObjectDecoder which knows how to convert an array of bytes back into a Java Object, or in this case, the Date. Once the Date is decoded, it is handed to the next handler in the pipeline which is a custom handler I invented called a DateHandler. You may have noticed that although Channels have a write method, they don't have an equivalent read method. That would be more of a synchronous API where a thread would read from an OutputStream and wait until data was available. That's why the diagram above does not have an arrow back to the ServerChannel. Rather, the last handler in the pipeline receives the fully decoded message from the client and does something useful with it.
Think of server channel pipelines as being your actual business service invokers. Our actual business service is the DateReceiver and the pipeline feeds it properly unmarshalled data. Of course, you could create a pipeline that contains just one handler that does everything, but chaining together simple components provides much more flexibility. For example, if I was sending not just a Date, but perhaps an array of 300 Dates, I might want to add Compression/Decompression handlers into the client and server pipelines to shrink the size of the transmitted payload, and I can do this by simply modifying the pipeline configurations, rather than have to add additional code to my rhetorical single-do-everything handler. Or I might want to add some authentication so not just any old client can send Dates to my server..... Or what if I needed to shift the Date in transit to account for Time Zone changes..... Or......  it makes more sense to componentize.
The Server side of the DateSender is fairly simple. We create a channel factory, a pipeline factory and put the ObjectDecoder and our custom DateHandler into the pipeline. We'll use a ServerBootstrap instead of a ClientBootstrap and rather than connecting, as we did in the client, we're going to bind to a server socket so we can listen for requests from clients.
 public static void bootServer() {
  // More terse code to setup the server
  ServerBootstrap bootstrap = new ServerBootstrap(
    new NioServerSocketChannelFactory(
      Executors.newCachedThreadPool(),
      Executors.newCachedThreadPool()));

  // Set up the pipeline factory.
  bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
   public ChannelPipeline getPipeline() throws Exception {
    return Channels.pipeline(
     new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
     new DateHandler()
    );
   };
  });

  // Bind and start to accept incoming connections.
  bootstrap.bind(new InetSocketAddress("0.0.0.0", 8080));
  slog("Listening on 8080");
 }

 static class DateHandler extends SimpleChannelHandler {
  public void messageReceived(ChannelHandlerContext ctx,MessageEvent e) throws Exception {
   Date date = (Date)e.getMessage();
   // Here's the REALLY important business service at the end of the pipeline
   slog("Hey Guys !  I got a date ! [" + date + "]");
   // Huh ?
   super.messageReceived(ctx, e);
  }  
 }
Starting at the bottom, the DateHandler extends SimpleChannelHandler which tends to be a good way to go when you're not doing anything out of the ordinary because it's callbacks are strongly typed. In other words, you do not have to listen on all ChannelEvents and test the type of the event for the ones you want. In this case, the only callback that is overridden is messageReceived which means the handler has received some actual payload.
slog and clog are simple wrappers for System.out.println but the former prefixes with [Server] and the latter with [Client] so's we can differentiate the output.

A Quick Note on messageReceived


As I mentioned, the messageReceived method is a callback when some actual payload is being received. That is, the method handles a special subtype of the ChannelEvent called a MessageEvent. To access the payload in the MessageEvent, we simply call its getMessage() method which returns a java.lang.Object which can then be cast to the type expected. Recap: If there are no prior handlers before this handler to convert the payload, the type of the message will be..... a ChannelBuffer. In this case, however, the ObjectDecoder already converted the bytes in the incoming ChannelBuffer to a Date, so the type is java.util.Date. As for the other guy, the ChannelHandlerContext, we'll get to that shortly.
Once we issue the critical business event of logging the received date, the handler is technically done, so what's the score with that code on line 30 ? Since there might be additional handlers further up the pipeline, it's a good idea to always send the payload on its way once it has been handled. If there are no additional handlers, the pipeline will discard the message.
The source code for DateSender is in the GitHub repository if you want to view the whole thing, and here's what the output looks like:

[Server]:Listening on 8080
[Client]:DateSender Example
[Client]:Issuing Channel Connect...
[Client]:Waiting for Channel Connect...
[Client]:Connected. Sending Date
[Server]:Hey Guys !  I got a date ! [Sat May 19 14:00:58 EDT 2012]

Note that this example is purely one way. We only send a Date up to the server and nothing is sent back. If it were to, the pipelines on both sides would need their counterparts,which I will discuss next.

The Return of the King Date


Both clients and servers will read and write in a bidirectional scenario. Consider the DateSender example. What if the server was to increment the date by some arbitrary value and return the modified date ? There another example of almost the same code as DateSender called DateModifier  but which has a server that modifies the Date and returns it to the client. In order to do this, these modifications are required:
  1. The Server will return a Date back to the client, so its pipeline will need an ObjectEncoder.
  2. The Client will receive a Date back from the Server so its pipeline will need an ObjectDecoder.
  3. The Client needs an extra handler to do something with the returned Date from the Server.
 Here's the code that creates the new Client pipeline:
   ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
     public ChannelPipeline getPipeline() throws Exception {
       return Channels.pipeline(
         new ObjectEncoder(),
         // Next 2 Lines are new
         new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
         new ClientDateHandler()
       );
     }
   };
The code for the client DateHandler is very simple and it is invoked when the client receives a Date back from the server and it has been decoded:
   static class ClientDateHandler extends SimpleChannelHandler {
    public void messageReceived(ChannelHandlerContext ctx,MessageEvent e) throws Exception {
     Date date = (Date)e.getMessage();
     clog("Hey Guys !  I got back a modified date ! [" + date + "]");
    }
   }
The server pipeline is almost the mirror of the client now:
   bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
    public ChannelPipeline getPipeline() throws Exception {
     return Channels.pipeline(
      new ObjectDecoder(ClassResolvers.cacheDisabled(getClass().getClassLoader())),
      // Next 2 Lines are new
      new ObjectEncoder(),
      new ServerDateHandler()
     );
    };
   });
This is the code for the new Server DateHandler which has a few new concepts.
   static class ServerDateHandler extends SimpleChannelHandler {
    Random random = new Random(System.nanoTime());
    public void messageReceived(ChannelHandlerContext ctx,MessageEvent e) throws Exception {
     Date date = (Date)e.getMessage();
     // Here's the REALLY important business service at the end of the pipeline
     long newTime = (date.getTime() + random.nextInt());
     Date newDate = new Date(newTime);
     slog("Hey Guys !  I got a date ! [" + date + "] and I modified it to [" + newDate + "]");
     // Send back the reponse
     Channel channel = e.getChannel();
     ChannelFuture channelFuture = Channels.future(e.getChannel());
     ChannelEvent responseEvent = new DownstreamMessageEvent(channel, channelFuture, newDate, channel.getRemoteAddress());
     ctx.sendDownstream(responseEvent);
     // But still send it upstream because there might be another handler
     super.messageReceived(ctx, e);
    }  
   }
There's some new stuff on lines 10-13 dedicated specifically to returning the modified Date to the calling client and a subtle concept.
  • On line 10, we get a reference to the Channel from the MessageEvent.
  • The Date is going to be written back to the client, which (at the risk of repeating myself) will be an asynchronous call, so there's always a ChannelFuture involved, so in this case, on line 11,  we're going to create one using the Channels class, a class stuffed full of useful static methods like future.
  • On line 12, a new MessageEvent is created by constructing a DownstreamMessageEvent. Basically, the return value is being packaged up to be sent back down the client.
  • On line 13, the new MessageEvent is sent back down to the client by calling sendDownstream on the ChannelHandlerContext.
Say what ?  Ok, the ChannelHandlerContext is basically a functional reference to the pipeline. It provides access to all the handlers in the pipeline as well as to the pipeline itself, and most importantly, in this case, it provides a means of sending a message back through the "same" path that the incoming message arrived on. The important points here are:
  1. Keeping in mind that there may be additional handlers in the pipeline beyond the ServerDateHandler, I want the handler to respond to the client by sending the modified Date back and then forward the current payload to the next handler in the pipeline.
  2. If we wrote to the Channel directly, it would start from the "top" of the pipeline and might be pushed through handlers that are not indented to be called with the return Date.
All this begs some additional details on how exactly handlers work inside of a pipeline and an explanation of Upstream and Downstream.

Upstream and Downstream


You might wonder why things don't get confused when both the ObjectEncoder and the ObjectDecoder are called within one pipeline invocation, after all, they're both in the pipeline, wouldn't they both get called ? They're not because handlers declare themselves as Upstream handlers, Downsteam handlers or both.Basically Downstream is when a pipeline is sending something to a remote. Upstream is when it is reading something from a remote. If the terminology is not immediately intuitive to you, think of this mnemonic sentence:
If you want to learn something, you might read up on it and then write down some notes.
... anyways, for handlers to participate in a pipeline, they will directly or indirectly implement one or both of these interfaces:
Therefore, on a downstream write, the pipeline orchestrates the payload to be passed to all channel handlers that implement ChannelDownstreamHandler and vice-versa. Keep in mind that a channel handler can implement both interfaces and participate in upstream and downstream events.



Notice that the notion of Upstream vs. Downstream is specific to the client and the server, and for a conversation between a client and a server, both sides will need Encoder/Decoder pairs defined in the correct order.  The next diagram illustrates this in the form of a simplified HTTP server serving a file to a requesting client.




Sequence of ChannelHandlers in the Pipeline

In order to achieve the proper sequence of modifications of the payload, the pipeline keeps strict ordering of the handlers in the pipeline. Consider a pipeline that is supposed to look like this:
  1. JSON Encoder
    • Receives: A widget object
    • Produces: A JSON string representing the widget object.
  2. String Encoder:
    • Receives: A string
    • Produces: A ChannelBuffer with the encoded string
If the order of these encoders were accidentally reversed, the String Encoder would receive a widget Object when it expects a string and a class cast exception would occur.
There are a few constructs that allow for specifying the order of handlers in the pipeline. The simplest is probably the static method pipeline in the org.jboss.netty.channel.Channels class which creates a pipeline placing handlers in the created pipeline in the same order they are specified in the passed array. The signature is:

static ChannelPipeline Channels.pipeline(ChannelHandler...handlers)

In the ChannelPipeline interface itself provides a number of location specifying methods to add handlers, and herein we can also assign handlers specific logical names to uniquely identify each handler in the pipeline. The name itself is purely arbitrary, but as we will see, it is sometimes necessary to be able to reference a handler directly by its name, or determine its positional notation via the assigned name. The handler names are actually mandatory and assigned automatically if you do not specify them yourself. The pipeline methods are as follows:

Dynamically Modifying ChannelHandlers in the Pipeline

Another aspect of pipelines is that they are not immutable*, so it is possible to add and remove handlers at runtime. (There will be a very salient example of this in Part 2). Netty advertises the pipeline as thread-safe for this exact purpose, the four methods above can be called at runtime in order to "fine-tune" the pipeline for a specific call or to switch state.

*Actually there is an immutable implementation called a StaticChannelPipeline. Since it is immutable once created, it cannot be modified and may provide better performance since the pipeline execution does not have to be guarded against runtime changes of the handlers.

Why might we want to suddenly change the handlers in a pipeline that we went to all the bother of creating just-so in the first place ? Here's a contrived example: Say you have a bunch of encoders in a client pipeline, with the last encoder being a ZlibEncoder which will compress the encoded payload before sending it out the door. However, let's say you have determined that if the payload is less than 1024 bytes, the compression step is actually a hindrance to performance, not a benefit. This means that you want to conditionally route the payload through the compression handler. Hmmmm.... since you won't actually know the size of the payload until it has passed through the preceding handlers, you don't even have the option of creating a compression enabled pipeline and a compressionless pipeline because your payload is most of the way down the pipeline before you know which one you want. To address this, we can create a content interrogating handler that examines the size of the payload to be compressed [or not] and add or remove the compression handler accordingly. We'll add this handler after all the other handlers, but before the compression handler.

At this point, you might be thinking there are several alternatives here, but I did say it was contrived, so bear with me.

First, we will create the pipeline with the compression handler enabled, then we'll look at the ConditionalCompressionHandler. Here's what the pipeline might look like:
 public ChannelPipeline getPipeline() throws Exception {
  ChannelPipeline pipeline = Channels.pipeline();
  /// Add all the pre-amble handlers 
  //pipeline.addLast("Foo", new SomeHandler());
  //pipeline.addLast("Bar", new SomeOtherHandler());
  // etc.
  pipeline.addLast("IAmTheDecider", new ConditionalCompressionHandler(1024, "MyCompressionHandler"));
  pipeline.addLast("MyCompressionHandler", new ZlibEncoder());
  return pipeline;
 }


The ConditionalCompressionHandler must examine the size of the ChannelBuffer it is passed and decide if the compression handler should be called or not. However, once removed, if the next payload that comes along requires compression, we need to add it back in again.

  
  public class ConditionalCompressionHandler extends SimpleChannelDownstreamHandler {
   /** The minimum size of a payload to be compressed */
   protected final int sizeThreshold;
   /** The name of the handler to remove if the payload is smaller than specified sizeThreshold */
   protected final String nameOfCompressionHandler;
   /** The compression handler */
   protected volatile ChannelHandler compressionHandler = null;
   
   /**
    * Creates a new ConditionalCompressionHandler
    * @param sizeThreshold The minimum size of a payload to be compressed 
    * @param nameOfCompressionHandler The name of the handler to remove if the payload is smaller than specified sizeThreshold
    */
   public ConditionalCompressionHandler(int sizeThreshold, String nameOfCompressionHandler) {
    this.sizeThreshold = sizeThreshold;
    this.nameOfCompressionHandler = nameOfCompressionHandler;
   }
   
   /**
    * see org.jboss.netty.channel.SimpleChannelDownstreamHandler
    */
   public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) {
    // If the message is not a ChannelBuffer, hello ClassCastException !
    ChannelBuffer cb = (ChannelBuffer)e.getMessage();
    // Check to see if we already removed the handler
    boolean pipelineContainsCompressor = ctx.getPipeline().getContext(nameOfCompressionHandler)!=null;
    if(cb.readableBytes() < sizeThreshold) {  
     if(pipelineContainsCompressor) {
      // The payload is too small to be compressed but the pipeline contains the compression handler
      // so we need to remove it.
      compressionHandler = ctx.getPipeline().remove(nameOfCompressionHandler);
     }
    } else {
     // We want to compress the payload, let's make sure the compressor is there
     if(!pipelineContainsCompressor) {
      // Oops, it's not there, so lets put it in
      ctx.getPipeline().addAfter(ctx.getName(), nameOfCompressionHandler , compressionHandler);
     }
    }
   }
   
  }


 ChannelHandlerContext

Note that in pretty much every handler event, the handler is passed a ChannelHandlerContext. This class is an adapter between a ChannelHandler and the ChannelPipeline that contains it, allowing a handler to interact with the pipeline, a crucial aspect of the preceding code sample. In addition, it allows a handler to inquire about other handlers and even interact with other handlers (though more indirectly).

  • On line 26 we determine if the compression handler is installed by requesting the pipeline from the ChannelHandlerContext and then requesting the ChannelHandlerContext of the compression handler. If the compression handler is absent, the call will return null.
  • On line 31, the handler requests the pipeline and removes the compression handler from the pipeline by name.
  • On line 37, the handler requests the pipeline and adds the compression handler back into the pipeline. We also use the getName() method of the ChannelHandlerContext to get the name of the current handler.


That's it for this entry. Part 2 will demonstrate some more specific (and useful) examples by way of an Ajax server built with Netty that implements pushed events from the server to a browser. Please feel free to send any feedback or questions through the comments.  Thanks !

Update:  Part 1.5 Has Been Published: Netty Tutorial Part 1.5: On Channel Handlers and Channel Options

Tuesday, January 17, 2012

GroovyMX: New Remote JVM Test Cases

I just completed a check in to gmx with a small number of critical test cases.
I am attempting to make gmx a Groovy oriented but Java usable API, so for the most part, each logical test case has a Java and a Groovy version. Here's an example of a Groovy test:

    public void testRemoteClosureForMBeanCountAndDomains() throws Exception {
        def port = 18900;
        def gmx = null;
        def jvmProcess = null;
        try {
            jvmProcess = JVMLauncher.newJVMLauncher().timeout(120000).basicPortJmx(port).start();
            gmx = Gmx.remote(jmxUrl(port));
            def remoteDomains = gmx.exec({ return it.getDomains();});
            def domains = gmx.getDomains();
            Assert.assertArrayEquals("Domain array", domains, remoteDomains);
            def remoteMBeanCount = gmx.exec({ return it.getMBeanCount();});
            def mbeanCount = gmx.getMBeanCount();
            Assert.assertEquals("MBean Count", mbeanCount, remoteMBeanCount);
        } finally {
            if(gmx!=null) try { gmx.close(); } catch (Exception e) {}
            if(jvmProcess!=null) try { jvmProcess.destroy(); } catch (Exception e) {}           
        }
    }
In a nutshell, this test:
  1. Starts a new JVM with  the JMX management agent enabled.
  2. Retrieves the MBeanServer's MBean domains using a remote JMX call and then using a remoted closure. Verifies they're equal.
  3. Retrieves the MBeanServer's MBean count using a remote JMX call and then using a remoted closure. Verifies they're equal.


The latest snapshot is available here:
 Of course, these tests raised a bunch of issues which I am in arrears entering into Github. If you encounter any issues, please leave me some feedback.

//Nicholas

Friday, January 13, 2012

Remoting Groovy with Generated Closures

On attempting to implement remote closure execution in Gmx, I envisioned simply generating a closure on the fly and passing it over the wire to a remote Gmx counterpart.
In my mind, it looked a bit like this very simple example that would print "Hello Jupiter" to the standard out on the target remote server:


This is the simplest version of the idea, and so serves as a good proof of concept.

Gmx already has a lot of the plumbing in place, namely:
  • The ReverseClassLoader service spins up an HTTP server to provide remote class loading to foreign MBeanServers and performs an acceptable job of locating and serving the bytecode (in the form of a byte[]) to requesting classloaders.
  • Gmx automatically detects when remoting will be required and starts the ReverseClassLoader and installs the remote counterpart (RemotableMBeanServer) on the foreign MBeanServer. (This means that some of the code in this example is redundant).
The major barrier I ran into was discovering that it is quite difficult to acquire the bytecode (the class representation in the form of bytes) of a closure compiled on the fly. Excuse my use of an imprecise term like "on the fly". What I mean is that if a closure undergoes a formal compilation process, such as using groovyc or a CompilationUnit, then the class itself is accessible from the java code source URL in the form of a stream of bytes. However, if you define an inline closure in an inline script and execute it (such as you might in GroovyConsole),  the class appears to have a "fake" code source URL that cannot be read from. I am not that familiar with the internals of Groovy so this needs further explanation by way of an example:


The output of this script, when run in GroovyConsole, is as follows:

Class [Name:ClosureFactory$_getClosure_closure1] Bytes:1192 Interfaces: [interface org.codehaus.groovy.runtime.GeneratedClosure] 
Hello Venus 
Exception:/groovy/shell (The system cannot find the file specified) 
Class [Name:ConsoleScript5$_run_closure1] Bytes:[] Interfaces: [interface org.codehaus.groovy.runtime.GeneratedClosure] 
Hello Jupiter 


In lines 4..6, the example defines a class that creates a closure and returns it from a call to getClosure(). Using the CompilationUnit is the equivalent of using groovyc. When the byte code of the closure is read using clozure.getClass().getProtectionDomain().getCodeSource().getLocation().getBytes(). In the first instance, this is successful. However, when script defines an "on the fly" closure on line 16 (the closure works fine, as can be seen on line 26), the same method does not work. In support of this, the bytecode for the "compiled" closure is writen to disk. The value of the URL returned by clozureClass.getProtectionDomain().getCodeSource().getLocation() is file:/home/nwhitehe/groovy/groovy-1.8.5/./ and the following can be seen in that directory:

-rw-r--r-- 1 nwhitehe nwhitehe 5893 2012-01-13 11:54 ClosureFactory.class 
-rw-r--r-- 1 nwhitehe nwhitehe 2454 2012-01-13 11:54 ClosureFactory$_getClosure_closure1.class 

For the "on the fly" closure on the other hand, the script gets an exception when attempting to read the bytes from the code source URL pointing to a file /groovy/shell which does not exist. The bytecode disappears off into the aether.

There are a few hints on this challenge in a Jira ticket filed at the end of 2010 titled Ability to get class bytes of closures at runtime, including nested closures (for remote control). RemoteControl is a groovy package for groovy closure remoting, so this seemed like a good place to start, but the documentation [more concisely than I did above] alerts the reader to the same problem:
The remote execution mechanism works by sending the definition of the closure class to the server. It does this by finding the corresponding .class file for the closure on the class path. This means that there must be a .class file for the closure on the class path for the closure to be able to be remotely executed. Closure's whose class has been generated dynamically at runtime are currently not supported.
The Jira issue also points to a script by Guillaume Laforge that outlines a method of finding nested closures with an advisory that this might be useful in resolving the problem, but the script uses a CompilationUnit to acquire the bytecode so it was not clear to me if that strategy would work.

Aside from using the Class/ProtectionDomain/CodeSource/Location.getBytes method to get class bytecode, another path I have used is Javassist which did not work, returning a null CtClass when requested from the ClassPool.

The solution that ended up working was using a ClassFileTransformer. This interface is defined in the java.lang.instrument package and instance of it can be registered with the JVM's Instrumentation
instance. It has a single method:

byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer)

Accordingly, a ClassFileTransformer can provide the bytecode of a class. For our purposes, it needs to:
  • Be registered before the class loads or be invoked by a request to retransform the target class.
  • Filters out the class that has been targeted.
The following is a contrived example that retrieves a flat (i.e. no nested closures) closure's class byte code. The instrumentation instance is provided by a Gmx utility class called ByteCodeRepository. (More on that later).


In short, the class file transformer captures the bytecode of the class it is configured to capture. Once the bytecode has been captured, the transformer is unregistered. In order to trigger a call to the transformer, the instrumentation instance requests a retransform on the closure's class. Acquiring an instrumentation can be simplified, but the underlying mechanics are complicated. The JVM's instrumentation instance is not automatically created or available. It is typically created when the JVM is started with a -javaagent JVM startup option.

Alternatively, it is possible to use the Java Attach API to load a Java Agent into a running JVM which will trigger the creation of an instrumentation instance. This step is implemented by the Gmx utility class LocalAgentInstaller's static method getInstrumentation().

That's pretty much all that is required to get a reference to the instrumentation with some caveats:
  • The Attach API is only supported in Java 1.6+
  • The Attach API is variably implemented by different JVMs. You may have trouble with IBM JVMs, for example.
  • The Attach API is contained in the JDK's tools.jar so if you're using the JRE, you need to specifically add tools.jar to the classpath.
Gmx uses a reflective wrapper around the Attach API to avoid sticky compilation sores, but the details can be seen in the eponymous classes in the org.helios.vm package.

The next piece of Gmx is the ByteCodeRepository class. It is a singleton and its functionality is as follows:
  • It is a ClassFileTransformer that targets classes that implement org.codehaus.groovy.runtime.GeneratedClosure. These are typically the closures we're looking for. In my parlance, GeneratedClosure means "compiled on the fly". See this gist for the transformer basics.
  • It is a caching repository and cross-indexer for closure classes, bytecode and class names (binary and resource).
  • It automatically loads an instrumentation instance, so one can ignore the LocalAgentInstaller when using the ByteCodeRepository.

Capturing a closure's bytecode is now siplified to:


There's one subtlety left [that will be discussed here] in the quest to get bytecode for remoting closures and that's the rider on the Groovy Jira issue I mentioned, namely "including nested closures". Consider a closure like this that prints the declared methos signatures methods for each class in an array:

def Class[] classes = .....; 
classes.each() { 
     it.getDeclaredMethods().each() { 
          println it.toGenericString(); 
     } 

That's a closure within a closure, and they're two different classes. The bytecode for the outer closure does not contain the bytecode for the inner, so the earlier examples have hidden this problem. The class file transformer will still see the inner closure, and even though the name of the inner closure class is not as obvious (we could derive or guess it), here's why you don't need it:
  1. For the pruposes of remoting, so long at the ByteCodeRepository is installed, it will have captured all closures and indexed them by class name. When the remote class loader gets a serialized closure to invoke, it will surely know and will request each class from the ReverseClassLoader, which in turn looks them up in the ByteCodeRepository. 
  2. The following code prints (among other things that my optimizing editor has elided) the names of the loaded generated closures in this modified example. 


The output is:

Generated Closure: ClosureFactory3$_getClosure_closure1
Generated Closure: ClosureFactory3$_getClosure_closure1_closure2
All this nonsense was dedicated to getting all the bytecode necessary to remote closures, which I have not addressed at all, but I will in the next post.

Cheers.



Friday, January 06, 2012

GroovyMX: A Groovy JMX Client


I bootstrapped a new project on Github called GroovyMX (or just gmx). It is a monitoring oriented API but you may find it useful for various things. In the spirit of Groovy SQL, I am attempting to provide a JMX client API that is rich in functionality, terse in code and that extends the natural abilities of the native Java client.

This is a quick example which demonstrates how to connect to a remote MBeanServer and list the committed memory in bytes of each of the JVM's Memory Pools:


The output of this script is:

java.lang:type=MemoryPool,name=PS Eden Space:  402653184
java.lang:type=MemoryPool,name=PS Survivor Space:   16777216
java.lang:type=MemoryPool,name=Code Cache:  3407872
java.lang:type=MemoryPool,name=PS Perm Gen: 84738048
java.lang:type=MemoryPool,name=PS Old Gen:  268435456
 
Briefly,what this code is doing:
  1. This is usually the only import you will need.
  2. The Gmx class represents an MBeanServer, or more specifically, an MBeanServerConnection. There are a few ways to acquire one, depending on the situation, but in this case, I am connecting to a remote MBeanServer using its JMXServiceURL.
  3. The mbeans method has several overloads. In this case, I am providing an ObjectName pattern that will match to all the JVM's MemoryPool MXBeans, and a closure which executes for each ObjectName returned. 
  4. The closure is passed an instance of a MetaMBean which is notionally a proxy that combines the ObjectName of the MBean that it represents, and a MBeanServerConnection to the MBeanServer where the MBean is registered (the Gmx). In as much as possible, MetaMBeans act just like regular Pojos (or Pogos)  so the MBean attributes are accessed as simple properties and MBean operations are invoked like regular methods.
  5. The MetaMBean also has various local properties which are also accessed as simple properties in a Pogo. An example of this is objectName in line 4.
  6. The MemoryPool MBeans publish an attribute called Usage which is an instance of CompositeData. Fortunately, Groovy allows the simple reference of the composite sub-values by simple dot notation so the expression it.Usage.committed retrieves the nested value from the Usage composite structure that is keyed by the key committed
  7. Keen observers might wonder why Usage is cased that way. This is because the Groovy property specifier Usage is directly mapped to the MemoryPool MBean attribute Usage. Since it is perfectly legal for an MBean to have two separate attributes Foo and foo, the MetaMBean only honors the correctly specified case.
One of the tricky parts of making MetaMBean behave like a regular Pojo is that MBean operation invocations require the exact signature of the operation to be passed as an argument, which is very exacting and JMX can be quite fussy in this regard. Unlike regular Java and Groovy, there is no implicit [un/]boxing or inheritance easing done on your behalf. Consider these signature pairs:

void foo(int)                void foo(Integer)
void log(CharSequence)       void log(String)

If these were MBeanOperations, they would represent two different signatures, so the tricky part is inspecting the MetaMBean invocation name and arguments and executing a multidimensional pattern match against the MBean provided MBeanOperationInfos.

Gmx is also integrated with the Java Attach API so another way of acquiring a Gmx instance is to specify the PID of the JVM you want to attach to. The following example illustrates a script that discovers all (Attach API compatible) JVMs on the local machine and then prints the MBeanServerID attribute for each from the MBeanServerDelegate MBean.


The output of this script is:

helios104_1324051453215
helios104_1325860886519
helios104_1325876067431
helios104_1325782180150

Not super interesting, but I think the brevity is great.
This last example demonstrates some of the powerful optimizations of the Groovy remoting implemented by Gmx. To contrive an example, consider determining the total number of thread blocks across all threads in a JVM. Rather than retrieving every ThreadInfo from the ThreadMXBean and computing the total locally, I install the Gmx remoting on the remote MBeanServer and pass a script to perform the computation on the remote JVM and then return the result.

 The raw script passing is a bit awkward, and I'm working on implementing seamless remote closure invocation (See Issue #15).  and native closures are now supported. See here and here.

There are several more features which are complete, defective, in the roadmap/documentation or just in my imagination, but if you are interested, please check out the Gmx GitHub Site's Source Code, Issues and Wiki. I just started this recently so its not ready for a release, but you can download a snapshot from my Cloudbees snapshot repository.

Snapshots:
The dependencies can be viewed in the Gmx Maven Pom.

There are some additional examples in the project unit tests:
If you have any feedback, please drop a comment on this blog.

Cheers.

Monday, August 15, 2011

Java 7 for .Net - IKVM Not Slouching

I've always been impressed with IKVM, a compiler that converts Java Byte Code into .NET CLR byte code in the form of assemblies. It is astonishing how much core (and a lot of non-core) Java re-compiles into .Net with relatively little effort.

Shortly after the release of Java 7, the IKVM team has already released a development snapshot of IKVM supporting it.

After a massive amount of work, we finally have a new development snapshot based on OpenJDK 7 b147. There is still a lot of work to do to implement all the new functionality, but at least all the OpenJDK code has now been integrated.
Nice work guys.

Tuesday, July 12, 2011

JBoss AS7 Wicked Fast Startup, But Has Serious Deal Breaker ?

The new JBoss AS7 Release definitely has a blazingly fast startup. When I ran it for the first time:

14:26:24,770 INFO  [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.0.Final "Lightning" started in 1489ms

And not for nothing, it has a wicked fast shutdown too !

14:39:03,703 INFO  [org.jboss.as] JBoss AS 7.0.0.Final "Lightning" stopped in 7ms

A colleague commented:


"Yes, I've already been heckling the Tomcat guys, since they staked all their arguments on startup times. :)"

So that's all very exciting (I mean the whole AS7) but I have to say that the one deal breaker is the absence of JMX-Console. (Note: I might be wrong here, but it sure doesn't look like its in there) For those not in the know, this is a very spartan, but genius and super functional admin and development console for JBoss AS. I always loved the darn thing. It cuts straight to the chase giving you the info and operation invocation links you need without any of the fluff other application servers seem to insist on putting in.

The JNDIViewer alone was incredibly useful and if more widely used would preempt a bunch a problems because developers can actually see what's bound in JNDI !  What a concept !

So has JMX been eschewed completely ? I seriously doubt it. Uh... wait...  what is that.... 
I mean, I seriously hope not, that's what it is.

At any rate, it seems the platform MBeanServer has a few bits and pieces registered in it, and I suspect (or is it hope ?)  there is more that is optionally configurable.