Mina_2.0_user_guide_en Mina 2.0 User Guide En

User Manual:

Open the PDF directly: View PDF PDF.
Page Count: 90

DownloadMina_2.0_user_guide_en Mina 2.0 User Guide En
Open PDF In BrowserView PDF
MINA 2.0 User Guide

Part I - Basics

Chapter 1 - Getting Started
In this chapter, we will give you first sense of what is MINA, what is NIO, why we developped a
framework on top of NIO and what you will find inside. We will also show you how to run a very
simple example of a server run with MINA

NIO Overview
The NIO API was introduced in Java 1.4 and had since been used for wide number of applications.
The NIO API covers IO non-blocking operations.
First of all, it's good to know that MINA is written on top of NIO 1. A new version has been
designed in Java 7, NIO-2, we don't yet benefit from the added features this version is carrying.
It's also important to know that the N in NIO means New, but we will use the Non-Blocking term
in many places. NIO-2 should be seen as a New New I/O...
The java.nio.* package contains following key constructs
Buffers - Data Containers
Chartsets - Containers translators for bytes and Unicode
Channels - represents connections to entities capable of I/O operations
Selectors - provide selectable, multiplexed non-blocking IO
Regexps - provide provide some tools to manipulate regular expressions
We are mostly interested in the Channels, _ Selectors_ and Buffers parts in the MINA framework,
except that we want to hide those elements to the user.
This user guide will thus focus on everything built on top of those internal components.

NIO vs BIO
It's important to understand the difference between those two APIs. BIO, or Blocking IO, relies on

plain sockets used in a blocking mode : when you read, write or do whatever operation on a socket,
the called operation will blcok the caller until the operation is completed.
In some cases, it's critical to be able to call the operation, and to expect the called operation to
inform the caller when the operation is done : the caller can then do something else in the mean
time.
This is also where NIO offers a better way to handle IO when you have numerous connected
sockets : you dn't have to create a specific thread for each connection, you can just use a few
threads to do the same job.
If you want to get more information about what covers NIO, there is a lot of good articles around
the web, and a few books cobering this matter.

Why MINA ?
Writing some network application is generrally seen as a burden, and a low level development. It's
a area which is not frequently studied or known by developpers, either because it has ben studied
in school a long time ago and everything has been forgotten, or because the complexity of this
network layer is frequently hidden by higher level layers, so you never get deep into it.
Add that when it comes to asynchronous IO, an extra layer of complexity comes into play : time.
The big difference between BIO (Blocking IO) and NIO (Non-Blocking IO) is that in BIO, you
send a request, and you wait until you get the response. On the server side, it means one thread wil
be associated with any incoming connection, so you won't have to deal with the complexity of
multiplexing the connections. In NIO, on the other hand, you have to deal with the synchronous
nature of a non-blocking system, wich means that your application will be invoked when some
events occur. In NIO, you don't call and wait for a result, you send a command and you are
informed when the result is ready.

The need of a framework
Considering those differences, and teh fact that most of the applications are usually expecting a
blocking mode when invoking the network layer, the best solution is to hide this aspect by writing
a framework that mimic a blocking mode. This is what MINA does !
But MINA does more. It provides a common IO vision to an application that needs to
communicate over TCP, UDP or whatever mechanism. If we consider only TCP and UDP, one is a
connected protocol (TCP) when the other one is connectionless (UDP). MINA masks this
difference, and make you focus on the two parts that are important for your application : the
applicive code and the application protocol encoding/decoding.
MINA does not only handles TCP and UDP, it's also offering a layer on top of serial
communication (RSC232), over VmpPipe or APR.
Last, not least, MINA is a network framework that has been specifically designed to work either
on the client side and on teh server side. Writing a server make it critical to have a scalable system,
which is tunnable to fit the server needs, in term of performance and memory usage : this is what
MINA is good for, still making it easy to devlop you server.

When to use MINA ?
This is a intersting question ! MINA does not expect to be the best possible choice in any case.
There are a few elements to consider when considering using MINA. Let's list them :
Ease of use When you have no special performance requirements, MINA is probably a good
choice as it allows you to dvelop a server or a client easily, without having to deal with the
various parameters and use cases to handle when writing the same application on top of BIO
or NIO. You can probably write your server with a few tens of lines, and there are a few
pitfalls in which you are likely to fall
A high number of connected users BIO is definitively faster that NIO. The difference is
something like 30% in favor of BIO. This is true for up to a few thousands of connected users,
but up to a point, the BIO approach just stop scaling : you won't e able to handle millions of
connected users using one thread per user ! NIO can. Now, one other aspect is that the time
spent in the MINA part of your code is probably non significant, compared to whatever your
application will consumme. At some point, it's probably not worthful to spend many times
more energy writing a faster network layer on your own for a gain which will be barely
noticable.
A proven system MINA is used by tens of applications all over the world. There are some
Apache projects based on MINA, and they are working pretty well. This is some kind of
guarantee that ypu won't have to spend hours on some cryptic errors in your own
implementation of the network layer.
Existing supportd protocols MINA comes with various implemented existing protocols :
HTTP, XML, TCP, LDAP, DHCP, NTP, DNS, XMPP, SSH, FTP... At some point, MINA can
be seen not only as a NIO framework, but as a network layer with some protocol
implementation. One of MINA feature in the near future is to offer a collection of existing
protocol you can use.

Features
MINA is a simple yet full-featured network application framework which provides:
Unified API for various transport types:
TCP/IP & UDP/IP via Java NIO
Serial communication (RS232) via RXTX
In-VM pipe communication
You can implement your own!
Filter interface as an extension point; similar to Servlet filters
Low-level and high-level API:
Low-level: uses ByteBuffers
High-level: uses user-defined message objects and codecs
Highly customizable thread model:
Single thread
One thread pool
More than one thread pools (i.e. SEDA)
Out-of-the-box SSL · TLS · StartTLS support using Java 5 SSLEngine

Overload shielding & traffic throttling
Unit testability using mock objects
JMX managability
Stream-based I/O support via StreamIoHandler
Integration with well known containers such as PicoContainer and Spring
Smooth migration from Netty, an ancestor of Apache MINA.

First Steps
We will show you how easy it is to use MINA, running a very simple example provided with the
MINA package.
The first thing you have to do is to setup your evnvironment when you want to use MINA in your
application. We will describe what you need to install and how to run a MINA program. Nothing
fancy, just a first taste of MINA...

Download
First, you have to download the latest MINA release from Downloads Section. Just take the latest
version, unless you have very good reasons not to do so...
Generally speaking, if you are going to use Maven to build your project, you won't even have to
download anything, as soon as you will depend on a repository which already contains the MINA
libraries : you just tell your Maven poms that you want to use the MINA jars you need.

What's inside
After the download is complete, extract the content of tar.gz or zip file to local hard drive. The
downloaded compressed file has following contents
On UNIX system, type :
$ tar xzpf apache-mina-2.0.7-tar.gz
In the apache-mina-2.0.7 directory, you will get :
|
+- dist
+- docs
+- lib
+- src
+- LICENSE.txt
+- LICENSE.jzlib.txt
+- LICENSE.ognl.txt
+- LICENSE.slf4j.txt
+- LICENSE.springframework.txt
+- NOTICE.txt

Content Details
dist - Contains jars for the MINA library code
docs - Contains API docs and Code xrefs
lib - Contains all needed jars for all the libraries needed for using MINA
Additional to these, the base directory has couple of license and notice files

Running your first MINA program
Well, we have downloaded the release, let's run our first MINA example, shipped with the release.
Put the following jars in the classpath
mina-core-2.0.7.jar
mina-example-2.0.7.jar
slf4j-api-1.6.6.jar
slf4j-log4j12-1.6.6.jar
log4j-1.2.17.jar
**Logging Tip** * Log4J 1.2 users: slf4j-api.jar, slf4j-log4j12.jar, and Log4J 1.2.x * Log4J 1.3
users: slf4j-api.jar, slf4j-log4j13.jar, and Log4J 1.3.x * java.util.logging users: slf4j-api.jar and
slf4j-jdk14.jar IMPORTANT: Please make sure you are using the right slf4j-*.jar that matches to
your logging framework. For instance, slf4j-log4j12.jar and log4j-1.3.x.jar can not be used
together, and will malfunction. If you don't need a logging framework you can use slf4j-nop.jar for
no logging or slf4j-simple.jar for very basic logging.
On the command prompt, issue the following command :
$ java org.apache.mina.example.gettingstarted.timeserver.MinaTimeServer
This shall start the server. Now telnet and see the program in action
Issue following command to telnet
telnet 127.0.0.1 9123
Well, we have run our first MINA program. Please try other sample programs shipped with MINA
as examples.

Summary
In this chapter, we looked at MINA based Application Architecture, for Client as well as Server.
We also touched upon the implementation of Sample TCP Server/Client, and UDP Server and
Client.
In the chapters to come we shall discuss about MINA Core constructs and advanced topics

Chapter 2 - Basics
In Chapter 1, we had a brief glimpse of Apache MINA. In this chapter we shall have a look at
Client/Server Architecture and details on working out a MINA based Server and Client.
We will also expose some very simple Servers and Clients, based on TCP and UDP.

MINA based Application Architecture
It's the question most asked : 'How does a MINA based application look like'? In this article lets
see what's the architecture of MINA based application. Have tried to gather the information from
presentations based on MINA.
A Bird's Eye View :

Here, we can see that MINA is the glue between your application (be it a client or a server) and
the underlying network layer, which can be based on TCP, UDP, in-VM comunication or even a
RS-232C serial protocol for a client.
You just have to design your application on top of MINA without having to handle all the
complexity of the newtork layer.
Lets take a deeper dive into the details now. The following image shows a bit more the internal of
MINA, and what are each of the MINA components doing :

(The image is from Emmanuel Lécharny presentation MINA in real life (ApacheCon EU 2009))
Broadly, MINA based applications are divided into 3 layers
I/O Service - Performs actual I/O
I/O Filter Chain - Filters/Transforms bytes into desired Data Structures and vice-versa
I/O Handler - Here resides the actual business logic
So, in order to create a MINA based Application, you have to :
1. Create an I/O service - Choose from already available Services (*Acceptor) or create
your own
2. Create a Filter Chain - Choose from already existing Filters or create a custom Filter for
transforming request/response
3. Create an I/O Handler - Write business logic, on handling different messages
This is pretty much it.
You can get a bit deeper by reading those two pages :
Server Architecture
Client Architecture
Of course, MINA offers more than just that, and you will robably have to take care of many oher
aspects, like the messages encoding/decoding, the network configuration how to scale up, etc...
We will have a further look at those aspects in the next chapters.

Server Architecture
We have exposed the MINA Application Architecture in the previous section. Let's now focus on
the Server Architecture. Basically, a Server listens on a port for incoming requests, process them
and send replies. It also creates and handles a session for each client (whenever we have a TCP or
UDP based protocol), this will be explain more extensively in the chapter 4.

IOAcceptor listens on the network for incoming connections/packets
For a new connection, a new session is created and all subsequent request from IP
Address/Port combination are handled in that Session
All packets received for a Session, traverses the Filter Chain as specified in the diagram.
Filters can be used to modify the content of packets (like converting to Objects,
adding/removing information etc). For converting to/from raw bytes to High Level Objects,
PacketEncoder/Decoder are particularly useful
Finally the packet or converted object lands in IOHandler. IOHandlers can be used to fulfill
business needs.

Session creation
Whenever a client connects on a MINA server, we will create a new session to store persistent
data into it. Even if the protocol is not connected, this session will be created. The following
schema shows how MINA handles incoming connections :

Incoming connections handling
We will now explain how MINA processes incoming messages.
Assuming that a session has been created, any new incoming message will result in a selector
being waken up

Client Architecture
We had a brief look at MINA based Server Architecture, lets see how Client looks like. Clients
need to connect to a Server, send message and process the responses.

Client first creates an IOConnector (MINA Construct for connecting to Socket), initiates a
bind with Server
Upon Connection creation, a Session is created and is associated with Connection

Application/Client writes to the Session, resulting in data being sent to Server, after
traversing the Filter Chain
All the responses/messages received from Server are traverses the Filter Chain and lands at
IOHandler, for processing

