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.

7 comments:

Renghen said...

hi Rich,

I am using your binding for scala. Unfortunately I have got issues with it, I am using maven 2.0.8, when I do a mvn install the binaries never gets done due to some errors in the testing, so I have to do a mvn install for each subproject e.g integration-jmx, integration-scala etc....

Also the example codes does not work scala 2.7.0 r1/r2. It says that it cannot recognize the Case classes OK etc ..

hope u can help,

Rich Dougherty said...

Hi Renghen

I'm sorry to hear that the tests are failing. This is probably happening because the bindings use MINA's trunk, which is sometimes unstable. I'll also try to update to a more stable version in the next few days. In the meantime, you can try disabling the tests with a flag: mvn -Dmaven.test.skip install.

I've tried compiling the bindings with the Scala 2.7.0 release candidates too. Unfortunately there are a couple of bugs in the current version of the compiler that stop it working for the moment. Here's the ticket for the case classes, and here's a ticket about a classfile-parsing problem that stops the NetCat example from compiling. There's a workaround for the case classes problem, but the NetCat example needs the compiler to be fixed.

Cheers
Rich

Rich Dougherty said...

Hi Renghen

I've updated the code on the project site. All tests should now pass, and it is built with Scala 2.7.0-rc2. I had to comment out one of the examples to get a clean compile - hopefully bug #489 gets fixed soon!

Cheers
Rich

renghenthecow said...

hi doug

I am tryinh to build agents using your library, basically what I want is to create clients, the NioSocketConnector does not seem to work at all.

Is there some other way to do it ???

thanx

Rich Dougherty said...

What is the problem you're having with the NioSocketConnector?

The Scala compiler did have a problem with parsing NioSocketConnector's bytecode, but that is now fixed in the nightly builds, and should make it into the 2.7.1 release.

renghenthecow said...

> Executing: C:\Program Files\ConTEXT\ConExec.exe "C:\scala-svn\bin\scalac.bat" "C:\work\scala\actors\Mina\MinaScala\MxitClient.scala"

error: error while loading AbstractPollingIoConnector$ConnectionRequest, class file 'C:\java-lib\mina-scala\mina-core-2.0.0-M2-SNAPSHOT.jar(org/apache/mina/common/AbstractPollingIoConnector$ConnectionRequest.class)' is broken
(key not found: H)
one error found
> Execution finished.

this is what i get compiling from either the scala-2.7.0.r14467-b20080330080028 version or the svn one

tried a variety of options to no avail.

whats the course of action

renghenthecow said...

ok, I made it work, but i had to redo everything. but its working well.

sorry for the incovenicence. your work is good, it is appreciated.

the abstraction is good and my work productivity got increase

scala rocks