Apache MINA is an open source library for writing client-server Java applications. The latest version to date - 2.0.4 - was released on June 14, 2011.
In this article I will describe the process of creating a simple server using this library.
What are we writing?
In this article we will write the simplest server to which you can connect using telnet. When connected, the server will greet the client, with the command
time will tell how many seconds have passed since the connection, and with the command
q it will say goodbye and close the connection.
')
So, let's begin.
Connect to the project
In addition to the main
mina-core library, you also need to connect the
slf4j (Simple Logging Facade for Java) library, which MINA uses as an interface to various logging systems, for example, Apache log4j. Connect requires
slf4j-api and one of the implementations of logging. In our example, logging will not be used, and therefore you can use
slf4j-nop .
Entry point and ioacceptor
In the main method of the application, we need to create and configure an
IoAcceptor — an object that will accept incoming connections. We will need the following methods (
IoAcceptor extends the
IoService interface and some methods are declared in it):
DefaultIoFilterChainBuilder getFilterChain()
- returns the so-called "filter chain"; void setHandler(IoHandler handler)
- sets the handler - the object in which the main server logic is located; void bind(SocketAddress localAddress)
- begins to listen to the specified address.
For our example, the
NioSocketAcceptor is suitable, which is a TCP / IP
IoAcceptor implementation.
Filter Chain and Protocol
Before getting into the handler, all events (connection acceptance, data acquisition, etc.) go through the so-called “filter chain”. Filters can perform any actions that might be in the handler, but they are not very appropriate - logging, etc.
One of the filters should be considered separately - this is
ProtocolCodecFilter . The task of this filter is to convert data from a certain object into a sequence of bytes during transmission and into a decorated object from a sequence of bytes during reception. One of the constructors of the filter looks like this:
ProtocolCodecFilter(ProtocolCodecFactory factory)
In this article I will not deal with creating my own “factory”, my own protocol, for our example one of the simplest and already implemented in MINA protocols - TextLine (“factory” -
TextLineCodecFactory ), which converts a sequence of bytes into a string (
String ) and vice versa . In addition, when sending to a line, the end of line character will be added, and when received, the line will be transferred further along the “filter chain” or to the handler only when the same end of line character is received. The encoding and end-of-line character can be specified in the constructor:
TextLineCodecFactory(Charset charset, String encodingDelimiter, String decodingDelimiter)
Thus, the handler will have to deal with already decorated objects, in our case - strings.
The
DefaultIoFilterChainBuilder object has several methods for adding filters, but we need only one:
void addLast(String name, IoFilter filter)
This method adds a filter to the end of the chain.
So now we can write the
main method:
public static void main(String[] args) throws IOException { IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.defaultCharset(), System.lineSeparator(), System.lineSeparator()))); acceptor.setHandler(new MyMinaServerHandler()); acceptor.bind(new InetSocketAddress(12345)); }
Handler, IoHandler and IoSession
A handler is an implementation of the
IoHandler interface, which contains the following methods:
void sessionCreated(IoSession session)
- called when accepting a connection; void sessionOpened(IoSession session)
- called after sessionCreated ; void sessionClosed(IoSession session)
- called when closing the connection; void sessionIdle(IoSession session, IdleStatus status)
- called when idle (by timeout); void messageReceived(IoSession session, Object message)
- called when receiving data; void messageSent(IoSession session, Object message)
- called when sending data; void exceptionCaught(IoSession session, Throwable cause)
- called when an exception occurs.
In our example, it is enough to implement only a couple of them -
sessionOpened and
messageReceived , so instead of implementing the interface directly, we can extend the
IoHandlerAdapter class, which is an empty interface implementation, and override the methods we need.
The
IoSession object is a session view (also
known as a connection). We will need some of his methods:
IoSession also allows us to store connection-related data, and we will use this opportunity to save connection time. To do this, we will use two methods:
So now we can write and handler:
public class MyMinaServerHandler extends IoHandlerAdapter { public void sessionOpened(IoSession session) { session.setAttribute("time", System.currentTimeMillis()); session.write("Hello!"); } public void messageReceived(IoSession session, Object message) { switch (((String) message).trim()) { case "time": session.write(String.format("You connected %d seconds ago", (System.currentTimeMillis() - (Long) session.getAttribute("time")) / 1000)); break; case "q": session.write("Bye!"); session.close(false); break; } } }
That's all
Run the newly assembled server and write on the command line or terminal:
telnet localhost 12345
The server responds to the commands:
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Hello! time You connected 11 seconds ago q Bye! Connection closed by foreign host.
The full source code can be downloaded
here .
Thank you for reading!