Sample TCP Server
This tutorial will walk you through the process of building a MINA based program. This tutorial
will walk through building a time server. The following prerequisites are required for this tutorial:
MINA 2.x Core
JDK 1.5 or greater
SLF4J 1.3.0 or greater
Log4J 1.2 users: slf4j-api.jar, slf4j-log4j12.jar, and Log4J 1.2.x
Log4J 1.3 users: slf4j-api.jar, slf4j-log4j13.jar, and Log4J 1.3.x
java.util.logging users: slf4j-api.jar and slf4j-jdk14.jar
IMPORTANT: Please make sure you are using the right slf4j-*.jar that matches to your
logging framework.
For instance, slf4j-log4j12.jar and log4j-1.3.x.jar can not be used together, and will malfunction.
We have tested this program on both Windows? 2000 professional and linux. If you have any
problems getting this program to work, please do not hesitate to contact us in order to talk to the
MINA developers. Also, this tutorial has tried to remain independent of development
environments (IDE, editors..etc). This tutorial will work with any environment that you are
comfortable with. Compilation commands and steps to execute the program have been removed
for brevity. If you need help learning how to either compile or execute java programs, please
consult the Java tutorial.

Writing the MINA time server
We will begin by creating a file called MinaTimeServer.java. The initial code can be found below:
public class MinaTimeServer {
public static void main(String[] args) {
// code will go here next
}
}
This code should be straightforward to all. We are simply defining a main method that will be
used to kick off the program. At this point, we will begin to add the code that will make up our
server. First off, we need an object that will be used to listen for incoming connections. Since this
program will be TCP/IP based, we will add a SocketAcceptor to our program.
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer
{

public static void main( String[] args )
{
IoAcceptor acceptor = new NioSocketAcceptor();
}
}
With the NioSocketAcceptor class in place, we can go ahead and define the handler class and bind
the NioSocketAcceptor to a port :
import java.net.InetSocketAddress;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer
{
private static final int PORT = 9123;
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.bind( new InetSocketAddress(PORT) );
}
}
As you see, there is a call to acceptor.setLocalAddress( new InetSocketAddress(PORT) );. This
method defines what host and port this server will listen on. The final method is a call to
IoAcceptor.bind(). This method will bind to the specified port and start processing of remote
clients.
Next we add a filter to the configuration. This filter will log all information such as newly created
sessions, messages received, messages sent, session closed. The next filter is a
ProtocolCodecFilter. This filter will translate binary or protocol specific data into message object
and vice versa. We use an existing TextLine factory because it will handle text base message for
you (you don't have to write the codec part)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer

{
public static void main( String[] args )
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
new
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.bind( new InetSocketAddress(PORT) );
}
}
At this point, we will define the handler that will be used to service client connections and the
requests for the current time. The handler class is a class that must implement the interface
IoHandler. For almost all programs that use MINA, this becomes the workhorse of the program, as
it services all incoming requests from the clients. For this tutorial, we will extend the class
IoHandlerAdapter. This is a class that follows the adapter design pattern which simplifies the
amount of code that needs to be written in order to satisfy the requirement of passing in a class
that implements the IoHandler interface.
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer
{
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
new
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.bind( new InetSocketAddress(PORT) );
}
}
We will now add in the NioSocketAcceptor configuration. This will allow us to make
socket-specific settings for the socket that will be used to accept connections from clients.
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer
{
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
new
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );
}
}
There are 2 new lines in the MinaTimeServer class. These methods set the set the IoHandler, input
buffer size and the idle property for the sessions. The buffer size will be specified in order to tell
the underlying operating system how much room to allocate for incoming data. The second line
will specify when to check for idle sessions. In the call to setIdleTime, the first parameter defines
what actions to check for when determining if a session is idle, the second parameter defines the
length of time in seconds that must occur before a session is deemed to be idle.
The code for the handler is shown below:
import java.util.Date;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
public class TimeServerHandler extends IoHandlerAdapter
{
@Override
public void exceptionCaught( IoSession session, Throwable cause ) throws Exception
{
cause.printStackTrace();
}

@Override
public void messageReceived( IoSession session, Object message ) throws Exception
{
String str = message.toString();
if( str.trim().equalsIgnoreCase("quit") ) {
session.close();
return;
}
Date date = new Date();
session.write( date.toString() );
System.out.println("Message written...");
}
@Override
public void sessionIdle( IoSession session, IdleStatus status ) throws Exception
{
System.out.println( "IDLE " + session.getIdleCount( status ));
}
}
The methods used in this class are exceptionCaught, messageReceived and sessionIdle.
exceptionCaught should always be defined in a handler to process and exceptions that are raised in
the normal course of handling remote connections. If this method is not defined, exceptions may
not get properly reported.
The exceptionCaught method will simply print the stack trace of the error and close the session.
For most programs, this will be standard practice unless the handler can recover from the
exception condition.
The messageReceived method will receive the data from the client and write back to the client the
current time. If the message received from the client is the word "quit", then the session will be
closed. This method will also print out the current time to the client. Depending on the protocol
codec that you use, the object (second parameter) that gets passed in to this method will be
different, as well as the object that you pass in to the session.write(Object) method. If you do not
specify a protocol codec, you will most likely receive a IoBuffer object, and be required to write
out a IoBuffer object.
The sessionIdle method will be called once a session has remained idle for the amount of time
specified in the call acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );.
All that is left to do is define the socket address that the server will listen on, and actually make
the call that will start the server. That code is shown below:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public class MinaTimeServer
{
private static final int PORT = 9123;
public static void main( String[] args ) throws IOException
{
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );
}
}

new

Try out the Time server
At this point, we can go ahead and compile the program. Once you have compiled the program
you can run the program in order to test out what happens. The easiest way to test the program is
to start the program, and then telnet in to the program:
Client Output

Server Output

user@myhost:~> telnet 127.0.0.1 9123
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
hello
Wed Oct 17 23:23:36 EDT 2007
quit
Connection closed by foreign host.
user@myhost:~>

MINA Time server started.
Message written...

What's Next?
Please visit our Documentation page to find out more resources. You can also keep reading other
tutorials.

Sample TCP Client
We have seen the Client Architecture. Lets explore a sample Client implementation.
We shall use Sumup Client as a reference implementation.
We will remove boiler plate code and concentrate on the important constructs. Below the code for
the Client :
public static void main(String[] args) throws Throwable {
NioSocketConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(CONNECT_TIMEOUT);
if (USE_CUSTOM_CODEC) {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
} else {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
}
connector.getFilterChain().addLast("logger", new LoggingFilter());
connector.setHandler(new ClientSessionHandler(values));
IoSession session;
for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME,
PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}

}
// wait until the summation is done
session.getCloseFuture().awaitUninterruptibly();
connector.dispose();
}
To construct a Client, we need to do following
Create a Connector
Create a Filter Chain
Create a IOHandler and add to Connector
Bind to Server
Lets examine each one in detail

Create a Connector
NioSocketConnector connector = new NioSocketConnector();
Here we have created a NIO Socket connector

Create a Filter Chain
if (USE_CUSTOM_CODEC) {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
} else {
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
}
We add Filters to the Filter Chain for the Connector. Here we have added a ProtocolCodec, to the
filter Chain.

Create IOHandler
connector.setHandler(new ClientSessionHandler(values));
Here we create an instance of ClientSessionHandler and set it as a handler for the Connector.

Bind to Server
IoSession session;

for (;;) {
try {
ConnectFuture future = connector.connect(new InetSocketAddress(HOSTNAME,
PORT));
future.awaitUninterruptibly();
session = future.getSession();
break;
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
Thread.sleep(5000);
}
}
Here is the most important stuff. We connect to remote Server. Since, connect is an async task, we
use the ConnectFuture class to know the when the connection is complete. Once the connection is
complete, we get the associated IoSession. To send any message to the Server, we shall have to
write to the session. All responses/messages from server shall traverse the Filter chain and finally
be handled in IoHandler.

Sample UDP Server
We will begin by looking at the code found in the org.apache.mina.example.udp package. To keep
life simple, we shall concentrate on MINA related constructs only.
To construct the server, we shall have to do the following:
1.
2.

Create a Datagram Socket to listen for incoming Client requests (See MemoryMonitor.java)
Create an IoHandler to handle the MINA framework generated events (See
MemoryMonitorHandler.java)
Here is the first snippet that addresses Point# 1:

NioDatagramAcceptor acceptor = new NioDatagramAcceptor();
acceptor.setHandler(new MemoryMonitorHandler(this));
Here, we create a NioDatagramAcceptor to listen for incoming Client requests, and set the
IoHandler.The variable 'PORT' is just an int. The next step is to add a logging filter to the filter
chain that this DatagramAcceptor will use. LoggingFilter is a very nice way to see MINA in
Action. It generate log statements at various stages, providing an insight into how MINA works.
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
chain.addLast("logger", new LoggingFilter());
Next we get into some more specific code for the UDP traffic. We will set the acceptor to reuse the
address

DatagramSessionConfig dcfg = acceptor.getSessionConfig();
dcfg.setReuseAddress(true);acceptor.bind(new InetSocketAddress(PORT));
Of course the last thing that is required here is to call bind().

IoHandler implementation
There are three major events of interest for our Server Implementation
Session Created
Message Received
Session Closed
Lets look at each of them in detail

Session Created Event
@Override
public void sessionCreated(IoSession session) throws Exception {
SocketAddress remoteAddress = session.getRemoteAddress();
server.addClient(remoteAddress);
}
In the session creation event, we just call addClient() function, which internally adds a Tab to the
UI

Message Received Event
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
if (message instanceof IoBuffer) {
IoBuffer buffer = (IoBuffer) message;
SocketAddress remoteAddress = session.getRemoteAddress();
server.recvUpdate(remoteAddress, buffer.getLong());
}
}
In the message received event, we just dump the data received in the message. Applications that
need to send responses, can process message and write the responses onto session in this function.

Session Closed Event
@Override

public void sessionClosed(IoSession session) throws Exception {
System.out.println("Session closed...");
SocketAddress remoteAddress = session.getRemoteAddress();
server.removeClient(remoteAddress);
}
In the Session Closed, event we just remove the Client tab from the UI

Sample UDP Client
Lets look at the client code for the UDP Server from previous section.
To implement the Client we need to do following:
Create Socket and Connect to Server
Set the IoHandler
Collect free memory
Send the Data to the Server
We will begin by looking at the file MemMonClient.java, found in the
org.apache.mina.example.udp.client java package. The first few lines of the code are simple and
straightforward.
connector = new NioDatagramConnector();
connector.setHandler( this );
ConnectFuture connFuture = connector.connect( new InetSocketAddress("localhost",
MemoryMonitor.PORT ));
Here we create a NioDatagramConnector, set the handler and connect to the server. One gotcha I
ran into was that you must set the host in the InetSocketAddress object or else nothing seems to
work. This example was mostly written and tested on a Windows XP machine, so things may be
different elsewhere. Next we will wait for acknowledgment that the client has connected to the
server. Once we know we are connected, we can start writing data to the server. Here is that code:
connFuture.addListener( new IoFutureListener(){
public void operationComplete(IoFuture future) {
ConnectFuture connFuture = (ConnectFuture)future;
if( connFuture.isConnected() ){
session = future.getSession();
try {
sendData();
} catch (InterruptedException e) {
e.printStackTrace();
}

} else {
log.error("Not connected...exiting");
}
}
});
Here we add a listener to the ConnectFuture object and when we receive a callback that the client
has connected, we will start to write data. The writing of data to the server will be handled by a
method called sendData. This method is shown below:
private void sendData() throws InterruptedException {
for (int i = 0; i < 30; i++) {
long free = Runtime.getRuntime().freeMemory();
IoBuffer buffer = IoBuffer.allocate(8);
buffer.putLong(free);
buffer.flip();
session.write(buffer);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
throw new InterruptedException(e.getMessage());
}
}
}
This method will write the amount of free memory to the server once a second for 30 seconds.
Here you can see that we allocate a IoBuffer large enough to hold a long variable and then place
the amount of free memory in the buffer. This buffer is then flipped and written to the server.
Our UDP Client implementation is complete.

Summary
In this chapter, we looked at MINA based Application Architecture, for Client as well as Server.
We also touched upon the implementation of Sample TCP Server/Client, and UDP Server and
Client.
In the chapters to come we shall discuss about MINA Core constructs and advanced topics

Chapter 3 - IoService
A MINA IoService - as seen in the application architecture chapter, is the base class supporting all
the IO services, either from the server side or the client side.
It will handle all the interaction with your application, and with the remote peer, send and receive
messages, manage sessions, conections, etc.
It's an interface, which is implemented as an IoAcceptor for the server side, and IoConnector for
the client side.
We will expose the interface in those chapters :

