Documentation cleanup

This commit is contained in:
Trustin Lee 2008-11-14 06:55:02 +00:00
parent 3d83768039
commit 927aaf8d12

View File

@ -22,10 +22,10 @@
<title>Before Getting Started</title> <title>Before Getting Started</title>
<para> <para>
The minimum requirements to run the examples which are introduced in The minimum requirements to run the examples which are introduced in
this chapter are just two; the latest version of Netty and JDK 1.5 or this chapter are only two; the latest version of Netty and JDK 1.5 or
above. The latest version of Netty is available in above. The latest version of Netty is available in
<ulink url="&Downloads;">the project download page</ulink>. To get the <ulink url="&Downloads;">the project download page</ulink>. To download
right version of JDK, please refer to your preferred JDK vendor's web the right version of JDK, please refer to your preferred JDK vendor's web
site. site.
</para> </para>
<para> <para>
@ -53,9 +53,9 @@
a protocol which discards any received data without any response. a protocol which discards any received data without any response.
</para> </para>
<para> <para>
What you are supposed to do to implement the DISCARD protocol is to log To implement the DISCARD protocol, you only need to log the received data.
the received data, and that's all. Let's start straight from the handler Let us start straight from the handler implementation, which handles I/O
implementation, which handles I/O events generated by Netty. events generated by Netty.
</para> </para>
<programlisting>package org.jboss.netty.example.discard; <programlisting>package org.jboss.netty.example.discard;
@ -80,8 +80,8 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
&ChannelPipelineCoverage; annotates a handler type to tell if the &ChannelPipelineCoverage; annotates a handler type to tell if the
handler instance of the annotated type can be shared by more than handler instance of the annotated type can be shared by more than
one &Channel; (and its associated &ChannelPipeline;). one &Channel; (and its associated &ChannelPipeline;).
<classname>DiscardServerHandler</classname> doesn't manage any <classname>DiscardServerHandler</classname> does not manage any
stateful information, and therefore it's annotated with the value stateful information, and therefore it is annotated with the value
<literal>"all"</literal>. <literal>"all"</literal>.
</para> </para>
</callout> </callout>
@ -90,7 +90,7 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
<classname>DiscardServerHandler</classname> extends <classname>DiscardServerHandler</classname> extends
&SimpleChannelHandler;, which is an implementation of &SimpleChannelHandler;, which is an implementation of
&ChannelHandler;. &SimpleChannelHandler; provides various event &ChannelHandler;. &SimpleChannelHandler; provides various event
handler methods that you can override. For now, it's just enough handler methods that you can override. For now, it is just enough
to extend &SimpleChannelHandler; rather than to implement to extend &SimpleChannelHandler; rather than to implement
the handler interfaces by yourself. the handler interfaces by yourself.
</para> </para>
@ -100,8 +100,8 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
We override the <methodname>messageReceived</methodname> event We override the <methodname>messageReceived</methodname> event
handler method here. This method is called with a &MessageEvent;, handler method here. This method is called with a &MessageEvent;,
which contains the received data, whenever new data is received which contains the received data, whenever new data is received
from a client. In this example, we just ignore the received data from a client. In this example, we ignore the received data by doing
by doing nothing to implement the DISCARD protocol. nothing to implement the DISCARD protocol.
</para> </para>
</callout> </callout>
<callout arearefs="example.discard.co4"> <callout arearefs="example.discard.co4">
@ -119,9 +119,9 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
</callout> </callout>
</calloutlist> </calloutlist>
<para> <para>
So far so good. We have implemented the half of the DISCARD server. So far so good. We have implemented the first half of the DISCARD server.
What's left now is to write the <methodname>main</methodname> method What's left now is to write the <methodname>main</methodname> method
which starts the server up with the <classname>DiscardServerHandler</classname>. which starts the server with the <classname>DiscardServerHandler</classname>.
</para> </para>
<programlisting>package org.jboss.netty.example.discard; <programlisting>package org.jboss.netty.example.discard;
@ -157,7 +157,7 @@ public class DiscardServer {
&ChannelFactory; implementations. We are implementing a server-side &ChannelFactory; implementations. We are implementing a server-side
application in this example, and therefore application in this example, and therefore
&NioServerSocketChannelFactory; was used. Another thing to note is &NioServerSocketChannelFactory; was used. Another thing to note is
that it doesn't create I/O threads by itself. It is supposed to that it does not create I/O threads by itself. It is supposed to
acquire threads from the thread pool you specified in the acquire threads from the thread pool you specified in the
constructor, and it gives you more control over how threads should constructor, and it gives you more control over how threads should
be managed in the environment where your application runs, such as be managed in the environment where your application runs, such as
@ -167,8 +167,9 @@ public class DiscardServer {
<callout arearefs="example.discard2.co2"> <callout arearefs="example.discard2.co2">
<para> <para>
&ServerBootstrap; is a helper class that sets up a server. You can &ServerBootstrap; is a helper class that sets up a server. You can
set up the server by yourself using a &Channel; directly, but it's a set up the server using a &Channel; directly. However, please note
tedious process and you won't need to do that in most cases. that this is a tedious process and you do not need to do that in most
cases.
</para> </para>
</callout> </callout>
<callout arearefs="example.discard2.co3"> <callout arearefs="example.discard2.co3">
@ -192,18 +193,18 @@ public class DiscardServer {
<literal>keepAlive</literal>. Please note that the <literal>keepAlive</literal>. Please note that the
<literal>"child."</literal> prefix was added to all options. It <literal>"child."</literal> prefix was added to all options. It
means the options will be applied to the accepted &Channel;s instead means the options will be applied to the accepted &Channel;s instead
of the options of the &ServerSocketChannel;. You could do like the following: of the options of the &ServerSocketChannel;. You could do the
following to set the options of the &ServerSocketChannel;:
<programlisting>bootstrap.setOption("reuseAddress", true);</programlisting> <programlisting>bootstrap.setOption("reuseAddress", true);</programlisting>
to set the options of the &ServerSocketChannel;.
</para> </para>
</callout> </callout>
<callout arearefs="example.discard2.co5"> <callout arearefs="example.discard2.co5">
<para> <para>
We are ready to go now. What's left is to bind to the port and to We are ready to go now. What's left is to bind to the port and to
start the server. Here, we bind to the port <literal>8080</literal> start the server. Here, we bind to the port <literal>8080</literal>
of all NICs (network interface cards) in the machine. You are fine of all NICs (network interface cards) in the machine. You can now
to call the <methodname>bind</methodname> method as many times as call the <methodname>bind</methodname> method as many times as
you want, with different bind addresses. you want (with different bind addresses.)
</para> </para>
</callout> </callout>
</calloutlist> </calloutlist>
@ -215,21 +216,21 @@ public class DiscardServer {
<section> <section>
<title>Looking into the Received Data</title> <title>Looking into the Received Data</title>
<para> <para>
Now that we wrote our first server, we need to test if it really works. Now that we have written our first server, we need to test if it really
The easiest way to test it is to use the <command>telnet</command> works. The easiest way to test it is to use the <command>telnet</command>
command. For example, you could enter "<command>telnet localhost command. For example, you could enter "<command>telnet localhost
8080</command>" in the command line and type something. 8080</command>" in the command line and type something.
</para> </para>
<para> <para>
However, can we say that the server is working fine? We can't really However, can we say that the server is working fine? We cannot really
know that because it's a discard server. You will not going to get any know that because it is a discard server. You will not get any response
response at all. To prove it's really working, let's modify the server at all. To prove it is really working, let us modify the server to print
to print what it has received. what it has received.
</para> </para>
<para> <para>
We already know that &MessageEvent; is generated whenever data is We already know that &MessageEvent; is generated whenever data is
received and the <methodname>messageReceived</methodname> handler method received and the <methodname>messageReceived</methodname> handler method
will be invoked. Let's put some code into the will be invoked. Let us put some code into the
<methodname>messageReceived</methodname> method of the <methodname>messageReceived</methodname> method of the
<classname>DiscardServerHandler</classname>: <classname>DiscardServerHandler</classname>:
</para> </para>
@ -246,7 +247,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
It is safe to assume the message type in socket transports is always It is safe to assume the message type in socket transports is always
&ChannelBuffer;. &ChannelBuffer; is a fundamental data structure &ChannelBuffer;. &ChannelBuffer; is a fundamental data structure
which stores a sequence of bytes in Netty. It's similar to NIO which stores a sequence of bytes in Netty. It's similar to NIO
<classname>ByteBuffer</classname>, but it's easier to use and more <classname>ByteBuffer</classname>, but it is easier to use and more
flexible. For example, Netty allows you to create a composite flexible. For example, Netty allows you to create a composite
&ChannelBuffer; which combines multiple &ChannelBuffer;s reducing &ChannelBuffer; which combines multiple &ChannelBuffer;s reducing
the number of unnecessary memory copy. the number of unnecessary memory copy.
@ -273,7 +274,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
<title>Writing an Echo Server</title> <title>Writing an Echo Server</title>
<para> <para>
So far, we have been consuming data without responding at all. A server, So far, we have been consuming data without responding at all. A server,
however, is usually supposed to respond to a request. Let's learn how to however, is usually supposed to respond to a request. Let us learn how to
write a response message to a client by implementing the write a response message to a client by implementing the
<ulink url="http://tools.ietf.org/html/rfc862">ECHO</ulink> protocol, <ulink url="http://tools.ietf.org/html/rfc862">ECHO</ulink> protocol,
where any received data is sent back. where any received data is sent back.
@ -281,9 +282,8 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
<para> <para>
The only difference from the discard server we have implemented in the The only difference from the discard server we have implemented in the
previous sections is that it sends the received data back instead of previous sections is that it sends the received data back instead of
printing the received data out to the console. Therefore, it's just printing the received data out to the console. Therefore, it is enough
enough again to modify the <methodname>messageReceived</methodname> again to modify the <methodname>messageReceived</methodname> method:
method:
</para> </para>
<programlisting>@Override <programlisting>@Override
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
@ -324,10 +324,10 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
</para> </para>
<para> <para>
Because we are going to ignore any received data but to send a message Because we are going to ignore any received data but to send a message
as soon as a connection is established, we can't use the as soon as a connection is established, we cannot use the
<methodname>messageReceived</methodname> method this time. Instead, <methodname>messageReceived</methodname> method this time. Instead,
we should override the <methodname>channelConnected</methodname> method. we should override the <methodname>channelConnected</methodname> method.
Here's the implementation: The following is the implementation:
</para> </para>
<programlisting>package org.jboss.netty.example.time; <programlisting>package org.jboss.netty.example.time;
@ -361,7 +361,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
<callout arearefs="example.time.co1"> <callout arearefs="example.time.co1">
<para> <para>
As explained, <methodname>channelConnected</methodname> method will As explained, <methodname>channelConnected</methodname> method will
be invoked when a connection is established. Let's write the 32-bit be invoked when a connection is established. Let us write the 32-bit
integer that represents the current time in seconds here. integer that represents the current time in seconds here.
</para> </para>
</callout> </callout>
@ -373,11 +373,11 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
<literal>4</literal> bytes. The &ChannelBuffers; helper class is <literal>4</literal> bytes. The &ChannelBuffers; helper class is
used to allocate a new buffer. Besides the used to allocate a new buffer. Besides the
<methodname>buffer</methodname> method, &ChannelBuffers; provides a <methodname>buffer</methodname> method, &ChannelBuffers; provides a
lot of useful methods related with &ChannelBuffer;. Please refer to lot of useful methods related to the &ChannelBuffer;. For more
the API reference. information, please refer to the API reference.
</para> </para>
<para> <para>
On the other hand, it's a good idea to use static imports for On the other hand, it is a good idea to use static imports for
&ChannelBuffers;: &ChannelBuffers;:
<programlisting>import static org.jboss.netty.buffer.&ChannelBuffers;.*; <programlisting>import static org.jboss.netty.buffer.&ChannelBuffers;.*;
... ...
@ -392,19 +392,19 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
<para> <para>
But wait, where's the <methodname>flip</methodname>? Didn't we used But wait, where's the <methodname>flip</methodname>? Didn't we used
to call <methodname>ByteBuffer.flip()</methodname> before sending a to call <methodname>ByteBuffer.flip()</methodname> before sending a
message in NIO? &ChannelBuffer; doesn't have such a method because message in NIO? &ChannelBuffer; does not have such a method because
it has two pointers; one for read operations and the other for write it has two pointers; one for read operations and the other for write
operations. The writer index increases when you write something to operations. The writer index increases when you write something to
a &ChannelBuffer; while the reader index doesn't change. The reader a &ChannelBuffer; while the reader index does not change. The reader
index and the writer index represents where the message starts and index and the writer index represents where the message starts and
ends respectively. ends respectively.
</para> </para>
<para> <para>
In contrast, NIO buffer doesn't provide a clean way to figure out In contrast, NIO buffer does not provide a clean way to figure out
where the message content starts and ends without calling the where the message content starts and ends without calling the
<methodname>flip</methodname> method. You will be in trouble when <methodname>flip</methodname> method. You will be in trouble when
you forget to flip the buffer because nothing or incorrect data will you forget to flip the buffer because nothing or incorrect data will
be sent. Such an error doesn't happen in Netty because we have be sent. Such an error does not happen in Netty because we have
different pointer for different operation types. You will find it different pointer for different operation types. You will find it
makes your life much easier as you get used to it -- a life without makes your life much easier as you get used to it -- a life without
flipping out! flipping out!
@ -412,7 +412,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
<para> <para>
Another point to note is that the <methodname>write</methodname> Another point to note is that the <methodname>write</methodname>
method returns a &ChannelFuture;. A &ChannelFuture; represents an method returns a &ChannelFuture;. A &ChannelFuture; represents an
I/O operation which was not occurred yet. It means, any requested I/O operation which has not yet occurred. It means, any requested
operation might not have been performed yet because all operations operation might not have been performed yet because all operations
are asynchronous in Netty. For example, the following code might are asynchronous in Netty. For example, the following code might
close the connection even before a message is sent: close the connection even before a message is sent:
@ -424,7 +424,7 @@ ch.close();</programlisting>
Therefore, you need to call the <methodname>close</methodname> Therefore, you need to call the <methodname>close</methodname>
method after the &ChannelFuture;, which was returned by the method after the &ChannelFuture;, which was returned by the
<methodname>write</methodname> method, notifies you when the write <methodname>write</methodname> method, notifies you when the write
operation has been done. Also, <methodname>close</methodname> operation has been done. Please note that, <methodname>close</methodname>
might not close the connection immediately, and it returns a might not close the connection immediately, and it returns a
&ChannelFuture;. &ChannelFuture;.
</para> </para>
@ -432,7 +432,7 @@ ch.close();</programlisting>
<callout arearefs="example.time.co4"> <callout arearefs="example.time.co4">
<para> <para>
How do we get notified when the write request is finished then? How do we get notified when the write request is finished then?
It's as simple as adding a &ChannelFutureListener; to the returned This is as simple as adding a &ChannelFutureListener; to the returned
&ChannelFuture;. Here, we created a new anonymous &ChannelFutureListener; &ChannelFuture;. Here, we created a new anonymous &ChannelFutureListener;
which closes the &Channel; when the operation is done. which closes the &Channel; when the operation is done.
</para> </para>
@ -449,9 +449,9 @@ ch.close();</programlisting>
<title>Writing a Time Client</title> <title>Writing a Time Client</title>
<para> <para>
Unlike DISCARD and ECHO servers, we need a client for the TIME protocol Unlike DISCARD and ECHO servers, we need a client for the TIME protocol
because a human can't translate a 32-bit binary data into a date on a because a human cannot translate a 32-bit binary data into a date on a
calendar. Let's make sure the server works correctly and learn how to calendar. In this section, we discuss how to make sure the server works
write a client with Netty. correctly and learn how to write a client with Netty.
</para> </para>
<para> <para>
The biggest and only difference between a server and a client in Netty The biggest and only difference between a server and a client in Netty
@ -500,7 +500,7 @@ public class TimeClient {
<callout arearefs="example.time2.co3"> <callout arearefs="example.time2.co3">
<para> <para>
Please note that there's no <literal>"child."</literal> prefix. Please note that there's no <literal>"child."</literal> prefix.
A client-side &SocketChannel; doesn't have a parent. A client-side &SocketChannel; does not have a parent.
</para> </para>
</callout> </callout>
<callout arearefs="example.time2.co4"> <callout arearefs="example.time2.co4">
@ -511,10 +511,10 @@ public class TimeClient {
</callout> </callout>
</calloutlist> </calloutlist>
<para> <para>
As you saw, it's not really different from the server side startup. What As you can see, it is not really different from the server side startup.
about the &ChannelHandler; implementation? It should receive a 32-bit What about the &ChannelHandler; implementation? It should receive a
integer from the server, translate it into a human readable format, print 32-bit integer from the server, translate it into a human readable format,
the translated time, and close the connection: print the translated time, and close the connection:
</para> </para>
<programlisting>package org.jboss.netty.example.time; <programlisting>package org.jboss.netty.example.time;
@ -536,10 +536,10 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
} }
}</programlisting> }</programlisting>
<para> <para>
It looks very simple and doesn't look any different from the server side It looks very simple and does not look any different from the server side
example. However, this handler sometimes will refuse to work raising an example. However, this handler sometimes will refuse to work raising an
<exceptionname>IndexOutOfBoundsException</exceptionname>. Let's figure <exceptionname>IndexOutOfBoundsException</exceptionname>. We discuss why
out why in the next section. this happens in the next section.
</para> </para>
</section> </section>
@ -554,20 +554,20 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
<para> <para>
In a stream-based transport such as TCP/IP, packets can be fragmented and In a stream-based transport such as TCP/IP, packets can be fragmented and
reassembled during transmission even in a LAN environment. For example, reassembled during transmission even in a LAN environment. For example,
let's assume you have received three packets: let us assume you have received three packets:
</para> </para>
<programlisting>+-----+-----+-----+ <programlisting>+-----+-----+-----+
| ABC | DEF | GHI | | ABC | DEF | GHI |
+-----+-----+-----+</programlisting> +-----+-----+-----+</programlisting>
<para> <para>
because of the packet fragmentation, a server can receive them like the because of the packet fragmentation, a server can receive them as
following: follows:
</para> </para>
<programlisting>+----+-------+---+---+ <programlisting>+----+-------+---+---+
| AB | CDEFG | H | I | | AB | CDEFG | H | I |
+----+-------+---+---+</programlisting> +----+-------+---+---+</programlisting>
<para> <para>
Therefore, a receiving part, regardless it's server-side or client-side, Therefore, a receiving part, regardless it is server-side or client-side,
should defrag the received packets into one or more meaningful should defrag the received packets into one or more meaningful
<firstterm>frames</firstterm> that could be easily understood by the <firstterm>frames</firstterm> that could be easily understood by the
application logic. In case of the example above, the received packets application logic. In case of the example above, the received packets
@ -582,17 +582,17 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
The First Solution The First Solution
</title> </title>
<para> <para>
Now let's get back to the TIME client example. We have the same problem Now let us get back to the TIME client example. We have the same problem
here. A 32-bit integer is a very small amount of data, and it is not here. A 32-bit integer is a very small amount of data, and it is not
likely to be fragmented often. However, the problem is that it likely to be fragmented often. However, the problem is that it
<emphasis>can</emphasis> be fragmented, and the possibility of <emphasis>can</emphasis> be fragmented, and the possibility of
fragmentation will increase as the traffic goes higher. fragmentation will increase as the traffic increases.
</para> </para>
<para> <para>
The simplistic solution is to create an internal cumulative buffer and The simplistic solution is to create an internal cumulative buffer and
wait until all 4 bytes are received into the internal buffer. Here's wait until all 4 bytes are received into the internal buffer. The
the modified <classname>TimeClientHandler</classname> implementation following is the modified <classname>TimeClientHandler</classname>
that fixes the problem. implementation that fixes the problem:
</para> </para>
<programlisting>package org.jboss.netty.example.time; <programlisting>package org.jboss.netty.example.time;
@ -630,7 +630,7 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
buffer and therefore cannot serve multiple &Channel;s. If an buffer and therefore cannot serve multiple &Channel;s. If an
instance of <classname>TimeClientHandler</classname> is shared by instance of <classname>TimeClientHandler</classname> is shared by
multiple &Channel;s (and consequently multiple &ChannelPipeline;s), multiple &Channel;s (and consequently multiple &ChannelPipeline;s),
the content of the <varname>buf</varname> will be messed up. the content of the <varname>buf</varname> will be corrupted.
</para> </para>
</callout> </callout>
<callout arearefs="example.time3.co2"> <callout arearefs="example.time3.co2">
@ -657,11 +657,11 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
</callout> </callout>
</calloutlist> </calloutlist>
<para> <para>
There's another place that needs a fix. Do you remember that we have There's another place that needs a fix. Do you remember that we
added a <classname>TimeClientHandler</classname> instance to the added a <classname>TimeClientHandler</classname> instance to the
<emphasis>default</emphasis> &ChannelPipeline; of the &ClientBootstrap;? <emphasis>default</emphasis> &ChannelPipeline; of the &ClientBootstrap;?
It means one <classname>TimeClientHandler</classname> is going to handle It means one <classname>TimeClientHandler</classname> is going to handle
multiple &Channel;s and consequently the data will be messed up. To multiple &Channel;s and consequently the data will be corrupted. To
create a new <classname>TimeClientHandler</classname> instance per create a new <classname>TimeClientHandler</classname> instance per
&Channel;, we should implement a &ChannelPipelineFactory;: &Channel;, we should implement a &ChannelPipelineFactory;:
</para> </para>
@ -676,7 +676,7 @@ public class TimeClientPipelineFactory implements &ChannelPipelineFactory; {
} }
}</programlisting> }</programlisting>
<para> <para>
Now let's replace the following lines of <classname>TimeClient</classname>: Now let us replace the following lines of <classname>TimeClient</classname>:
</para> </para>
<programlisting>TimeClientHandler handler = new TimeClientHandler(); <programlisting>TimeClientHandler handler = new TimeClientHandler();
bootstrap.getPipeline().addLast("handler", handler);</programlisting> bootstrap.getPipeline().addLast("handler", handler);</programlisting>
@ -702,17 +702,17 @@ bootstrap.getPipeline().addLast("handler", handler);</programlisting>
</title> </title>
<para> <para>
Although the first solution has resolved the problem with the TIME Although the first solution has resolved the problem with the TIME
client, the modified handler doesn't look that clean. Imagine a more client, the modified handler does not look that clean. Imagine a more
complicated protocol which is composed of multiple fields such as a complicated protocol which is composed of multiple fields such as a
variable length field. Your &ChannelHandler; implementation will variable length field. Your &ChannelHandler; implementation will
become unmaintainable very quickly. become unmaintainable very quickly.
</para> </para>
<para> <para>
As you already might have noticed, you can add more than one As you may have noticed, you can add more than one &ChannelHandler; to
&ChannelHandler; to a &ChannelPipeline;, and therefore, you can a &ChannelPipeline;, and therefore, you can split one monolithic
split one monolithic &ChannelHandler; into multiple modular ones to &ChannelHandler; into multiple modular ones to reduce the complexity of
reduce the complexity of your application. For example, you could your application. For example, you could split
split <classname>TimeClientHandler</classname> into two handlers: <classname>TimeClientHandler</classname> into two handlers:
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
@ -764,8 +764,8 @@ public class TimeDecoder extends &FrameDecoder; {
<callout arearefs="example.time4.co3"> <callout arearefs="example.time4.co3">
<para> <para>
If <literal>null</literal> is returned, it means there's not If <literal>null</literal> is returned, it means there's not
enough data yet. &FrameDecoder; will call again when more data is enough data yet. &FrameDecoder; will call again when there is a
in. sufficient amount of data.
</para> </para>
</callout> </callout>
<callout arearefs="example.time4.co3"> <callout arearefs="example.time4.co3">
@ -781,7 +781,7 @@ public class TimeDecoder extends &FrameDecoder; {
</callout> </callout>
</calloutlist> </calloutlist>
<para> <para>
If you are a adventurous person, you might want to try the If you are an adventurous person, you might want to try the
&ReplayingDecoder; which simplifies the decoder even more. You will &ReplayingDecoder; which simplifies the decoder even more. You will
need to consult the API reference for more information though. need to consult the API reference for more information though.
</para> </para>
@ -798,10 +798,9 @@ public class TimeDecoder extends &ReplayingDecoder;&lt;&VoidEnum;&gt; {
}</programlisting> }</programlisting>
<para> <para>
Additionally, Netty provides out-of-the-box decoders which enables Additionally, Netty provides out-of-the-box decoders which enables
you to implement most protocol very easily and helps you avoid from you to implement most protocols very easily and helps you avoid from
ending up with a monolithic unmaintainable handler implementation. ending up with a monolithic unmaintainable handler implementation.
You might want to take a look into the following packages for more Please refer to the following packages for more detailed examples:
detailed examples:
<itemizedlist> <itemizedlist>
<listitem> <listitem>
<para> <para>
@ -825,9 +824,9 @@ public class TimeDecoder extends &ReplayingDecoder;&lt;&VoidEnum;&gt; {
Speaking in POJO instead of ChannelBuffer Speaking in POJO instead of ChannelBuffer
</title> </title>
<para> <para>
All the examples we visited so far used a &ChannelBuffer; as a primary All the examples we have reviewed so far used a &ChannelBuffer; as a
data structure of a protocol message. In this section, we will improve primary data structure of a protocol message. In this section, we will
the TIME protocol client and server example to use a improve the TIME protocol client and server example to use a
<ulink url="http://en.wikipedia.org/wiki/POJO">POJO</ulink> instead of a <ulink url="http://en.wikipedia.org/wiki/POJO">POJO</ulink> instead of a
&ChannelBuffer;. &ChannelBuffer;.
</para> </para>
@ -836,12 +835,12 @@ public class TimeDecoder extends &ReplayingDecoder;&lt;&VoidEnum;&gt; {
your handler becomes more maintainable and reusable by separating the your handler becomes more maintainable and reusable by separating the
code which extracts information from &ChannelBuffer; out from the code which extracts information from &ChannelBuffer; out from the
handler. In the TIME client and server examples, we read only one handler. In the TIME client and server examples, we read only one
32-bit integer and it's not a big deal to use &ChannelBuffer; directly. 32-bit integer and it is not a major issue to use &ChannelBuffer; directly.
However, you will find it is necessary to make the separation as you However, you will find it is necessary to make the separation as you
implement a real world protocol. implement a real world protocol.
</para> </para>
<para> <para>
First, let's define a new type called <classname>UnixTime</classname>. First, let us define a new type called <classname>UnixTime</classname>.
</para> </para>
<programlisting>package org.jboss.netty.example.time; <programlisting>package org.jboss.netty.example.time;
@ -887,7 +886,7 @@ protected Object decode(
</calloutlist> </calloutlist>
<para> <para>
With the updated decoder, the <classname>TimeClientHandler</classname> With the updated decoder, the <classname>TimeClientHandler</classname>
doesn't use &ChannelBuffer; anymore: does not use &ChannelBuffer; anymore:
</para> </para>
<programlisting>@Override <programlisting>@Override
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
@ -897,7 +896,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
}</programlisting> }</programlisting>
<para> <para>
Much simpler and elegant, right? The same technique can be applied on Much simpler and elegant, right? The same technique can be applied on
the server side. Let's update the the server side. Let us update the
<classname>TimeServerHandler</classname> first this time: <classname>TimeServerHandler</classname> first this time:
</para> </para>
<programlisting>@Override <programlisting>@Override
@ -963,7 +962,7 @@ public class TimeEncoder implements &SimpleChannelHandler; {
in the &ChannelPipeline;. in the &ChannelPipeline;.
</para> </para>
<para> <para>
On the other hand, it's a good idea to use static imports for On the other hand, it is a good idea to use static imports for
&Channels;: &Channels;:
<programlisting>import static org.jboss.netty.channel.&Channels;.*; <programlisting>import static org.jboss.netty.channel.&Channels;.*;
... ...
@ -975,7 +974,7 @@ fireChannelDisconnected(ctx, e.getChannel());</programlisting>
</calloutlist> </calloutlist>
<para> <para>
The last task left is to insert a <classname>TimeEncoder</classname> The last task left is to insert a <classname>TimeEncoder</classname>
into the &ChannelPipeline; on the server side, and it's left as a into the &ChannelPipeline; on the server side, and it is left as a
trivial exercise. trivial exercise.
</para> </para>
</section> </section>
@ -985,12 +984,11 @@ fireChannelDisconnected(ctx, e.getChannel());</programlisting>
Summary Summary
</title> </title>
<para> <para>
In this chapter, we had a quick tour of Netty so that you can start to In this chapter, we had a quick tour of Netty with a demonstration on how
write a network application on top of Netty right away. I believe you to write a network application on top of Netty. More questions you may
still have a lot of questions about various topics, and they will be have will be covered in the upcoming chapters and the revised version of
covered in the upcoming chapters and the revised version of this chapter. this chapter. Please also note that <ulink url="&Community;">the Netty
Also, please don't forget that <ulink url="&Community;">the Netty project project community</ulink> is always here to help you.
community</ulink> is always here to help you.
</para> </para>
</section> </section>
</chapter> </chapter>