* More comments on examples

* Added JavaDoc for ChannelPipelineCoverage which is somewhat difficult to understand.
This commit is contained in:
Trustin Lee 2008-08-12 11:58:15 +00:00
parent 5e99787df6
commit 93f76ce594
36 changed files with 357 additions and 41 deletions

View File

@ -30,6 +30,31 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies if the same instance of the annotated {@link ChannelHandler} type
* can be added to more than one {@link ChannelPipeline}.
* <p>
* All handler types are expected to specify this annotation. Otherwise you
* will be warned in runtime. Only two values are allowed for this annotation:
* {@code "all"} and {@code "one"}.
* <p>
* Please note that this annotation doesn't prevent a handler annotated with
* the value {@code "one"} from being added to more than one pipeline. This
* annotation is used for documentation purpose only.
*
* <h3>{@code ChannelPipelineCoverage("all")}</h3>
*
* {@code "all"} means you can add the same instance of the annotated handler
* type to more than one {@link ChannelPipeline}. It means the member
* variables of the handler instance is shared among multiple channels and it
* is OK to do so.
*
* <h3>{@code ChannelPipelineCoverage("one")}</h3>
*
* {@code "one"} means you must create a new instance of the annotated handler
* type for each new channel. It means the member variables of the handler
* instance can not be shared at all, and violating this contract will lead
* the handler to a race condition.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
@ -42,8 +67,19 @@ import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ChannelPipelineCoverage {
/**
* {@code "all"}
*/
public static final String ALL = "all";
/**
* {@code "one"}
*/
public static final String ONE = "one";
/**
* The value of this annotation
*/
String value();
}

View File

@ -59,7 +59,7 @@ public class DiscardClient {
firstMessageSize = 256;
}
// Start client.
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -72,6 +72,7 @@ public class DiscardClient {
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
// Start the connection attempt.
bootstrap.connect(new InetSocketAddress(host, port));
}
}

View File