IoService Introduction
IoService provides basic I/O Service and manages I/O Sessions within MINA. Its one of the most
crucial part of MINA Architecture. The implementing classes of IoService and child interface, are
where most of the low level I/O operations are handled.

IoService Mind Map
Let's try to see what are the responsibilities of the IoService and it implementing class
AbstractIoService. Let's take a slightly different approach of first using a Mind Map and then
jump into the inner working. The Mind Map was created using XMind.

Responsabilities
As seen in the previous graphic, The IoService has many responsabilities :
sessions management : Creates and deletes sessions, detect idleness.
filter chain management : Handles the filtr chain, allowing the user to change the chain on the
fly
handler invocation : Calls the handler when some new message is received, etc
statistics management : Updates the number of messages sent, bytes sent, and many others
listeners management : Manages the Listeners a suer can set up
communication management : Handles the transmission of data, in both side
All those aspects will be described in the following chapters.

Interface Details
IoService is the base interface for all the IoConnector's and IoAcceptor's that provides I/O services
and manages I/O sessions. The interface has all the functions need to perform I/O related
operations.
Lets take a deep dive into the various methods in the interface
getTransportMetadata()
addListener()
removeListener()
isDisposing()
isDisposed()
dispose()

getHandler()
setHandler()
getManagedSessions()
getManagedSessionCount()
getSessionConfig()
getFilterChainBuilder()
setFilterChainBuilder()
getFilterChain()
isActive()
getActivationTime()
broadcast()
setSessionDataStructureFactory()
getScheduledWriteBytes()
getScheduledWriteMessages()
getStatistics()

getTransportMetadata()
This method returns the Transport meta-data the IoAcceptor or IoConnector is running. The
typical details include provider name (nio, apr, rxtx), connection type (connectionless/connection
oriented) etc.

addListener
Allows to add a IoServiceListener to listen to specific events related to IoService.

removeListener
Removes specified IoServiceListener attached to this IoService.

isDisposing
This method tells if the service is currently being disposed. As it can take a while, it's useful to
know the current status of the service.

isDisposed
This method tells if the service has been disposed. A service will be considered as disposed only
when all the resources it has allocated have been released.

dispose
This method releases all the resources the service has allocated. As it may take a while, the user
should check the service status using the isDisposing() and isDisposed() to know if the service is
now disposed completely.
Always call dispose() when you shutdown a service !

getHandler
Returns the IoHandler associated with the service.

setHandler
Sets the IoHandler that will be responsible for handling all the events for the service. The handler
contains your application logic !

getManagedSessions
Returns the map of all sessions which are currently managed by this service. A managed session is
a session which is added to the service listener. It will be used to process the idle sessions, and
other session aspects, depending on the kind of listeners a user adds to a service.

getManagedSessionCount
Returns the number of all sessions which are currently managed by this service.

getSessionConfig
Returns the session configuration.

getFilterChainBuilder
Returns the Filter chain builder. This is useful if one wants to add some new filter that will be
injected when the sessions will be created.

setFilterChainBuilder
Defines the Filter chain builder to use with the service.

getFilterChain
Returns the current default Filter chain for the service.

isActive
Tells if the service is active or not.

getActivationTime
Returns the time when this service was activated. It returns the last time when this service was
activated if the service is not anymore active.

broadcast
Writes the given message to all the managed sessions.

setSessionDataStructureFactory
Sets the IoSessionDataStructureFactory that provides related data structures for a new session
created by this service.

getScheduledWriteBytes
Returns the number of bytes scheduled to be written (ie, the bytes stored in memory waiting for
the socket to be ready for write).

getScheduledWriteMessages
Returns the number of messages scheduled to be written (ie, the messages stored in memory
waiting for the socket to be ready for write).

getStatistics
Returns the IoServiceStatistics object for this service.

IoService Details
IoService is an interface that is implemented by the two most important classes in MINA :
IoAcceptor
IoConnector
In order to build a server, you need to select an implementation of the IoAcceptor interface. For
client applications, you need to implement an implementation of the IoConnector interface.

IoAcceptor
Basically, this interface is named because of the accept() method, responsible for the creation of
new connections between a client and the server. The server accepts incoming connection
requests.
At some point, we could have named this interface 'Server' (and this is the new name in the
coming MINA 3.0).
As we may deal with more than one kind of transport (TCP/UDP/...), we have more than one
implementation for this interface. It would be very unlikely that you need to implement a new one.
We have many of those implementing classes
NioSocketAcceptor : the non-blocking Socket transport IoAcceptor
NioDatagramAcceptor : the non-blocking UDP transport IoAcceptor
AprSocketAcceptor : the blocking Socket transport IoAcceptor, based on APR
VmPipeSocketAcceptor : the in-VM IoAcceptor
Just pick the one that fit your need.
Here is the class diagram for the IoAcceptor interfaces and classes :

IoConnector
As we have to use an IoAcceptor for servers, you have to implement the IoConnector for clients.
Again, we have many implementation classes :
NioSocketConnector : the non-blocking Socket transport IoConnector
NioDatagramConnector : the non-blocking UDP transport IoConnector
AprSocketConnector : the blocking Socket transport IoConnector, based on APR
ProxyConnector : a IoConnector providing proxy support
SerialConnector : a IoConnector for a serial transport
VmPipeConnector : the in-VM IoConnector
Just pick the one that fit your need.
Here is the class diagram for the IoConnector interfaces and classes :

Acceptor
In order to build a server, you need to select an implementation of the IoAcceptor interface.

IoAcceptor
Basically, this interface is named because of the accept() method, responsible for the creation of
new connection between a client and the server. The server accepts incoming connection request.
At some point, we could have named this interface 'Server'.
As we may deal with more than one kind of transport (TCP/UDP/...), we have more than one
implementation for this interface. It would be very unlikely that you need to implement a new one.
We have many of those implementing classes
NioSocketAcceptor : the non-blocking Socket transport IoAcceptor
NioDatagramAcceptor : the non-blocking UDP transport IoAcceptor
AprSocketAcceptor : the blocking Socket transport IoAcceptor, based on APR
VmPipeSocketAcceptor : the in-VM IoAcceptor
Just pick the one that fit your need.
Here is the class diagram for the IoAcceptor interfaces and classes :

Creation
You first have to select the type of IoAcceptor you want to instanciate. This is a choice you will
made early in the process, as it all boils down to which network protocol you will use. Let's see
with an example how it works :
public TcpServer() throws IOException {
// Create a TCP acceptor
IoAcceptor acceptor = new NioSocketAcceptor();
// Associate the acceptor to an IoHandler instance (your application)
acceptor.setHandler(this);
// Bind : this will start the server...
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Server started...");
}
That's it ! You have created a TCP server. If you want to start an UDP server, simply replace the
first line of code :

...
// Create an UDP acceptor
IoAcceptor acceptor = new NioDatagramAcceptor();
...

Disposal
The service can be stopped by calling the dispose() method. The service will be stopped only
when all the pending sessions have been processed :
// Stop the service, waiting for the pending sessions to be inactive
acceptor.dispose();
You can also wait for every thread being executed to be properly completed by passing a boolean
parameter to this method :
// Stop the service, waiting for the processing session to be properly completed
acceptor.dispose( true );

Status
You can get the IoService status by calling one of the following methods :
isActive() : true if the service can accept incoming requests
isDisposing() : true if the dispose() method has been called. It does not tell if the service is
actually stopped (some sessions might be processed)
isDisposed() : true if the dispose(boolean) method has been called, and the executing threads
have been completed.

Managing the IoHandler
You can add or get the associated IoHandler when the service has been instanciated. Youjust have
to call the setHandler(IoHandler) or getHandler() methods.

Managing the Filters chain
if you want to manage the filters chain, you will have to call the getFilterChain() method. Here is
an example :
// Add a logger filter

DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
chain.addLast("logger", new LoggingFilter());
You can also create the chain before and set it into the service :
// Add a logger filter
DefaultIoFilterChainBuilder chain = new DefaultIoFilterChainBuilder();
chain.addLast("logger", new LoggingFilter());
// And inject the created chain builder in the service
acceptor.setFilterChainBuilder(chain);

Connector
For client applications, you need to implement an implementation of the IoConnector interface.

IoConnector
As we have to use an IoAcceptor for servers, you have to implement the IoConnector. Again, we
have many implementation classes :
NioSocketConnector : the non-blocking Socket transport Connector
NioDatagramConnector : the non-blocking UDP transport * Connector*
AprSocketConnector : the blocking Socket transport * Connector*, based on APR
ProxyConnector : a Connector providing proxy support
SerialConnector : a Connector for a serial transport
VmPipeConnector : the in-VM * Connector*
Just pick the one that fit your need.
Here is the class diagram for the IoConnector interfaces and classes :

Chapter 4 - Session
The Session is at the heart of MINA : every time a client connects to the server, a new session is
created, and will be kept in memory until the client is disconnected.
A session is used to store persistent informations about the connection, plus any kind of
information the server might need to use during the request processing, and eventually during the
whole session life.

Session state
A session has a state, which will evolve during time.
Connected : the session has been created and is available
Idle : the session hasn't processed any request for at least a period of time (this period is
configurable)
Idle for read : no read has actually been made for a period of time
Idle for write : no write has actually been made for a period of time
Idle for both : no read nor write for a period of time
Closing : the session is being closed (the remaining messages are being flushed, cleaning up
is not terminated)
Closed : The session is now closed, nothing else can be done to revive it.
The following state diagram exposes all the possible states and transitions :

Configuration
Many different parameters can be set for a specific session :
receive buffer size
sending buffer size
Idle time
Write timeOut
plus other configuration, depending on the transport type used (see Chapter 6 - Transports)

Managing user-defined attributes
It might be necessary to store some data which may be used later. This is done using the dedicated
data structure associated which each session. This is a key-value association, which can store any
type of data the developer might want to keep remanent.
For instance, if you want to track the number of request a user has sent since the session has been
created, it's easy to store it into this map: just create a key that will be associated with this value.
...
int counterValue = session.getAttribute( "counter" );
session.setAttribute( "counter", counterValue + 1 );
...
We have a way to handle stored Attributes into the session : an Attribute is a key/value pair, which
can be added, removed and read from the session's container.
This container is created automatically when the session is created, and will be disposed when the
session is terminated.

