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 incomingByteBuffer
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.
Table of Contents
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 |
Blocks connections from blacklisted remote addresses |
|
Buffered Write |
Buffers outgoing requests like the BufferedOutputStream does |
|
Compression |
|
|
ConnectionThrottle |
|
|
ErrorGenerating |
|
|
Executor |
|
|
FileRegionWrite |
|
|
KeepAlive |
|
|
Logging |
Logs event messages, like MessageReceived, MessageSent, SessionOpened, ... |
|
MDC Injection |
Inject key IoSession properties into the MDC |
|
Noop |
A filter that does nothing. Useful for tests. |
|
Profiler |
Profile event messages, like MessageReceived, MessageSent, SessionOpened, ... |
|
ProtocolCodec |
A filter in charge of encoding and decoding messages |
|
Proxy |
|
|
Reference counting |
Keeps track of the number of usages of this filter |
|
RequestResponse |
|
|
SessionAttributeInitializing |
|
|
StreamWrite |
|
|
SslFilter |
|
|
WriteRequest |
|
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:
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:
Now, we need to do the reverse in messageSent()
:
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:
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.
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:
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 theif
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.