@ -39,6 +39,7 @@ import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* Handles a client-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
@ -106,6 +107,10 @@ public class DiscardClientHandler extends SimpleChannelHandler {
}
private void generateTraffic(ChannelStateEvent e) {
// Keep generating traffic until the channel is unwritable.
// A channel becomes unwritable when its internal buffer is full.
// If you keep writing messages ignoring this property,
// you will end up with an OutOfMemoryError.
Channel channel = e.getChannel();
while (channel.isWritable()) {
ChannelBuffer m = nextMessage();

View File

@ -40,7 +40,7 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
public class DiscardServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -53,6 +53,7 @@ public class DiscardServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
// Start performance monitor.

View File

@ -36,6 +36,7 @@ import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* Handles a server-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)

View File

@ -22,12 +22,20 @@
*/
package org.jboss.netty.example.discard;
/**
* Measures and prints the current throughput every 3 seconds.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class ThroughputMonitor extends Thread {
private final DiscardServerHandler handler;
public ThroughputMonitor(DiscardServerHandler echoHandler) {
this.handler = echoHandler;
public ThroughputMonitor(DiscardServerHandler handler) {
this.handler = handler;
}
@Override

View File

@ -29,6 +29,16 @@ import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* Sends one message when a connection is open and echoes back any received
* data to the server.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class EchoClient {
public static void main(String[] args) throws Exception {
@ -51,7 +61,7 @@ public class EchoClient {
firstMessageSize = 256;
}
// Start client.
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -64,6 +74,7 @@ public class EchoClient {
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
// Start the connection attempt.
bootstrap.connect(new InetSocketAddress(host, port));
// Start performance monitor.

View File

@ -36,6 +36,15 @@ import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* Handles both client-side and server-side handler depending on which
* constructor was called.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class EchoHandler extends SimpleChannelHandler {
@ -45,12 +54,18 @@ public class EchoHandler extends SimpleChannelHandler {
private final ChannelBuffer firstMessage;
private final AtomicLong transferredBytes = new AtomicLong();
/**
* Creates a server-side handler.
*/
public EchoHandler() {
this(0);
firstMessage = ChannelBuffer.EMPTY_BUFFER;
}
/**
* Creates a client-side handler.
*/
public EchoHandler(int firstMessageSize) {
if (firstMessageSize < 0) {
if (firstMessageSize <= 0) {
throw new IllegalArgumentException(
"firstMessageSize: " + firstMessageSize);
}
@ -70,18 +85,23 @@ public class EchoHandler extends SimpleChannelHandler {
if (e instanceof ChannelStateEvent) {
logger.info(e.toString());
}
// Let SimpleChannelHandler call actual event handler methods below.
super.handleUpstream(ctx, e);
}
@Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) {
// Send the first message. Server will not send anything here
// because the firstMessage's capacity is 0.
e.getChannel().write(firstMessage);
}
@Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) {
// Send back the received message to the remote peer.
transferredBytes.addAndGet(((ChannelBuffer) e.getMessage()).readableBytes());
e.getChannel().write(e.getMessage());
}
@ -89,6 +109,7 @@ public class EchoHandler extends SimpleChannelHandler {
@Override
public void exceptionCaught(
ChannelHandlerContext ctx, ExceptionEvent e) {
// Close the connection when an exception is raised.
logger.log(
Level.WARNING,
"Unexpected exception from downstream.",

View File

@ -29,10 +29,19 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* Echoes back any received data from a client.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class EchoServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -45,6 +54,7 @@ public class EchoServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
// Start performance monitor.

View File

@ -22,17 +22,25 @@
*/
package org.jboss.netty.example.echo;
/**
* Measures and prints the current throughput every 3 seconds.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class ThroughputMonitor extends Thread {
private final EchoHandler echoHandler;
private final EchoHandler handler;
public ThroughputMonitor(EchoHandler echoHandler) {
this.echoHandler = echoHandler;
public ThroughputMonitor(EchoHandler handler) {
this.handler = handler;
}
@Override
public void run() {
long oldCounter = echoHandler.getTransferredBytes();
long oldCounter = handler.getTransferredBytes();
long startTime = System.currentTimeMillis();
for (;;) {
try {
@ -42,7 +50,7 @@ public class ThroughputMonitor extends Thread {
}
long endTime = System.currentTimeMillis();
long newCounter = echoHandler.getTransferredBytes();
long newCounter = handler.getTransferredBytes();
System.err.format(
"%4.3f MiB/s%n",
(newCounter - oldCounter) * 1000 / (endTime - startTime) /

View File

@ -30,28 +30,35 @@ import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
/**
* Decodes the binary representation of a {@link BigInteger} with 32-bit
* integer length prefix into a Java {@link BigInteger} instance. For example,
* { 0, 0, 0, 1, 42 } will be decoded into new BigInteger("42").
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class BigIntegerDecoder extends FrameDecoder {
@Override
protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
// Wait until the length prefix is available.
if (buffer.readableBytes() < 4) {
return null;
}
int dataLength = buffer.getInt(buffer.readerIndex());
// Wait until the whole data is available.
if (buffer.readableBytes() < dataLength + 4) {
return null;
}
// Skip the length field because we know it already.
buffer.skipBytes(4);
// Convert the received data into a new BigInteger.
byte[] decoded = new byte[dataLength];
buffer.readBytes(decoded);

View File

@ -30,6 +30,15 @@ import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* Sends a sequence of integers to a {@link FactorialServer} to calculate
* the factorial of the specified integer.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class FactorialClient {
public static void main(String[] args) throws Exception {

View File

@ -39,12 +39,27 @@ import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
@ChannelPipelineCoverage("one")
/**
* Handler for a client-side channel. Please note that this handler's
* {@link ChannelPipelineCoverage} annotation value is "one". It means
* this handler maintains some stateful information which is specific to
* a certain channel. Therefore, an instance of this handler can
* cover only one ChannelPipeline and Channel pair. You have to create
* a new handler instance whenever you create a new channel and insert
* this handler to avoid a race condition.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("one") // <-- HERE
public class FactorialClientHandler extends SimpleChannelHandler {
private static final Logger logger = Logger.getLogger(
FactorialClientHandler.class.getName());
// Stateful properties
private int i = 1;
private int receivedMessages = 0;
private final int count;

View File

@ -28,11 +28,12 @@ import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
/**
* Creates a newly configured {@link ChannelPipeline} for a client-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class FactorialClientPipelineFactory implements
ChannelPipelineFactory {

View File

@ -23,11 +23,12 @@
package org.jboss.netty.example.factorial;
/**
* Thrown when there was a protocol violation during communication.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class FactorialProtocolException extends Exception {

View File

@ -29,10 +29,19 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* Receives a sequence of integers from a {@link FactorialClient} to calculate
* the factorial of the specified integer.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class FactorialServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -44,6 +53,7 @@ public class FactorialServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
}
}

View File

@ -35,12 +35,27 @@ import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
@ChannelPipelineCoverage("one")
/**
* Handler for a server-side channel. Please note that this handler's
* {@link ChannelPipelineCoverage} annotation value is "one". It means
* this handler maintains some stateful information which is specific to
* a certain channel. Therefore, an instance of this handler can
* cover only one ChannelPipeline and Channel pair. You have to create
* a new handler instance whenever you create a new channel and insert
* this handler to avoid a race condition.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("one") // <-- HERE
public class FactorialServerHandler extends SimpleChannelHandler {
private static final Logger logger = Logger.getLogger(
FactorialServerHandler.class.getName());
// Stateful properties.
private int lastMultiplier = 1;
private BigInteger factorial = new BigInteger(new byte[] { 1 });

View File

@ -28,6 +28,8 @@ import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
/**
* Creates a newly configured {@link ChannelPipeline} for a server-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
@ -45,6 +47,8 @@ public class FactorialServerPipelineFactory implements
pipeline.addLast("encoder", new NumberEncoder());
// and then business logic.
// Please note we create a handler for every new channel
// because it has stateful properties.
pipeline.addLast("handler", new FactorialServerHandler());
return pipeline;

View File

@ -35,6 +35,9 @@ import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.MessageEvent;
/**
* Encodes a {@link Number} into the binary representation with a 32-bit length
* prefix. For example, 42 will be encoded to { 0, 0, 0, 1, 42 }.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*

View File

@ -28,7 +28,16 @@ import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.example.echo.EchoClient;
/**
* Modification of {@link EchoClient} which utilizes Java object serialization.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class ObjectEchoClient {
public static void main(String[] args) throws Exception {
@ -51,7 +60,7 @@ public class ObjectEchoClient {
firstMessageSize = 256;
}
// Start client.
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -64,6 +73,7 @@ public class ObjectEchoClient {
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
// Start the connection attempt.
bootstrap.connect(new InetSocketAddress(host, port));
// Start performance monitor.

View File

@ -39,6 +39,15 @@ import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.serialization.ObjectDecoder;
import org.jboss.netty.handler.codec.serialization.ObjectEncoder;
/**
* Handles both client-side and server-side handler depending on which
* constructor was called.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class ObjectEchoHandler extends SimpleChannelHandler {
@ -48,12 +57,18 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
private final List<Integer> firstMessage;
private final AtomicLong transferredMessages = new AtomicLong();
/**
* Creates a server-side handler.
*/
public ObjectEchoHandler() {
this(0);
firstMessage = new ArrayList<Integer>();
}
/**
* Creates a client-side handler.
*/
public ObjectEchoHandler(int firstMessageSize) {
if (firstMessageSize < 0) {
if (firstMessageSize <= 0) {
throw new IllegalArgumentException(
"firstMessageSize: " + firstMessageSize);
}
@ -80,6 +95,8 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
@Override
public void channelOpen(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
// Add encoder and decoder as soon as a new channel is created so that
// a Java object is serialized and deserialized.
e.getChannel().getPipeline().addFirst("encoder", new ObjectEncoder());
e.getChannel().getPipeline().addFirst("decoder", new ObjectDecoder());
}
@ -87,6 +104,7 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
@Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) {
// Send the first message if this handler is a client-side handler.
if (!firstMessage.isEmpty()) {
e.getChannel().write(firstMessage);
}
@ -95,6 +113,7 @@ public class ObjectEchoHandler extends SimpleChannelHandler {
@Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) {
// Echo back the received object to the client.
transferredMessages.incrementAndGet();
e.getChannel().write(e.getMessage());
}

View File

@ -28,11 +28,20 @@ import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.example.echo.EchoServer;
/**
* Modification of {@link EchoServer} which utilizes Java object serialization.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class ObjectEchoServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -45,6 +54,7 @@ public class ObjectEchoServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
// Start performance monitor.

View File

@ -22,17 +22,25 @@
*/
package org.jboss.netty.example.objectecho;
/**
* Measures and prints the current throughput every 3 seconds.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class ThroughputMonitor extends Thread {
private final ObjectEchoHandler echoHandler;
private final ObjectEchoHandler handler;
public ThroughputMonitor(ObjectEchoHandler echoHandler) {
this.echoHandler = echoHandler;
public ThroughputMonitor(ObjectEchoHandler handler) {
this.handler = handler;
}
@Override
public void run() {
long oldCounter = echoHandler.getTransferredMessages();
long oldCounter = handler.getTransferredMessages();
long startTime = System.currentTimeMillis();
for (;;) {
try {
@ -42,7 +50,7 @@ public class ThroughputMonitor extends Thread {
}
long endTime = System.currentTimeMillis();
long newCounter = echoHandler.getTransferredMessages();
long newCounter = handler.getTransferredMessages();
System.err.format(
"%4.3f msgs/s%n",
(newCounter - oldCounter) * 1000 / (double) (endTime - startTime));

View File

@ -32,7 +32,17 @@ import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.example.telnet.TelnetClient;
/**
* Simple SSL chat client modified from {@link TelnetClient}.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class SecureChatClient {
public static void main(String[] args) throws Exception {
@ -48,7 +58,7 @@ public class SecureChatClient {
String host = args[0];
int port = Integer.parseInt(args[1]);
// Start client.
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -61,6 +71,7 @@ public class SecureChatClient {
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
// Wait until the connection attempt succeeds or fails.
@ -86,6 +97,8 @@ public class SecureChatClient {
lastWriteFuture.awaitUninterruptibly();
}
// Close the connection. Make sure the close operation ends because
// all I/O operations are asynchronous in Netty.
channel.close().awaitUninterruptibly();
// We should shut down all thread pools here to exit normally.

View File

@ -34,6 +34,14 @@ import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.ssl.SslHandler;
/**
* Handles a client-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class SecureChatClientHandler extends SimpleChannelHandler {
@ -52,9 +60,11 @@ public class SecureChatClientHandler extends SimpleChannelHandler {
@Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Get the SslHandler and begin handshake ASAP.
// Get the SslHandler from the pipeline
// which were added in SecureChatPipelineFactory.
SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
// Begin handshake.
sslHandler.handshake(e.getChannel());
}

View File

@ -26,11 +26,13 @@ import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
* A bogus key store which provides all the required information to
* create an example SSL connection.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class SecureChatKeyStore {
private static final short[] DATA = new short[] {

View File

@ -36,6 +36,8 @@ import org.jboss.netty.handler.codec.string.StringEncoder;
import org.jboss.netty.handler.ssl.SslHandler;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
@ -55,8 +57,11 @@ public class SecureChatPipelineFactory implements
ChannelPipeline pipeline = pipeline();
// Add SSL handler first to encrypt and decrypt everything.
// In this example, we use a bogus certificate in the server side
// and accept any invalid certificates in the client side.
// You will need something more complicated to identify both
// and server in the real world.
SSLEngine engine;
if (handler instanceof SecureChatClientHandler) {
engine = SecureChatSslContextFactory.getClientContext().createSSLEngine();

View File

@ -28,11 +28,20 @@ import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.example.telnet.TelnetServer;
/**
* Simple SSL chat server modified from {@link TelnetServer}.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class SecureChatServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -45,6 +54,7 @@ public class SecureChatServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
}
}

View File

@ -41,6 +41,14 @@ import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.util.MapBackedSet;
/**
* Handles a server-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class SecureChatServerHandler extends SimpleChannelHandler {
@ -64,6 +72,7 @@ public class SecureChatServerHandler extends SimpleChannelHandler {
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Get the SslHandler in the current pipeline.
// We added it in SecureChatPipelineFactory.
final SslHandler sslHandler = ctx.getPipeline().get(SslHandler.class);
// Get notified when SSL handshake is done.
@ -113,7 +122,7 @@ public class SecureChatServerHandler extends SimpleChannelHandler {
}
}
// Close the connection if the client sent 'bye'.
// Close the connection if the client has sent 'bye'.
if (request.toLowerCase().equals("bye")) {
e.getChannel().close();
}

View File

@ -29,11 +29,16 @@ import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
/**
* Creates a bogus {@link SSLContext}. A client-side context created by this
* factory accepts any certificate even if it's invalid. A server-side context
* created by this factory sends a bogus certificate defined in {@link SecureChatKeyStore}.
*
* You will have to create your context differently in a real world application.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class SecureChatSslContextFactory {

View File

@ -34,11 +34,13 @@ import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager;
/**
* Bogus {@link TrustManagerFactorySpi} which accepts any certificate
* even if it's invalid.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*
*/
public class SecureChatTrustManagerFactory extends TrustManagerFactorySpi {

View File

@ -33,6 +33,14 @@ import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
/**
* Simplistic telnet client.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class TelnetClient {
public static void main(String[] args) throws Exception {
@ -48,7 +56,7 @@ public class TelnetClient {
String host = args[0];
int port = Integer.parseInt(args[1]);
// Start client.
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -61,6 +69,7 @@ public class TelnetClient {
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
// Start the connection attempt.
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
// Wait until the connection attempt succeeds or fails.
@ -78,6 +87,8 @@ public class TelnetClient {
if (line == null) {
break;
}
// Sends the received line to the server.
lastWriteFuture = channel.write(line + '\n');
}
@ -86,6 +97,8 @@ public class TelnetClient {
lastWriteFuture.awaitUninterruptibly();
}
// Close the connection. Make sure the close operation ends because
// all I/O operations are asynchronous in Netty.
channel.close().awaitUninterruptibly();
// We should shut down all thread pools here to exit normally.

View File

@ -33,6 +33,14 @@ import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* Handles a client-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class TelnetClientHandler extends SimpleChannelHandler {
@ -51,6 +59,7 @@ public class TelnetClientHandler extends SimpleChannelHandler {
@Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) {
// Print out the line received from the server.
System.err.println(e.getMessage());
}

View File

@ -33,6 +33,8 @@ import org.jboss.netty.handler.codec.string.StringDecoder;
import org.jboss.netty.handler.codec.string.StringEncoder;
/**
* Creates a newly configured {@link ChannelPipeline} for a new channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
@ -49,9 +51,10 @@ public class TelnetPipelineFactory implements
}
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = pipeline();
// Add the text line codec first,
// Add the text line codec combination first,
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(
8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());

View File

@ -29,10 +29,18 @@ import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
/**
* Simplistic telnet server.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
public class TelnetServer {
public static void main(String[] args) throws Exception {
// Start server.
// Configure the server.
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
@ -45,6 +53,7 @@ public class TelnetServer {
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
}
}

View File

@ -37,6 +37,14 @@ import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
/**
* Handles a server-side channel.
*
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Trustin Lee (tlee@redhat.com)
*
* @version $Rev$, $Date$
*/
@ChannelPipelineCoverage("all")
public class TelnetServerHandler extends SimpleChannelHandler {
@ -55,7 +63,7 @@ public class TelnetServerHandler extends SimpleChannelHandler {
@Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Send greeting.
// Send greeting for a new connection.
e.getChannel().write(
"Welcome to " + InetAddress.getLocalHost().getHostName() + "!\n");
e.getChannel().write("It's " + new Date() + " now.\n");
@ -65,7 +73,8 @@ public class TelnetServerHandler extends SimpleChannelHandler {
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) {
// Convert to a String first.
// Cast to a String first.
// We know it's a String because we put some codec in TelnetPipelineFactory.
String request = (String) e.getMessage();
// Generate and write a response.
@ -80,9 +89,12 @@ public class TelnetServerHandler extends SimpleChannelHandler {
response = "Did you say '" + request + "'?\n";
}
// We don't need to write a ChannelBuffer here.
// We know the encoder inserted at TelnetPipelineFactory will do the conversion.
ChannelFuture future = e.getChannel().write(response);
// Close the connection after sending 'Have a good day!'
// if the client has sent 'bye'.
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}