Defining the container
As we said, this container is a key/value container, which default to a Map, but it's also possible to
define another data structure if one want to handle long lived data, or to avoid storing all those
data in memory if they are large : we can implement an interface and a factory that will be used to
create this container when the session is created.
This snippet of code shows how the container is created during the session initialization :
protected final void initSession(IoSession session,
IoFuture future, IoSessionInitializer sessionInitializer) {

...
try {
((AbstractIoSession) session).setAttributeMap(session.getService()
.getSessionDataStructureFactory().getAttributeMap(session));
} catch (IoSessionInitializationException e) {
throw e;
} catch (Exception e) {
throw new IoSessionInitializationException(
"Failed to initialize an attributeMap.", e);
}
...
and here is the factory interface we can implement if we want to define another kind of container :
public interface IoSessionDataStructureFactory {
/**
* Returns an {@link IoSessionAttributeMap} which is going to be associated
* with the specified session. Please note that the returned
* implementation must be thread-safe.
*/
IoSessionAttributeMap getAttributeMap(IoSession session) throws Exception;
}

Filter chain
Each session is associated with a chain of filters, which will be processed when an incoming
request or an outgoing message is received or emitted. Those filters are specific for each session
individually, even if most of the cases, we will use the very same chain of filters for all the
existing sessions.
However, it's possible to dynamically modify the chain for a single session, for instance by adding
a Logger Filter in the chain for a specific session.

Statistics
Each session also keep a track of records about what has been done for the session :
number of bytes received/sent
number of messages received/sent
Idle status
throughput
and many other useful informations.

Handler
Last, not least, a session is attached to a Handler, in charge of dispatching the messages to your
application. This handler will also send pack response by using the session, simply by calling the
write() method :
...
session.write(  );
...

Chapter 5 - Filters
IoFilter is one of the MINA core constructs that serves a very important role. It filters all I/O
events and requests between IoService and IoHandler. If you have an experience with web
application programming, you can safely think that it's a cousin of Servlet filter. Many
out-of-the-box filters are provided to accelerate network application development pace by
simplifying typical cross-cutting concerns using the out-of-the-box filters such as:
LoggingFilter logs all events and requests.
ProtocolCodecFilter converts an incoming ByteBuffer into message POJO and vice versa.
CompressionFilter compresses all data.
SSLFilter adds SSL - TLS - StartTLS support.
and many more!
In this tutorial, we will walk through how to implement an IoFilter for a real world use case. It's
easy to implement an IoFilter in general, but you might also need to know specifics of MINA
internals. Any related internal properties will be explained here.
Filters already present
We have many filters already written. The following table list all the existing filters, with a short
description of their usage.
Filter

class

Description

Blacklist

BlacklistFilter

Blocks connections from
blacklisted remote addresses

Buffered Write

BufferedWriteFilter

Buffers outgoing requests
like
the
BufferedOutputStream does

Compression

CompressionFilter

ConnectionThrottle

ConnectionThrottleFilter

ErrorGenerating

ErrorGeneratingFilter

Executor

ExecutorFilter

FileRegionWrite

FileRegionWriteFilter

KeepAlive

KeepAliveFilter

Logging

LoggingFilter

Logs event messages, like
MessageReceived,
MessageSent,
SessionOpened, ...

MDC Injection

MdcInjectionFilter

Inject
key
IoSession
properties into the MDC

Noop

NoopFilter

A filter that does nothing.
Useful for tests.

Profiler

ProfilerTimerFilter

Profile event messages, like
MessageReceived,
MessageSent,
SessionOpened, ...

ProtocolCodec

ProtocolCodecFilter

A filter in charge of encoding
and decoding messages

Proxy

ProxyFilter

Reference counting

ReferenceCountingFilter

RequestResponse

RequestResponseFilter

SessionAttributeInitializing

SessionAttributeInitializingFilter

StreamWrite

StreamWriteFilter

SslFilter

SslFilter

WriteRequest

WriteRequestFilter

Keeps track of the number of
usages of this filter

Overriding Events Selectively
You can extend IoAdapter instead of implementing IoFilter directly. Unless overriden, any
received events will be forward to the next filter immediately:
public class MyFilter extends IoFilterAdapter {
@Override
public void sessionOpened(NextFilter nextFilter, IoSession session) throws Exception {
// Some logic here...
nextFilter.sessionOpened(session);
// Some other logic here...
}
}

Transforming a Write Request
If you are going to transform an incoming write request via IoSession.write(), things can get pretty
tricky. For example, let's assume your filter transforms HighLevelMessage to LowLevelMessage
when IoSession.write() is invoked with a HighLevelMessage object. You could insert appropriate
transformation code to your filter's filterWrite() method and think that's all. However, you have to
note that you also need to take care of messageSent event because an IoHandler or any filters next
to yours will expect messageSent() method is called with HighLevelMessage as a parameter,
because it's irrational for the caller to get notified that LowLevelMessage is sent when the caller
actually wrote HighLevelMessage. Consequently, you have to implement both filterWrite() and
messageSent() if your filter performs transformation.

Please also note that you still need to implement similar mechanism even if the types of the input
object and the output object are identical (e.g. CompressionFilter) because the caller of
IoSession.write() will expect exactly what he wrote in his or her messageSent() handler method.
Let's assume that you are implementing a filter that transforms a String into a char[]. Your filter's
filterWrite() will look like the following:
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {
nextFilter.filterWrite(
session, new DefaultWriteRequest(
((String)
request.getMessage()).toCharArray(),
request.getFuture(),
request.getDestination()));
}
Now, we need to do the reverse in messageSent():
public void messageSent(NextFilter nextFilter, IoSession session, Object message) {
nextFilter.messageSent(session, new String((char[]) message));
}
What about String-to-ByteBuffer transformation? We can be a little bit more efficient because we
don't need to reconstruct the original message (String). However, it's somewhat more complex
than the previous example:
public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {
String m = (String) request.getMessage();
ByteBuffer newBuffer = new MyByteBuffer(m, ByteBuffer.wrap(m.getBytes());
nextFilter.filterWrite(
session,
request.getDestination()));
}

new

WriteRequest(newBuffer,

request.getFuture(),

public void messageSent(NextFilter nextFilter, IoSession session, Object message) {
if (message instanceof MyByteBuffer) {
nextFilter.messageSent(session, ((MyByteBuffer) message).originalValue);
} else {
nextFilter.messageSent(session, message);
}
}
private static class MyByteBuffer extends ByteBufferProxy {
private final Object originalValue;
private MyByteBuffer(Object originalValue, ByteBuffer encodedValue) {
super(encodedValue);
this.originalValue = originalValue;

}
}
If you are using MINA 2.0, it will be somewhat different from 1.0 and 1.1. Please refer to
CompressionFilter and RequestResponseFilter meanwhile.

Be Careful When Filtering sessionCreated Event
sessionCreated is a special event that must be executed in the I/O processor thread (see
Configuring Thread Model). Never forward sessionCreated event to the other thread.
public void sessionCreated(NextFilter nextFilter, IoSession session) throws Exception {
// ...
nextFilter.sessionCreated(session);
}
// DON'T DO THIS!
public void sessionCreated(final NextFilter nextFilter, final IoSession session) throws Exception {
Executor executor = ...;
executor.execute(new Runnable() {
nextFilter.sessionCreated(session);
});
}

Watch out the Empty Buffers!
MINA uses an empty buffer as an internal signal at a couple of cases. Empty buffers sometimes
become a problem because it's a cause of various exceptions such as IndexOutOfBoundsException.
This section explains how to avoid such a unexpected situation.
ProtocolCodecFilter uses an empty buffer (i.e. buf.hasRemaining() = 0) to mark the end of the
message. If your filter is placed before the ProtocolCodecFilter, please make sure your filter
forward the empty buffer to the next filter if your filter implementation can throw a unexpected
exception if the buffer is empty:
public void messageSent(NextFilter nextFilter, IoSession session, Object message) {
if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) {
nextFilter.messageSent(nextFilter, session, message);
return;
}
...
}

public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest request) {
Object message = request.getMessage();
if (message instanceof ByteBuffer && !((ByteBuffer) message).hasRemaining()) {
nextFilter.filterWrite(nextFilter, session, request);
return;
}
...
}
Do we always have to insert the if block for every filters? Fortunately, you don't have to. Here's
the golden rule of handling empty buffers:
If your filter works without any problem even if the buffer is empty, you don't need to add the
if blocks at all.
If your filter is placed after ProtocolCodecFilter, you don't need to add the if blocks at all.
Otherwise, you need the if blocks.
If you need the if blocks, please remember you don't always need to follow the example above.
You can check if the buffer is empty wherever you want as long as your filter doesn't throw a
unexpected exception.

Chapter 6 - Transports
APR Transport
Introduction
APR (Apache Portable Runtime) provide superior scalability, performance, and better integration
with native server technologies. APR transport is supported by MINA. In this section, we shall
touch base upon how to use APR transport with MINA. We shall the Time Server example for this.

Pre-requisite
APR transport depends following components
APR library - Download/install appropriate library for
http://www.apache.org/dist/tomcat/tomcat-connectors/native/
JNI wrapper (tomcat-apr-5.5.23.jar) The jar is shipped with release
Put the native library in PATH

the

platform

from

Using APR Transport
Refer Time Server example for complete source
Lets see how NIO based Time server implementation looks like
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
TextLineCodecFactory( Charset.forName( "UTF-8" ))));
acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );

new

Lets see how to use APR Transport
IoAcceptor acceptor = new AprSocketAcceptor();
acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
acceptor.getFilterChain().addLast(
"codec",
new
ProtocolCodecFilter(
TextLineCodecFactory( Charset.forName( "UTF-8" ))));

new

acceptor.setHandler( new TimeServerHandler() );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
acceptor.bind( new InetSocketAddress(PORT) );
We just change the NioSocketAcceptor to AprSocketAcceptor. That's it, now our Time Server
shall use APR transport.
Rest complete process remains same.

Serial Transport
With the MINA 2.0 you are able to connect to serial port like you use to connect to a TCP/IP port
with MINA.

Getting MINA 2.0
You you can download the latest built version (2.0.2).
If you prefer to build the code from the trunk, and need assistance to do so, please consult the
Developer Guide.

Prerequisite
Useful Information
Before accessing serial port from a Java program you need a native library (.DLL or .so depending
of
your
OS).
MINA
use
the
one
from
RXTX.org
:
ftp://ftp.qbang.org/pub/rxtx/rxtx-2.1-7-bins-r2.zip.
Just put the good .dll or .so in the jre/lib/i386/ path of your JDK/JRE or use the
-Djava.library.path= argument for specify where you placed the native libraries
Useful Information
The mina-transport-serial jar is not included in the full distribution. You can download it from here

Connecting to a serial port
Serial communication for MINA provide only an IoConnector, due to the point-to-point nature of
the communication media.
At this point you are supposed to have already read the MINA tutorial.
Now for connecting to a serial port you need a SerialConnector :
// create your connector
IoConnector connector = new SerialConnector()
connector.setHandler( ... here your buisness logic IoHandler ... );
Nothing very different of a SocketConnector.
Let's create an address for connecting to our serial port.
SerialAddress portAddress=new SerialAddress( "/dev/ttyS0", 38400, 8, StopBits.BITS_1,
Parity.NONE, FlowControl.NONE );
The first parameter is your port identifier. For Windows computer, the serial ports are called
"COM1", "COM2", etc... For Linux and some other Unix : "/dev/ttyS0", "/dev/ttyS1",
"/dev/ttyUSB0".
The remaining parameters are depending of the device you are driving and the supposed
communications characteristics.
the baud rate
the data bits
the parity
the flow control mecanism
Once it's done, connect the connector to the address :
ConnectFuture future = connector.connect( portAddress );
future.await();
IoSession sessin = future.getSession();
And voila ! Everything else is as usual, you can plug your filters and codecs. for learn more about
RS232 : http://en.wikipedia.org/wiki/RS232

Chapter 7 - Handler
Handles all I/O events fired by MINA. The interface is hub of all activities done at the end of the
Filter Chain.
IoHandler has following functions
sessionCreated
sessionOpened
sessionClosed
sessionIdle
exceptionCaught
messageReceived
messageSent

sessionCreated Event
Session Created event is fired when a new connection is created. For TCP its the result of
connection accept, and for UDP this is generated when a UDP packet is received. This function
can be used to initialize session attributes, and perform one time activities for a particular
connection.
This function is invoked from the I/O processor thread context, hence should be implemented in a
way that it consumes minimal amount of time, as the same thread handles multiple sessions.

sessionOpened Event
Session opened event is invoked when a connection is opened. Its is always called after
sessionCreated event. If a thread model is configured, this function is called in a thread other than
the I/O processor thread.

sessionClosed Event
Session Closed event is closed, when a session is closed. Session cleaning activities like cash
cleanup can be performed here.

sessionIdle Event
Session Idle event is fired when a session becomes idle. This function is not invoked for UDP
transport.

exceptionCaught Event
This functions is called, when an Exception is thrown by user code or by MINA. The connection
is closed, if its an IOException.

messageReceived Event
Message Received event is fired whenever a message is received. This is where the most of the
processing of an application happens. You need to take care of all the message type you expect
here.

messageSent Event
Message Sent event is fired, whenever a message aka response has been sent(calling
IoSession.write()).

Part II - MINA Core

Chapter 8 - IoBuffer
A byte buffer used by MINA applications.
This is a replacement for ByteBuffer. MINA does not use NIO ByteBuffer directly for two
reasons:
It doesn't provide useful getters and putters such as fill, get/putString, and get/putAsciiInt() .
It is difficult to write variable-length data due to its fixed capacity
This will change in MINA 3. The main reason why MINA has its own wrapper on top of nio
ByteBuffer is to have extensible buffers. This was a very bad decision. Buffers are just buffers : a
temporary place to store temporary data, before it is used. Many other solutions exist, like defining
a wrapper which relies on a list of NIO ByteBuffers, instead of copying the existing buffer to a
bigger one just because we want to extend the buffer capacity.
It might also be more comfortable to use an InputStream instead of a byte buffer all along the
filters, as it does not imply anything about the nature of the stored data : it can be a byte array,
strings, messages...
Last, not least, the current implementation defeat one of the target : zero-copy strategy (ie, once
we have read the data from the socket, we want to avoid a copy being done later). As we use
extensible byte buffers, we will most certainly copy those data if we have to manage big messages.

Assuming that the MINA ByteBuffer is just a wrapper on top of NIO ByteBuffer, this can be a real
problem when using direct buffers.

