Monday, November 5, 2007

MINA bindings for Scala

I've written some proof-of-concept Scala bindings for Apache MINA, a non-blocking I/O library. It was fairly straightforward to translate MINA's event-based model into one that uses message-passing between Scala actors. Although the bindings aren't complete, there's enough there to write a simple server.

Here's how we use the bindings to write a Hello World server.

First, we create an IoService to listen to port 12345. This IoService uses MINA's built-in protocol for sending and receiving lines of text. Note that we're not using the bindings yet; this is all normal MINA code.

val service = new NioAcceptor() service.setReuseAddress(true) service.getFilterChain().addLast("codec", new ProtocolCodecFilter( new TextLineCodecFactory(Charset.forName( "UTF-8" )))) service.setLocalAddress(new InetSocketAddress(12345))

Now we create an actor which listens for new sessions. (A session is MINA's name for a connection.) When we get a new session, we ask it to write Hello world, then we ask it close. The underlying MINA IoSession has been wrapped with an actor, and these requests are made by sending (!) messages to that actor.

val serviceHandler = actor { loop { react { case SessionCreated(session:Actor) => { session ! Write("Hello world") session ! Close(false) } } } }

We attach the actor to the IoService using the normal setHandler() method. An implicit conversion function automatically converts our serviceHandler actor into a MINA IoHandler.

service.setHandler(serviceHandler)

Finally, we bind the service so we can accept connections.

service.bind()

You can download the patched MINA source from here. The source based on a recent snapshot of MINA's trunk. I've added two modules: integration-scala and example-scala. You'll need Maven to compile the source.

Here's an example sequence of commands to compile the project, install the JARs and run the Reverser example server. (I set maven.test.skip because some tests are failing on MINA's trunk.)

$ mvn -Dmaven.test.skip install $ cd example-scala $ mvn scala:run ... Listening on port 12345.

Now you can telnet from another shell and use the server to reverse strings!

$ telnet localhost 12345 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello olleh goodbye eybdoog quit Connection closed by foreign host.

A few extra notes:

  • AsyncWeb should be added to MINA soon, and this will let MINA speak HTTP. This is the main reason I'm working with MINA's trunk rather than with one of the stable releases.
  • I've had problems on Mac OS X 10.4 with sockets staying in the CLOSE_WAIT state. I suspect that this is caused by a bug in Apple's version of the JRE. I have no problems when I use a more recent JRE on Linux.
  • I haven't done any performance testing at this stage.
  • I took inspiration for my Maven configuration from Lift. Thanks.