Grizzly : Create a server and client with only few changes
Posted by survivant on December 29, 2008 at 3:05 PM EST
With Grizzly it can be simple to create a server and client with a few lines changes. I want to show you how you can create or convert a server to a client without to much troubles. I will use ProtocolChain in the server. For the client there are two ways to create it using the logic of the server. You can use the ProtocolChain or use a CallbackHandler.
I'll show you how to create the client for the both alternatives.
Let's start by showing the main lines and later I'll show you the differences between the ProtocolChain and CallbackHandler implementations for the client.
The complete source code is available at the end of this post.
Let's take a look if the init method of the server and the clients implementations.
Server
f_tg = new ThreadGroup("ThreadGroup");final CountDownLatch started = new CountDownLatch(1);controller = new Controller();TCPSelectorHandler tcpSelectorHandler = new TCPSelectorHandler();tcpSelectorHandler.setPort(port);controller.addSelectorHandler(tcpSelectorHandler);
Client
f_tg = new ThreadGroup("ThreadGroup");final CountDownLatch started = new CountDownLatch(1);controller = new Controller();TCPSelectorHandler tcpSelectorHandler = new TCPSelectorHandler();controller.addSelectorHandler(tcpSelectorHandler);
The difference is that you need to specify the listening port of the server.
Let's continue with the next step : the close notification and the state listener
The server and clients are identical.
BaseSelectionKeyHandler selectionKeyHandler = new BaseSelectionKeyHandler();// to be notify when a client/server close the connectionselectionKeyHandler.setConnectionCloseHandler(new ConnectionCloseHandler() {public void locallyClosed(SelectionKey key) {if(s_logger.isDebugEnabled()){s_logger.debug(key + " is being locally cancelled");}}public void remotlyClosed(SelectionKey key) {if(s_logger.isDebugEnabled()){s_logger.debug(key + " is being remotly cancelled (connection closed)");}}});tcpSelectorHandler.setSelectionKeyHandler(selectionKeyHandler);// STATE Listenercontroller.addStateListener(new ControllerStateListenerAdapter() {public void onException(Throwable e) {s_logger.error("Grizzly controller exception:" + e.getMessage());}public void onReady() {if(s_logger.isDebugEnabled()){s_logger.debug("Ready!");}started.countDown();}});
Now it's time the see the ProtocolChain in the server and the client implementations
Server and client (the ProtocolChain implementation)
// the protocol chainEchoQueryProtocolFilter protocolParser = new EchoQueryProtocolFilter();EchoQueryManagerFilter echoManagerFilter = new EchoQueryManagerFilter(this);final ProtocolChain protocolChain = new DefaultProtocolChain();protocolChain.addFilter(protocolParser);protocolChain.addFilter(echoManagerFilter);((DefaultProtocolChain) protocolChain).setContinuousExecution(true);ProtocolChainInstanceHandler pciHandler = new DefaultProtocolChainInstanceHandler() {public boolean offer(ProtocolChain protocolChain) {return false;}public ProtocolChain poll() {return protocolChain;}};controller.setProtocolChainInstanceHandler(pciHandler);
Client (the Callbackhandler) : nothing here.
We finish by starting the server and clients
Server
// start the servernew Thread(f_tg, controller).start();try {started.await();} catch (Exception e) {s_logger.error("Timeout in wait" + e.getMessage());}// extra stuff : QueueblockingQueue = new LinkedBlockingQueue(); queueConsumer = new QueueConsumer(this, blockingQueue);new Thread(f_tg, queueConsumer,"QueueConsumer").start();
Client
// start the clientnew Thread(f_tg, controller).start();try {started.await();} catch (Exception e) {s_logger.error("Timeout in wait" + e.getMessage());}// extra stuff : QueueblockingQueue = new LinkedBlockingQueue(); queueConsumer = new QueueConsumer(this, blockingQueue);new Thread(f_tg, queueConsumer,"QueueConsumer").start();connector_handler = (TCPConnectorHandler) controller.acquireConnectorHandler(Controller.Protocol.TCP);
The difference this time is on the client side. We need to acquire a ConnectorHandler that will be used later.
I want to say that the ProtocolChain is this example are identical for the client and server. Normally you could have a different implementations but the logic is the same. The init method will look alike.
Now let's take a deeper look into the Client implementations.
On the client side we need to add a method to connect to the server.
There is a little difference between the ProtocolChain and CallbackHandler. In the connect you need to specify a CallbackHandler.
For the ProtocolChain implementation, I was expecting the pass a null handler, and the API will use the ProtocolChain instead, but it doesn't work. I sent a email to the developers to find if it's a bug or not. I'll describe the current way that work.
UPDATED : It was a bug. It fixed with Grizzly 1.9.3+.
Client (ProtocolChain)
Client (CallbackHandler)public boolean connect() {try {//connector_handler.connect(new InetSocketAddress(host, port), new ProtocolChainCallbackHandler(connector_handler));
connector_handler.connect(new InetSocketAddress(host, port), (CallbackHandler)null); // don't need the callback anymore with 1.9.3+} catch (Exception e) {s_logger.error("Exception in execute..." + e);return false;}return true;}
public boolean connect() {try {connector_handler.connect(new InetSocketAddress(host, port), new ClientCallbackHandler(connector_handler));} catch (Exception e) {s_logger.error("Exception in execute..." + e);return false;}return true;}
The last part is the CallbackHandler. I'll start by the ProtocolChain implementation followed by the CallbackHandler implementation. I'll describe
the difference between the implementations, I suggested that you take a look at the source code to get the full overview.
ProtocolChainCallbackHandler
// read eventpublic void onRead(IOEventioEvent) { SelectionKey key = ioEvent.attachment().getSelectionKey();SelectorHandler selectorHandler = ioEvent.attachment().getSelectorHandler();// CALL THE PROTOCOL CHAINtry {if(key.isValid() && key.isReadable()){Context ctx = ioEvent.attachment();ctx.getProtocolChain().execute(ioEvent.attachment());}} catch (Exception ex){if(s_logger.isDebugEnabled()){s_logger.debug("IOException", ex);}selectorHandler.getSelectionKeyHandler().cancel(key);}}
ClientCallbackHandler
I hope that can show you that with Grizzly it can be really simple to create a server and a client in the same time with just few changes. The source code can be downloaded here.// read eventpublic void onRead(IOEventioEvent) { SelectionKey key = ioEvent.attachment().getSelectionKey();SelectorHandler selectorHandler = ioEvent.attachment().getSelectorHandler();SocketChannel socketChannel = (SocketChannel)key.channel();try {if(key.isValid() && key.isReadable()){// parse the responses hereint count = socketChannel.read(response);if(count>0){response.flip();byte[] b = new byte[(int)count];response.get(b);// ...... do your own logic. You can use EchoQueryProtocolParser as exampleresponse.clear();if(s_logger.isDebugEnabled()){s_logger.debug(new String(b)); // for debug purpose}}selectorHandler.register(key, SelectionKey.OP_READ);}} catch (IOException ex){if(s_logger.isDebugEnabled()){s_logger.debug("IOException", ex);}selectorHandler.getSelectionKeyHandler().cancel(key);}}
Related Topics >>
Blog Links >>
- Login or register to post comments
- Printer-friendly version
- survivant's blog
- 1046 reads






Comments
by survivant - 2009-01-05 18:36
Grizzly 1.9.3+ fixed the problem with ProtocolChain client implementation that needed to have a CallbackHandler to execute the ProtocolChain.