IoBuffer Operations
Allocating a new Buffer
IoBuffer is an abstract class, hence can't be instantiated directly. To allocate IoBuffer, we need to
use one of the two allocate() methods.
// Allocates a new buffer with a specific size, defining its type (direct or heap)
public static IoBuffer allocate(int capacity, boolean direct)
// Allocates a new buffer with a specific size
public static IoBuffer allocate(int capacity)
The allocate() method takes one or two arguments. The first form takes two arguments :
capacity - the capacity of the buffer
direct - type of buffer. true to get direct buffer, false to get heap buffer
The default buffer allocation is handled by SimpleBufferAllocator
Alternatively, following form can also be used
// Allocates heap buffer by default.
IoBuffer.setUseDirectBuffer(false);
// A new heap buffer is returned.
IoBuffer buf = IoBuffer.allocate(1024);
When using the second form, don't forget to set the default buffer type before, otherwise you will
get Heap buffers by default.

Creating Auto Expanding Buffer
Creating auto expanding buffer is not very easy with java NIO API's, because of the fixed size of
the buffers. Having a buffer, that can auto expand on needs is a big plus for networking
applications. To address this, IoBuffer has introduced the autoExpand property. It automatically
expands its capacity and limit value.
Lets see how to create an auto expanding buffer :
IoBuffer buffer = IoBuffer.allocate(8);
buffer.setAutoExpand(true);

buffer.putString("12345678", encoder);
// Add more to this buffer
buffer.put((byte)10);
The underlying ByteBuffer is reallocated by IoBuffer behind the scene if the encoded data is
larger than 8 bytes in the example above. Its capacity will double, and its limit will increase to the
last position the string is written. This behavior is very similar to the way StringBuffer class
works.
This mechanism is very likely to be removed from MINA 3.0, as it's not really the best way to
handle increased buffer size. It should be replaced by something like a InputStream hiding a list or
an array of fixed sized ByteBuffers.

Creating Auto Shrinking Buffer
There are situations which calls for releasing additionally allocated bytes from the buffer, to
preserve memory. IoBuffer provides autoShrink property to address the need. If autoShrink is
turned on, IoBuffer halves the capacity of the buffer when compact() is invoked and only 1/4 or
less of the current capacity is being used. To manually shrink the buffer, use shrink() method.
Lets see this in action :
IoBuffer buffer = IoBuffer.allocate(16);
buffer.setAutoShrink(true);
buffer.put((byte)1);
System.out.println("Initial Buffer capacity = "+buffer.capacity());
buffer.shrink();
System.out.println("Initial Buffer capacity after shrink = "+buffer.capacity());
buffer.capacity(32);
System.out.println("Buffer capacity after incrementing capacity to 32 = "+buffer.capacity());
buffer.shrink();
System.out.println("Buffer capacity after shrink= "+buffer.capacity());
We have initially allocated a capacity as 16, and set the autoShrink property as true.
Lets see the output of this :
Initial Buffer capacity = 16
Initial Buffer capacity after shrink = 16
Buffer capacity after incrementing capacity to 32 = 32
Buffer capacity after shrink= 16
Lets take a break and analyze the output
Initial buffer capacity is 16, as we created the buffer with this capacity. Internally this becomes the
minimum capacity of the buffer

After calling shrink(), the capacity remains 16, as capacity shall never be less than minimum
capacity
After incrementing capacity to 32, the capacity becomes 32
Call to shrink(), reduces the capacity to 16, thereby eliminating extra storage
Again, this mechanism should be a default one, without needing to explicitely tells the buffer that
it can shrink.

Buffer Allocation
IoBufferAllocater is responsible for allocating and managing buffers. To have precise control on
the buffer allocation policy, implement the IoBufferAllocater interface.
MINA ships with following implementations of IoBufferAllocater
SimpleBufferAllocator (default) - Create a new buffer every time
CachedBufferAllocator - caches the buffer which are likely to be reused during expansion
With the new available JVM, using cached IoBuffer is very unlikely to improve performances.
You can implement you own implementation of IoBufferAllocator and call setAllocator() on
IoBuffer to use the same.

Chapter 9 - Codec Filter
This tutorial tries to explain why and how to use a ProtocolCodecFilter.

Why use a ProtocolCodecFilter?
TCP guarantess delivery of all packets in the correct order. But there is no guarantee that one
write operation on the sender-side will result in one read event on the receiving side. see
http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly
and
http://en.wikipedia.org/wiki/Nagle%27s_algorithm In MINA terminology: without a
ProtocolCodecFilter one call of IoSession.write(Object message) by the sender can result in
multiple messageReceived(IoSession session, Object message) events on the receiver; and
multiple calls of IoSession.write(Object message) can lead to a single messageReceived
event. You might not encounter this behavior when client and server are running on the same
host (or an a local network) but your applications should be able to cope with this.
Most network applications need a way to find out where the current message ends and where
the next message starts.
You could implement all this logic in your IoHandler, but adding a ProtocolCodecFilter will
make your code much cleaner and easier to maintain.
It allows you to separate your protocol logic from your business logic (IoHandler).

How ?
Your application is basically just receiving a bunch of bytes and you need to convert these bytes
into messages (higher level objects).
There are three common techniques for splitting the stream of bytes into messages:
use fixed length messages
use a fixed length header that indicates the length of the body
using a delimiter; for example many text-based protocols append a newline (or CR LF pair)
after every message (http://www.faqs.org/rfcs/rfc977.html)
In this tutorial we will use the first and second method since they are definitely easier to
implement. Afterwards we will look at using a delimiter.

Example
We will develop a (pretty useless) graphical chargen server to illustrate how to implement your
own protocol codec (ProtocolEncoder, ProtocolDecoder, and ProtocolCodecFactory). The protocol
is really simple. This is the layout of a request message:
4 bytes

4 bytes

4 bytes

width

height

numchars

width: the width of the requested image (an integer in network byte-order)
height: the height of the requested image (an integer in network byte-order)
numchars: the number of chars to generate (an integer in network byte-order)
The server responds with two images of the requested dimensions, with the requested number of
characters painted on it. This is the layout of a response message:
4 bytes

variable length body

4 bytes

variable length body

length1

image1

length2

image2

Overview of the classes we need for encoding and decoding requests and responses:
ImageRequest: a simple POJO representing a request to our ImageServer.
ImageRequestEncoder: encodes ImageRequest objects into protocol-specific data (used by
the client)
ImageRequestDecoder: decodes protocol-specific data into ImageRequest objects (used by
the server)
ImageResponse: a simple POJO representing a response from our ImageServer.
ImageResponseEncoder: used by the server for encoding ImageResponse objects
ImageResponseDecoder: used by the client for decoding ImageResponse objects
ImageCodecFactory: this class creates the necesarry encoders and decoders
Here is the ImageRequest class :
public class ImageRequest {
private int width;
private int height;
private int numberOfCharacters;
public ImageRequest(int width, int height, int numberOfCharacters) {
this.width = width;
this.height = height;
this.numberOfCharacters = numberOfCharacters;
}

public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public int getNumberOfCharacters() {
return numberOfCharacters;
}
}
Encoding is usually simpler than decoding, so let's start with the ImageRequestEncoder:
public class ImageRequestEncoder implements ProtocolEncoder {
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws
Exception {
ImageRequest request = (ImageRequest) message;
IoBuffer buffer = IoBuffer.allocate(12, false);
buffer.putInt(request.getWidth());
buffer.putInt(request.getHeight());
buffer.putInt(request.getNumberOfCharacters());
buffer.flip();
out.write(buffer);
}
public void dispose(IoSession session) throws Exception {
// nothing to dispose
}
}
Remarks:
MINA will call the encode function for all messages in the IoSession's write queue. Since our
client will only write ImageRequest objects, we can safely cast message to ImageRequest.
We allocate a new IoBuffer from the heap. It's best to avoid using direct buffers, since
generally
heap
buffers
perform
better.
see
http://issues.apache.org/jira/browse/DIRMINA-289)
You do not have to release the buffer, MINA will do it for you, see
http://mina.apache.org/mina-project/apidocs/org/apache/mina/core/buffer/IoBuffer.html
In the dispose() method you should release all resources acquired during encoding for the
specified session. If there is nothing to dispose you could let your encoder inherit from
ProtocolEncoderAdapter.
Now let's have a look at the decoder. The CumulativeProtocolDecoder is a great help for writing

your own decoder: it will buffer all incoming data until your decoder decides it can do something
with it. In this case the message has a fixed size, so it's easiest to wait until all data is available:
public class ImageRequestDecoder extends CumulativeProtocolDecoder {
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
if (in.remaining() >= 12) {
int width = in.getInt();
int height = in.getInt();
int numberOfCharachters = in.getInt();
ImageRequest request = new ImageRequest(width, height, numberOfCharachters);
out.write(request);
return true;
} else {
return false;
}
}
}
Remarks:
everytime a complete message is decoded, you should write it to the ProtocolDecoderOutput;
these messages will travel along the filter-chain and eventually arrive in your
IoHandler.messageReceived method
you are not responsible for releasing the IoBuffer
when there is not enough data available to decode a message, just return false
The response is also a very simple POJO:
public class ImageResponse {
private BufferedImage image1;
private BufferedImage image2;
public ImageResponse(BufferedImage image1, BufferedImage image2) {
this.image1 = image1;
this.image2 = image2;
}
public BufferedImage getImage1() {
return image1;
}
public BufferedImage getImage2() {

return image2;
}
}
Encoding the response is also trivial:
public class ImageResponseEncoder extends ProtocolEncoderAdapter {
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws
Exception {
ImageResponse imageResponse = (ImageResponse) message;
byte[] bytes1 = getBytes(imageResponse.getImage1());
byte[] bytes2 = getBytes(imageResponse.getImage2());
int capacity = bytes1.length + bytes2.length + 8;
IoBuffer buffer = IoBuffer.allocate(capacity, false);
buffer.setAutoExpand(true);
buffer.putInt(bytes1.length);
buffer.put(bytes1);
buffer.putInt(bytes2.length);
buffer.put(bytes2);
buffer.flip();
out.write(buffer);
}
private byte[] getBytes(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", baos);
return baos.toByteArray();
}
}
Remarks:
when it is impossible to calculate the length of the IoBuffer beforehand, you can use an
auto-expanding buffer by calling buffer.setAutoExpand(true);
Now let's have a look at decoding the response:
public class ImageResponseDecoder extends CumulativeProtocolDecoder {
private
static
final
String
ImageResponseDecoder.class.getName() + ".STATE";

DECODER_STATE_KEY

public static final int MAX_IMAGE_SIZE = 5 * 1024 * 1024;
private static class DecoderState {
BufferedImage image1;

=

}
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
throws Exception {
DecoderState
decoderState
=
(DecoderState)
session.getAttribute(DECODER_STATE_KEY);
if (decoderState == null) {
decoderState = new DecoderState();
session.setAttribute(DECODER_STATE_KEY, decoderState);
}
if (decoderState.image1 == null) {
// try to read first image
if (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
decoderState.image1 = readImage(in);
} else {
// not enough data available to read first image
return false;
}
}
if (decoderState.image1 != null) {
// try to read second image
if (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
BufferedImage image2 = readImage(in);
ImageResponse imageResponse = new ImageResponse(decoderState.image1,
image2);
out.write(imageResponse);
decoderState.image1 = null;
return true;
} else {
// not enough data available to read second image
return false;
}
}
return false;
}
private BufferedImage readImage(IoBuffer in) throws IOException {
int length = in.getInt();
byte[] bytes = new byte[length];
in.get(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
return ImageIO.read(bais);
}
}

Remarks:
We store the state of the decoding process in a session attribute. It would also be possible to
store this state in the Decoder object itself but this has several disadvantages:
every IoSession would need its own Decoder instance
MINA ensures that there will never be more than one thread simultaneously executing
the decode() function for the same IoSession, but it does not guarantee that it will
always be the same thread. Suppose the first piece of data is handled by thread-1 who
decides it cannot yet decode, when the next piece of data arrives, it could be handled by
another thread. To avoid visibility problems, you must properly synchronize access to
this decoder state (IoSession attributes are stored in a ConcurrentHashMap, so they are
automatically visible to other threads).
a discussion on the mailing list has lead to this conclusion: choosing between storing
state in the IoSession or in the Decoder instance itself is more a matter of taste. To
ensure that no two threads will run the decode method for the same IoSession, MINA
needs to do some form of synchronization => this synchronization will also ensure you
can't have the visibility problem described above. (Thanks to Adam Fisk for pointing
this
out)
see
http://www.nabble.com/Tutorial-on-ProtocolCodecFilter,-state-and-threads-t3965413.ht
ml
IoBuffer.prefixedDataAvailable() is very convenient when your protocol uses a length-prefix;
it supports a prefix of 1, 2 or 4 bytes.
don't forget to reset the decoder state when you've decoded a response (removing the session
attribute is another way to do it)
If the response would consist of a single image, we would not need to store decoder state:
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws
Exception {
if (in.prefixedDataAvailable(4)) {
int length = in.getInt();
byte[] bytes = new byte[length];
in.get(bytes);
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(bais);
out.write(image);
return true;
} else {
return false;
}
}
Now let's glue it all together:
public class ImageCodecFactory implements ProtocolCodecFactory {
private ProtocolEncoder encoder;

private ProtocolDecoder decoder;
public ImageCodecFactory(boolean client) {
if (client) {
encoder = new ImageRequestEncoder();
decoder = new ImageResponseDecoder();
} else {
encoder = new ImageResponseEncoder();
decoder = new ImageRequestDecoder();
}
}
public ProtocolEncoder getEncoder(IoSession ioSession) throws Exception {
return encoder;
}
public ProtocolDecoder getDecoder(IoSession ioSession) throws Exception {
return decoder;
}
}
Remarks:
for every new session, MINA will ask the ImageCodecFactory for an encoder and a decoder.
since our encoders and decoders store no conversational state, it is safe to let all sessions
share a single instance.
This is how the server would use the ProtocolCodecFactory:
public class ImageServer {
public static final int PORT = 33789;
public static void main(String[] args) throws IOException {
ImageServerIoHandler handler = new ImageServerIoHandler();
NioSocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("protocol",
new
ProtocolCodecFilter(new
ImageCodecFactory(false)));
acceptor.setLocalAddress(new InetSocketAddress(PORT));
acceptor.setHandler(handler);
acceptor.bind();
System.out.println("server is listenig at port " + PORT);
}
}
Usage by the client is identical:
public class ImageClient extends IoHandlerAdapter {

public static final int CONNECT_TIMEOUT = 3000;
private String host;
private int port;
private SocketConnector connector;
private IoSession session;
private ImageListener imageListener;
public ImageClient(String host, int port, ImageListener imageListener) {
this.host = host;
this.port = port;
this.imageListener = imageListener;
connector = new NioSocketConnector();
connector.getFilterChain().addLast("codec",
new
ProtocolCodecFilter(new
ImageCodecFactory(true)));
connector.setHandler(this);
}
public void messageReceived(IoSession session, Object message) throws Exception {
ImageResponse response = (ImageResponse) message;
imageListener.onImages(response.getImage1(), response.getImage2());
}
...
For completeness, I will add the code for the server-side IoHandler:
public class ImageServerIoHandler extends IoHandlerAdapter {
private
final
static
String
abcdefghijklmnopqrstuvwxyz0123456789";

characters

=

"mina

rocks

public static final String INDEX_KEY = ImageServerIoHandler.class.getName() +
".INDEX";
private Logger logger = LoggerFactory.getLogger(this.getClass());
public void sessionOpened(IoSession session) throws Exception {
session.setAttribute(INDEX_KEY, 0);
}
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
IoSessionLogger sessionLogger = IoSessionLogger.getLogger(session, logger);
sessionLogger.warn(cause.getMessage(), cause);
}

public void messageReceived(IoSession session, Object message) throws Exception {
ImageRequest request = (ImageRequest) message;
String text1 = generateString(session, request.getNumberOfCharacters());
String text2 = generateString(session, request.getNumberOfCharacters());
BufferedImage image1 = createImage(request, text1);
BufferedImage image2 = createImage(request, text2);
ImageResponse response = new ImageResponse(image1, image2);
session.write(response);
}
private BufferedImage createImage(ImageRequest request, String text) {
BufferedImage image = new BufferedImage(request.getWidth(), request.getHeight(),
BufferedImage.TYPE_BYTE_INDEXED);
Graphics graphics = image.createGraphics();
graphics.setColor(Color.YELLOW);
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
Font serif = new Font("serif", Font.PLAIN, 30);
graphics.setFont(serif);
graphics.setColor(Color.BLUE);
graphics.drawString(text, 10, 50);
return image;
}
private String generateString(IoSession session, int length) {
Integer index = (Integer) session.getAttribute(INDEX_KEY);
StringBuffer buffer = new StringBuffer(length);
while (buffer.length() < length) {
buffer.append(characters.charAt(index));
index++;
if (index >= characters.length()) {
index = 0;
}
}
session.setAttribute(INDEX_KEY, index);
return buffer.toString();
}
}

Conclusion
There is a lot more to tell about encoding and decoding. But I hope this tutorial already gets you
started. I will try to add something about the DemuxingProtocolCodecFactory in the near future.
And then we will also have a look at how to use a delimiter instead of a length prefix.

Chapter 10 - Executor Filter
MINA 1.X version let the user define the Thread Model at the Acceptor level. It was part of the
Acceptor configuration. This led to complexity, and the MINA team decided to remove this option,
replacing it with a much more versatile system, based on a filter : the ExecutorFilter.

The ExecutorFilter class
This class is implementing the IoFilter interface, and basically, it contains an Executor to spread
the incoming events to a pool of threads. This will allow an application to use more efficiently the
processors, if some task is CPU intensive.
This Filter can be used just before the handlers, assuming that most of the processing will be done
in your application, or somewhere before some CPU intensive filter (for instance, a CodecFilter).
More to come ...

Chapter 11 - SSL Filter
To be completed...

Chapter 12 - Logging Filter
Background
The Apache MINA uses a system that allows for the developer of the MINA-base application to
use their own logging system.

SLF4J
MINA employs the Simple Logging Facade for Java (SLF4J). You can find information on SLF4J
here. This logging utility allows for the implementation of any number of logging systems. You
may use log4j, java.util.logging or other logging systems. The nice part about this is that if you
want to change from java.util.logging to log4j later on in the development process, you do not
need to change your source code at all.

Choosing the Right JARs
SLF4J uses a static binding. This means there is one JAR file for each supported logging
framework. You can use your favorite logging framework by choosing the JAR file that calls the
logging framework you chose statically. The following is the table of required JAR files to use a
certain logging framework.
Logging framework

Required JARs

Log4J 1.2.x

slf4j-api.jar, slf4j-log4j12.jar**

Log4J 1.3.x

slf4j-api.jar, slf4j-log4j13.jar

java.util.logging

slf4j-api.jar, slf4j-jdk14.jar**

Commons Logging

slf4j-api.jar, slf4j-jcl.jar

There are a few things to keep in mind:
slf4j-api.jar is used commonly across any implementation JARs.
IMPORTANT You should not put more than one implementation JAR files in the class path
(e.g. slf4j-log4j12.jar and slf4j-jdk14.jar); it might lead your application to a unexpected
behavior.
The version of slf4j-api.jar and slf4j-.jar should be identical.
Once configured properly, you can continue to configure the actual logging framework you chose
(e.g. modifying log4j.properties).

Overriding Jakarta Commons Logging
SLF4J also provides a way to convert the existing applications that use Jakarta Commons Logging
to use SLF4J without changing the application code. Just remove commons-loggong JAR file
from the class path, and add jcl104-over-slf4j.jar to the class path.

log4j example
For this example we will use the log4j logging system. We set up a project and place the following
snippet into a file called log4j.properties:
# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1
# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c{1} %x - %m%n
This file will be placed in the src directory of our project. If you are using an IDE, you essentially
want the configuration file to be in the classpath for the JVM when you are testing your code.
Although this shows you how to set up an IoAcceptor to use logging, understand that the SLF4J
API may be used anywhere in your program in order to generate proper logging information
suitable to your needs.
Next we will set up a simple example server in order to generate some logs. Here we have taken
the EchoServer example project and added logging to the class:
public static void main(String[] args) throws Exception {
IoAcceptor acceptor = new SocketAcceptor();
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
LoggingFilter loggingFilter = new LoggingFilter();
chain.addLast("logging", loggingFilter);
acceptor.setLocalAddress(new InetSocketAddress(PORT));
acceptor.setHandler(new EchoProtocolHandler());
acceptor.bind();
System.out.println("Listening on port " + PORT);

}
As you can see we removed the addLogger method and added in the 2 lines added to the example
EchoServer. With a reference to the LoggingFilter, you can set the logging level per event type in
your handler that is associated with the IoAcceptor here. In order to specify the IoHandler events
that trigger logging and to what levels the logging is performed, there is a method in the
LoggingFilter called setLogLevel(IoEventType, LogLevel). Below are the options for this method:
IoEventType

Description

SESSION_CREATED

Called when a new session has been created

SESSION_OPENED

Called when a new session has been opened

SESSION_CLOSED

Called when a session has been closed

MESSAGE_RECEIVED

Called when data has been received

MESSAGE_SENT

Called when a message has been sent

SESSION_IDLE

Called when a session idle time has been
reached

EXCEPTION_CAUGHT

Called when an exception has been thrown

Here are the descriptions of the LogLevels:
LogLevel

Description

NONE

This will result in no log event being created
regardless of the configuration

TRACE

Creates a TRACE event in the logging system

DEBUG

Generates debug messages in the logging
system

INFO

Generates informational
logging system

WARN

Generates warning messages in the logging
system

ERROR

Generates error messages in the logging system

messages

in

the

With this information, you should be able to get a basic system up and running and be able to
expand upon this simple example in order to be generating log information for your system.

Part III - MINA Advanced

Chapter 13 - Debugging
To be completed...

Chapter 14 - State Machine
If you are using MINA to develop an application with complex network interactions you may at
some point find yourself reaching for the good old State pattern to try to sort out some of that
complexity. However, before you do that you might want to checkout mina-statemachine which
tries to address some of the shortcomings of the State pattern.

A simple example
Let's demonstrate how mina-statemachine works with a simple example. The picture below shows
a state machine for a typical tape deck. The ellipsis are the states while the arrows are the
transitions. Each transition is labeled with an event name which triggers that transition.

Initially, the tape deck is in the Empty state. When a tape is inserted the load event is fired and the
tape deck moves to the Loaded state. In Loaded the eject event will trigger a move back to Empty
while the play event will trigger a move to the Playing state. And so on... I think you can work out
the rest on your own.
Now let's write some code. The outside world (the code interfacing with the tape deck) will only
see the TapeDeck interface:
public interface TapeDeck {
void load(String nameOfTape);
void eject();
void start();

void pause();
void stop();
}
Next we will write the class which contains the actual code executed when a transition occurs in
the state machine. First we will define the states. The states are all defined as constant String
objects and are annotated using the @State annotation:
public class TapeDeckHandler {
@State public static final String EMPTY = "Empty";
@State public static final String LOADED = "Loaded";
@State public static final String PLAYING = "Playing";
@State public static final String PAUSED = "Paused";
}
Now when we have the states defined we can set up the code corresponding to each transition.
Each transition will correspond to a method in TapeDeckHandler. Each transition method is
annotated using the @Transition annotation which defines the event id which triggers the
transition (on), the start state of the transition (in) and the end state of the transition (next):
public class TapeDeckHandler {
@State public static final String EMPTY = "Empty";
@State public static final String LOADED = "Loaded";
@State public static final String PLAYING = "Playing";
@State public static final String PAUSED = "Paused";
@Transition(on = "load", in = EMPTY, next = LOADED)
public void loadTape(String nameOfTape) {
System.out.println("Tape '" + nameOfTape + "' loaded");
}
@Transitions({
@Transition(on = "play", in = LOADED, next = PLAYING),
@Transition(on = "play", in = PAUSED, next = PLAYING)
})
public void playTape() {
System.out.println("Playing tape");
}
@Transition(on = "pause", in = PLAYING, next = PAUSED)
public void pauseTape() {
System.out.println("Tape paused");
}
@Transition(on = "stop", in = PLAYING, next = LOADED)
public void stopTape() {

System.out.println("Tape stopped");
}
@Transition(on = "eject", in = LOADED, next = EMPTY)
public void ejectTape() {
System.out.println("Tape ejected");
}
}
Please note that the TapeDeckHandler class does not implement the TapeDeck interface. That's
intentional.
Now, let's have a closer look at some of this code. The @Transition annotation on the loadTape
method
@Transition(on = "load", in = EMPTY, next = LOADED)
public void loadTape(String nameOfTape) {
specifies that when the tape deck is in the EMPTY state and the load event occurs the loadTape
method will be invoked and then the tape deck will move on to the LOADED state. The
@Transition annotations on the pauseTape, stopTape and ejectTape methods should not require
any further explanation. The annotation on the playTape method looks slightly different though.
As can be seen in the diagram above, when the tape deck is in either the LOADED or in the
PAUSED state the play event will play the tape. To have the same method called for multiple
transitions the @Transitions annotation has to be used:
@Transitions({
@Transition(on = "play", in = LOADED, next = PLAYING),
@Transition(on = "play", in = PAUSED, next = PLAYING)
})
public void playTape() {
The @Transitions annotation simply lists multiple transitions for which the annotated method will
be called.
More about the @Transition parameters
If you omit the on parameter it will default to "*" which will match any event.
If you omit the next parameter it will default to "_self_" which is an alias for the current state.
To create a loop transition in your state machine all you have to do is to omit the next
parameter.
The weight parameter can be used to define in what order transitions will be searched.
Transitions for a particular state will be ordered in ascending order according to their weight
value. weight is 0 by default.
Now the final step is to create a StateMachine object from the annotated class and use it to create a
proxy object which implements TapeDeck:
public static void main(String[] args) {

TapeDeckHandler handler = new TapeDeckHandler();
StateMachine
sm
=
StateMachineFactory.getInstance(Transition.class).create(TapeDeckHandler.EMPTY, handler);
TapeDeck deck = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
deck.load("The Knife - Silent Shout");
deck.play();
deck.pause();
deck.play();
deck.stop();
deck.eject();
}
The lines
TapeDeckHandler handler = new TapeDeckHandler();
StateMachine
sm
=
StateMachineFactory.getInstance(Transition.class).create(TapeDeckHandler.EMPTY, handler);
creates the StateMachine instance from an instance of TapeDeckHandler. The Transition.class in
the call to StateMachineFactory.getInstance(...) tells the factory that we've used the @Transition
annotation to build the state machine. We specify EMPTY as the start state. A StateMachine is
basically a directed graph. State objects correspond to nodes in the graph while Transition objects
correspond to edges. Each @Transition annotation we used in the TapeDeckHandler will
correspond to a Transition instance.
Uhhm, what's the difference between @Transition and Transition?
@Transition is the annotation you use to mark a method which should be used when a transition
between states occur. Behind the scenes mina-statemachine will create instances of the
MethodTransition class for each @Transition annotated method. MethodTransition implements
the Transition interface. As a mina-statemachine user you will never use the Transition or
MethodTransition types directly.
The TapeDeck instance is created by calling StateMachineProxyBuilder:
TapeDeck deck = new StateMachineProxyBuilder().create(TapeDeck.class, sm);
The StateMachineProxyBuilder.create() method takes the interfaces the returned proxy object
should implement and the StateMachine instance which will receive the events generated by the
method calls on the proxy.
When the code is executed the output should be:
Tape 'The Knife - Silent Shout' loaded
Playing tape
Tape paused
Playing tape
Tape stopped

Tape ejected
What does all this have to do with MINA?
As you might have noticed there's nothing MINA specific about this example. But don't be
alarmed. Later on we will see how to create state machines for MINA's IoHandler interface.

How does it work?
Let's walk through what happens when a method is called on the proxy.

Lookup a StateContext object
The StateContext object is important because it holds the current State. When a method is called
on the proxy it will ask a StateContextLookup instance to get the StateContext from the method's
arguments. Normally, the StateContextLookup implementation will loop through the method
arguments and look for a particular type of object and use it to retrieve a StateContext object. If no
StateContext has been assigned yet the StateContextLookup will create one and store it in the
object.
When proxying MINA's IoHandler we will use a IoSessoinStateContextLookup instance which
looks for an IoSession in the method arguments. It will use the IoSession's attributes to store a
separate instance of StateContext for each MINA session. That way the same state machine can be
used for all MINA sessions without them interfering with each other.
In the example above we never specified what StateContextLookup implementation to use when
we created the proxy using StateMachineProxyBuilder. If not specified a
SingletonStateContextLookup will be used. SingletonStateContextLookup totally disregards the
method arguments passed to it – it'll always return the same StateContext object. Obviously this
won't be very useful when the same state machine is used concurrently by many clients as will be
the case when we proxy IoHandler later on.

Convert the method invocation into an Event object
All method invocations on the proxy object will be translated into Event objects by the proxy. An
Event has an id and zero or more arguments. The id corresponds to the name of the method and
the event arguments correspond to the method arguments. The method call deck.load("The Knife Silent Shout") corresponds to the event {id = "load", arguments = ["The Knife - Silent Shout"]}.
The Event object also contains a reference to the StateContext object looked up previously.

Invoke the StateMachine
Once the Event object has been created the proxy will call StateMachine.handle(Event).
StateMachine.handle(Event) loops through the Transition objects of the current State in search for
a Transition instance which accepts the current Event. This process will stop after a Transition has
been found. The Transition objects will be searched in order of weight (typically specified by the
@Transition annotation).

Execute the Transition
The final step is to call Transition.execute(Event) on the Transition which matched the Event.
After the Transition has been executed the StateMachine will update the current State with the end
state defined by the Transition.
Transition is an interface. Every time you use the @Transition annotation a MethodTransition
object will be created.

MethodTransition
MethodTransition is very important and requires some further explanation. MethodTransition
matches an Event if the event's id matches the on parameter of the @Transition annotation and the
annotated method's arguments are assignment compatible with a subset of the event's arguments.
So, if the Event looks like {id = "foo", arguments = [a, b, c]} the method
@Transition(on = "foo")
public void someMethod(One one, Two two, Three three) { ... }
matches if and only if ((a instanceof One && b instanceof Two && c instanceof Three) == true).
On match the method will be called with the matching event arguments bound to the method's
arguments:
someMethod(a, b, c);
Integer, Double, Float, etc also match their primitive counterparts int, double, float, etc.
As stated above also a subset would match:
@Transition(on = "foo")
public void someMethod(Two two) { ... }
matches if ((a instanceof Two || b instanceof Two || c instanceof Two) == true). In this case the first
matching event argument will be bound to the method argument named two when someMethod is
called.

A method which takes no arguments always matches if the event id matches:
@Transition(on = "foo")
public void someMethod() { ... }
To make things even more complicated the first two method arguments also matches against the
Event class and the StateContext interface. This means that
@Transition(on = "foo")
public void someMethod(Event event, StateContext context, One one, Two two, Three three)
{ ... }
@Transition(on = "foo")
public void someMethod(Event event, One one, Two two, Three three) { ... }
@Transition(on = "foo")
public void someMethod(StateContext context, One one, Two two, Three three) { ... }
also matches the Event {id = "foo", arguments = [a, b, c]} if ((a instanceof One && b instanceof
Two && c instanceof Three) == true). The current Event object will be bound to the event method
argument and the current StateContext will be bound to context when someMethod is invoked.
As before a subset of the event arguments can be used. Also, a specific StateContext
implementation may be specified instead of using the generic interface:
@Transition(on = "foo")
public void someMethod(MyStateContext context, Two two) { ... }
The order of the method arguments is important. If the method needs access to the current Event it
must be specified as the first method argument. StateContext has to be the either the second
arguments if the first is Event or the first argument. The event arguments also have to match in the
correct order. MethodTransition will not try to reorder the event's arguments in search for a match.
If you've made it this far, congratulations! I realize that the section above might be a little hard to
digest. Hopefully some examples could make things clearer:
Consider the Event {id = "messageReceived", arguments = [ArrayList a = [...], Integer b = 1024]}.
The following methods match this Event:
// All method arguments matches all event arguments directly
@Transition(on = "messageReceived")
public void messageReceived(ArrayList l, Integer i) { ... }
// Matches since ((a instanceof List && b instanceof Number) == true)
@Transition(on = "messageReceived")
public void messageReceived(List l, Number n) { ... }
// Matches since ((b instanceof Number) == true)
@Transition(on = "messageReceived")

public void messageReceived(Number n) { ... }
// Methods with no arguments always matches
@Transition(on = "messageReceived")
public void messageReceived() { ... }
// Methods only interested in the current Event or StateContext always matches
@Transition(on = "messageReceived")
public void messageReceived(StateContext context) { ... }
// Matches since ((a instanceof Collection) == true)
@Transition(on = "messageReceived")
public void messageReceived(Event event, Collection c) { ... }
The following would not match:
// Incorrect ordering
@Transition(on = "messageReceived")
public void messageReceived(Integer i, List l) { ... }
// ((a instanceof LinkedList) == false)
@Transition(on = "messageReceived")
public void messageReceived(LinkedList l, Number n) { ... }
// Event must be first argument
@Transition(on = "messageReceived")
public void messageReceived(ArrayList l, Event event) { ... }
// StateContext must be second argument if Event is used
@Transition(on = "messageReceived")
public void messageReceived(Event event, ArrayList l, StateContext context) { ... }
// Event must come before StateContext
@Transition(on = "messageReceived")
public void messageReceived(StateContext context, Event event) { ... }

State inheritance
State instances may have a parent State. If StateMachine.handle(Event) cannot find a Transition
matching the current Event in the current State it will search the parent State. If no match is found
there either the parent's parent will be searched and so on.
This feature is useful when you want to add some generic code to all states without having to
specify @Transition annotations for each state. Here's how you create a hierarchy of states using

the @State annotation:
@State
public static final String A = "A";
@State(A) public static final String B = "A->B";
@State(A) public static final String C = "A->C";
@State(B) public static final String D = "A->B->D";
@State(C) public static final String E = "A->C->E";

Error handling using state inheritance
Let's go back to the TapeDeck example. What happens if you call deck.play() when there's no tape
in the deck? Let's try:
public static void main(String[] args) {
...
deck.load("The Knife - Silent Shout");
deck.play();
deck.pause();
deck.play();
deck.stop();
deck.eject();
deck.play();
}
...
Tape stopped
Tape ejected
Exception in thread "main" o.a.m.sm.event.UnhandledEventException:
Unhandled event: org.apache.mina.statemachine.event.Event@15eb0a9[id=play,...]
at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:285)
at org.apache.mina.statemachine.StateMachine.processEvents(StateMachine.java:142)
...
Oops! We get an UnhandledEventException because when we're in the Empty state there's no
transition which handles the play event. We could add a special transition to all states which
handles unmatched Event objects:
@Transitions({
@Transition(on = "*", in = EMPTY, weight = 100),
@Transition(on = "*", in = LOADED, weight = 100),
@Transition(on = "*", in = PLAYING, weight = 100),
@Transition(on = "*", in = PAUSED, weight = 100)
})
public void error(Event event) {

System.out.println("Cannot '" + event.getId() + "' at this time");
}
Now when you run the main() method above you won't get an exception. The output should be:
...
Tape stopped
Tape ejected
Cannot 'play' at this time.
Now this seems to work very well, right? But what if we had 30 states instead of only 4? Then we
would need 30 @Transition annotations on the error() method. Not good. Let's use state
inheritance instead:
public static class TapeDeckHandler {
@State public static final String ROOT = "Root";
@State(ROOT) public static final String EMPTY = "Empty";
@State(ROOT) public static final String LOADED = "Loaded";
@State(ROOT) public static final String PLAYING = "Playing";
@State(ROOT) public static final String PAUSED = "Paused";
...
@Transition(on = "*", in = ROOT)
public void error(Event event) {
System.out.println("Cannot '" + event.getId() + "' at this time");
}
}
The result will be the same but things will be much easier to maintain whith this last approach.

mina-statemachine with IoHandler
Now we're going to convert our tape deck into a TCP server and extend it with some more
functionality. The server will receive commands like load , play, stop, etc. The responses will
either be positive + or negative - . The protocol is text based, all commands and responses are
lines of UTF-8 text terminated by CRLF (i.e. \r\n in Java). Here's an example session:
telnet localhost 12345
S: + Greetings from your tape deck!
C: list
S: + (1: "The Knife - Silent Shout", 2: "Kings of convenience - Riot on an empty street")
C: load 1
S: + "The Knife - Silent Shout" loaded
C: play
S: + Playing "The Knife - Silent Shout"

C: pause
S: + "The Knife - Silent Shout" paused
C: play
S: + Playing "The Knife - Silent Shout"
C: info
S: + Tape deck is playing. Current tape: "The Knife - Silent Shout"
C: eject
S: - Cannot eject while playing
C: stop
S: + "The Knife - Silent Shout" stopped
C: eject
S: + "The Knife - Silent Shout" ejected
C: quit
S: + Bye! Please come back!
The complete code for the TapeDeckServer described in this section is available in the
org.apache.mina.example.tapedeck package in the mina-example module in the Subversion
repository. The code uses a MINA ProtocolCodecFilter to convert bytes from/to Command objects.
There is one Command implementation for each type of request the server recognizes. We will not
describe the codec implementation here in any detail.
Now, let's have a look at how this server works. The important class which implements the state
machine is the TapeDeckServer class. The first thing we do is to define the states:
@State public static final String ROOT = "Root";
@State(ROOT) public static final String EMPTY = "Empty";
@State(ROOT) public static final String LOADED = "Loaded";
@State(ROOT) public static final String PLAYING = "Playing";
@State(ROOT) public static final String PAUSED = "Paused";
Nothing new there. However, the methods which handle the events now look different. Let's look
at the playTape method:
@IoHandlerTransitions({
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = LOADED, next = PLAYING),
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = PAUSED, next = PLAYING)
})
public void playTape(TapeDeckContext context, IoSession session, PlayCommand cmd) {
session.write("+ Playing \"" + context.tapeName + "\"");
}
This code doesn't use the general @Transition and @Transitions annotations used previously but
rather the MINA specific @IoHandlerTransition and @IoHandlerTransitions annotations. This are
preferred when creating state machines for MINA's IoHandler interface as they let you use a Java
enum for the event ids instead of strings as we used before. There are also corresponding
annotations for MINA's IoFilter interface.

We're now using MESSAGE_RECEIVED instead of "play" for the event name (the on attribute in
@IoHandlerTransition).
This
constant
is
defined
in
org.apache.mina.statemachine.event.IoHandlerEvents and has the value "messageReceived" which
of course corresponds to the messageReceived() method in MINA's IoHandler interface. Thanks
to Java5's static imports we don't have to write out the name of the class holding the constant. We
just need to put the
import static org.apache.mina.statemachine.event.IoHandlerEvents.*;
statement in the imports section.
Another thing that has changed is that we're using a custom StateContext implementation,
TapeDeckContext. This class is used to keep track of the name of the current tape:
static class TapeDeckContext extends AbstractStateContext {
public String tapeName;
}
Why not store tape name in IoSession?
We could have stored the name of the tape as an attribute in the IoSession but using a custom
StateContext is recommended since it provides type safety.
The last thing to note about the playTape() method is that it takes a PlayCommand as its last
argument. The last argument corresponds to the message argument of IoHandler's
messageReceived(IoSession session, Object message) method. This means that playTape() method
will only be called if the bytes sent by the client can be decoded as a PlayCommand.
Before the tape deck can play anything a tape has to be loaded. When a LoadCommand is received
from the client the supplied tape number will be used to get the name of the tape to load from the
tapes array of available tapes:
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = EMPTY, next = LOADED)
public void loadTape(TapeDeckContext context, IoSession session, LoadCommand cmd) {
if (cmd.getTapeNumber() < 1 || cmd.getTapeNumber() > tapes.length) {
session.write("- Unknown tape number: " + cmd.getTapeNumber());
StateControl.breakAndGotoNext(EMPTY);
} else {
context.tapeName = tapes[cmd.getTapeNumber() - 1];
session.write("+ \"" + context.tapeName + "\" loaded");
}
}
This code uses the StateControl class to override the next state. If the user specify an unknown
tape number we shouldn't move to the LOADED state but instead remain in EMPTY which is
what the
StateControl.breakAndGotoNext(EMPTY);
line does. The StateControl class is described more in a later section.

The connect() method will always be called at the start of a session when MINA calls
sessionOpened() on the IoHandler:
@IoHandlerTransition(on = SESSION_OPENED, in = EMPTY)
public void connect(IoSession session) {
session.write("+ Greetings from your tape deck!");
}
All it does is to write the greeting to the client. The state machine will remain in the EMPTY state.
The pauseTape(), stopTape() and ejectTape() methods are very similar to playTape() and won't be
described in any detail. The listTapes(), info() and quit() methods should be simple enough to
understand by now, too. Please note how these last three methods are used for the ROOT state.
This means that the list, info and quit commands can be issued in any state.
Now let's have a look at error handling. The error() method will be called when the client sends a
Command which isn't legal in the current state:
@IoHandlerTransition(on = MESSAGE_RECEIVED, in = ROOT, weight = 10)
public void error(Event event, StateContext context, IoSession session, Command cmd) {
session.write("- Cannot " + cmd.getName() + " while "
+ context.getCurrentState().getId().toLowerCase());
}
error() has been given a higher weight than listTapes(), info() and quit() to prevent it to be called
for any of those commands. Notice how error() uses the StateContext object to get hold of the id
of the current state. The values of the String constants which are annotated with the @State
annotation (Empty, Loaded etc) will be used by mina-statemachine as state id.
The commandSyntaxError() method will be called when a CommandSyntaxException has been
thrown by our ProtocolDecoder. It simply prints out that the line sent by the client couldn't be
converted into a Command.
The exceptionCaught() will be called for any thrown exception except CommandSyntaxException
(it has a higher weight than the commandSyntaxError() method). It closes the session
immediately.
The last @IoHandlerTransition method is unhandledEvent() which will be called if none of the
other @IoHandlerTransition methods match the Event. We need this since we don't have
@IoHandlerTransition annotations for all possible types of events in all states (e.g., we never
handle messageSent events). Without this mina-statemachine throws an exception if an Event is
handled by the state machine.
The last piece of code we're going to have a look at is the code which creates the IoHandler proxy
and the main() method:

private static IoHandler createIoHandler() {
StateMachine
sm
StateMachineFactory.getInstance(IoHandlerTransition.class).create(EMPTY,
TapeDeckServer());

=
new

return new StateMachineProxyBuilder().setStateContextLookup(
new IoSessionStateContextLookup(new StateContextFactory() {
public StateContext create() {
return new TapeDeckContext();
}
})).create(IoHandler.class, sm);
}
// This code will work with MINA 1.0/1.1:
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new SocketAcceptor();
SocketAcceptorConfig config = new SocketAcceptorConfig();
config.setReuseAddress(true);
ProtocolCodecFilter pcf = new ProtocolCodecFilter(
new TextLineEncoder(), new CommandDecoder());
config.getFilterChain().addLast("codec", pcf);
acceptor.bind(new InetSocketAddress(12345), createIoHandler(), config);
}
// This code will work with MINA trunk:
public static void main(String[] args) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.setReuseAddress(true);
ProtocolCodecFilter pcf = new ProtocolCodecFilter(
new TextLineEncoder(), new CommandDecoder());
acceptor.getFilterChain().addLast("codec", pcf);
acceptor.setHandler(createIoHandler());
acceptor.setLocalAddress(new InetSocketAddress(PORT));
acceptor.bind();
}
createIoHandler() creates a StateMachine just like we did before except that we specify
IoHandlerTransition.class
instead
of
Transition.class
in
the
call
to
StateMachineFactory.getInstance(...). This is necessary since we're now using the
@IoHandlerTransition annotation. Also, this time we use IoSessionStateContextLookup and a
custom StateContextFactory when we create the IoHandler proxy. If we didn't use
IoSessionStateContextLookup all clients would share the same state machine which isn't desirable.
The main() method creates the SocketAcceptor and attaches a ProtocolCodecFilter which

decodes/encodes Command objects to its filter chain. Finally, it binds to port 12345 using an
IoHandler instance created by the createIoHandler() method.

Advanced topics
Changing state programmatically
To be written...

Calling the state machine recursively
To be written...

Chapter 15 - Proxy
To be completed...

Chapter 16 - JMX Support
Java Management Extensions (JMX) is used for managing and monitoring java applications. This
tutorial will provide you with an example as to how you can JMX-enable your MINA based
application.
This tutorial is designed to help you get the JMX technology integrated in to your MINA-based
application. In this tutorial, we will integrate the MINA-JMX classes into the imagine server
example program.

Adding JMX Support
To JMX enable MINA application we have to perform following
Create/Get MBean server
Instantiate desired MBeans (IoAcceptor, IoFilter)
Register MBeans with MBean server
We shall follow \src\main\java\org\apache\mina\example\imagine\step3\server\ImageServer.java,
for the rest of our discussion

Create/Get MBean server
// create a JMX MBean Server server instance
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
This lines get the MBean Server instance.

Instantiate MBean(s)
We create an MBean for IoService
// create a JMX-aware bean that wraps a MINA IoService object. In this
// case, a NioSocketAcceptor.
IoServiceMBean acceptorMBean = new IoServiceMBean( acceptor );
This creates an IoService MBean. It accepts instance of an acceptor that it exposed via JMX.

Similarly, you can add IoFilterMBean and other custom MBeans as well

Registering MBeans with MBean Server
// create a JMX ObjectName. This has to be in a specific format.
ObjectName acceptorName = new ObjectName( acceptor.getClass().getPackage().getName() +
":type=acceptor,name=" + acceptor.getClass().getSimpleName());
// register the bean on the MBeanServer. Without this line, no JMX will happen for
// this acceptor.
mBeanServer.registerMBean( acceptorMBean, acceptorName );
We create an ObjectName that need to be used as logical name for accessing the MBean and
register the MBean to the MBean Server. Our application in now JMX enabled. Lets see it in
action.

Start the Imagine Server
If you are using Java 5 or earlier:
java
-Dcom.sun.management.jmxremote
-classpath
org.apache.mina.example.imagine.step3.server.ImageServer
If you are using Java 6:



java
 }}{{{}org.apache.mina.example.imagine.step3.server.ImageServer

Start JConsole
Start JConsole using the following command:
/bin/jconsole
We can see the different attributes and operations that are exposed by the MBeans

-classpath

Chapter 17 - Spring Integration
This article demonstrates integrating MINA application with Spring. I wrote this article on my
blog, and though to put it here, where this information actually belongs to. Can find the original
copy at Integrating Apache MINA with Spring.

Application Structure
We shall take a standard MINA application which has following construct
One Handler
Two Filter - Logging Filter and a ProtocolCodec Filter
NioDatagram Socket

Initialization Code
Lets see the code first. For simplicity we have omitted the glue code.
public void initialize() throws IOException {
// Create an Acceptor
NioDatagramAcceptor acceptor = new NioDatagramAcceptor();
// Add Handler
acceptor.setHandler(new ServerHandler());
acceptor.getFilterChain().addLast("logging",
new LoggingFilter());
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new SNMPCodecFactory()));
// Create Session Configuration
DatagramSessionConfig dcfg = acceptor.getSessionConfig();

dcfg.setReuseAddress(true);
logger.debug("Starting Server......");
// Bind and be ready to listen
acceptor.bind(new InetSocketAddress(DEFAULT_PORT));
logger.debug("Server listening on "+DEFAULT_PORT);
}

Integration Process
To integrate with Spring, we need to do following:
Set the IO handler
Create the Filters and add to the chain
Create the Socket and set Socket Parameters
NOTE: The latest MINA releases doesn't have the package specific to Spring, like its earlier
versions. The package is now named Integration Beans, to make the implementation work for all
DI frameworks.
Lets see the Spring xml file. Please see that I have removed generic part from xml and have put
only the specific things needed to pull up the implementation. This example has been derived from
Chat example shipped with MINA release. Please refer the xml shipped with chat example.
Now lets pull things together
Lets set the IO Handler in the spring context file


Lets create the Filter chain











id="filterChainBuilder"






Here, we create instance of our IoFilter. See that for the ProtocolCodec factory, we have used
Constructor injection. Logging Filter creation is straight forward. Once we have defined the beans
for the filters to be used, we now create the Filter Chain to be used for the implementation. We
define a bean with id "FilterChainBuidler" and add the defined filters to it. We are almost ready,
and we just need to create the Socket and call bind
Lets complete the last part of creating the Socket and completing the chain















Now we create our ioAcceptor, set IO handler and Filter Chain. Now we have to write a function
to read this file using Spring and start our application. Here's the code
public void initializeViaSpring() throws Exception {
new ClassPathXmlApplicationContext("trapReceiverContext.xml");
}



Source Exif Data:
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Page Count                      : 90
XMP Toolkit                     : XMP toolkit 2.9.1-13, framework 1.6
About                           : uuid:850a7899-801c-11e3-0000-e06c3fc3bb3f
Producer                        : GPL Ghostscript 9.10
Modify Date                     : 2014:01:15 16:42:52+08:00
Create Date                     : 2014:01:15 16:42:52+08:00
Creator Tool                    : PDF24 Creator
Document ID                     : uuid:850a7899-801c-11e3-0000-e06c3fc3bb3f
Format                          : application/pdf
Title                           : Microsoft Word - mina_2.0_user_guide_en.doc
Creator                         : kdf2858
Has XFA                         : No
Author                          : kdf2858
EXIF Metadata provided by EXIF.tools

Navigation menu